Files
kennethreitz 72c84d202c Flesh out the empty study workspace page
The /study landing with no passage loaded was three sentences. Add:
- a proper lede explaining what the workspace gathers onto one desk
- a "What Is On The Desk" tour: six cards covering the passage text,
  commentary, cross-references, original language, chapter context,
  and private on-device notes
- a "How To Write A Reference" guide with accepted citation formats
  (single verse, spans, whole chapters, abbreviations)
- a closing note that notes autosave and noted passages reappear here

Existing tested strings, ids, and their order are preserved.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 23:39:48 -04:00

826 lines
23 KiB
HTML

{% extends "base.html" %}
{% from "macros/seo.html" import breadcrumb_jsonld %}
{% block title %}{% if workspace %}{{ workspace.label }} Study Workspace{% else %}Passage Study Workspace{% endif %} - KJV Study{% endblock %}
{% block description %}{% if workspace %}Study {{ workspace.label }} with KJV text, commentary, cross-references, original language data, and notes.{% else %}Open a KJV passage study workspace with commentary, cross-references, original language data, and notes.{% endif %}{% endblock %}
{% block structured_data %}
{{ breadcrumb_jsonld([
('Home', 'https://kjvstudy.org'),
('Study', 'https://kjvstudy.org/study'),
(workspace.label if workspace else 'Passage Workspace', none),
]) }}
{% endblock %}
{% block head %}
<style>
.workspace-title {
margin-top: 0.25rem;
margin-bottom: 0.5rem;
}
.workspace-subtitle {
max-width: 62%;
color: var(--text-secondary);
font-size: 1.15rem;
font-style: italic;
line-height: 1.7;
margin: 0 0 1.5rem;
}
.study-lookup {
max-width: 42rem;
margin: 1.5rem 0 2.5rem;
}
.study-lookup-row {
display: flex;
gap: 0;
align-items: stretch;
border: 1px solid var(--border-color-darker);
border-radius: 8px;
overflow: hidden;
background: var(--bg-elevated);
box-shadow: var(--shadow-soft);
transition: box-shadow 0.2s ease, border-color 0.2s ease;
}
.study-lookup-row:focus-within {
border-color: var(--accent);
box-shadow: 0 0 0 3px var(--accent-soft), var(--shadow-soft);
}
.study-lookup label {
display: block;
margin-bottom: 0.5rem;
color: var(--bronze);
font-size: 0.72rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.18em;
}
.study-lookup input {
flex: 1;
min-width: 0;
padding: 0.8rem 1rem;
border: none;
background: transparent;
color: var(--text-color);
font-family: inherit;
font-size: 1rem;
}
.study-lookup input:focus {
outline: none;
}
.study-lookup input::placeholder {
color: var(--text-tertiary);
font-style: italic;
}
.study-lookup button {
padding: 0.72rem 1.5rem;
border: none;
background: var(--accent-fill);
color: var(--on-accent);
font-family: inherit;
font-size: 0.82rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.14em;
cursor: pointer;
transition: background 0.15s ease;
}
.study-lookup button:hover {
background: var(--accent-fill-hover);
}
.study-error {
margin: 0.75rem 0 0;
color: var(--rubric);
font-size: 0.95rem;
}
.workspace-actions {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
margin: 1rem 0 2rem;
}
.workspace-action {
display: inline-flex;
align-items: center;
padding: 0.5rem 1rem;
border-radius: 6px;
font-family: inherit;
font-size: 0.9rem;
text-decoration: none;
border-bottom: none;
cursor: pointer;
transition: all 0.2s;
}
.workspace-action.secondary {
background: var(--bg-elevated);
color: var(--text-secondary);
border: 1px solid var(--border-color-dark);
}
.workspace-action.secondary:hover {
background: var(--accent-softer);
border-color: var(--accent);
color: var(--accent-strong);
}
.workspace-layout {
display: grid;
grid-template-columns: minmax(0, 1fr) minmax(18rem, 0.42fr);
gap: 2rem;
align-items: start;
}
.study-panel {
margin-top: 2rem;
}
/* Panel headings — unified small caps over a gold-tipped rule */
.study-panel h2,
.empty-study h2 {
position: relative;
font-size: 0.95rem;
font-weight: 600;
font-style: normal;
text-transform: uppercase;
letter-spacing: 0.18em;
color: var(--text-secondary) !important;
margin: 0 0 1rem 0;
padding-bottom: 0.6rem;
border-bottom: 1px solid var(--border-color);
}
.study-panel h2::after,
.empty-study h2::after {
content: '';
position: absolute;
left: 0;
bottom: -1px;
width: 2.75rem;
height: 1px;
background: var(--gold);
}
.study-panel h3 {
margin-top: 0;
}
/* The passage itself — an elevated reading plate */
.passage-text {
position: relative;
background: var(--bg-elevated);
border: 1px solid var(--border-color-darker);
box-shadow: var(--shadow-soft);
padding: 1.75rem 2rem;
}
.passage-text::before {
content: '';
position: absolute;
inset: 6px;
border: 1px solid var(--border-color-dark);
pointer-events: none;
}
.passage-text p {
position: relative;
font-size: 1.35rem;
line-height: 2;
margin: 0.75rem 0;
}
.passage-text p:first-child {
margin-top: 0;
}
.passage-text p:last-child {
margin-bottom: 0;
}
.verse-study {
border-top: 1px solid var(--border-color);
padding-top: 1.5rem;
margin-top: 1.75rem;
}
.verse-study:first-child {
border-top: none;
padding-top: 0;
}
.verse-study h3 {
font-size: 0.85rem;
font-style: normal;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.16em;
color: var(--bronze) !important;
margin-bottom: 0.75rem;
}
.verse-line {
margin: 0.75rem 0 1rem;
}
.verse-number-link {
font-family: et-book-roman-old-style, et-book, Georgia, serif;
font-size: 0.95rem;
vertical-align: super;
margin-right: 0.25rem;
border-bottom: none;
color: var(--bronze);
}
.study-note {
margin: 1rem 0;
padding: 0.85rem 1.1rem;
border-left: 3px solid var(--accent);
border-radius: 0 6px 6px 0;
background: var(--accent-softer);
}
.study-note h4,
.study-list h4 {
margin: 1rem 0 0.35rem;
font-size: 0.72rem;
font-style: normal;
font-weight: 600;
color: var(--bronze);
text-transform: uppercase;
letter-spacing: 0.16em;
}
.study-note h4:first-child {
margin-top: 0;
}
.study-note p,
.study-note li,
.study-list li {
font-size: 1rem;
line-height: 1.7;
}
.study-list {
margin-top: 1rem;
}
.study-list ul {
margin-top: 0.35rem;
}
.study-list li {
margin-bottom: 0.25rem;
}
.interlinear-preview {
display: flex;
flex-wrap: wrap;
gap: 0.45rem;
margin-top: 0.5rem;
}
.word-chip {
border: 1px solid var(--border-color-dark);
border-radius: 6px;
padding: 0.35rem 0.5rem;
font-size: 0.85rem;
background: var(--bg-elevated);
}
.word-chip a {
border-bottom: none;
color: var(--bronze) !important;
font-size: 0.78rem;
letter-spacing: 0.04em;
}
.word-chip a:hover {
color: var(--accent-strong) !important;
}
.side-workspace {
position: sticky;
top: 4rem;
}
/* Side panels sit on cards */
.side-workspace .study-panel {
background: var(--bg-elevated);
border: 1px solid var(--border-color-dark);
border-radius: 8px;
padding: 1.25rem 1.4rem 1.4rem;
}
.side-workspace .study-panel:first-child {
margin-top: 2rem;
}
.side-workspace textarea {
width: 100%;
min-height: 14rem;
box-sizing: border-box;
padding: 0.9rem;
border: 1px solid var(--border-color-dark);
border-radius: 6px;
background: var(--bg-color);
color: var(--text-color);
font-family: inherit;
font-size: 1rem;
line-height: 1.6;
resize: vertical;
transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
.side-workspace textarea:focus {
outline: none;
border-color: var(--accent);
box-shadow: 0 0 0 3px var(--accent-soft);
}
.note-status {
min-height: 1.25rem;
color: var(--text-tertiary);
font-size: 0.85rem;
margin-top: 0.45rem;
}
.related-list {
list-style: none;
padding-left: 0;
}
.related-list li {
font-size: 0.95rem;
line-height: 1.5;
margin-bottom: 0.45rem;
}
.empty-study {
max-width: 42rem;
}
/* Undo tufte's narrow section columns inside the empty state */
.empty-study > p {
width: 100%;
}
.empty-study .empty-lede {
font-size: 1.2rem;
line-height: 1.9;
}
/* The desk tour — what the workspace gathers for a passage */
.desk-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 1rem;
margin-top: 1.25rem;
}
.desk-item {
padding: 1.1rem 1.25rem;
background: var(--bg-elevated);
border: 1px solid var(--border-color-dark);
border-radius: 8px;
}
.desk-item h3 {
font-size: 0.78rem;
font-weight: 600;
font-style: normal;
text-transform: uppercase;
letter-spacing: 0.16em;
color: var(--bronze) !important;
margin: 0 0 0.4rem;
}
.desk-item p {
font-size: 0.95rem;
line-height: 1.65;
color: var(--text-secondary);
margin: 0;
}
/* Reference-format guide */
.cite-list {
width: 100% !important;
list-style: none;
padding-left: 0 !important;
margin-top: 0.75rem;
}
.cite-list li {
font-size: 1.05rem;
line-height: 1.9;
margin-bottom: 0.35rem;
}
.cite-list strong {
font-weight: 600;
}
.cite-list .cite-example {
font-style: italic;
color: var(--text-secondary);
}
@media (max-width: 700px) {
.desk-grid {
grid-template-columns: 1fr;
}
}
.example-links {
margin-top: 1.5rem;
}
.example-links a {
display: inline-block;
margin: 0 0.6rem 0.6rem 0;
padding: 0.45rem 0.85rem;
background: var(--bg-elevated);
border: 1px solid var(--border-color-dark) !important;
border-radius: 6px;
transition: all 0.2s;
}
.example-links a:hover {
border-color: var(--accent) !important;
color: var(--accent-strong);
background: var(--accent-softer);
}
.saved-note-list {
padding-left: 1.4rem;
margin: 0.75rem 0 0;
}
.saved-note-list li {
font-size: 1.05rem;
line-height: 1.7;
margin-bottom: 0.25rem;
}
@media (max-width: 900px) {
.workspace-layout {
grid-template-columns: 1fr;
}
.side-workspace {
position: static;
}
}
@media (max-width: 760px) {
.workspace-subtitle {
max-width: 100%;
}
.study-lookup-row {
flex-direction: column;
}
.study-lookup button {
width: 100%;
min-height: 44px;
}
.passage-text p {
font-size: 1.25rem;
}
}
</style>
{% endblock %}
{% block content %}
{% if workspace %}<span class="page-kicker">Passage Workspace</span>{% endif %}
<h1 class="workspace-title">{% if workspace %}{{ workspace.label }}{% else %}Passage Study Workspace{% endif %}</h1>
<p class="workspace-subtitle">A focused desk for reading, commentary, cross-references, original language notes, and your own observations.</p>
<form class="study-lookup" method="get" action="/study">
<label for="study-reference">Passage</label>
<div class="study-lookup-row">
<input
id="study-reference"
name="q"
value="{{ query or '' }}"
placeholder="John 3:16 or Romans 8:28-30"
autocomplete="off"
autocorrect="off"
autocapitalize="off"
spellcheck="false"
>
<button type="submit">Open</button>
</div>
{% if error %}
<p class="study-error">{{ error }}</p>
{% endif %}
</form>
{% if not workspace %}
<section class="empty-study" id="saved-study-notes" hidden>
<h2>Your Passages With Notes</h2>
<ul class="saved-note-list" id="saved-note-list"></ul>
</section>
<section class="empty-study">
<h2>Begin With A Passage</h2>
<p class="empty-lede"><span class="newthought">Every passage of Scripture</span> rewards patient attention. Enter a verse, a span of verses, or a whole chapter above, and the workspace gathers everything onto one desk: the full text of the Authorized Version, verse-by-verse commentary, cross-references arranged by theme, the Hebrew or Greek behind each English word, and a place for your own observations.</p>
<p>Or begin with a passage that generations of readers have returned to:</p>
<p class="example-links">
<a href="/study/John/3/16">John 3:16</a>
<a href="/study/Romans/8/28/30">Romans 8:28-30</a>
<a href="/study/1%20Corinthians/13">1 Corinthians 13</a>
<a href="/study/Isaiah/53">Isaiah 53</a>
<a href="/study/Matthew/5/3/12">Matthew 5:3-12</a>
<a href="/study/Ephesians/6/10/18">Ephesians 6:10-18</a>
<a href="/study/Psalms/23">Psalm 23</a>
</p>
</section>
<section class="empty-study">
<h2>What Is On The Desk</h2>
<div class="desk-grid">
<div class="desk-item">
<h3>The Passage</h3>
<p>The complete King James text of your selection, with the words of Christ rubricated in red.</p>
</div>
<div class="desk-item">
<h3>Commentary</h3>
<p>Analysis, historical context, and theological significance for each verse, with questions for reflection.</p>
</div>
<div class="desk-item">
<h3>Cross-References</h3>
<p>Related passages gathered by theme, each one a doorway into the wider witness of Scripture.</p>
</div>
<div class="desk-item">
<h3>Original Language</h3>
<p>Hebrew and Greek word previews with Strong's numbers, linking into the full concordance.</p>
</div>
<div class="desk-item">
<h3>Chapter Context</h3>
<p>An orientation to the surrounding chapter, so no verse is read apart from its setting.</p>
</div>
<div class="desk-item">
<h3>Your Notes</h3>
<p>A private notepad for each passage, kept in your browser on this device — nothing is sent to a server.</p>
</div>
</div>
</section>
<section class="empty-study">
<h2>How To Write A Reference</h2>
<p>The passage box understands the usual ways of citing Scripture:</p>
<ul class="cite-list">
<li><strong>A single verse</strong><span class="cite-example">John 3:16</span></li>
<li><strong>A span of verses</strong><span class="cite-example">Romans 8:28-30</span></li>
<li><strong>A whole chapter</strong><span class="cite-example">Psalm 23</span> or <span class="cite-example">1 Corinthians 13</span></li>
<li><strong>Common abbreviations</strong><span class="cite-example">Gen 1:1</span>, <span class="cite-example">1 Cor 13</span>, <span class="cite-example">Mt 5:3-12</span></li>
</ul>
<p>Your notes are saved automatically as you type, and any passage you have written notes on will be listed here when you return.</p>
</section>
<script>
(function () {
var notesPrefix = 'kjvstudy:notes:';
var section = document.getElementById('saved-study-notes');
var list = document.getElementById('saved-note-list');
if (!section || !list) return;
function pathToLabel(path) {
var parts = path.split('/').filter(Boolean);
if (parts[0] !== 'study' || parts.length < 3) return path;
var book = decodeURIComponent(parts[1].replace(/\+/g, ' '));
var chapter = parts[2];
var verseStart = parts[3];
var verseEnd = parts[4];
if (!verseStart) return book + ' ' + chapter;
if (!verseEnd || verseEnd === verseStart) return book + ' ' + chapter + ':' + verseStart;
return book + ' ' + chapter + ':' + verseStart + '-' + verseEnd;
}
try {
var passages = [];
for (var i = 0; i < localStorage.length; i += 1) {
var key = localStorage.key(i);
if (!key || key.indexOf(notesPrefix) !== 0) continue;
var note = localStorage.getItem(key);
if (!note || !note.trim()) continue;
var path = key.slice(notesPrefix.length);
if (path.indexOf('/study/') !== 0) continue;
passages.push({ href: path, label: pathToLabel(path) });
}
passages.sort(function (a, b) {
return a.label.localeCompare(b.label);
});
passages.forEach(function (passage) {
var item = document.createElement('li');
var link = document.createElement('a');
link.href = passage.href;
link.textContent = passage.label;
item.appendChild(link);
list.appendChild(item);
});
section.hidden = passages.length === 0;
} catch (e) {}
})();
</script>
{% else %}
<div class="workspace-actions">
<a class="workspace-action secondary" href="{{ workspace.chapter_url }}">Chapter</a>
<a class="workspace-action secondary" href="{{ workspace.interlinear_url }}">Interlinear</a>
{% if workspace.verse_start %}
<a class="workspace-action secondary" href="/book/{{ workspace.book }}/chapter/{{ workspace.chapter }}/verse/{{ workspace.verse_start }}">Verse Page</a>
{% endif %}
</div>
<div class="workspace-layout">
<main>
<section class="study-panel">
<h2>{{ workspace.label }}</h2>
<div class="passage-text">
{% for row in workspace.rows %}
<p>
<a href="#verse-{{ row.verse.verse }}" class="verse-number-link">{{ row.verse.verse }}</a>
{{ row.verse.text | red_letter(workspace.book, workspace.chapter, row.verse.verse) | safe }}
</p>
{% endfor %}
</div>
</section>
{% if workspace.chapter_overview %}
<section class="study-panel">
<h2>Chapter Context</h2>
<div>{{ workspace.chapter_overview | safe }}</div>
</section>
{% endif %}
<section class="study-panel">
<h2>Verse Study</h2>
{% for row in workspace.rows %}
<article class="verse-study" id="verse-{{ row.verse.verse }}">
<h3>{{ row.reference }}</h3>
<p class="verse-line">
<a href="/book/{{ workspace.book }}/chapter/{{ workspace.chapter }}/verse/{{ row.verse.verse }}" class="verse-number-link">{{ row.verse.verse }}</a>
{{ row.verse.text | red_letter(workspace.book, workspace.chapter, row.verse.verse) | safe }}
</p>
{% if row.has_commentary %}
<div class="study-note">
{% if row.commentary.analysis %}
<h4>Analysis</h4>
{{ row.commentary.analysis | split_paragraphs | safe }}
{% endif %}
{% if row.commentary.historical %}
<h4>Historical Context</h4>
{{ row.commentary.historical | split_paragraphs | safe }}
{% endif %}
{% if row.commentary.theological %}
<h4>Theological Significance</h4>
{{ row.commentary.theological | split_paragraphs | safe }}
{% endif %}
{% if row.commentary.questions %}
<h4>Reflection</h4>
<ul>
{% for question in row.commentary.questions %}
<li>{{ question }}</li>
{% endfor %}
</ul>
{% endif %}
</div>
{% endif %}
{% if row.word_studies %}
<div class="study-list">
<h4>Word Studies</h4>
<ul>
{% for study in row.word_studies %}
<li>
<strong>{{ study.word }}</strong>:
{{ study.term }}{% if study.translit %} ({{ study.translit }}){% endif %}
{% if study.strongs %}<a href="/strongs/{{ study.strongs }}">{{ study.strongs }}</a>{% endif %}
- {{ study.meaning }}
</li>
{% endfor %}
</ul>
</div>
{% endif %}
{% if row.cross_reference_groups %}
<div class="study-list">
<h4>Cross-References</h4>
<ul>
{% for group in row.cross_reference_groups %}
<li>
<strong>{{ group.description }}:</strong>
{% for ref in group.refs[:6] %}
<a href="{{ ref.url }}">{{ ref.display }}</a>{% if not loop.last %}, {% endif %}
{% endfor %}
</li>
{% endfor %}
</ul>
</div>
{% endif %}
{% if row.interlinear_preview %}
<div class="study-list">
<h4>Original Language</h4>
<div class="interlinear-preview">
{% for word in row.interlinear_preview %}
<span class="word-chip">
{{ word.original }}
{% if word.strongs %}<a href="/strongs/{{ word.strongs }}">{{ word.strongs }}</a>{% endif %}
</span>
{% endfor %}
{% if row.interlinear_count > row.interlinear_preview|length %}
<span class="word-chip">+{{ row.interlinear_count - (row.interlinear_preview|length) }}</span>
{% endif %}
</div>
</div>
{% endif %}
</article>
{% endfor %}
</section>
</main>
<aside class="side-workspace">
<section class="study-panel">
<h2>Notes</h2>
<textarea id="study-notes" data-note-key="kjvstudy:notes:{{ workspace.url }}" aria-label="Notes for {{ workspace.label }}"></textarea>
<div id="note-status" class="note-status"></div>
</section>
{% set related = workspace.related_content %}
{% if related and (related.stories or related.topics or related.resources or related.people or related.study_guides) %}
<section class="study-panel">
<h2>Related</h2>
<ul class="related-list">
{% for item in related.stories[:4] %}
<li><a href="{{ item.url }}">{{ item.name }}</a></li>
{% endfor %}
{% for item in related.topics[:4] %}
<li><a href="{{ item.url }}">{{ item.name }}</a></li>
{% endfor %}
{% for item in related.resources[:4] %}
<li><a href="{{ item.url }}">{{ item.name }}</a></li>
{% endfor %}
{% for item in related.people[:4] %}
<li><a href="{{ item.url }}">{{ item.name }}</a></li>
{% endfor %}
{% for item in related.study_guides[:4] %}
<li><a href="{{ item.url }}">{{ item.name }}</a></li>
{% endfor %}
</ul>
</section>
{% endif %}
</aside>
</div>
<script>
(function () {
var notes = document.getElementById('study-notes');
var status = document.getElementById('note-status');
if (!notes) return;
var key = notes.getAttribute('data-note-key');
try {
notes.value = localStorage.getItem(key) || '';
} catch (e) {}
var timer = null;
notes.addEventListener('input', function () {
window.clearTimeout(timer);
timer = window.setTimeout(function () {
try {
localStorage.setItem(key, notes.value);
status.textContent = notes.value ? 'Saved on this device' : '';
if (typeof updateStudyNotesBadge === 'function') {
updateStudyNotesBadge();
}
} catch (e) {
status.textContent = 'Notes unavailable';
}
}, 180);
});
})();
</script>
{% endif %}
{% endblock %}