mirror of
https://github.com/kennethreitz/kjvstudy.org.git
synced 2026-06-05 23:00:16 +00:00
d1fc66291a
User preference is still saved in localStorage - if expanded, stays expanded on subsequent visits. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1305 lines
39 KiB
HTML
1305 lines
39 KiB
HTML
{% extends "base.html" %}
|
|
|
|
{% block title %}{{ book }} {{ chapter }}:{{ verse_num }} - KJV Bible{% endblock %}
|
|
|
|
{% block description %}{{ verse_text[:155] if verse_text else 'Study the Authorized King James Version Bible' }}{% endblock %}
|
|
|
|
{% block og_type %}article{% endblock %}
|
|
{% block og_title %}{{ book }} {{ chapter }}:{{ verse_num }} - KJV Bible{% endblock %}
|
|
{% block og_description %}{{ verse_text[:150] if verse_text else '' }}...{% endblock %}
|
|
|
|
{% block structured_data %}
|
|
<script type="application/ld+json">
|
|
{
|
|
"@context": "https://schema.org",
|
|
"@type": "Article",
|
|
"headline": {{ (book ~ " " ~ chapter|string ~ ":" ~ verse_num|string) | tojson }},
|
|
"articleBody": {{ verse_text | tojson }},
|
|
"author": {
|
|
"@type": "Organization",
|
|
"name": "KJV Study"
|
|
},
|
|
"publisher": {
|
|
"@type": "Organization",
|
|
"name": "KJV Study",
|
|
"url": "https://kjvstudy.org"
|
|
},
|
|
"mainEntityOfPage": {
|
|
"@type": "WebPage",
|
|
"@id": "https://kjvstudy.org/book/{{ book }}/chapter/{{ chapter }}/verse/{{ verse_num }}"
|
|
},
|
|
"isPartOf": {
|
|
"@type": "Book",
|
|
"name": {{ book | tojson }},
|
|
"@id": "https://kjvstudy.org/book/{{ book }}"
|
|
}
|
|
}
|
|
</script>
|
|
{% endblock %}
|
|
|
|
{% block head %}
|
|
<style>
|
|
/* Red Letter Edition - Words of Christ */
|
|
.words-of-christ {
|
|
color: #c41e3a;
|
|
}
|
|
|
|
[data-theme="dark"] .words-of-christ {
|
|
color: #ff6b6b;
|
|
}
|
|
|
|
/* Links inside red letter text should also be red */
|
|
.words-of-christ a {
|
|
color: #c41e3a;
|
|
text-decoration: underline;
|
|
}
|
|
|
|
[data-theme="dark"] .words-of-christ a {
|
|
color: #ff6b6b;
|
|
}
|
|
|
|
/* Prevent sidenotes from inheriting red color */
|
|
.words-of-christ .sidenote,
|
|
.words-of-christ .marginnote,
|
|
.words-of-christ .sidenote-number,
|
|
.words-of-christ label.sidenote-number {
|
|
color: var(--text-color);
|
|
}
|
|
|
|
[data-theme="dark"] .words-of-christ .sidenote,
|
|
[data-theme="dark"] .words-of-christ .marginnote,
|
|
[data-theme="dark"] .words-of-christ .sidenote-number,
|
|
[data-theme="dark"] .words-of-christ label.sidenote-number {
|
|
color: var(--text-color);
|
|
}
|
|
|
|
/* Prevent word definitions from inheriting red color */
|
|
.words-of-christ .word-original,
|
|
.words-of-christ .word-english,
|
|
.words-of-christ .word-definition {
|
|
color: #111;
|
|
}
|
|
|
|
.words-of-christ .word-transliteration,
|
|
.words-of-christ .word-parsing {
|
|
color: #666;
|
|
}
|
|
|
|
.words-of-christ .word-strongs {
|
|
color: #0066cc;
|
|
}
|
|
|
|
[data-theme="dark"] .words-of-christ .word-original,
|
|
[data-theme="dark"] .words-of-christ .word-english,
|
|
[data-theme="dark"] .words-of-christ .word-definition {
|
|
color: #ddd;
|
|
}
|
|
|
|
[data-theme="dark"] .words-of-christ .word-transliteration,
|
|
[data-theme="dark"] .words-of-christ .word-parsing {
|
|
color: #999;
|
|
}
|
|
|
|
[data-theme="dark"] .words-of-christ .word-strongs {
|
|
color: #6699ff;
|
|
}
|
|
|
|
.verse-text {
|
|
font-size: 1.8rem;
|
|
line-height: 2.4rem;
|
|
margin: 2rem 0;
|
|
font-style: italic;
|
|
}
|
|
|
|
.verse-reference {
|
|
color: #666;
|
|
font-size: 1.2rem;
|
|
margin-bottom: 0.5rem;
|
|
}
|
|
|
|
.share-container {
|
|
max-width: 60%;
|
|
margin: 1.5rem 0;
|
|
padding: 1rem;
|
|
border-top: 1px solid var(--border-color);
|
|
border-bottom: 1px solid var(--border-color);
|
|
}
|
|
|
|
/* Generic collapsible section toggle */
|
|
.section-toggle {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.5rem;
|
|
width: 100%;
|
|
padding: 0.5rem 0;
|
|
background: none;
|
|
border: none;
|
|
cursor: pointer;
|
|
text-align: left;
|
|
font-family: inherit;
|
|
transition: opacity 0.2s;
|
|
}
|
|
|
|
.section-toggle:hover {
|
|
opacity: 0.7;
|
|
}
|
|
|
|
.section-toggle:focus {
|
|
outline: 2px solid var(--link-color);
|
|
outline-offset: 2px;
|
|
}
|
|
|
|
.section-toggle .toggle-icon {
|
|
font-size: 0.8rem;
|
|
color: var(--text-secondary);
|
|
transition: transform 0.2s;
|
|
}
|
|
|
|
.section-heading {
|
|
font-size: 0.9rem;
|
|
color: var(--text-secondary);
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.05em;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.share-label {
|
|
font-size: 0.9rem;
|
|
color: var(--text-secondary);
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.05em;
|
|
margin-bottom: 0.75rem;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.cross-references-section .section-heading {
|
|
font-size: 1.4rem;
|
|
text-transform: none;
|
|
letter-spacing: normal;
|
|
color: var(--text-color);
|
|
}
|
|
|
|
.share-buttons {
|
|
display: flex;
|
|
gap: 0.75rem;
|
|
flex-wrap: wrap;
|
|
}
|
|
|
|
.share-btn {
|
|
padding: 0.5rem 1rem;
|
|
font-size: 0.9rem;
|
|
font-weight: 600;
|
|
border: 1px solid var(--border-color-darker);
|
|
border-radius: 4px;
|
|
background: var(--bg-color);
|
|
color: var(--text-color);
|
|
cursor: pointer;
|
|
transition: all 0.2s;
|
|
text-decoration: none;
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 0.4rem;
|
|
}
|
|
|
|
.share-btn:hover {
|
|
background: var(--code-bg);
|
|
border-color: var(--link-color);
|
|
color: var(--link-color);
|
|
}
|
|
|
|
.share-btn.copied {
|
|
border-color: var(--link-color);
|
|
color: var(--link-color);
|
|
}
|
|
|
|
/* Cross-reference tooltip styles */
|
|
.cross-ref-link {
|
|
position: relative;
|
|
cursor: help;
|
|
}
|
|
|
|
.cross-ref-tooltip {
|
|
display: none;
|
|
position: absolute;
|
|
bottom: 100%;
|
|
left: 0;
|
|
min-width: 300px;
|
|
max-width: 400px;
|
|
padding: 0.75rem 1rem;
|
|
margin-bottom: 0.5rem;
|
|
background: var(--bg-color);
|
|
border: 1px solid var(--border-color-darker);
|
|
border-radius: 6px;
|
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
|
z-index: 1000;
|
|
font-size: 0.9rem;
|
|
line-height: 1.6;
|
|
color: var(--text-color);
|
|
}
|
|
|
|
.cross-ref-link:hover .cross-ref-tooltip {
|
|
display: block;
|
|
}
|
|
|
|
.tooltip-verse-text {
|
|
font-weight: 600;
|
|
font-style: italic;
|
|
color: var(--text-secondary);
|
|
margin-top: 0.5rem;
|
|
padding-top: 0.5rem;
|
|
border-top: 1px solid var(--border-color);
|
|
}
|
|
|
|
[data-theme="dark"] .cross-ref-tooltip {
|
|
background: var(--bg-color);
|
|
border-color: var(--border-color-darker);
|
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
|
|
}
|
|
|
|
/* Interlinear styles - inline flow design */
|
|
.interlinear-container {
|
|
max-width: 100%;
|
|
margin: 2rem 0;
|
|
}
|
|
|
|
/* Collapsible toggle button */
|
|
.interlinear-toggle {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.5rem;
|
|
width: 100%;
|
|
padding: 0.75rem 0;
|
|
background: none;
|
|
border: none;
|
|
border-bottom: 1px solid var(--border-color);
|
|
cursor: pointer;
|
|
text-align: left;
|
|
font-family: inherit;
|
|
transition: background 0.2s;
|
|
}
|
|
|
|
.interlinear-toggle:hover {
|
|
background: var(--code-bg);
|
|
}
|
|
|
|
.interlinear-toggle:focus {
|
|
outline: 2px solid var(--link-color);
|
|
outline-offset: 2px;
|
|
}
|
|
|
|
.toggle-icon {
|
|
font-size: 0.8rem;
|
|
color: var(--text-secondary);
|
|
transition: transform 0.2s;
|
|
}
|
|
|
|
.interlinear-toggle.expanded .toggle-icon {
|
|
transform: rotate(0deg);
|
|
}
|
|
|
|
.toggle-hint {
|
|
font-size: 0.85rem;
|
|
color: var(--text-tertiary);
|
|
font-weight: normal;
|
|
}
|
|
|
|
.interlinear-heading {
|
|
font-size: 1.1rem;
|
|
font-weight: 600;
|
|
margin: 0;
|
|
color: var(--text-color);
|
|
}
|
|
|
|
.interlinear-flow {
|
|
line-height: 2.8;
|
|
font-size: 1rem;
|
|
margin: 1rem 0;
|
|
padding: 0.5rem 0;
|
|
}
|
|
|
|
.word-unit {
|
|
display: inline-block;
|
|
text-align: center;
|
|
margin: 0 0.05rem 0.5rem 0.05rem;
|
|
vertical-align: top;
|
|
cursor: pointer;
|
|
position: relative;
|
|
padding: 0.25rem 0.3rem;
|
|
border-radius: 4px;
|
|
transition: all 0.2s ease;
|
|
min-width: auto;
|
|
border: 1px solid transparent;
|
|
}
|
|
|
|
.word-unit:hover {
|
|
background: #f8f9fa;
|
|
border-color: #e0e0e0;
|
|
transform: translateY(-2px);
|
|
box-shadow: 0 2px 8px rgba(0,0,0,0.06);
|
|
}
|
|
|
|
.word-unit.expanded {
|
|
background: #f0f7f4;
|
|
border-color: #4a7c59;
|
|
transform: translateY(-2px);
|
|
box-shadow: 0 4px 12px rgba(74,124,89,0.15);
|
|
z-index: 101;
|
|
}
|
|
|
|
.word-original {
|
|
display: block;
|
|
font-size: 2rem;
|
|
font-weight: 400;
|
|
color: #222;
|
|
margin-bottom: 0.35rem;
|
|
line-height: 1.4;
|
|
transition: color 0.2s;
|
|
letter-spacing: 0.02em;
|
|
}
|
|
|
|
/* Hebrew text (RTL) */
|
|
.word-original.hebrew {
|
|
direction: rtl;
|
|
font-family: "SBL Hebrew", "Ezra SIL", "Times New Roman", "Noto Serif Hebrew", serif;
|
|
font-size: 2.2rem;
|
|
letter-spacing: 0.04em;
|
|
}
|
|
|
|
/* Greek text */
|
|
.word-original.greek {
|
|
font-family: "SBL Greek", "Gentium Plus", "Times New Roman", "Noto Serif", serif;
|
|
font-size: 2rem;
|
|
letter-spacing: 0.03em;
|
|
}
|
|
|
|
.word-unit:hover .word-original {
|
|
color: #111;
|
|
}
|
|
|
|
.word-english {
|
|
display: block;
|
|
font-size: 0.85rem;
|
|
color: #4a7c59;
|
|
font-weight: 600;
|
|
line-height: 1.3;
|
|
}
|
|
|
|
.word-strongs {
|
|
display: block;
|
|
font-size: 0.65rem;
|
|
color: #999;
|
|
font-family: monospace;
|
|
margin-top: 0.2rem;
|
|
opacity: 0.7;
|
|
transition: opacity 0.2s;
|
|
}
|
|
|
|
.word-unit:hover .word-strongs {
|
|
opacity: 1;
|
|
}
|
|
|
|
.word-strongs a {
|
|
color: inherit;
|
|
text-decoration: none;
|
|
}
|
|
|
|
.word-strongs a:hover {
|
|
color: #4a7c59;
|
|
}
|
|
|
|
/* Hover tooltip - full definition preview */
|
|
.word-tooltip {
|
|
position: absolute;
|
|
bottom: 100%;
|
|
left: 50%;
|
|
transform: translateX(-50%) translateY(-4px);
|
|
background: #333;
|
|
color: #fff;
|
|
padding: 0.65rem 1rem;
|
|
border-radius: 4px;
|
|
font-size: 0.95rem;
|
|
white-space: normal;
|
|
max-width: 380px;
|
|
min-width: 200px;
|
|
text-align: center;
|
|
line-height: 1.5;
|
|
opacity: 0;
|
|
pointer-events: none;
|
|
transition: opacity 0.15s, transform 0.15s;
|
|
z-index: 999999999;
|
|
}
|
|
|
|
.word-tooltip::after {
|
|
content: "";
|
|
position: absolute;
|
|
top: 100%;
|
|
left: 50%;
|
|
transform: translateX(-50%);
|
|
border: 5px solid transparent;
|
|
border-top-color: #333;
|
|
}
|
|
|
|
.word-unit:hover .word-tooltip {
|
|
opacity: 1;
|
|
transform: translateX(-50%) translateY(-8px);
|
|
}
|
|
|
|
.word-unit.expanded .word-tooltip {
|
|
opacity: 0;
|
|
}
|
|
|
|
/* Full detail panel */
|
|
.word-detail {
|
|
display: none;
|
|
position: absolute;
|
|
top: 100%;
|
|
left: 50%;
|
|
transform: translateX(-50%) translateY(8px);
|
|
background: #fff;
|
|
border: 1px solid #ddd;
|
|
border-radius: 8px;
|
|
padding: 1rem 1.25rem;
|
|
min-width: 260px;
|
|
max-width: 320px;
|
|
box-shadow: 0 8px 24px rgba(0,0,0,0.12);
|
|
z-index: 100;
|
|
text-align: left;
|
|
line-height: 1.5;
|
|
opacity: 0;
|
|
transition: opacity 0.2s, transform 0.2s;
|
|
}
|
|
|
|
.word-unit.expanded .word-detail {
|
|
display: block;
|
|
opacity: 1;
|
|
transform: translateX(-50%) translateY(12px);
|
|
}
|
|
|
|
.word-detail-header {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.75rem;
|
|
margin-bottom: 0.75rem;
|
|
padding-bottom: 0.75rem;
|
|
border-bottom: 1px solid #eee;
|
|
}
|
|
|
|
.word-detail-original {
|
|
font-size: 2.2rem;
|
|
font-weight: 400;
|
|
color: #222;
|
|
line-height: 1.3;
|
|
}
|
|
|
|
.word-detail-original.hebrew {
|
|
direction: rtl;
|
|
font-family: "SBL Hebrew", "Ezra SIL", "Times New Roman", "Noto Serif Hebrew", serif;
|
|
font-size: 2.4rem;
|
|
}
|
|
|
|
.word-detail-original.greek {
|
|
font-family: "SBL Greek", "Gentium Plus", "Times New Roman", "Noto Serif", serif;
|
|
font-size: 2.2rem;
|
|
}
|
|
|
|
.word-detail-english {
|
|
font-size: 1rem;
|
|
color: #4a7c59;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.word-detail-row {
|
|
display: flex;
|
|
margin-bottom: 0.35rem;
|
|
font-size: 0.85rem;
|
|
}
|
|
|
|
.word-detail-row:last-child {
|
|
margin-bottom: 0;
|
|
}
|
|
|
|
.word-detail-label {
|
|
color: #888;
|
|
min-width: 85px;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.word-detail-value {
|
|
color: #333;
|
|
}
|
|
|
|
.word-detail-definition {
|
|
margin-top: 0.75rem;
|
|
padding-top: 0.75rem;
|
|
border-top: 1px solid #eee;
|
|
font-size: 0.85rem;
|
|
color: #555;
|
|
line-height: 1.6;
|
|
}
|
|
|
|
.interlinear-link {
|
|
display: inline-block;
|
|
margin-top: 0.5rem;
|
|
font-size: 0.9rem;
|
|
color: #4a7c59;
|
|
text-decoration: none;
|
|
}
|
|
|
|
.interlinear-link:hover {
|
|
text-decoration: underline;
|
|
}
|
|
|
|
/* Dark mode for interlinear */
|
|
[data-theme="dark"] .word-original {
|
|
color: #eee;
|
|
}
|
|
|
|
[data-theme="dark"] .word-unit:hover .word-original {
|
|
color: #fff;
|
|
}
|
|
|
|
[data-theme="dark"] .word-unit:hover {
|
|
background: #2a2a2a;
|
|
border-color: #444;
|
|
}
|
|
|
|
[data-theme="dark"] .word-unit.expanded {
|
|
background: #1a2e1a;
|
|
border-color: #4a7c59;
|
|
}
|
|
|
|
[data-theme="dark"] .word-detail {
|
|
background: #222;
|
|
border-color: #444;
|
|
}
|
|
|
|
[data-theme="dark"] .word-detail-original {
|
|
color: #eee;
|
|
}
|
|
|
|
[data-theme="dark"] .word-detail-value {
|
|
color: #ddd;
|
|
}
|
|
|
|
[data-theme="dark"] .word-detail-definition {
|
|
color: #bbb;
|
|
border-top-color: #444;
|
|
}
|
|
|
|
[data-theme="dark"] .word-detail-header {
|
|
border-bottom-color: #444;
|
|
}
|
|
|
|
[data-theme="dark"] .interlinear-heading {
|
|
color: #ddd;
|
|
border-bottom-color: #444;
|
|
}
|
|
|
|
/* Only apply OS dark mode when explicit theme is not set to light */
|
|
@media (prefers-color-scheme: dark) {
|
|
html:not([data-theme="light"]) .word-original {
|
|
color: #eee;
|
|
}
|
|
|
|
html:not([data-theme="light"]) .word-unit:hover .word-original {
|
|
color: #fff;
|
|
}
|
|
|
|
html:not([data-theme="light"]) .word-unit:hover {
|
|
background: #2a2a2a;
|
|
border-color: #444;
|
|
}
|
|
|
|
html:not([data-theme="light"]) .word-unit.expanded {
|
|
background: #1a2e1a;
|
|
border-color: #4a7c59;
|
|
}
|
|
|
|
html:not([data-theme="light"]) .word-detail {
|
|
background: #222;
|
|
border-color: #444;
|
|
}
|
|
|
|
html:not([data-theme="light"]) .word-detail-original {
|
|
color: #eee;
|
|
}
|
|
|
|
html:not([data-theme="light"]) .word-detail-value {
|
|
color: #ddd;
|
|
}
|
|
|
|
html:not([data-theme="light"]) .word-detail-definition {
|
|
color: #bbb;
|
|
border-top-color: #444;
|
|
}
|
|
|
|
html:not([data-theme="light"]) .word-detail-header {
|
|
border-bottom-color: #444;
|
|
}
|
|
|
|
html:not([data-theme="light"]) .interlinear-heading {
|
|
color: #ddd;
|
|
border-bottom-color: #444;
|
|
}
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.interlinear-flow {
|
|
line-height: 4.5;
|
|
}
|
|
|
|
.word-unit {
|
|
margin: 0 0.2rem 1.5rem 0.2rem;
|
|
min-width: 50px;
|
|
padding: 0.5rem 0.6rem;
|
|
}
|
|
|
|
.word-original {
|
|
font-size: 1.8rem;
|
|
}
|
|
|
|
.word-original.hebrew {
|
|
font-size: 2rem;
|
|
}
|
|
|
|
.word-original.greek {
|
|
font-size: 1.8rem;
|
|
}
|
|
|
|
.word-tooltip {
|
|
display: none;
|
|
}
|
|
|
|
.word-detail {
|
|
position: fixed;
|
|
top: auto;
|
|
bottom: 0;
|
|
left: 0;
|
|
right: 0;
|
|
transform: none;
|
|
max-width: none;
|
|
border-radius: 16px 16px 0 0;
|
|
padding: 1.5rem;
|
|
box-shadow: 0 -4px 24px rgba(0,0,0,0.15);
|
|
}
|
|
|
|
.word-unit.expanded .word-detail {
|
|
transform: none;
|
|
}
|
|
}
|
|
|
|
@media print {
|
|
.share-container,
|
|
nav {
|
|
display: none !important;
|
|
}
|
|
|
|
.verse-text {
|
|
max-width: 100% !important;
|
|
}
|
|
|
|
section {
|
|
page-break-inside: avoid;
|
|
}
|
|
|
|
.sidenote,
|
|
.marginnote {
|
|
display: block;
|
|
float: none;
|
|
width: 100%;
|
|
margin: 0.5rem 0;
|
|
font-size: 0.9rem;
|
|
color: var(--text-color);
|
|
}
|
|
|
|
.interlinear-container,
|
|
.word-tooltip,
|
|
.word-detail {
|
|
display: none !important;
|
|
}
|
|
}
|
|
</style>
|
|
{% endblock %}
|
|
|
|
{% block content %}
|
|
<h1>{{ book }} {{ chapter }}:{{ verse_num }}</h1>
|
|
<p class="subtitle"><a href="/books">Authorized King James Version</a></p>
|
|
|
|
<section>
|
|
<p class="verse-text">{{ verse_text | format_lists | red_letter(book, chapter, verse_num) | link_names | safe }}</p>
|
|
|
|
{% if interlinear_words %}
|
|
<section class="interlinear-container" id="interlinear">
|
|
<button class="interlinear-toggle expanded" onclick="toggleInterlinear()" aria-expanded="true" aria-controls="interlinear-content">
|
|
<span class="toggle-icon">▼</span>
|
|
<span class="interlinear-heading">Original Language Analysis</span>
|
|
<span class="toggle-hint">({{ interlinear_words|length }} words)</span>
|
|
</button>
|
|
<div class="interlinear-flow" id="interlinear-content">
|
|
{% for word in interlinear_words %}
|
|
<div class="word-unit" onclick="toggleWord(this)">
|
|
<span class="word-original {% if is_old_testament %}hebrew{% else %}greek{% endif %}">{{ word.original }}</span>
|
|
<span class="word-english">{{ word.english }}</span>
|
|
<span class="word-strongs">
|
|
{% if word.strongs %}
|
|
<a href="/strongs/{{ word.strongs }}" onclick="event.stopPropagation()">{{ word.strongs }}</a>
|
|
{% endif %}
|
|
</span>
|
|
<div class="word-detail">
|
|
<div class="word-detail-header">
|
|
<span class="word-detail-original {% if is_old_testament %}hebrew{% else %}greek{% endif %}">{{ word.original }}</span>
|
|
<span class="word-detail-english">{{ word.english }}</span>
|
|
</div>
|
|
{% if word.transliteration %}
|
|
<div class="word-detail-row">
|
|
<span class="word-detail-label">Pronunciation:</span>
|
|
<span class="word-detail-value">{{ word.transliteration }}</span>
|
|
</div>
|
|
{% endif %}
|
|
<div class="word-detail-row">
|
|
<span class="word-detail-label">Strong's:</span>
|
|
<span class="word-detail-value">
|
|
{% if word.strongs %}
|
|
<a href="/strongs/{{ word.strongs }}" style="color: #4a7c59;">{{ word.strongs }}</a>
|
|
{% else %}
|
|
—
|
|
{% endif %}
|
|
</span>
|
|
</div>
|
|
{% if word.parsing %}
|
|
<div class="word-detail-row">
|
|
<span class="word-detail-label">Grammar:</span>
|
|
<span class="word-detail-value">{{ word.parsing }}</span>
|
|
</div>
|
|
{% endif %}
|
|
<div class="word-detail-row">
|
|
<span class="word-detail-label">Word #:</span>
|
|
<span class="word-detail-value">{{ word.position }} of {{ interlinear_words|length }}</span>
|
|
</div>
|
|
{% if word.definition %}
|
|
<div class="word-detail-definition">
|
|
{{ word.definition }}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
</section>
|
|
<script>
|
|
function toggleInterlinear() {
|
|
var content = document.getElementById('interlinear-content');
|
|
var toggle = document.querySelector('.interlinear-toggle');
|
|
var icon = toggle.querySelector('.toggle-icon');
|
|
var isExpanded = toggle.getAttribute('aria-expanded') === 'true';
|
|
|
|
if (isExpanded) {
|
|
content.hidden = true;
|
|
toggle.setAttribute('aria-expanded', 'false');
|
|
icon.textContent = '▶';
|
|
toggle.classList.remove('expanded');
|
|
localStorage.setItem('interlinear-expanded', 'false');
|
|
} else {
|
|
content.hidden = false;
|
|
toggle.setAttribute('aria-expanded', 'true');
|
|
icon.textContent = '▼';
|
|
toggle.classList.add('expanded');
|
|
localStorage.setItem('interlinear-expanded', 'true');
|
|
}
|
|
}
|
|
// Apply user preference on load
|
|
(function() {
|
|
var pref = localStorage.getItem('interlinear-expanded');
|
|
if (pref === 'false') {
|
|
var content = document.getElementById('interlinear-content');
|
|
var toggle = document.querySelector('.interlinear-toggle');
|
|
var icon = toggle.querySelector('.toggle-icon');
|
|
content.hidden = true;
|
|
toggle.setAttribute('aria-expanded', 'false');
|
|
icon.textContent = '▶';
|
|
toggle.classList.remove('expanded');
|
|
}
|
|
})();
|
|
function toggleWord(el) {
|
|
document.querySelectorAll('.word-unit.expanded').forEach(function(word) {
|
|
if (word !== el) word.classList.remove('expanded');
|
|
});
|
|
el.classList.toggle('expanded');
|
|
}
|
|
document.addEventListener('click', function(e) {
|
|
if (!e.target.closest('.word-unit')) {
|
|
document.querySelectorAll('.word-unit.expanded').forEach(function(word) {
|
|
word.classList.remove('expanded');
|
|
});
|
|
}
|
|
});
|
|
document.addEventListener('keydown', function(e) {
|
|
if (e.key === 'Escape') {
|
|
document.querySelectorAll('.word-unit.expanded').forEach(function(word) {
|
|
word.classList.remove('expanded');
|
|
});
|
|
}
|
|
});
|
|
</script>
|
|
{% endif %}
|
|
|
|
<nav style="margin: 1.5rem 0;">
|
|
<p>
|
|
<a href="/book/{{ book }}">← {{ book }}</a> |
|
|
<a href="/book/{{ book }}/chapter/{{ chapter }}#verse-{{ verse_num }}">View in Chapter {{ chapter }}</a>
|
|
{% if verse_num > 1 %} | <a href="/book/{{ book }}/chapter/{{ chapter }}/verse/{{ verse_num - 1 }}">← Verse {{ verse_num - 1 }}</a>{% endif %}
|
|
{% if verse_num < total_verses %} | <a href="/book/{{ book }}/chapter/{{ chapter }}/verse/{{ verse_num + 1 }}">Verse {{ verse_num + 1 }} →</a>{% endif %}
|
|
</p>
|
|
</nav>
|
|
|
|
<div class="share-container">
|
|
<div class="share-label">Share This Verse</div>
|
|
<div class="share-buttons">
|
|
<button class="share-btn" onclick="copyToClipboard()" id="copy-btn">
|
|
<span>📋</span> Copy
|
|
</button>
|
|
<a class="share-btn twitter"
|
|
href="https://twitter.com/intent/tweet?text={{ verse_text | striptags | urlencode }}%20—%20{{ book }}%20{{ chapter }}:{{ verse_num }}%20(KJV)&url={{ request.url }}"
|
|
target="_blank"
|
|
rel="noopener">
|
|
<span>🐦</span> Tweet
|
|
</a>
|
|
<a class="share-btn facebook"
|
|
href="https://www.facebook.com/sharer/sharer.php?u={{ request.url }}"
|
|
target="_blank"
|
|
rel="noopener">
|
|
<span>f</span> Share
|
|
</a>
|
|
{% if pdf_available and pdf_url %}
|
|
<a class="share-btn pdf" href="{{ pdf_url }}">
|
|
<span>📄</span> PDF
|
|
</a>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
function copyToClipboard() {
|
|
const verseText = {{ verse_text | striptags | tojson }};
|
|
const reference = "{{ book }} {{ chapter }}:{{ verse_num }} (KJV)";
|
|
const fullText = verseText + " — " + reference;
|
|
|
|
navigator.clipboard.writeText(fullText).then(function() {
|
|
const btn = document.getElementById('copy-btn');
|
|
const originalHTML = btn.innerHTML;
|
|
btn.classList.add('copied');
|
|
btn.innerHTML = '<span>✓</span> Copied!';
|
|
|
|
setTimeout(function() {
|
|
btn.classList.remove('copied');
|
|
btn.innerHTML = originalHTML;
|
|
}, 2000);
|
|
}, function(err) {
|
|
alert('Failed to copy: ' + err);
|
|
});
|
|
}
|
|
|
|
function toggleSection(sectionId) {
|
|
var contentId = sectionId + '-content';
|
|
var content = document.getElementById(contentId);
|
|
var toggle = content.previousElementSibling;
|
|
while (toggle && !toggle.classList.contains('section-toggle')) {
|
|
toggle = toggle.previousElementSibling;
|
|
}
|
|
if (!toggle) return;
|
|
|
|
var icon = toggle.querySelector('.toggle-icon');
|
|
var isExpanded = toggle.getAttribute('aria-expanded') === 'true';
|
|
|
|
if (isExpanded) {
|
|
content.hidden = true;
|
|
toggle.setAttribute('aria-expanded', 'false');
|
|
icon.textContent = '▶';
|
|
toggle.classList.remove('expanded');
|
|
localStorage.setItem(sectionId + '-expanded', 'false');
|
|
} else {
|
|
content.hidden = false;
|
|
toggle.setAttribute('aria-expanded', 'true');
|
|
icon.textContent = '▼';
|
|
toggle.classList.add('expanded');
|
|
localStorage.setItem(sectionId + '-expanded', 'true');
|
|
}
|
|
}
|
|
|
|
</script>
|
|
</section>
|
|
|
|
{% if cross_references %}
|
|
<div class="cross-references-section" id="cross-references">
|
|
<button class="section-toggle" onclick="toggleSection('crossrefs')" aria-expanded="false" aria-controls="crossrefs-content">
|
|
<span class="toggle-icon">▶</span>
|
|
<span class="section-heading">Cross References</span>
|
|
<span class="toggle-hint">({{ cross_references|length }} references)</span>
|
|
</button>
|
|
<div id="crossrefs-content" hidden>
|
|
<p>Related verses that illuminate this passage:</p>
|
|
<ul style="max-width: 65%; list-style: none; padding: 0;">
|
|
{% for ref in cross_references %}
|
|
<li style="margin: 1.25rem 0; padding-left: 1.5rem; border-left: 2px solid var(--border-color-dark);">
|
|
{% set ref_parts = ref.ref.rsplit(' ', 1) %}
|
|
{% if ref_parts|length == 2 %}
|
|
{% set book_name = ref_parts[0] %}
|
|
{% set chapter_verse = ref_parts[1] %}
|
|
{% if ':' in chapter_verse %}
|
|
{% set ch = chapter_verse.split(':')[0] %}
|
|
{% set v = chapter_verse.split(':')[1] %}
|
|
<strong><a href="/book/{{ book_name }}/chapter/{{ ch }}/verse/{{ v }}">{{ ref.ref }}</a></strong>
|
|
{% else %}
|
|
<strong>{{ ref.ref }}</strong>
|
|
{% endif %}
|
|
{% else %}
|
|
<strong>{{ ref.ref }}</strong>
|
|
{% endif %}
|
|
{% if ref.note %} — <em style="color: var(--text-secondary);">{{ ref.note }}</em>{% endif %}
|
|
{% if ref.text %}
|
|
<p style="margin: 0.5rem 0 0 0; font-style: italic; color: var(--text-secondary); line-height: 1.6;">{{ ref.text }}</p>
|
|
{% endif %}
|
|
</li>
|
|
{% endfor %}
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
<script>
|
|
(function() {
|
|
var pref = localStorage.getItem('crossrefs-expanded');
|
|
if (pref === 'true') {
|
|
var content = document.getElementById('crossrefs-content');
|
|
var toggle = document.querySelector('.cross-references-section .section-toggle');
|
|
if (content && toggle) {
|
|
var icon = toggle.querySelector('.toggle-icon');
|
|
content.hidden = false;
|
|
toggle.setAttribute('aria-expanded', 'true');
|
|
icon.textContent = '▼';
|
|
toggle.classList.add('expanded');
|
|
}
|
|
}
|
|
})();
|
|
</script>
|
|
{% endif %}
|
|
|
|
{% if commentary %}
|
|
<div id="commentary"></div>
|
|
{% if commentary.analysis %}
|
|
<div class="commentary-section" id="analysis">
|
|
<h2>Analysis</h2>
|
|
{{ commentary.analysis|split_paragraphs|safe }}
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% if commentary.historical %}
|
|
<div class="commentary-section" id="historical">
|
|
<h2>Historical Context</h2>
|
|
{{ commentary.historical|split_paragraphs|safe }}
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% if commentary.theological %}
|
|
<div class="commentary-section" id="theological">
|
|
<h2>Theological Significance</h2>
|
|
{{ commentary.theological|split_paragraphs|safe }}
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% if commentary.questions %}
|
|
<div class="commentary-section" id="questions">
|
|
<h2>Questions for Reflection</h2>
|
|
<ul>
|
|
{% for question in commentary.questions %}
|
|
<li>{{ question }}</li>
|
|
{% endfor %}
|
|
</ul>
|
|
</div>
|
|
{% endif %}
|
|
{% endif %}
|
|
|
|
<!-- Keyboard Navigation - Paragraph-based with drill-down -->
|
|
<script>
|
|
(function() {
|
|
// Collect all readable elements: paragraphs, list items, cross-ref items
|
|
function getElements() {
|
|
return Array.from(document.querySelectorAll(
|
|
'section > p.verse-text, ' +
|
|
'.interlinear-container, ' +
|
|
'#crossrefs-content > p, ' +
|
|
'.cross-references-section li, ' +
|
|
'.commentary-section p, ' +
|
|
'.commentary-section li'
|
|
)).filter(function(el) {
|
|
// Filter out empty or very short elements, and UI elements
|
|
if (el.closest('.word-detail') || el.closest('.share-container') || el.closest('nav')) return false;
|
|
// Skip elements inside collapsed sections
|
|
var hiddenParent = el.closest('[hidden]');
|
|
if (hiddenParent) return false;
|
|
return el.textContent.trim().length > 10 || el.classList.contains('interlinear-container');
|
|
});
|
|
}
|
|
var elements = getElements();
|
|
|
|
var selectedIndex = -1;
|
|
var inWordMode = false;
|
|
var selectedWordIndex = -1;
|
|
|
|
function getWords() {
|
|
var container = document.querySelector('.interlinear-container');
|
|
if (!container) return [];
|
|
return Array.from(container.querySelectorAll('.word-unit'));
|
|
}
|
|
|
|
function clearWordSelection() {
|
|
document.querySelectorAll('.word-unit.keyboard-selected').forEach(function(word) {
|
|
word.classList.remove('keyboard-selected');
|
|
word.style.outline = '';
|
|
word.style.outlineOffset = '';
|
|
});
|
|
document.querySelectorAll('.word-unit.expanded').forEach(function(word) {
|
|
word.classList.remove('expanded');
|
|
});
|
|
selectedWordIndex = -1;
|
|
inWordMode = false;
|
|
}
|
|
|
|
function clearSelection() {
|
|
if (selectedIndex >= 0 && selectedIndex < elements.length) {
|
|
elements[selectedIndex].style.outline = '';
|
|
elements[selectedIndex].style.outlineOffset = '';
|
|
elements[selectedIndex].classList.remove('selected');
|
|
}
|
|
clearWordSelection();
|
|
}
|
|
|
|
function selectElement(index) {
|
|
clearSelection();
|
|
// Refresh elements list in case sections were toggled
|
|
elements = getElements();
|
|
if (elements.length === 0) return;
|
|
selectedIndex = Math.max(0, Math.min(index, elements.length - 1));
|
|
elements[selectedIndex].style.outline = '2px solid #4a7c59';
|
|
elements[selectedIndex].style.outlineOffset = '4px';
|
|
elements[selectedIndex].classList.add('selected');
|
|
elements[selectedIndex].scrollIntoView({ behavior: 'auto', block: 'center' });
|
|
}
|
|
|
|
function selectWord(index) {
|
|
var words = getWords();
|
|
if (words.length === 0) return;
|
|
|
|
// Clear previous word selection
|
|
document.querySelectorAll('.word-unit.keyboard-selected').forEach(function(word) {
|
|
word.classList.remove('keyboard-selected');
|
|
word.style.outline = '';
|
|
word.style.outlineOffset = '';
|
|
});
|
|
|
|
selectedWordIndex = Math.max(0, Math.min(index, words.length - 1));
|
|
var word = words[selectedWordIndex];
|
|
word.classList.add('keyboard-selected');
|
|
word.style.outline = '2px solid #4a7c59';
|
|
word.style.outlineOffset = '2px';
|
|
word.scrollIntoView({ behavior: 'auto', block: 'center' });
|
|
}
|
|
|
|
function isInterlinearSelected() {
|
|
return selectedIndex >= 0 && elements[selectedIndex] &&
|
|
elements[selectedIndex].classList.contains('interlinear-container');
|
|
}
|
|
|
|
document.addEventListener('keydown', function(e) {
|
|
if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA' || e.target.tagName === 'SELECT') {
|
|
return;
|
|
}
|
|
// Don't handle if sidebar navigation is active
|
|
if (KJVNav.sidebarActive) return;
|
|
|
|
// Escape: Exit word mode or clear selection
|
|
if (e.key === 'Escape') {
|
|
e.preventDefault();
|
|
if (inWordMode) {
|
|
clearWordSelection();
|
|
// Re-highlight the interlinear container
|
|
if (isInterlinearSelected()) {
|
|
elements[selectedIndex].style.outline = '2px solid #4a7c59';
|
|
elements[selectedIndex].style.outlineOffset = '4px';
|
|
}
|
|
} else {
|
|
clearSelection();
|
|
selectedIndex = -1;
|
|
}
|
|
return;
|
|
}
|
|
|
|
// In word mode - navigate words
|
|
if (inWordMode) {
|
|
var words = getWords();
|
|
if (e.key === 'ArrowRight' || e.key === 'l') {
|
|
e.preventDefault();
|
|
selectWord(selectedWordIndex + 1);
|
|
} else if (e.key === 'ArrowLeft' || e.key === 'h') {
|
|
e.preventDefault();
|
|
if (selectedWordIndex > 0) {
|
|
selectWord(selectedWordIndex - 1);
|
|
} else {
|
|
// Exit word mode
|
|
clearWordSelection();
|
|
if (isInterlinearSelected()) {
|
|
elements[selectedIndex].style.outline = '2px solid #4a7c59';
|
|
elements[selectedIndex].style.outlineOffset = '4px';
|
|
}
|
|
}
|
|
} else if (e.key === 'ArrowDown' || e.key === 'j') {
|
|
e.preventDefault();
|
|
// Exit word mode and go to next element
|
|
clearWordSelection();
|
|
selectElement(selectedIndex + 1);
|
|
} else if (e.key === 'ArrowUp' || e.key === 'k') {
|
|
e.preventDefault();
|
|
// Exit word mode and go to previous element
|
|
clearWordSelection();
|
|
if (selectedIndex > 0) selectElement(selectedIndex - 1);
|
|
} else if (e.key === 'Enter') {
|
|
e.preventDefault();
|
|
// Toggle word expansion
|
|
if (selectedWordIndex >= 0 && words[selectedWordIndex]) {
|
|
document.querySelectorAll('.word-unit.expanded').forEach(function(word) {
|
|
if (word !== words[selectedWordIndex]) {
|
|
word.classList.remove('expanded');
|
|
}
|
|
});
|
|
words[selectedWordIndex].classList.toggle('expanded');
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Normal navigation mode
|
|
if (e.key === 'ArrowDown' || e.key === 'j') {
|
|
e.preventDefault();
|
|
if (KJVNav.isSelectionOffScreen(elements, selectedIndex)) {
|
|
selectElement(KJVNav.findFirstVisibleIndex(elements));
|
|
} else {
|
|
selectElement(selectedIndex < 0 ? 0 : selectedIndex + 1);
|
|
}
|
|
} else if (e.key === 'ArrowUp' || e.key === 'k') {
|
|
e.preventDefault();
|
|
if (KJVNav.isSelectionOffScreen(elements, selectedIndex)) {
|
|
selectElement(KJVNav.findFirstVisibleIndex(elements));
|
|
} else if (selectedIndex <= 0) {
|
|
selectElement(0);
|
|
} else {
|
|
selectElement(selectedIndex - 1);
|
|
}
|
|
} else if (e.key === 'Enter') {
|
|
e.preventDefault();
|
|
// If on interlinear, toggle expand or enter word mode
|
|
if (isInterlinearSelected()) {
|
|
var toggle = document.querySelector('.interlinear-toggle');
|
|
var content = document.getElementById('interlinear-content');
|
|
if (content && content.hidden) {
|
|
// First expand the section
|
|
toggleInterlinear();
|
|
} else {
|
|
// Already expanded, enter word mode
|
|
inWordMode = true;
|
|
elements[selectedIndex].style.outline = '';
|
|
selectWord(0);
|
|
}
|
|
} else if (selectedIndex >= 0) {
|
|
// If on a cross-reference, navigate to it
|
|
var link = elements[selectedIndex].querySelector('a');
|
|
if (link) window.location.href = link.href;
|
|
}
|
|
} else if (e.key === 'ArrowLeft' || e.key === 'h') {
|
|
e.preventDefault();
|
|
history.back();
|
|
} else if (e.key === 'ArrowRight' || e.key === 'l') {
|
|
e.preventDefault();
|
|
{% if verse_num < total_verses %}
|
|
window.location.href = '/book/{{ book }}/chapter/{{ chapter }}/verse/{{ verse_num + 1 }}';
|
|
{% endif %}
|
|
} else if (e.key === '[') {
|
|
// Previous verse
|
|
e.preventDefault();
|
|
{% if verse_num > 1 %}
|
|
window.location.href = '/book/{{ book }}/chapter/{{ chapter }}/verse/{{ verse_num - 1 }}';
|
|
{% endif %}
|
|
} else if (e.key === ']') {
|
|
// Next verse
|
|
e.preventDefault();
|
|
{% if verse_num < total_verses %}
|
|
window.location.href = '/book/{{ book }}/chapter/{{ chapter }}/verse/{{ verse_num + 1 }}';
|
|
{% endif %}
|
|
}
|
|
});
|
|
})();
|
|
</script>
|
|
|
|
{% if related_content and (related_content.topics or related_content.people or related_content.resources or related_content.stories) %}
|
|
<div style="border-top: 1px solid var(--border-color); padding-top: 2rem; margin-top: 3rem;">
|
|
<h2>Related Resources</h2>
|
|
<p style="font-size: 0.95rem; color: var(--text-secondary); margin-bottom: 1.5rem;">
|
|
Explore related topics, people, and study resources to deepen your understanding of this passage.
|
|
</p>
|
|
|
|
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 1.5rem;">
|
|
{% if related_content.topics %}
|
|
<div>
|
|
<h3 style="font-size: 1rem; margin-bottom: 0.75rem; color: var(--text-secondary);">Topics</h3>
|
|
<ul style="list-style: none; padding: 0; margin: 0;">
|
|
{% for topic in related_content.topics %}
|
|
<li style="margin-bottom: 0.5rem;">
|
|
<a href="{{ topic.url }}" style="font-size: 0.95rem;">{{ topic.name }}</a>
|
|
</li>
|
|
{% endfor %}
|
|
</ul>
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% if related_content.people %}
|
|
<div>
|
|
<h3 style="font-size: 1rem; margin-bottom: 0.75rem; color: var(--text-secondary);">People</h3>
|
|
<ul style="list-style: none; padding: 0; margin: 0;">
|
|
{% for person in related_content.people %}
|
|
<li style="margin-bottom: 0.5rem;">
|
|
<a href="{{ person.url }}" style="font-size: 0.95rem;">{{ person.name }}</a>
|
|
</li>
|
|
{% endfor %}
|
|
</ul>
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% if related_content.resources %}
|
|
<div>
|
|
<h3 style="font-size: 1rem; margin-bottom: 0.75rem; color: var(--text-secondary);">Study Resources</h3>
|
|
<ul style="list-style: none; padding: 0; margin: 0;">
|
|
{% for resource in related_content.resources %}
|
|
<li style="margin-bottom: 0.5rem;">
|
|
<a href="{{ resource.url }}" style="font-size: 0.95rem;">{{ resource.name }}</a>
|
|
</li>
|
|
{% endfor %}
|
|
</ul>
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% if related_content.stories %}
|
|
<div>
|
|
<h3 style="font-size: 1rem; margin-bottom: 0.75rem; color: var(--text-secondary);">Bible Stories</h3>
|
|
<ul style="list-style: none; padding: 0; margin: 0;">
|
|
{% for story in related_content.stories %}
|
|
<li style="margin-bottom: 0.5rem;">
|
|
<a href="{{ story.url }}" style="font-size: 0.95rem;">{{ story.name }}</a>
|
|
</li>
|
|
{% endfor %}
|
|
</ul>
|
|
</div>
|
|
{% endif %}
|
|
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% endblock %}
|