Files
familienarchiv/docs/specs/transcription-read-mode-final-spec.html
Marcel 46d64f50a5
Some checks failed
CI / Unit & Component Tests (push) Failing after 1m52s
CI / Backend Unit Tests (push) Failing after 2m54s
CI / E2E Tests (push) Failing after 1h13m6s
docs(specs): add final specs for transcription feature
Three final UI/UX specs for the collaborative transcription system:
- expandable-metadata-header-spec: labeled "Details" toggle with drawer
- annotation-transcription-final-spec: annotation-backed transcription with block-level comment threads
- transcription-read-mode-final-spec: clean split read mode with flowing prose and scroll sync

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 09:27:22 +02:00

805 lines
59 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>Transcription Read Mode — Final Spec (Clean Split)</title>
<link href="https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght@9..144,300;9..144,400;9..144,500&family=DM+Sans:wght@300;400;500;600&family=DM+Mono:wght@400;500&family=Tinos:ital,wght@0,400;0,700;1,400&display=swap" rel="stylesheet"/>
<style>
:root{--color-page:#FAFAF7;--color-surface:#F5F4EE;--color-subtle:#EDECEA;--color-border:#D8D7D0;--color-text-muted:#6B6A63;--color-text:#1C1C18;--navy:#012851;--mint:#A1DCD8;--sand:#F0EFE9;--turquoise:#00C7B1;--accent-bg:rgba(161,220,216,.12);--blue-tint:#E6F1FB;--blue:#2D7DD2;--blue-dark:#185FA5;--green-tint:#E8F5EA;--green:#3D8C4A;--green-dark:#2E6E39;--orange-tint:#FEF0E6;--orange:#E8862A;--orange-dark:#B46820;--color-error:#DC4C3E;--font-display:'Fraunces',Georgia,serif;--font-sans:'DM Sans',system-ui,sans-serif;--font-mono:'DM Mono',monospace;--font-read:'Tinos',Georgia,serif;--radius-sm:4px;--radius-md:6px;--radius-lg:10px;--radius-xl:16px;--shadow-card:0 1px 3px rgba(28,28,24,.06),0 1px 2px rgba(28,28,24,.04);--shadow-raised:0 4px 12px rgba(28,28,24,.08),0 2px 4px rgba(28,28,24,.04);--shadow-overlay:0 8px 32px rgba(28,28,24,.12),0 2px 8px rgba(28,28,24,.06);}
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0;}
body{font-family:var(--font-sans);background:#E8E7E2;color:var(--color-text);font-size:14px;line-height:1.6;}
.doc{max-width:1200px;margin:0 auto;padding:48px 40px 120px;}
.doc-header{display:flex;justify-content:space-between;align-items:flex-end;padding-bottom:28px;border-bottom:1px solid var(--color-border);margin-bottom:48px;background:var(--color-page);margin:-48px -40px 48px;padding:48px 40px 28px;border-radius:var(--radius-xl) var(--radius-xl) 0 0;}
.doc-header h1{font-family:var(--font-display);font-size:28px;font-weight:500;letter-spacing:-.02em;margin-bottom:4px;}
.doc-header p{font-size:13px;color:var(--color-text-muted);max-width:680px;}
.doc-meta{font-family:var(--font-mono);font-size:11px;color:var(--color-text-muted);text-align:right;line-height:1.9;}
.pill{display:inline-block;padding:2px 8px;border-radius:var(--radius-sm);font-size:10px;font-weight:500;letter-spacing:.05em;}
.pill-g{background:var(--green-tint);color:var(--green-dark);}
.section{margin-bottom:64px;}
.section-title{font-size:10px;font-weight:500;letter-spacing:.12em;text-transform:uppercase;color:var(--color-text-muted);padding-bottom:10px;border-bottom:1px solid var(--color-border);margin-bottom:24px;}
.prose{font-size:13px;color:var(--color-text-muted);line-height:1.65;max-width:720px;margin-bottom:20px;}
.scr{margin-bottom:56px;}
.scr-head{display:flex;justify-content:space-between;align-items:center;margin-bottom:6px;}
.scr-head h3{font-family:var(--font-display);font-size:20px;font-weight:500;letter-spacing:-.02em;}
.scr-id{font-family:var(--font-mono);font-size:11px;color:var(--color-text-muted);padding:2px 8px;border:1px solid var(--color-border);border-radius:var(--radius-sm);background:var(--color-page);}
.scr-desc{font-size:12px;color:var(--color-text-muted);line-height:1.6;max-width:720px;margin-bottom:6px;}
.scr-var{font-size:11px;color:var(--color-text-muted);margin-bottom:20px;}.scr-var strong{color:var(--color-text);}
.previews{display:flex;gap:32px;flex-wrap:wrap;justify-content:center;align-items:flex-start;margin-bottom:20px;}
.prev-col{display:flex;flex-direction:column;align-items:center;gap:10px;}
.bp-lbl{font-family:var(--font-mono);font-size:10px;color:var(--color-text-muted);}
.desk{width:100%;max-width:1040px;background:var(--color-page);border-radius:var(--radius-xl);overflow:hidden;box-shadow:var(--shadow-overlay),0 0 0 1px rgba(0,0,0,.06);display:flex;flex-direction:column;min-height:520px;}
.phone{width:320px;flex-shrink:0;background:var(--color-page);border-radius:36px;overflow:hidden;box-shadow:var(--shadow-overlay),0 0 0 1px rgba(0,0,0,.07);display:flex;flex-direction:column;border:6px solid #1C1C18;}
.pst{padding:10px 20px 0;display:flex;justify-content:space-between;align-items:center;font-size:12px;background:var(--color-page);}.pst b{font-weight:600;}.pst span{font-size:10px;}
.pb{flex:1;overflow-y:auto;display:flex;flex-direction:column;}
/* ── FA chrome ── */
.fa-nav{height:32px;background:var(--navy);display:flex;align-items:center;padding:0 12px;gap:8px;flex-shrink:0;}
.fa-logo{font-size:7px;font-weight:900;color:#fff;letter-spacing:.8px;border-bottom:2px solid var(--mint);padding-bottom:1px;}
.fa-link{font-size:5.5px;color:rgba(255,255,255,.4);font-weight:700;text-transform:uppercase;}
.fa-nav-r{margin-left:auto;display:flex;gap:5px;align-items:center;}
.fa-av{width:16px;height:16px;background:rgba(255,255,255,.1);border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:5px;font-weight:800;color:rgba(255,255,255,.5);}
/* ── Topbar ── */
.fa-topbar{background:#fff;border-bottom:1px solid #e4e2d7;display:flex;align-items:center;padding:0 12px;gap:6px;height:42px;flex-shrink:0;}
.fa-topbar .back{width:20px;height:20px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:9px;color:var(--color-text-muted);}
.fa-topbar .title{font-family:Georgia,serif;font-size:11px;color:var(--navy);flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
.fa-chip{display:inline-flex;align-items:center;gap:2px;padding:1px 5px 1px 2px;background:var(--sand);border:1px solid #e4e2d7;border-radius:8px;white-space:nowrap;font-size:7px;color:var(--color-text);}
.fa-chip .av{width:12px;height:12px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:5px;font-weight:800;flex-shrink:0;}
.fa-chip .av.navy{background:var(--navy);color:var(--mint);}
.fa-chip .av.purple{background:#5A3080;color:#fff;}
.fa-topbar-btn{font-size:7px;font-weight:600;padding:3px 8px;border-radius:4px;border:1px solid var(--navy);color:var(--navy);background:transparent;display:flex;align-items:center;gap:3px;cursor:pointer;}
.fa-topbar-btn.ghost{border-color:var(--color-border);color:var(--color-text-muted);font-weight:500;}
.details-toggle{display:inline-flex;align-items:center;gap:3px;padding:2px 8px 2px 6px;border-radius:4px;font-size:7px;font-weight:600;color:var(--color-text-muted);cursor:pointer;border:1px solid var(--color-border);background:transparent;white-space:nowrap;}
/* ── Mode switcher ── */
.mode-sw{display:inline-flex;border:1px solid var(--color-border);border-radius:4px;overflow:hidden;font-size:6px;font-weight:600;}
.mode-sw span{padding:3px 8px;cursor:pointer;color:var(--color-text-muted);}
.mode-sw span.active{background:var(--navy);color:#fff;border-color:var(--navy);}
.mode-sw span:not(.active):hover{background:var(--sand);}
/* ── PDF area ── */
.pdf-area{background:#D4D0C8;flex:1;display:flex;align-items:center;justify-content:center;position:relative;overflow:hidden;}
.paper{background:#FFFEF8;box-shadow:0 2px 8px rgba(0,0,0,.14);border-radius:1px;padding:9px 11px;display:flex;flex-direction:column;gap:2px;position:relative;}
.pl{height:3px;background:#C4BDB0;border-radius:1px;opacity:.5;margin-bottom:2px;}
.ps{height:2px;background:#C4BDB0;border-radius:1px;opacity:.28;margin-bottom:1.5px;}
/* ── Annotation rects (dimmed in read mode) ── */
.ann-rect{position:absolute;border-radius:2px;}
.ann-rect.trans{border:1.5px solid var(--turquoise);background:rgba(0,199,177,.1);}
.ann-rect.trans.dimmed{border-color:rgba(0,199,177,.3);background:rgba(0,199,177,.04);}
/* ── Split ── */
.split{display:flex;flex:1;overflow:hidden;}
.split-handle{width:4px;background:var(--color-border);cursor:col-resize;flex-shrink:0;display:flex;align-items:center;justify-content:center;}
.split-handle::after{content:'';width:2px;height:20px;background:var(--color-text-muted);border-radius:1px;opacity:.3;}
/* ── Read-mode text ── */
.read-text{font-family:var(--font-read);font-size:10px;line-height:1.85;color:var(--color-text);padding:16px 20px;}
.read-text .para{margin-bottom:10px;cursor:pointer;padding:2px 4px;border-radius:3px;transition:background .15s ease;}
.read-text .para:hover{background:rgba(0,199,177,.06);}
.read-text .para.highlighted{background:rgba(0,199,177,.1);transition:background .3s ease;}
.read-text .greeting{font-style:italic;}
.read-text .closing{margin-top:12px;text-align:right;font-style:italic;}
.read-text .illegible{color:var(--color-text-muted);font-style:italic;font-size:9px;}
/* ── Status bar ── */
.status-bar{background:var(--sand);border-top:1px solid #e4e2d7;height:18px;display:flex;align-items:center;padding:0 8px;font-size:7px;color:var(--color-text-muted);gap:8px;flex-shrink:0;}
/* ── Scroll sync highlight on PDF ── */
.ann-rect.highlight-flash{border-color:var(--turquoise) !important;background:rgba(0,199,177,.18) !important;transition:all .3s ease;animation:flash-fade 1.5s ease-out forwards;}
@keyframes flash-fade{0%{background:rgba(0,199,177,.18);border-color:var(--turquoise);}100%{background:rgba(0,199,177,.04);border-color:rgba(0,199,177,.3);}}
/* ── Agent table ── */
.agent{background:var(--color-text);color:#E8E8E2;padding:24px;border-radius:var(--radius-lg);margin-top:20px;}
.agent h4{font-size:9px;font-weight:500;letter-spacing:.1em;text-transform:uppercase;color:#5A5A55;margin-bottom:12px;}
.agent pre{font-family:var(--font-mono);font-size:10px;color:#444440;margin-bottom:16px;line-height:1.8;white-space:pre-wrap;}
.at{width:100%;border-collapse:collapse;font-family:var(--font-mono);font-size:10px;}
.at thead tr{border-bottom:1px solid #2A2A26;}.at th{text-align:left;padding:6px 10px;font-size:8px;font-weight:500;letter-spacing:.08em;text-transform:uppercase;color:#5A5A55;font-family:var(--font-sans);}.at td{padding:5px 10px;border-bottom:1px solid #1E1E1A;vertical-align:top;line-height:1.5;}.at tr:last-child td{border-bottom:none;}.at td:first-child{color:#7A7A72;}.at td:nth-child(2){color:#E8E8E2;font-weight:500;}.at td:nth-child(3){color:#5A5A55;}.at .grp td{padding-top:14px;font-family:var(--font-sans);font-size:8px;font-weight:500;letter-spacing:.08em;text-transform:uppercase;color:#3A3A36;}
.llm{background:var(--color-page);border:2px solid var(--navy);border-radius:var(--radius-xl);padding:32px 40px;margin-top:64px;}
.llm h2{font-family:var(--font-display);font-size:22px;font-weight:500;letter-spacing:-.02em;margin-bottom:8px;color:var(--navy);}
.llm h3{font-size:14px;font-weight:600;margin:20px 0 8px;color:var(--color-text);}
.llm h4{font-size:12px;font-weight:600;margin:16px 0 6px;color:var(--color-text);}
.llm p,.llm li{font-size:13px;color:var(--color-text-muted);line-height:1.65;}
.llm ul,.llm ol{padding-left:20px;margin-bottom:12px;}
.llm li{margin-bottom:4px;}
.llm code{font-family:var(--font-mono);font-size:11px;background:var(--color-surface);padding:1px 5px;border-radius:3px;}
.llm pre{font-family:var(--font-mono);font-size:11px;background:var(--color-surface);padding:12px 16px;border-radius:var(--radius-md);overflow-x:auto;margin:8px 0 12px;line-height:1.6;}
.llm table{width:100%;border-collapse:collapse;margin:12px 0;font-size:12px;}
.llm th,.llm td{text-align:left;padding:6px 10px;border-bottom:1px solid var(--color-border);}
.llm th{font-weight:500;color:var(--color-text);font-size:11px;text-transform:uppercase;letter-spacing:.05em;}
.llm td{color:var(--color-text-muted);}
@media(max-width:900px){.doc{padding:24px 16px 80px;}}
</style>
</head>
<body>
<div class="doc">
<div class="doc-header">
<div>
<h1>Transcription Read Mode &mdash; Final Spec</h1>
<p>A focused reading experience for completed transcriptions. Uses the <strong>clean split</strong> layout: PDF scan on the left, flowing prose on the right. All editing chrome is stripped &mdash; no block borders, no comment threads, no toolbars. The text reads like a letter, not like an editing interface.</p>
</div>
<div class="doc-meta">
Familienarchiv<br/>
<span class="pill pill-g">Final</span><br/>
2026-04-05 &middot; @leonievoss
</div>
</div>
<!-- ═══════════════════════════════════════════════════════════════════════════
SECTION: DESIGN RATIONALE
═══════════════════════════════════════════════════════════════════════════ -->
<div class="section">
<div class="section-title">Design rationale</div>
<p class="prose">Transcribe mode is for editing. But most visits to a completed transcription are for <strong>reading</strong> &mdash; comparing the handwriting with the typed text, sharing with family, or just revisiting a letter. Read mode strips away all editing chrome and presents the transcription as flowing prose alongside the original scan.</p>
<p class="prose">The <strong>clean split</strong> was chosen over the full-page reader (PDF hidden) and the interleaved view (cropped PDF per block) because it preserves the familiar side-by-side layout from transcribe mode while dramatically reducing visual noise. Users can switch between reading and editing without re-learning the spatial layout.</p>
<div style="display:flex;gap:12px;flex-wrap:wrap;font-size:12px;margin-top:20px;">
<div style="background:#fff;border:1px solid var(--color-border);border-radius:var(--radius-md);padding:10px 14px;flex:1;min-width:180px;">
<div style="font-weight:600;color:var(--green);margin-bottom:4px;">Kept</div>
<ul style="padding-left:16px;color:var(--color-text-muted);line-height:1.8;">
<li>Transcription text (flowing prose)</li>
<li>PDF scan viewer (same position)</li>
<li>Topbar (title, Details toggle, person chips)</li>
<li>Mode switcher (Lesen / Bearbeiten)</li>
<li>Resizable split handle</li>
<li>Scroll sync (click paragraph &harr; PDF)</li>
</ul>
</div>
<div style="background:#fff;border:1px solid var(--color-border);border-radius:var(--radius-md);padding:10px 14px;flex:1;min-width:180px;">
<div style="font-weight:600;color:var(--color-error);margin-bottom:4px;">Removed</div>
<ul style="padding-left:16px;color:var(--color-text-muted);line-height:1.8;">
<li>Block borders &amp; numbered badges</li>
<li>Contenteditable / cursor</li>
<li>Comment threads &amp; &ldquo;Kommentieren&rdquo; buttons</li>
<li>Presence dots &amp; user indicators</li>
<li>Hint strip (&ldquo;Markiere eine Passage&hellip;&rdquo;)</li>
<li>Drag handles, sort controls</li>
<li>Auto-save status indicator</li>
<li>Add-block CTA</li>
<li>History / &ldquo;Verlauf&rdquo; button</li>
</ul>
</div>
</div>
</div>
<!-- ═══════════════════════════════════════════════════════════════════════════
S1: DESKTOP — READ MODE
═══════════════════════════════════════════════════════════════════════════ -->
<div class="scr" id="s1">
<div class="scr-head"><h3>S1 &mdash; Desktop read mode</h3><span class="scr-id">S1</span></div>
<div class="scr-desc">Side-by-side split: PDF scan on the left with dimmed annotation outlines, flowing serif prose on the right. The mode switcher shows &ldquo;Lesen&rdquo; as active. Clicking a paragraph briefly highlights the matching PDF region (turquoise flash, 1.5s fade).</div>
<div class="scr-var"><strong>Primary reading state</strong> &mdash; the default view when a transcription exists.</div>
<div class="previews">
<div class="prev-col">
<div class="bp-lbl">Desktop &middot; 1040px</div>
<div class="desk">
<div class="fa-nav">
<div class="fa-logo">FAMILIENARCHIV</div>
<div class="fa-link">Dokumente</div>
<div class="fa-link">Personen</div>
<div class="fa-nav-r"><div class="fa-av">MR</div></div>
</div>
<div class="fa-topbar">
<div class="back">&larr;</div>
<div class="title">Brief von Heinrich an Martha, 14. Mai 1943</div>
<div style="flex:1"></div>
<div class="fa-chip"><div class="av navy">HR</div> Heinrich R.</div>
<span style="font-size:7px;color:var(--color-text-muted);">&rarr;</span>
<div class="fa-chip"><div class="av purple">MR</div> Martha R.</div>
<div style="width:1px;height:16px;background:#e4e2d7;margin:0 4px;"></div>
<div class="details-toggle">Details &#9660;</div>
<div style="width:1px;height:16px;background:#e4e2d7;margin:0 4px;"></div>
<!-- Mode switcher: Lesen active -->
<div class="mode-sw"><span class="active">Lesen</span><span>Bearbeiten</span></div>
</div>
<div class="split" style="height:430px;">
<!-- PDF scan — annotations dimmed -->
<div style="flex:1;display:flex;flex-direction:column;">
<div class="pdf-area" style="flex:1;">
<div class="paper" style="width:55%;min-height:260px;position:relative;">
<div style="font-size:7px;color:#8A8070;font-style:italic;margin-bottom:4px;opacity:.7;">Liebe Martha,</div>
<div class="pl" style="width:90%;"></div><div class="ps" style="width:85%;"></div><div class="ps" style="width:92%;"></div>
<div class="pl" style="width:78%;"></div><div class="ps" style="width:88%;"></div><div class="ps" style="width:70%;"></div>
<div class="pl" style="width:84%;"></div><div class="ps" style="width:90%;"></div><div class="ps" style="width:60%;"></div>
<div class="pl" style="width:75%;"></div><div class="ps" style="width:82%;"></div>
<div style="font-size:6px;color:#8A8070;margin-top:6px;text-align:right;opacity:.7;">Dein Heinrich</div>
<!-- Dimmed annotation outlines — no numbered badges -->
<div class="ann-rect trans dimmed" style="left:2%;top:0%;width:50%;height:10%;"></div>
<div class="ann-rect trans dimmed" style="left:2%;top:14%;width:96%;height:32%;"></div>
<div class="ann-rect trans dimmed" style="left:2%;top:50%;width:96%;height:22%;"></div>
<div class="ann-rect trans dimmed" style="left:20%;top:80%;width:60%;height:12%;"></div>
</div>
</div>
</div>
<div class="split-handle"></div>
<!-- Right panel: flowing prose, no block chrome -->
<div style="width:400px;display:flex;flex-direction:column;border-left:1px solid #e4e2d7;background:#fff;">
<div class="read-text" style="flex:1;overflow-y:auto;">
<div class="para greeting">Liebe Martha,</div>
<div class="para">ich schreibe Dir heute aus dem Lazarett in Breslau. Mach Dir keine Sorgen, es geht mir den Umst&auml;nden entsprechend gut. Der Arzt sagt <span class="illegible">[unleserlich]</span> Wochen noch dauern wird.</div>
<div class="para">Die Kinder sollen wissen, dass ich an sie denke. Sag dem kleinen Fritz, er soll auf seine Mutter aufpassen. Und Lotte soll weiter so flei&szlig;ig in der Schule sein.</div>
<div class="para closing">In ewiger Liebe,<br/>Dein Heinrich</div>
</div>
<div class="status-bar">
<span>4 Abschnitte</span>
<span style="margin-left:auto;">Zuletzt bearbeitet: Oma Inge, 14:23</span>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="agent">
<h4>S1 &middot; Desktop read mode</h4>
<pre>/* Same side-by-side layout as transcribe mode, but the right panel renders
* the transcription as continuous flowing prose instead of block cards.
*
* Key differences from transcribe mode:
* - No block borders, headers, footers, or numbered badges
* - No contenteditable — text is plain rendered HTML
* - No comment threads, no "Kommentieren" buttons
* - No presence dots, no hint strip, no auto-save indicator
* - Annotation rects on PDF are dimmed (opacity ~0.3, no badges)
* - Still clickable for scroll-sync
* - Status bar shows: "4 Abschnitte · Zuletzt bearbeitet: Oma Inge, 14:23"
*
* Scroll sync:
* - Click paragraph → matching PDF annotation flashes turquoise (1.5s fade)
* - Click PDF annotation → matching paragraph gets subtle bg highlight (1.5s fade)
* - PDF auto-scrolls to center the annotation in the viewport */</pre>
<table class="at"><thead><tr><th>Element</th><th>Value</th><th>Notes</th></tr></thead><tbody>
<tr class="grp"><td colspan="3">Text panel</td></tr>
<tr><td>Font</td><td>Tinos (serif), 16px, line-height 1.85</td><td>Generous reading typography</td></tr>
<tr><td>Padding</td><td>24px 32px</td><td>Comfortable margins like a book page</td></tr>
<tr><td>Paragraphs</td><td>One &lt;p&gt; per transcription block</td><td>mb-4 between paragraphs</td></tr>
<tr><td>[unleserlich]</td><td>italic, text-ink-2, font-size: 0.9em</td><td>Subtle but readable</td></tr>
<tr><td>Hover</td><td>Subtle turquoise bg at 6% opacity</td><td>Hint that paragraphs are clickable</td></tr>
<tr class="grp"><td colspan="3">PDF panel</td></tr>
<tr><td>Annotations</td><td>Dimmed: border-opacity 0.3, bg-opacity 0.04</td><td>Still clickable for scroll-sync</td></tr>
<tr><td>Badges</td><td>Hidden</td><td>No numbered circles in read mode</td></tr>
<tr><td>Scroll sync</td><td>Click para &rarr; PDF scrolls, flash 1.5s</td><td>Turquoise tint at 18% &rarr; fade to 4%</td></tr>
<tr class="grp"><td colspan="3">Status bar</td></tr>
<tr><td>Content</td><td>"N Abschnitte &middot; Zuletzt bearbeitet: Name, HH:mm"</td><td>Uses most recent updated_at across blocks</td></tr>
<tr><td>Height</td><td>28px, sand background</td><td>Same as transcribe mode status bar</td></tr>
</tbody></table>
</div>
</div>
<!-- ═══════════════════════════════════════════════════════════════════════════
S2: DESKTOP — SCROLL SYNC INTERACTION
═══════════════════════════════════════════════════════════════════════════ -->
<div class="scr" id="s2">
<div class="scr-head"><h3>S2 &mdash; Scroll sync highlight</h3><span class="scr-id">S2</span></div>
<div class="scr-desc">The user clicked the second paragraph. The matching PDF annotation flashes with a turquoise highlight that fades over 1.5 seconds. The paragraph itself gets a subtle background tint. This is the <strong>only interactive element</strong> in read mode &mdash; no editing, no comments.</div>
<div class="scr-var"><strong>Click-to-highlight interaction</strong> &mdash; bidirectional scroll sync between text and scan.</div>
<div class="previews">
<div class="prev-col">
<div class="bp-lbl">Desktop &middot; 1040px</div>
<div class="desk">
<div class="fa-nav">
<div class="fa-logo">FAMILIENARCHIV</div>
<div class="fa-link">Dokumente</div>
<div class="fa-link">Personen</div>
<div class="fa-nav-r"><div class="fa-av">MR</div></div>
</div>
<div class="fa-topbar">
<div class="back">&larr;</div>
<div class="title">Brief von Heinrich an Martha, 14. Mai 1943</div>
<div style="flex:1"></div>
<div class="fa-chip"><div class="av navy">HR</div> Heinrich R.</div>
<span style="font-size:7px;color:var(--color-text-muted);">&rarr;</span>
<div class="fa-chip"><div class="av purple">MR</div> Martha R.</div>
<div style="width:1px;height:16px;background:#e4e2d7;margin:0 4px;"></div>
<div class="details-toggle">Details &#9660;</div>
<div style="width:1px;height:16px;background:#e4e2d7;margin:0 4px;"></div>
<div class="mode-sw"><span class="active">Lesen</span><span>Bearbeiten</span></div>
</div>
<div class="split" style="height:430px;">
<div style="flex:1;display:flex;flex-direction:column;">
<div class="pdf-area" style="flex:1;">
<div class="paper" style="width:55%;min-height:260px;position:relative;">
<div style="font-size:7px;color:#8A8070;font-style:italic;margin-bottom:4px;opacity:.7;">Liebe Martha,</div>
<div class="pl" style="width:90%;"></div><div class="ps" style="width:85%;"></div><div class="ps" style="width:92%;"></div>
<div class="pl" style="width:78%;"></div><div class="ps" style="width:88%;"></div><div class="ps" style="width:70%;"></div>
<div class="pl" style="width:84%;"></div><div class="ps" style="width:90%;"></div><div class="ps" style="width:60%;"></div>
<div class="pl" style="width:75%;"></div><div class="ps" style="width:82%;"></div>
<div style="font-size:6px;color:#8A8070;margin-top:6px;text-align:right;opacity:.7;">Dein Heinrich</div>
<div class="ann-rect trans dimmed" style="left:2%;top:0%;width:50%;height:10%;"></div>
<!-- THIS annotation is highlighted — user clicked paragraph 2 -->
<div class="ann-rect trans highlight-flash" style="left:2%;top:14%;width:96%;height:32%;"></div>
<div class="ann-rect trans dimmed" style="left:2%;top:50%;width:96%;height:22%;"></div>
<div class="ann-rect trans dimmed" style="left:20%;top:80%;width:60%;height:12%;"></div>
</div>
</div>
</div>
<div class="split-handle"></div>
<div style="width:400px;display:flex;flex-direction:column;border-left:1px solid #e4e2d7;background:#fff;">
<div class="read-text" style="flex:1;overflow-y:auto;">
<div class="para greeting">Liebe Martha,</div>
<!-- THIS paragraph is highlighted -->
<div class="para highlighted">ich schreibe Dir heute aus dem Lazarett in Breslau. Mach Dir keine Sorgen, es geht mir den Umst&auml;nden entsprechend gut. Der Arzt sagt <span class="illegible">[unleserlich]</span> Wochen noch dauern wird.</div>
<div class="para">Die Kinder sollen wissen, dass ich an sie denke. Sag dem kleinen Fritz, er soll auf seine Mutter aufpassen. Und Lotte soll weiter so flei&szlig;ig in der Schule sein.</div>
<div class="para closing">In ewiger Liebe,<br/>Dein Heinrich</div>
</div>
<div class="status-bar">
<span>4 Abschnitte</span>
<span style="margin-left:auto;">Zuletzt bearbeitet: Oma Inge, 14:23</span>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="agent">
<h4>S2 &middot; Scroll sync highlight</h4>
<pre>/* Bidirectional scroll sync with visual feedback.
*
* Text → PDF:
* 1. User clicks a paragraph
* 2. Paragraph gets .highlighted class (turquoise bg at 10%)
* 3. Matching annotation rect gets .highlight-flash class
* 4. PDF viewport scrolls to center the annotation
* 5. Both highlights fade over 1.5s via CSS animation
*
* PDF → Text:
* 1. User clicks a dimmed annotation rect
* 2. Annotation flashes (same .highlight-flash)
* 3. Matching paragraph gets .highlighted
* 4. Text panel scrolls to center the paragraph
* 5. Both fade over 1.5s
*
* Implementation: each paragraph has data-block-id matching the
* transcription block's annotation_id. The annotation rects already
* have annotation IDs from transcribe mode. */</pre>
<table class="at"><thead><tr><th>Element</th><th>Value</th><th>Notes</th></tr></thead><tbody>
<tr class="grp"><td colspan="3">Highlight animation</td></tr>
<tr><td>Paragraph bg</td><td>rgba(0,199,177,.10)</td><td>Turquoise at 10%, fades to 0</td></tr>
<tr><td>Annotation flash</td><td>rgba(0,199,177,.18) &rarr; .04</td><td>Border returns to .3 opacity</td></tr>
<tr><td>Duration</td><td>1.5s ease-out</td><td>CSS animation, no JS timers needed</td></tr>
<tr><td>Scroll behavior</td><td>smooth, block: center</td><td>scrollIntoView({ behavior: 'smooth', block: 'center' })</td></tr>
<tr class="grp"><td colspan="3">Data binding</td></tr>
<tr><td>Paragraph attr</td><td>data-block-id="{annotation_id}"</td><td>Links text to PDF annotation</td></tr>
<tr><td>Annotation attr</td><td>data-annotation-id="{id}"</td><td>Already exists from transcribe mode</td></tr>
</tbody></table>
</div>
</div>
<!-- ═══════════════════════════════════════════════════════════════════════════
S3: DESKTOP — NO TRANSCRIPTION YET
═══════════════════════════════════════════════════════════════════════════ -->
<div class="scr" id="s3">
<div class="scr-head"><h3>S3 &mdash; No transcription (empty state)</h3><span class="scr-id">S3</span></div>
<div class="scr-desc">When no transcription blocks exist, the mode switcher defaults to &ldquo;Bearbeiten&rdquo; and the right panel shows an empty state encouraging the user to start transcribing. The &ldquo;Lesen&rdquo; tab is disabled (greyed out).</div>
<div class="scr-var"><strong>Empty state</strong> &mdash; no read mode available until at least one block exists.</div>
<div class="previews">
<div class="prev-col">
<div class="bp-lbl">Desktop &middot; 1040px</div>
<div class="desk" style="min-height:400px;">
<div class="fa-nav">
<div class="fa-logo">FAMILIENARCHIV</div>
<div class="fa-link">Dokumente</div>
<div class="fa-link">Personen</div>
<div class="fa-nav-r"><div class="fa-av">MR</div></div>
</div>
<div class="fa-topbar">
<div class="back">&larr;</div>
<div class="title">Brief von Heinrich an Martha, 14. Mai 1943</div>
<div style="flex:1"></div>
<div class="details-toggle">Details &#9660;</div>
<div style="width:1px;height:16px;background:#e4e2d7;margin:0 4px;"></div>
<!-- Mode switcher: Bearbeiten active, Lesen disabled -->
<div class="mode-sw"><span style="opacity:.35;cursor:not-allowed;">Lesen</span><span class="active">Bearbeiten</span></div>
</div>
<div class="split" style="height:320px;">
<div style="flex:1;display:flex;flex-direction:column;">
<div class="pdf-area" style="flex:1;">
<div class="paper" style="width:55%;min-height:200px;">
<div style="font-size:7px;color:#8A8070;font-style:italic;margin-bottom:4px;opacity:.7;">Liebe Martha,</div>
<div class="pl" style="width:90%;"></div><div class="ps" style="width:85%;"></div><div class="ps" style="width:92%;"></div>
<div class="pl" style="width:78%;"></div><div class="ps" style="width:88%;"></div>
<div style="font-size:6px;color:#8A8070;margin-top:6px;text-align:right;opacity:.7;">Dein Heinrich</div>
</div>
</div>
</div>
<div class="split-handle"></div>
<div style="width:400px;display:flex;flex-direction:column;border-left:1px solid #e4e2d7;background:#fff;align-items:center;justify-content:center;text-align:center;padding:32px;">
<div style="width:48px;height:48px;border-radius:50%;background:var(--sand);display:flex;align-items:center;justify-content:center;margin-bottom:12px;">
<span style="font-size:20px;opacity:.5;">&#9998;</span>
</div>
<div style="font-family:var(--font-sans);font-size:11px;font-weight:600;color:var(--color-text);margin-bottom:4px;">Noch keine Transkription</div>
<div style="font-family:var(--font-sans);font-size:10px;color:var(--color-text-muted);max-width:200px;line-height:1.6;">Zeichne Bereiche auf dem Scan und tippe den Text ab, um eine Transkription zu erstellen.</div>
</div>
</div>
</div>
</div>
</div>
<div class="agent">
<h4>S3 &middot; Empty state</h4>
<pre>/* When transcription_blocks count is 0:
* - Mode switcher defaults to "Bearbeiten"
* - "Lesen" tab is disabled: opacity 0.35, cursor: not-allowed, not clickable
* - Right panel shows empty state with pencil icon, title, and description
* - No status bar (nothing to show)
*
* As soon as the first block is saved, "Lesen" becomes enabled.
* The mode does NOT auto-switch — user stays in Bearbeiten. */</pre>
<table class="at"><thead><tr><th>Element</th><th>Value</th><th>Notes</th></tr></thead><tbody>
<tr class="grp"><td colspan="3">Empty state</td></tr>
<tr><td>Icon</td><td>Pencil in 48px sand circle</td><td>Centered vertically in panel</td></tr>
<tr><td>Title</td><td>"Noch keine Transkription"</td><td>i18n key: transcription_empty_title</td></tr>
<tr><td>Description</td><td>"Zeichne Bereiche auf dem Scan&hellip;"</td><td>i18n key: transcription_empty_desc</td></tr>
<tr class="grp"><td colspan="3">Mode switcher</td></tr>
<tr><td>Lesen tab</td><td>Disabled: opacity .35, not-allowed</td><td>Enabled when block count &gt; 0</td></tr>
<tr><td>Default</td><td>"Bearbeiten" active</td><td>Enters transcribe mode directly</td></tr>
</tbody></table>
</div>
</div>
<!-- ═══════════════════════════════════════════════════════════════════════════
S4: MOBILE — READ MODE
═══════════════════════════════════════════════════════════════════════════ -->
<div class="scr" id="s4">
<div class="scr-head"><h3>S4 &mdash; Mobile read mode</h3><span class="scr-id">S4</span></div>
<div class="scr-desc">On mobile, the split view becomes vertical: a collapsible PDF strip (70px) at the top, flowing text below. The mode switcher abbreviates &ldquo;Bearbeiten&rdquo; to &ldquo;Bearb.&rdquo; to fit. Tapping the PDF strip expands it; tapping again collapses. Paragraphs are still tappable for scroll-sync.</div>
<div class="scr-var"><strong>Mobile layout</strong> &mdash; stacked vertical with collapsible scan strip.</div>
<div class="previews">
<div class="prev-col">
<div class="bp-lbl">Mobile &middot; 320px</div>
<div class="phone" style="height:620px;">
<div class="pst"><b>14:23</b><span>&bull;&bull;&bull; WiFi &#128267;</span></div>
<div class="pb">
<div style="background:#fff;border-bottom:1px solid #e4e2d7;padding:6px 12px;display:flex;align-items:center;gap:6px;">
<span style="font-size:11px;color:var(--color-text-muted);">&larr;</span>
<span style="font-family:Georgia,serif;font-size:11px;color:var(--navy);flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;">Brief von Heinrich, 14.05.1943</span>
<div class="mode-sw"><span class="active" style="font-size:7px;">Lesen</span><span style="font-size:7px;">Bearb.</span></div>
</div>
<!-- Collapsible PDF strip -->
<div style="background:#D4D0C8;height:70px;display:flex;align-items:center;justify-content:center;border-bottom:1px solid #C4C0B8;position:relative;">
<div style="background:#FFFEF8;width:42%;padding:5px 7px;box-shadow:0 1px 3px rgba(0,0,0,.1);border-radius:1px;">
<div style="font-size:5px;color:#8A8070;font-style:italic;opacity:.7;">Liebe Martha,</div>
<div style="height:2px;background:#C4BDB0;opacity:.4;margin:2px 0;width:80%;"></div>
<div style="height:1.5px;background:#C4BDB0;opacity:.2;margin:1px 0;width:90%;"></div>
</div>
<!-- Expand hint -->
<div style="position:absolute;bottom:3px;right:8px;font-size:6px;color:var(--color-text-muted);opacity:.6;">&#9650; Scan vergr&ouml;&szlig;ern</div>
</div>
<!-- Flowing text -->
<div style="flex:1;overflow-y:auto;padding:16px 16px;background:#fff;">
<div style="font-family:'Tinos',Georgia,serif;font-size:13px;line-height:1.9;color:var(--color-text);">
<p style="margin-bottom:10px;font-style:italic;">Liebe Martha,</p>
<p style="margin-bottom:10px;">ich schreibe Dir heute aus dem Lazarett in Breslau. Mach Dir keine Sorgen, es geht mir den Umst&auml;nden entsprechend gut. Der Arzt sagt <em style="color:var(--color-text-muted);">[unleserlich]</em> Wochen noch dauern wird.</p>
<p style="margin-bottom:10px;">Die Kinder sollen wissen, dass ich an sie denke. Sag dem kleinen Fritz, er soll auf seine Mutter aufpassen. Und Lotte soll weiter so flei&szlig;ig in der Schule sein.</p>
<p style="text-align:right;font-style:italic;">In ewiger Liebe,<br/>Dein Heinrich</p>
</div>
</div>
<!-- Status bar -->
<div style="background:var(--sand);border-top:1px solid #e4e2d7;padding:4px 12px;font-size:8px;color:var(--color-text-muted);display:flex;justify-content:space-between;">
<span>4 Abschnitte</span>
<span>Oma Inge, 14:23</span>
</div>
</div>
</div>
</div>
<!-- Mobile: expanded PDF strip -->
<div class="prev-col">
<div class="bp-lbl">Mobile &middot; 320px &middot; scan expanded</div>
<div class="phone" style="height:620px;">
<div class="pst"><b>14:23</b><span>&bull;&bull;&bull; WiFi &#128267;</span></div>
<div class="pb">
<div style="background:#fff;border-bottom:1px solid #e4e2d7;padding:6px 12px;display:flex;align-items:center;gap:6px;">
<span style="font-size:11px;color:var(--color-text-muted);">&larr;</span>
<span style="font-family:Georgia,serif;font-size:11px;color:var(--navy);flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;">Brief von Heinrich, 14.05.1943</span>
<div class="mode-sw"><span class="active" style="font-size:7px;">Lesen</span><span style="font-size:7px;">Bearb.</span></div>
</div>
<!-- Expanded PDF strip -->
<div style="background:#D4D0C8;height:200px;display:flex;align-items:center;justify-content:center;border-bottom:1px solid #C4C0B8;position:relative;">
<div style="background:#FFFEF8;width:50%;padding:8px 10px;box-shadow:0 2px 6px rgba(0,0,0,.12);border-radius:1px;position:relative;">
<div style="font-size:6px;color:#8A8070;font-style:italic;opacity:.7;margin-bottom:3px;">Liebe Martha,</div>
<div class="pl" style="width:90%;height:2px;"></div><div class="ps" style="width:85%;height:1.5px;"></div><div class="ps" style="width:92%;height:1.5px;"></div>
<div class="pl" style="width:78%;height:2px;"></div><div class="ps" style="width:88%;height:1.5px;"></div>
<div class="pl" style="width:84%;height:2px;"></div><div class="ps" style="width:70%;height:1.5px;"></div>
<div style="font-size:5px;color:#8A8070;margin-top:4px;text-align:right;opacity:.7;">Dein Heinrich</div>
<!-- Dimmed annotations visible when expanded -->
<div class="ann-rect trans dimmed" style="left:3%;top:0%;width:45%;height:12%;"></div>
<div class="ann-rect trans dimmed" style="left:3%;top:16%;width:94%;height:35%;"></div>
</div>
<!-- Collapse hint -->
<div style="position:absolute;bottom:3px;right:8px;font-size:6px;color:var(--color-text-muted);opacity:.6;">&#9660; Scan verkleinern</div>
</div>
<!-- Text below (shorter) -->
<div style="flex:1;overflow-y:auto;padding:14px 16px;background:#fff;">
<div style="font-family:'Tinos',Georgia,serif;font-size:13px;line-height:1.9;color:var(--color-text);">
<p style="margin-bottom:10px;font-style:italic;">Liebe Martha,</p>
<p style="margin-bottom:10px;">ich schreibe Dir heute aus dem Lazarett in Breslau&hellip;</p>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="agent">
<h4>S4 &middot; Mobile read mode</h4>
<pre>/* On viewports < 768px, the side-by-side split becomes vertical:
* - PDF scan strip at top (collapsed: 70px, expanded: ~50vh)
* - Flowing text below, full-width
* - Tap PDF strip to toggle expand/collapse
* - Expand hint text: " Scan vergrößern" / " Scan verkleinern"
*
* Mode switcher abbreviates: "Lesen | Bearb."
* Scroll-sync: tapping a paragraph briefly highlights the matching
* region in the expanded PDF. If PDF is collapsed, it auto-expands
* first, then scrolls to the annotation.
*
* Same status bar at the bottom, same flowing prose styling. */</pre>
<table class="at"><thead><tr><th>Element</th><th>Value</th><th>Notes</th></tr></thead><tbody>
<tr class="grp"><td colspan="3">PDF strip</td></tr>
<tr><td>Collapsed height</td><td>70px</td><td>Shows miniature scan preview</td></tr>
<tr><td>Expanded height</td><td>~50vh or max 300px</td><td>Smooth CSS transition (300ms ease)</td></tr>
<tr><td>Toggle</td><td>Tap anywhere on strip</td><td>Hint text in bottom-right corner</td></tr>
<tr class="grp"><td colspan="3">Text area</td></tr>
<tr><td>Font</td><td>Tinos, 15px, line-height 1.9</td><td>Slightly larger than desktop for touch</td></tr>
<tr><td>Padding</td><td>16px</td><td>Full-width, no wasted space</td></tr>
<tr class="grp"><td colspan="3">Mode switcher</td></tr>
<tr><td>Labels</td><td>"Lesen | Bearb."</td><td>Abbreviated to fit mobile topbar</td></tr>
<tr><td>Font size</td><td>10px</td><td>Compact but readable</td></tr>
<tr class="grp"><td colspan="3">Scroll sync on mobile</td></tr>
<tr><td>Tap paragraph</td><td>Expand PDF if collapsed, then highlight</td><td>Auto-expand + scroll + flash</td></tr>
<tr><td>Tap annotation</td><td>Collapse PDF, scroll text to paragraph</td><td>Smart collapse after showing match</td></tr>
</tbody></table>
</div>
</div>
<!-- ═══════════════════════════════════════════════════════════════════════════
S5: MODE SWITCHER DETAIL
═══════════════════════════════════════════════════════════════════════════ -->
<div class="scr" id="s5">
<div class="scr-head"><h3>S5 &mdash; Mode switcher states</h3><span class="scr-id">S5</span></div>
<div class="scr-desc">The mode switcher is a segmented control in the topbar that replaces the previous &ldquo;Transkribieren&rdquo; turquoise button. It governs three visual states: <strong>Lesen</strong> (read mode, this spec), <strong>Bearbeiten</strong> (transcribe/edit mode), and the existing <strong>Annotieren</strong> button (yellow comment annotations). The modes are mutually exclusive.</div>
<div class="scr-var"><strong>Segmented control</strong> &mdash; replacing the turquoise &ldquo;Transkribieren&rdquo; button.</div>
<div class="previews" style="gap:16px;">
<!-- State 1: Lesen active -->
<div class="prev-col" style="align-items:center;">
<div class="bp-lbl">Lesen active</div>
<div style="background:#fff;border:1px solid #e4e2d7;border-radius:var(--radius-md);padding:12px 16px;display:flex;align-items:center;gap:8px;">
<div class="mode-sw" style="font-size:8px;"><span class="active" style="padding:4px 12px;">Lesen</span><span style="padding:4px 12px;">Bearbeiten</span></div>
<div style="width:1px;height:20px;background:#e4e2d7;"></div>
<div class="fa-topbar-btn ghost" style="font-size:8px;padding:4px 10px;">&#9998; Annotieren</div>
</div>
</div>
<!-- State 2: Bearbeiten active -->
<div class="prev-col" style="align-items:center;">
<div class="bp-lbl">Bearbeiten active</div>
<div style="background:#fff;border:1px solid #e4e2d7;border-radius:var(--radius-md);padding:12px 16px;display:flex;align-items:center;gap:8px;">
<div class="mode-sw" style="font-size:8px;"><span style="padding:4px 12px;">Lesen</span><span class="active" style="padding:4px 12px;">Bearbeiten</span></div>
<div style="width:1px;height:20px;background:#e4e2d7;"></div>
<div class="fa-topbar-btn ghost" style="font-size:8px;padding:4px 10px;">&#9998; Annotieren</div>
</div>
</div>
<!-- State 3: Annotieren active -->
<div class="prev-col" style="align-items:center;">
<div class="bp-lbl">Annotieren active (separate button)</div>
<div style="background:#fff;border:1px solid #e4e2d7;border-radius:var(--radius-md);padding:12px 16px;display:flex;align-items:center;gap:8px;">
<div class="mode-sw" style="font-size:8px;"><span style="padding:4px 12px;opacity:.5;">Lesen</span><span style="padding:4px 12px;opacity:.5;">Bearbeiten</span></div>
<div style="width:1px;height:20px;background:#e4e2d7;"></div>
<div class="fa-topbar-btn" style="font-size:8px;padding:4px 10px;background:var(--navy);color:#fff;border-color:var(--navy);">&#9998; Annotieren</div>
</div>
</div>
<!-- State 4: Lesen disabled (no blocks) -->
<div class="prev-col" style="align-items:center;">
<div class="bp-lbl">No transcription blocks</div>
<div style="background:#fff;border:1px solid #e4e2d7;border-radius:var(--radius-md);padding:12px 16px;display:flex;align-items:center;gap:8px;">
<div class="mode-sw" style="font-size:8px;"><span style="padding:4px 12px;opacity:.35;cursor:not-allowed;">Lesen</span><span class="active" style="padding:4px 12px;">Bearbeiten</span></div>
<div style="width:1px;height:20px;background:#e4e2d7;"></div>
<div class="fa-topbar-btn ghost" style="font-size:8px;padding:4px 10px;">&#9998; Annotieren</div>
</div>
</div>
</div>
<div class="agent">
<h4>S5 &middot; Mode switcher states</h4>
<pre>/* Three mutually exclusive modes:
*
* 1. Lesen (read) — this spec. Flowing prose, dimmed annotations, no editing.
* 2. Bearbeiten (edit) — annotation-transcription-final-spec. Block cards, contenteditable.
* 3. Annotieren — yellow comment annotations on PDF. Separate button, not in segmented control.
*
* The segmented control only contains Lesen + Bearbeiten.
* Annotieren is a separate button that, when active, deselects both Lesen and Bearbeiten
* (both appear deselected/dimmed in the segmented control).
*
* When the user clicks Annotieren while in read/transcribe mode:
* → Enter annotate mode, both segmented items dim
* When the user clicks a segmented item while in annotate mode:
* → Exit annotate mode, enter the selected mode
*
* State: let mode: 'read' | 'transcribe' | 'annotate' = $state(...)
* Default: 'read' if blocks.length > 0, else 'transcribe'
* The "Annotieren" button is hidden if !canAnnotate || !isPdf */</pre>
<table class="at"><thead><tr><th>Element</th><th>Value</th><th>Notes</th></tr></thead><tbody>
<tr class="grp"><td colspan="3">Segmented control</td></tr>
<tr><td>Items</td><td>"Lesen" | "Bearbeiten"</td><td>Mobile: "Lesen" | "Bearb."</td></tr>
<tr><td>Active style</td><td>bg:navy, color:#fff</td><td>Rounded within the pill</td></tr>
<tr><td>Inactive style</td><td>bg:transparent, color:muted</td><td>Hover: bg:sand</td></tr>
<tr><td>Dimmed style</td><td>Both items at opacity .5</td><td>Only when annotate mode is active</td></tr>
<tr><td>Disabled (Lesen)</td><td>opacity .35, cursor not-allowed</td><td>When no transcription blocks exist</td></tr>
<tr class="grp"><td colspan="3">Annotieren button</td></tr>
<tr><td>Default</td><td>Ghost style (border:muted)</td><td>Same as current topbar button</td></tr>
<tr><td>Active</td><td>bg:navy, color:#fff</td><td>Filled state when annotate mode on</td></tr>
<tr><td>Visibility</td><td>canAnnotate &amp;&amp; isPdf</td><td>Hidden for non-PDF documents</td></tr>
<tr class="grp"><td colspan="3">Accessibility</td></tr>
<tr><td>Segmented</td><td>role="tablist", children role="tab"</td><td>aria-selected on active tab</td></tr>
<tr><td>Annotieren</td><td>aria-pressed={annotateMode}</td><td>Toggle button semantics</td></tr>
<tr><td>Disabled tab</td><td>aria-disabled="true", tabindex="-1"</td><td>Not focusable when no blocks</td></tr>
</tbody></table>
</div>
</div>
<!-- ═══ LLM IMPLEMENTATION GUIDE ═══ -->
<div class="llm">
<h2>Implementation Guide &mdash; Transcription Read Mode (Clean Split)</h2>
<h3>1. Overview</h3>
<p>Read mode is the default view for documents that have transcription blocks. It reuses the same side-by-side split layout as transcribe mode but replaces the editable block cards with flowing serif prose. The goal is a distraction-free reading experience that still lets users compare handwriting with typed text.</p>
<h3>2. Mode State Management</h3>
<p>The document detail page manages a single <code>mode</code> state that governs the entire view:</p>
<pre>let mode: 'read' | 'transcribe' | 'annotate' = $state(
blocks.length > 0 ? 'read' : 'transcribe'
);</pre>
<ul>
<li><code>mode === 'read'</code> &rarr; this spec (flowing prose, dimmed annotations, no editing)</li>
<li><code>mode === 'transcribe'</code> &rarr; annotation-transcription-final-spec (block cards, contenteditable)</li>
<li><code>mode === 'annotate'</code> &rarr; yellow comment annotations on PDF</li>
<li>The segmented control in the topbar toggles between <code>'read'</code> and <code>'transcribe'</code>.</li>
<li>The &ldquo;Annotieren&rdquo; button toggles <code>'annotate'</code> on/off. When entering annotate mode, the previous mode (read or transcribe) is stored so the user returns to it when exiting.</li>
</ul>
<h3>3. Component Architecture</h3>
<h4>3a. New components</h4>
<table>
<thead><tr><th>Component</th><th>Purpose</th></tr></thead>
<tbody>
<tr><td><code>TranscriptionReadView.svelte</code></td><td>Right panel content in read mode. Renders transcription blocks as flowing prose (<code>&lt;article&gt;</code> with <code>&lt;p&gt;</code> per block). Handles scroll-sync click handlers.</td></tr>
<tr><td><code>ModeSwitcher.svelte</code></td><td>Segmented control (<code>Lesen | Bearbeiten</code>). Props: <code>mode</code> (bindable), <code>hasBlocks</code> (disables Lesen when false). Emits mode changes.</td></tr>
</tbody>
</table>
<h4>3b. Modified components</h4>
<table>
<thead><tr><th>Component</th><th>Change</th></tr></thead>
<tbody>
<tr><td><code>DocumentTopBar.svelte</code></td><td>Replace the <code>Transkribieren</code> button with <code>ModeSwitcher</code>. Keep the <code>Annotieren</code> button separate. Add <code>mode</code> bindable prop.</td></tr>
<tr><td><code>[id]/+page.svelte</code></td><td>Add <code>mode</code> state. Conditionally render <code>TranscriptionReadView</code> vs <code>TranscriptionEditView</code> in the right panel based on <code>mode</code>.</td></tr>
<tr><td><code>PdfAnnotationLayer.svelte</code></td><td>Accept <code>dimmed</code> prop. When true: annotation rects get opacity 0.3, no numbered badges, but remain clickable for scroll-sync.</td></tr>
</tbody>
</table>
<h3>4. Read View Rendering</h3>
<h4>4a. Text rendering</h4>
<ul>
<li>Fetch transcription blocks from <code>GET /api/documents/{id}/transcription-blocks</code> (same endpoint as transcribe mode).</li>
<li>Render each block as a <code>&lt;p data-block-id="{block.annotation_id}"&gt;</code> inside an <code>&lt;article&gt;</code> element.</li>
<li>Typography: <code>font-family: Tinos, Georgia, serif; font-size: 16px; line-height: 1.85</code>.</li>
<li>Padding: <code>24px 32px</code> for comfortable reading margins.</li>
<li><code>[unleserlich]</code> markers: detect via regex <code>/\[unleserlich\]/g</code> and wrap in <code>&lt;em class="text-ink-2 italic text-[0.9em]"&gt;</code>.</li>
<li>Text is <strong>not</strong> contenteditable. No cursor, no selection highlights, no editing.</li>
</ul>
<h4>4b. Scroll sync</h4>
<ul>
<li>Each paragraph has a click handler that dispatches a <code>highlight-annotation</code> event with the <code>annotation_id</code>.</li>
<li>The PDF viewer listens for this event, scrolls to the annotation, and applies a CSS animation (<code>flash-fade</code>, 1.5s ease-out).</li>
<li>Reverse direction: clicking a dimmed annotation on the PDF dispatches <code>highlight-paragraph</code> with the <code>annotation_id</code>. The text panel scrolls the matching paragraph into view and applies a background highlight that fades.</li>
<li>Use <code>scrollIntoView({ behavior: 'smooth', block: 'center' })</code> for both directions.</li>
<li>The highlight CSS animation: <code>background rgba(0,199,177,.10) &rarr; transparent</code> over 1.5s.</li>
</ul>
<h3>5. PDF Annotations in Read Mode</h3>
<ul>
<li>Turquoise annotation rectangles are rendered but <strong>dimmed</strong>: border opacity 0.3, background opacity 0.04.</li>
<li>No numbered badges (the <code>.ann-num</code> elements are hidden via <code>display: none</code>).</li>
<li>Annotations remain clickable &mdash; they trigger scroll-sync to the matching paragraph.</li>
<li>When an annotation is flash-highlighted (via scroll-sync), it briefly returns to full opacity before fading back to dimmed.</li>
<li>Yellow comment annotations are not shown in read mode (they belong to annotate mode only).</li>
</ul>
<h3>6. Status Bar</h3>
<ul>
<li>Positioned at the bottom of the text panel (not the full viewport).</li>
<li>Content: <code>"{n} Abschnitte &middot; Zuletzt bearbeitet: {userName}, {HH:mm}"</code></li>
<li>The &ldquo;Zuletzt bearbeitet&rdquo; timestamp is the most recent <code>updated_at</code> across all transcription blocks for this document.</li>
<li>The user name comes from the <code>updated_by</code> field of that most recently updated block.</li>
<li>i18n keys: <code>transcription_status_sections</code>, <code>transcription_status_last_edited</code>.</li>
</ul>
<h3>7. Mobile Layout (viewport &lt; 768px)</h3>
<ul>
<li>The side-by-side split becomes vertical: PDF strip at top, text below.</li>
<li>PDF strip collapsed height: <code>70px</code>. Shows a miniature scan preview.</li>
<li>Tap strip to expand (~50vh, max 300px). Tap again to collapse. Smooth CSS transition (300ms ease).</li>
<li>Expand/collapse hint text in bottom-right corner of the strip.</li>
<li>Mode switcher abbreviation: &ldquo;Lesen | Bearb.&rdquo; (i18n key: <code>mode_edit_short</code>).</li>
<li>Scroll-sync on paragraph tap: if PDF is collapsed, auto-expand first, then scroll to annotation.</li>
<li>Text typography: <code>15px</code> (slightly larger than desktop) with <code>line-height: 1.9</code>.</li>
</ul>
<h3>8. Empty State</h3>
<ul>
<li>When <code>transcription_blocks</code> count is 0, &ldquo;Lesen&rdquo; tab is disabled (<code>opacity: 0.35</code>, <code>cursor: not-allowed</code>, <code>aria-disabled="true"</code>).</li>
<li>Mode defaults to <code>'transcribe'</code>.</li>
<li>Right panel shows empty state: pencil icon in 48px sand circle, title (&ldquo;Noch keine Transkription&rdquo;), description (&ldquo;Zeichne Bereiche auf dem Scan&hellip;&rdquo;).</li>
<li>As soon as the first block is saved, &ldquo;Lesen&rdquo; becomes clickable. Mode does not auto-switch.</li>
</ul>
<h3>9. i18n Keys</h3>
<table>
<thead><tr><th>Key</th><th>de</th><th>en</th></tr></thead>
<tbody>
<tr><td><code>mode_read</code></td><td>Lesen</td><td>Read</td></tr>
<tr><td><code>mode_edit</code></td><td>Bearbeiten</td><td>Edit</td></tr>
<tr><td><code>mode_edit_short</code></td><td>Bearb.</td><td>Edit</td></tr>
<tr><td><code>transcription_status_sections</code></td><td>{n} Abschnitte</td><td>{n} sections</td></tr>
<tr><td><code>transcription_status_last_edited</code></td><td>Zuletzt bearbeitet: {name}, {time}</td><td>Last edited: {name}, {time}</td></tr>
<tr><td><code>transcription_empty_title</code></td><td>Noch keine Transkription</td><td>No transcription yet</td></tr>
<tr><td><code>transcription_empty_desc</code></td><td>Zeichne Bereiche auf dem Scan und tippe den Text ab, um eine Transkription zu erstellen.</td><td>Draw regions on the scan and type the text to create a transcription.</td></tr>
<tr><td><code>scan_expand</code></td><td>Scan vergr&ouml;&szlig;ern</td><td>Expand scan</td></tr>
<tr><td><code>scan_collapse</code></td><td>Scan verkleinern</td><td>Collapse scan</td></tr>
</tbody>
</table>
<h3>10. Accessibility</h3>
<ul>
<li>Mode switcher: <code>role="tablist"</code> with <code>role="tab"</code> children, <code>aria-selected</code> on active tab.</li>
<li>Disabled &ldquo;Lesen&rdquo; tab: <code>aria-disabled="true"</code>, <code>tabindex="-1"</code>.</li>
<li>Read view text: semantic HTML &mdash; <code>&lt;article&gt;</code> wrapping <code>&lt;p&gt;</code> elements. No <code>contenteditable</code>.</li>
<li>Paragraphs are clickable: <code>role="button"</code>, <code>tabindex="0"</code>, <code>aria-label="Abschnitt N &mdash; klicken um Scan-Position anzuzeigen"</code>.</li>
<li>PDF strip toggle on mobile: <code>role="button"</code>, <code>aria-expanded="{expanded}"</code>, <code>aria-label="Scan {expanded ? 'verkleinern' : 'vergr&ouml;&szlig;ern'}"</code>.</li>
<li>Scroll-sync animations respect <code>prefers-reduced-motion</code>: skip the 1.5s fade, apply instant highlight that disappears after 200ms.</li>
</ul>
</div>
</div>
</body>
</html>