diff --git a/kjvstudy_org/routes/about.py b/kjvstudy_org/routes/about.py
index e6468de..c0102bb 100644
--- a/kjvstudy_org/routes/about.py
+++ b/kjvstudy_org/routes/about.py
@@ -83,6 +83,32 @@ async def stats(request: Request):
resource_files = len(list((data_dir / 'resources').glob('*.json')))
story_files = len(list((data_dir / 'stories').glob('*.json')))
+ # Bible Stories statistics
+ total_stories = 0
+ stories_with_kids = 0
+ total_story_characters = set()
+ total_story_themes = set()
+ total_story_words = 0
+ total_kids_story_words = 0
+ for file in (data_dir / 'stories').glob('*.json'):
+ try:
+ story_data = json.load(open(file))
+ stories = story_data.get('stories', [])
+ total_stories += len(stories)
+ for story in stories:
+ # Count words in narrative
+ narrative = story.get('narrative', '')
+ total_story_words += len(narrative.split())
+ if story.get('kids_narrative'):
+ stories_with_kids += 1
+ total_kids_story_words += len(story.get('kids_narrative', '').split())
+ for char in story.get('characters', []):
+ total_story_characters.add(char)
+ for theme in story.get('themes', []):
+ total_story_themes.add(theme)
+ except (json.JSONDecodeError, IOError):
+ continue
+
# Interlinear data size
interlinear_file = data_dir / 'interlinear.json.gz'
interlinear_size_mb = interlinear_file.stat().st_size / 1024 / 1024 if interlinear_file.exists() else 0
@@ -151,6 +177,15 @@ async def stats(request: Request):
'biographies': total_biographies,
'reading_plans': reading_plan_files,
},
+ 'bible_stories': {
+ 'categories': story_files,
+ 'total_stories': total_stories,
+ 'stories_with_kids': stories_with_kids,
+ 'unique_characters': len(total_story_characters),
+ 'unique_themes': len(total_story_themes),
+ 'total_words': total_story_words,
+ 'kids_words': total_kids_story_words,
+ },
'language_tools': {
'hebrew_entries': total_hebrew_entries,
'greek_entries': total_greek_entries,
diff --git a/kjvstudy_org/templates/stats.html b/kjvstudy_org/templates/stats.html
index bcd50b6..acbc378 100644
--- a/kjvstudy_org/templates/stats.html
+++ b/kjvstudy_org/templates/stats.html
@@ -111,6 +111,43 @@
+
+ Bible Stories
+ Narrative retellings of Scripture for all ages
+
→ Browse Bible Stories
+
+
+
+ | Story Categories |
+ {{ stats.bible_stories.categories }} |
+
+
+ | Total Stories |
+ {{ stats.bible_stories.total_stories }} |
+
+
+ | Kids Versions |
+ {{ stats.bible_stories.stories_with_kids }} |
+
+
+ | Total Words (Adult) |
+ {{ "{:,}".format(stats.bible_stories.total_words) }} |
+
+
+ | Total Words (Kids) |
+ {{ "{:,}".format(stats.bible_stories.kids_words) }} |
+
+
+ | Biblical Characters |
+ {{ stats.bible_stories.unique_characters }} |
+
+
+ | Unique Themes |
+ {{ stats.bible_stories.unique_themes }} |
+
+
+
+
Language Tools
Hebrew, Greek, and interlinear resources
@@ -195,6 +232,7 @@
{{ "{:,}".format(stats.commentary.total_words) }} words of theological analysis
{{ "{:,}".format(stats.cross_references.total_references) }} cross-references linking related passages
{{ "{:,}".format(stats.language_tools.total_strongs) }} Strong's Concordance entries for word studies
+ {{ stats.bible_stories.total_stories }} Bible stories across {{ stats.bible_stories.categories }} categories
{{ stats.study_resources.biographies }} biblical biographies
{{ stats.study_resources.study_guides }} topical study guides
{{ stats.study_resources.reading_plans }} reading plans
diff --git a/kjvstudy_org/templates/stories_index.html b/kjvstudy_org/templates/stories_index.html
index 3e99968..9810050 100644
--- a/kjvstudy_org/templates/stories_index.html
+++ b/kjvstudy_org/templates/stories_index.html
@@ -136,12 +136,20 @@
border: 1px solid var(--border-color, #ddd);
border-radius: 6px;
padding: 1.25rem;
- background: #fff;
+ background: var(--bg-color, #fff);
text-decoration: none;
color: inherit;
position: relative;
transition: box-shadow 0.2s ease, border-color 0.2s ease, transform 0.2s ease;
}
+[data-theme="dark"] .story-card {
+ background: #252525;
+}
+@media (prefers-color-scheme: dark) {
+ html:not([data-theme="light"]) .story-card {
+ background: #252525;
+ }
+}
.story-card:hover,
.story-card:focus-visible {
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
@@ -158,7 +166,7 @@
}
.story-card .description {
font-size: 0.9rem;
- color: #555;
+ color: var(--text-secondary, #555);
margin-bottom: 0.75rem;
line-height: 1.5;
}
@@ -179,7 +187,7 @@
background: var(--code-bg, #f5f5f5);
border-radius: 3px;
font-size: 0.75rem;
- color: #666;
+ color: var(--text-secondary, #666);
}
.story-card .tag.character {
background: #e8f4f8;
@@ -189,6 +197,24 @@
background: #f0f4e8;
color: #5a7a2a;
}
+[data-theme="dark"] .story-card .tag.character {
+ background: #1e3a4a;
+ color: #7fc4e0;
+}
+[data-theme="dark"] .story-card .tag.theme {
+ background: #2a3a1e;
+ color: #a8c87a;
+}
+@media (prefers-color-scheme: dark) {
+ html:not([data-theme="light"]) .story-card .tag.character {
+ background: #1e3a4a;
+ color: #7fc4e0;
+ }
+ html:not([data-theme="light"]) .story-card .tag.theme {
+ background: #2a3a1e;
+ color: #a8c87a;
+ }
+}
.story-card .links {
display: flex;
gap: 1rem;
@@ -207,12 +233,15 @@
.category-section.hidden {
display: none;
}
+.story-toc li.hidden {
+ display: none;
+}
.category-section {
margin-bottom: 3rem;
}
.category-description {
font-size: 1rem;
- color: #555;
+ color: var(--text-secondary, #555);
margin-bottom: 1rem;
max-width: 65ch;
}
@@ -239,11 +268,16 @@
View Kids Version — Stories written for younger readers
+
+
Categories
-
-
@@ -300,6 +329,7 @@ document.addEventListener('DOMContentLoaded', function() {
var dropdown = document.getElementById('story-search-dropdown');
var stories = document.querySelectorAll('.story-card');
var categorySections = document.querySelectorAll('.category-section');
+ var tocItems = document.querySelectorAll('.story-toc li');
var noResults = document.getElementById('no-results');
var currentResults = [];
var selectedIndex = -1;
@@ -308,6 +338,7 @@ document.addEventListener('DOMContentLoaded', function() {
if (!query) {
stories.forEach(function(s) { s.classList.remove('hidden'); });
categorySections.forEach(function(c) { c.classList.remove('hidden'); });
+ tocItems.forEach(function(t) { t.classList.remove('hidden'); });
noResults.classList.remove('visible');
return;
}
@@ -333,12 +364,26 @@ document.addEventListener('DOMContentLoaded', function() {
});
categorySections.forEach(function(category) {
+ var categoryId = category.id;
var visibleStories = category.querySelectorAll('.story-card:not(.hidden)');
- if (visibleStories.length === 0) {
+ var isEmpty = visibleStories.length === 0;
+
+ if (isEmpty) {
category.classList.add('hidden');
} else {
category.classList.remove('hidden');
}
+
+ // Also hide/show corresponding TOC item
+ tocItems.forEach(function(tocItem) {
+ if (tocItem.dataset.category === categoryId) {
+ if (isEmpty) {
+ tocItem.classList.add('hidden');
+ } else {
+ tocItem.classList.remove('hidden');
+ }
+ }
+ });
});
if (matchCount === 0) {
@@ -358,17 +403,19 @@ document.addEventListener('DOMContentLoaded', function() {
if (title.includes(query) || description.includes(query) ||
characters.includes(query) || themes.includes(query)) {
- var link = story.querySelector('h3 a');
- if (link) {
+ // The story card itself is the anchor tag
+ var storyTitle = story.querySelector('h3');
+ var storyDesc = story.querySelector('.description');
+ if (storyTitle) {
matches.push({
- title: link.textContent,
- url: link.getAttribute('href'),
- description: story.querySelector('.description')?.textContent?.substring(0, 60) + '...'
+ title: storyTitle.textContent,
+ url: story.href,
+ description: storyDesc ? storyDesc.textContent.substring(0, 60) + '...' : ''
});
}
}
});
- return matches.slice(0, 5);
+ return matches.slice(0, 8);
}
function showDropdown(html) {
diff --git a/kjvstudy_org/templates/stories_kids_index.html b/kjvstudy_org/templates/stories_kids_index.html
index fe7d094..5a604f7 100644
--- a/kjvstudy_org/templates/stories_kids_index.html
+++ b/kjvstudy_org/templates/stories_kids_index.html
@@ -229,6 +229,9 @@
.category-section.hidden {
display: none;
}
+.toc-item.hidden {
+ display: none;
+}
.category-section {
margin-bottom: 3rem;
}
@@ -256,6 +259,194 @@
.no-results.visible {
display: block;
}
+
+/* Dark mode styles */
+[data-theme="dark"] .kids-page-header {
+ background: linear-gradient(135deg, #1e3a5f 0%, #2d1f4e 50%, #3d1f3a 100%);
+}
+[data-theme="dark"] .kids-page-header h1 {
+ color: #a5b4fc;
+}
+[data-theme="dark"] .kids-page-header .subtitle {
+ color: #c4b5fd;
+}
+[data-theme="dark"] .kids-page-header .intro {
+ color: #d1d5db;
+}
+[data-theme="dark"] .adult-link {
+ color: #c4b5fd;
+}
+[data-theme="dark"] .toc-item {
+ background: linear-gradient(135deg, #1e2a3a 0%, #2a1f3a 100%);
+ border-left-color: #8b5cf6;
+}
+[data-theme="dark"] .toc-item a {
+ color: #a5b4fc;
+}
+[data-theme="dark"] .toc-item .count {
+ color: #9ca3af;
+}
+[data-theme="dark"] .story-search input {
+ background: #252525;
+ border-color: #404040;
+ color: #e5e7eb;
+}
+[data-theme="dark"] .story-search input::placeholder {
+ color: #6b7280;
+}
+[data-theme="dark"] .story-search-dropdown {
+ background: #252525;
+ border-color: #404040;
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
+}
+[data-theme="dark"] .search-result-category {
+ background: linear-gradient(135deg, #1e2a3a 0%, #2a1f3a 100%);
+ border-bottom-color: #404040;
+ color: #9ca3af;
+}
+[data-theme="dark"] .search-result-item {
+ color: #a5b4fc;
+ border-bottom-color: #404040;
+}
+[data-theme="dark"] .search-result-item:hover,
+[data-theme="dark"] .search-result-item.selected {
+ background: linear-gradient(135deg, #1e2a3a 0%, #2a1f3a 100%);
+}
+[data-theme="dark"] .search-result-subtitle {
+ color: #9ca3af;
+}
+[data-theme="dark"] .kids-story-card {
+ background: #252525;
+ border-color: #404040;
+}
+[data-theme="dark"] .kids-story-card:hover,
+[data-theme="dark"] .kids-story-card:focus-visible {
+ border-color: #8b5cf6;
+ box-shadow: 0 4px 12px rgba(139, 92, 246, 0.25);
+}
+[data-theme="dark"] .kids-story-card h3 {
+ color: #a5b4fc;
+}
+[data-theme="dark"] .kids-story-card .description {
+ color: #d1d5db;
+}
+[data-theme="dark"] .kids-story-card .scripture {
+ color: #c4b5fd;
+}
+[data-theme="dark"] .character-badge {
+ background: linear-gradient(135deg, #1e3a5f, #2a2f5f);
+ color: #93c5fd;
+}
+[data-theme="dark"] .theme-badge {
+ background: linear-gradient(135deg, #4a3f1a, #3a3520);
+ color: #fcd34d;
+}
+[data-theme="dark"] .category-section h2 {
+ color: #a5b4fc;
+ border-bottom-color: #3730a3;
+}
+[data-theme="dark"] .category-description {
+ color: #d1d5db;
+}
+[data-theme="dark"] .no-results {
+ background: linear-gradient(135deg, #4a3f1a 0%, #3a3520 100%);
+ color: #fcd34d;
+}
+
+/* System preference dark mode */
+@media (prefers-color-scheme: dark) {
+ html:not([data-theme="light"]) .kids-page-header {
+ background: linear-gradient(135deg, #1e3a5f 0%, #2d1f4e 50%, #3d1f3a 100%);
+ }
+ html:not([data-theme="light"]) .kids-page-header h1 {
+ color: #a5b4fc;
+ }
+ html:not([data-theme="light"]) .kids-page-header .subtitle {
+ color: #c4b5fd;
+ }
+ html:not([data-theme="light"]) .kids-page-header .intro {
+ color: #d1d5db;
+ }
+ html:not([data-theme="light"]) .adult-link {
+ color: #c4b5fd;
+ }
+ html:not([data-theme="light"]) .toc-item {
+ background: linear-gradient(135deg, #1e2a3a 0%, #2a1f3a 100%);
+ border-left-color: #8b5cf6;
+ }
+ html:not([data-theme="light"]) .toc-item a {
+ color: #a5b4fc;
+ }
+ html:not([data-theme="light"]) .toc-item .count {
+ color: #9ca3af;
+ }
+ html:not([data-theme="light"]) .story-search input {
+ background: #252525;
+ border-color: #404040;
+ color: #e5e7eb;
+ }
+ html:not([data-theme="light"]) .story-search input::placeholder {
+ color: #6b7280;
+ }
+ html:not([data-theme="light"]) .story-search-dropdown {
+ background: #252525;
+ border-color: #404040;
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
+ }
+ html:not([data-theme="light"]) .search-result-category {
+ background: linear-gradient(135deg, #1e2a3a 0%, #2a1f3a 100%);
+ border-bottom-color: #404040;
+ color: #9ca3af;
+ }
+ html:not([data-theme="light"]) .search-result-item {
+ color: #a5b4fc;
+ border-bottom-color: #404040;
+ }
+ html:not([data-theme="light"]) .search-result-item:hover,
+ html:not([data-theme="light"]) .search-result-item.selected {
+ background: linear-gradient(135deg, #1e2a3a 0%, #2a1f3a 100%);
+ }
+ html:not([data-theme="light"]) .search-result-subtitle {
+ color: #9ca3af;
+ }
+ html:not([data-theme="light"]) .kids-story-card {
+ background: #252525;
+ border-color: #404040;
+ }
+ html:not([data-theme="light"]) .kids-story-card:hover,
+ html:not([data-theme="light"]) .kids-story-card:focus-visible {
+ border-color: #8b5cf6;
+ box-shadow: 0 4px 12px rgba(139, 92, 246, 0.25);
+ }
+ html:not([data-theme="light"]) .kids-story-card h3 {
+ color: #a5b4fc;
+ }
+ html:not([data-theme="light"]) .kids-story-card .description {
+ color: #d1d5db;
+ }
+ html:not([data-theme="light"]) .kids-story-card .scripture {
+ color: #c4b5fd;
+ }
+ html:not([data-theme="light"]) .character-badge {
+ background: linear-gradient(135deg, #1e3a5f, #2a2f5f);
+ color: #93c5fd;
+ }
+ html:not([data-theme="light"]) .theme-badge {
+ background: linear-gradient(135deg, #4a3f1a, #3a3520);
+ color: #fcd34d;
+ }
+ html:not([data-theme="light"]) .category-section h2 {
+ color: #a5b4fc;
+ border-bottom-color: #3730a3;
+ }
+ html:not([data-theme="light"]) .category-description {
+ color: #d1d5db;
+ }
+ html:not([data-theme="light"]) .no-results {
+ background: linear-gradient(135deg, #4a3f1a 0%, #3a3520 100%);
+ color: #fcd34d;
+ }
+}
{% endblock %}
@@ -276,7 +467,7 @@
Categories
{% for category in categories %}
-
+
@@ -331,6 +522,7 @@ document.addEventListener('DOMContentLoaded', function() {
var dropdown = document.getElementById('story-search-dropdown');
var stories = document.querySelectorAll('.kids-story-card');
var categorySections = document.querySelectorAll('.category-section');
+ var tocItems = document.querySelectorAll('.toc-item');
var noResults = document.getElementById('no-results');
var currentResults = [];
var selectedIndex = -1;
@@ -339,6 +531,7 @@ document.addEventListener('DOMContentLoaded', function() {
if (!query) {
stories.forEach(function(s) { s.classList.remove('hidden'); });
categorySections.forEach(function(c) { c.classList.remove('hidden'); });
+ tocItems.forEach(function(t) { t.classList.remove('hidden'); });
noResults.classList.remove('visible');
return;
}
@@ -364,12 +557,26 @@ document.addEventListener('DOMContentLoaded', function() {
});
categorySections.forEach(function(category) {
+ var categoryId = category.id;
var visibleStories = category.querySelectorAll('.kids-story-card:not(.hidden)');
- if (visibleStories.length === 0) {
+ var isEmpty = visibleStories.length === 0;
+
+ if (isEmpty) {
category.classList.add('hidden');
} else {
category.classList.remove('hidden');
}
+
+ // Also hide/show corresponding TOC item
+ tocItems.forEach(function(tocItem) {
+ if (tocItem.dataset.category === categoryId) {
+ if (isEmpty) {
+ tocItem.classList.add('hidden');
+ } else {
+ tocItem.classList.remove('hidden');
+ }
+ }
+ });
});
if (matchCount === 0) {
@@ -389,17 +596,19 @@ document.addEventListener('DOMContentLoaded', function() {
if (title.includes(query) || description.includes(query) ||
characters.includes(query) || themes.includes(query)) {
- var link = story.querySelector('h3 a');
- if (link) {
+ // The story card itself is the anchor tag
+ var storyTitle = story.querySelector('h3');
+ var storyDesc = story.querySelector('.description');
+ if (storyTitle) {
matches.push({
- title: link.textContent,
- url: link.getAttribute('href'),
- description: story.querySelector('.description')?.textContent?.substring(0, 50) + '...'
+ title: storyTitle.textContent,
+ url: story.href,
+ description: storyDesc ? storyDesc.textContent.substring(0, 50) + '...' : ''
});
}
}
});
- return matches.slice(0, 5);
+ return matches.slice(0, 8);
}
function showDropdown(html) {