Files
familienarchiv/docs/specs/geschichten-document-integration-spec.html
2026-05-05 12:39:20 +02:00

632 lines
36 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Geschichten — Dokumentverknüpfung · Familienarchiv</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&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;--blue-tint:#E6F1FB;--blue:#2D7DD2;--blue-dark:#185FA5;--green-tint:#E8F5EA;--green:#3D8C4A;--green-dark:#2E6E39;--orange-tint:#FEF0E6;--orange:#E8862A;--orange-dark:#B46820;--font-display:'Fraunces',Georgia,serif;--font-sans:'DM Sans',system-ui,sans-serif;--font-mono:'DM Mono',monospace;--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-o{background:var(--orange-tint);color:var(--orange-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;}
.jh{padding:20px 24px;border-radius:var(--radius-xl);margin-bottom:40px;display:flex;align-items:center;gap:16px;}
.jh .jn{font-family:var(--font-display);font-size:48px;font-weight:300;line-height:1;opacity:.5;}
.jh h2{font-family:var(--font-display);font-size:22px;font-weight:500;letter-spacing:-.02em;margin-bottom:4px;}
.jh p{font-size:13px;line-height:1.5;}.jh .fl{font-family:var(--font-mono);font-size:11px;margin-top:6px;opacity:.7;}
.jh-o{background:var(--orange-tint);border:1px solid #F0C89A;}.jh-o .jn{color:var(--orange);}.jh-o p,.jh-o .fl{color:var(--orange-dark);}
.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:480px;}
.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-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;letter-spacing:.05em;}
.fa-link.on{color:rgba(255,255,255,.9);}
.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);}
.agent{background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-lg);padding:24px;margin-top:20px;}
.agent h4{font-family:var(--font-mono);font-size:12px;font-weight:500;margin-bottom:8px;color:var(--navy);}
.agent pre{font-family:var(--font-mono);font-size:11px;color:var(--color-text-muted);margin-bottom:12px;white-space:pre-wrap;}
.at{width:100%;border-collapse:collapse;font-size:11px;}
.at th{font-family:var(--font-mono);font-size:10px;font-weight:500;text-align:left;color:var(--color-text-muted);padding:6px 8px;border-bottom:2px solid var(--color-border);}
.at td{padding:5px 8px;border-bottom:1px solid var(--color-subtle);vertical-align:top;line-height:1.5;}
.at tr.grp td{font-family:var(--font-mono);font-size:9px;color:var(--color-text-muted);background:var(--color-subtle);font-weight:500;letter-spacing:.06em;text-transform:uppercase;padding:4px 8px;}
.at code{font-family:var(--font-mono);font-size:10px;color:var(--navy);background:rgba(1,40,81,.06);padding:1px 4px;border-radius:2px;}
/* ── Topbar inside frame ── */
.fa-topbar{background:#fff;border-bottom:1px solid #E4E2D8;display:flex;align-items:center;height:44px;flex-shrink:0;padding-right:10px;}
.fa-accent{width:3px;background:var(--navy);height:44px;flex-shrink:0;}
.fa-back{width:24px;height:24px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:9px;color:#6b7280;flex-shrink:0;margin:0 4px;}
.fa-tb-divider{width:1px;height:16px;background:#E4E2D8;flex-shrink:0;margin:0 6px;}
.fa-tb-title-block{flex:1;min-width:0;padding:0 4px;}
.fa-tb-title{font-family:Georgia,serif;font-size:10px;font-weight:700;color:var(--navy);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
.fa-tb-date{font-size:7px;color:#6b7280;margin-top:1px;}
.fa-chip{display:inline-flex;align-items:center;gap:2px;padding:1px 5px 1px 2px;background:var(--sand);border:1px solid #E4E2D8;border-radius:8px;white-space:nowrap;font-size:7px;color:var(--color-text);flex-shrink:0;}
.fa-chip .av{width:12px;height:12px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:5px;font-weight:800;color:#fff;flex-shrink:0;}
.av-navy{background:var(--navy);}
.av-purple{background:#5A3080;}
.fa-tb-chips{display:flex;align-items:center;gap:4px;margin:0 6px;flex-shrink:0;}
.fa-tb-btn{height:22px;padding:0 8px;font-size:6.5px;font-weight:700;letter-spacing:.3px;text-transform:uppercase;border-radius:3px;display:inline-flex;align-items:center;gap:3px;flex-shrink:0;border:1.5px solid #E4E2D8;color:#4b5563;}
.fa-tb-btn.primary{background:var(--navy);color:var(--mint);border-color:var(--navy);}
.fa-tb-btn.active{background:var(--navy);color:#fff;border-color:var(--navy);}
.fa-tb-btn-ico{width:22px;height:22px;border:1.5px solid #E4E2D8;border-radius:3px;display:inline-flex;align-items:center;justify-content:center;color:#6b7280;flex-shrink:0;}
.fa-tb-kebab{width:22px;height:22px;border:1.5px solid #E4E2D8;border-radius:3px;display:inline-flex;align-items:center;justify-content:center;flex-direction:column;gap:2px;flex-shrink:0;}
.fa-tb-kebab span{display:block;width:3px;height:3px;border-radius:50%;background:#6b7280;}
/* ── Drawer ── */
.fa-drawer{background:#fff;border-bottom:1px solid #E4E2D8;padding:12px 16px;flex-shrink:0;}
.fa-drawer-grid{display:grid;gap:16px;}
.fa-drawer-grid.cols-3{grid-template-columns:1fr 1fr 1fr;}
.fa-drawer-grid.cols-4{grid-template-columns:1fr 1fr 1fr 1fr;}
.fa-drawer-col-head{font-size:6.5px;font-weight:800;text-transform:uppercase;letter-spacing:.09em;color:#6b7280;margin-bottom:8px;}
.fa-drawer-label{font-size:6.5px;font-weight:600;text-transform:uppercase;letter-spacing:.06em;color:#9ca3af;margin-bottom:3px;}
.fa-drawer-value{font-family:Georgia,serif;font-size:9.5px;color:var(--navy);}
.fa-drawer-field{margin-bottom:7px;}
.fa-person-row{display:flex;align-items:center;gap:5px;padding:3px 4px;border-radius:3px;}
.fa-p-av{width:18px;height:18px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:6px;font-weight:800;color:#fff;flex-shrink:0;}
.fa-p-name{font-family:Georgia,serif;font-size:9px;color:var(--navy);}
.fa-tag{display:inline-block;padding:1px 5px;border-radius:2px;font-size:6px;font-weight:800;text-transform:uppercase;letter-spacing:.05em;background:#EDECEA;color:#6B6A63;margin:1.5px 1.5px;}
.fa-story-entry{margin-bottom:8px;}
.fa-story-title{font-family:Georgia,serif;font-size:9px;color:var(--navy);display:block;text-decoration:none;}
.fa-story-title:hover{text-decoration:underline;}
.fa-story-meta{font-size:6.5px;color:#9ca3af;margin-top:1px;}
.fa-story-link-muted{font-size:6.5px;color:#9ca3af;display:block;margin-top:8px;}
.fa-story-link-turquoise{font-size:6.5px;color:var(--turquoise);display:block;margin-top:4px;}
.fa-status-badge{display:inline-flex;align-items:center;gap:3px;padding:1px 5px;border-radius:3px;font-size:6px;font-weight:700;text-transform:uppercase;letter-spacing:.04em;background:#D1FAE5;color:#065F46;border:1px solid #A7F3D0;}
.fa-status-dot{width:4px;height:4px;border-radius:50%;background:#10B981;}
/* ── PDF area ── */
.fa-pdf{background:#D4D0C8;flex:1;display:flex;align-items:center;justify-content:center;min-height:120px;}
.fa-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;width:42%;}
.fa-pl{height:3px;background:#C4BDB0;border-radius:1px;opacity:.5;margin-bottom:2px;}
.fa-ps{height:2px;background:#C4BDB0;border-radius:1px;opacity:.28;margin-bottom:1.5px;}
/* ── Mobile nav slim ── */
.fa-nav-slim{height:26px;background:var(--navy);display:flex;align-items:center;padding:0 10px;gap:6px;flex-shrink:0;}
.fa-nav-slim .fa-logo{font-size:6px;}
/* ── LLM guide ── */
.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 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 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">
<!-- ══ DOCUMENT HEADER ══════════════════════════════════════════════════════ -->
<div class="doc-header">
<div>
<h1>Geschichten — Dokumentverknüpfung</h1>
<p>Entdeckung verlinkter Geschichten über die Dokumentdetailseite. Die Geschichten-Spalte erscheint im Details-Drawer als vierte Spalte neben Details, Personen und Schlagwörtern — nur wenn mindestens eine veröffentlichte Geschichte mit diesem Dokument verknüpft ist.</p>
</div>
<div class="doc-meta">
<span class="pill pill-o">Final Spec</span><br>
2026-05-02<br>
@leonievoss
</div>
</div>
<!-- ══ JOURNEY HEADER ═══════════════════════════════════════════════════════ -->
<div class="jh jh-o">
<div class="jn">D</div>
<div>
<h2>Dokument-Entdeckung</h2>
<p>Lesende entdecken Geschichten, die sich auf einen Brief beziehen, direkt beim Öffnen des Details-Drawers auf der Dokumentdetailseite.</p>
<div class="fl">/documents/[id] · DocumentMetadataDrawer</div>
</div>
</div>
<!-- ══ SECTION: KONZEPT ══════════════════════════════════════════════════════ -->
<div class="section">
<div class="section-title">Konzept — US-BLOG-005</div>
<p class="prose">
Es wird <strong>kein neuer Button in die DocumentTopBar</strong> eingefügt. Der bestehende "Details"-Toggle ist der einzige Einstiegspunkt — er öffnet den <code>DocumentMetadataDrawer</code>, der bereits Details, Personen und Schlagwörter enthält. Die Geschichten-Spalte erscheint dort als <strong>vierte Spalte</strong>, wenn mindestens eine veröffentlichte Geschichte mit diesem Dokument verknüpft ist.
</p>
<p class="prose">
Wenn keine Geschichte verknüpft ist, zeigt der Drawer das gewohnte 3-spaltige Layout — kein leerer Zustand, kein Hinweis, kein Platzhalter. Stille ist korrekt (US-BLOG-005 verlangt keinen Empty State). Die Grid-Klasse wechselt konditionell von <code>lg:grid-cols-3</code> zu <code>lg:grid-cols-4</code> über eine Prop.
</p>
<p class="prose">
Die Prop <code>geschichten: Geschichte[]</code> wird vom <code>+page.server.ts</code> der Dokumentdetailseite befüllt. Nur <strong>veröffentlichte</strong> Geschichten werden übergeben — die Filterung erfolgt auf Serviceebene im Backend. Der <code>DocumentMetadataDrawer</code> ist rein präsentational und trifft keine eigenen Datenbankabfragen.
</p>
</div>
<!-- ══ SCREEN D-1: DRAWER GESCHLOSSEN ═══════════════════════════════════════ -->
<div class="scr" id="d1">
<div class="scr-head">
<h3>D-1 — Dokument ohne geöffneten Drawer</h3>
<span class="scr-id">D-1</span>
</div>
<div class="scr-desc">Normalzustand: Der Details-Drawer ist geschlossen. Für den Lesenden ändert sich optisch nichts — die Geschichten-Spalte ist erst sichtbar, wenn der Drawer geöffnet wird.</div>
<div class="scr-var"><strong>Desktop ≥1024px</strong> — Drawer geschlossen, PDF-Viewer füllt die verbleibende Höhe.</div>
<div class="previews">
<div class="prev-col">
<div class="bp-lbl">Desktop · 1040px · Drawer geschlossen</div>
<div class="desk">
<!-- Nav -->
<div class="fa-nav">
<div class="fa-logo">FAMILIENARCHIV</div>
<div class="fa-link on">Dokumente</div>
<div class="fa-link">Personen</div>
<div class="fa-link">Geschichten</div>
<div class="fa-nav-r"><div class="fa-av">MR</div></div>
</div>
<!-- TopBar — drawer closed -->
<div class="fa-topbar">
<div class="fa-accent"></div>
<div class="fa-back">&#8592;</div>
<div class="fa-tb-divider"></div>
<div class="fa-tb-title-block">
<div class="fa-tb-title">Brief an Franz Raddatz, 12. Juli 1938</div>
<div class="fa-tb-date">12. Juli 1938</div>
</div>
<div class="fa-tb-chips">
<div class="fa-chip"><div class="av av-navy">FR</div>&nbsp;Franz Raddatz</div>
<div class="fa-chip"><div class="av av-purple">EM</div>&nbsp;Emma Müller</div>
</div>
<div class="fa-tb-divider"></div>
<!-- Details toggle — closed state -->
<button class="fa-tb-btn" style="margin-right:6px;">Details ▾</button>
<div class="fa-tb-divider"></div>
<button class="fa-tb-btn primary" style="margin-left:6px;">Transkribieren</button>
<button class="fa-tb-btn" style="margin-left:4px;">Bearbeiten</button>
<div class="fa-tb-btn-ico" style="margin-left:4px;">
<svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>
</div>
</div>
<!-- PDF viewer area — drawer closed, fills height -->
<div class="fa-pdf" style="flex:1;min-height:340px;">
<div class="fa-paper" style="min-height:200px;">
<div style="font-size:7px;color:#8A8070;font-style:italic;margin-bottom:6px;opacity:.75;">Breslau, den 12. Juli 1938</div>
<div class="fa-pl" style="width:92%;"></div>
<div class="fa-ps" style="width:85%;"></div>
<div class="fa-ps" style="width:90%;"></div>
<div class="fa-pl" style="width:78%;"></div>
<div class="fa-ps" style="width:88%;"></div>
<div class="fa-ps" style="width:70%;"></div>
<div class="fa-pl" style="width:84%;"></div>
<div class="fa-ps" style="width:90%;"></div>
<div class="fa-ps" style="width:60%;"></div>
<div class="fa-pl" style="width:75%;"></div>
<div class="fa-ps" style="width:82%;"></div>
<div class="fa-ps" style="width:46%;"></div>
<div style="font-size:6px;color:#8A8070;margin-top:8px;text-align:right;opacity:.7;">Dein Vater</div>
</div>
</div>
</div><!-- /desk -->
</div><!-- /prev-col -->
</div><!-- /previews -->
<div class="agent">
<h4>Hinweis — D-1</h4>
<pre>Keine Änderung an der DocumentTopBar. Der "Details"-Button ist bereits implementiert und öffnet/schließt den Drawer. Dieser Screen zeigt den geschlossenen Zustand — identisch mit dem heutigen Verhalten.</pre>
</div>
</div><!-- /scr D-1 -->
<!-- ══ SCREEN D-2: DRAWER OFFEN — 4-SPALTEN-GRID ═══════════════════════════ -->
<div class="scr" id="d2">
<div class="scr-head">
<h3>D-2 — Drawer offen · 4 Spalten mit Geschichten</h3>
<span class="scr-id">D-2</span>
</div>
<div class="scr-desc">Der Drawer wird geöffnet. Weil dieses Dokument mit veröffentlichten Geschichten verknüpft ist, schaltet das Grid von 3 auf 4 Spalten um. Die Geschichten-Spalte erscheint ganz rechts — gleiche typografische Behandlung wie die anderen Spalten.</div>
<div class="scr-var"><strong>Desktop ≥1024px</strong> — Prop <code>geschichten.length &gt; 0</code> ist true, Grid-Klasse ist <code>lg:grid-cols-4</code>.</div>
<div class="previews">
<div class="prev-col">
<div class="bp-lbl">Desktop · 1040px · Drawer offen · 4 Spalten</div>
<div class="desk">
<!-- Nav -->
<div class="fa-nav">
<div class="fa-logo">FAMILIENARCHIV</div>
<div class="fa-link on">Dokumente</div>
<div class="fa-link">Personen</div>
<div class="fa-link">Geschichten</div>
<div class="fa-nav-r"><div class="fa-av">MR</div></div>
</div>
<!-- TopBar — drawer open (Details button active/filled) -->
<div class="fa-topbar">
<div class="fa-accent"></div>
<div class="fa-back">&#8592;</div>
<div class="fa-tb-divider"></div>
<div class="fa-tb-title-block">
<div class="fa-tb-title">Brief an Franz Raddatz, 12. Juli 1938</div>
<div class="fa-tb-date">12. Juli 1938</div>
</div>
<div class="fa-tb-chips">
<div class="fa-chip"><div class="av av-navy">FR</div>&nbsp;Franz Raddatz</div>
<div class="fa-chip"><div class="av av-purple">EM</div>&nbsp;Emma Müller</div>
</div>
<div class="fa-tb-divider"></div>
<!-- Details toggle — open/active state -->
<button class="fa-tb-btn active" style="margin-right:6px;">Details ▴</button>
<div class="fa-tb-divider"></div>
<button class="fa-tb-btn primary" style="margin-left:6px;">Transkribieren</button>
<button class="fa-tb-btn" style="margin-left:4px;">Bearbeiten</button>
<div class="fa-tb-btn-ico" style="margin-left:4px;">
<svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>
</div>
</div>
<!-- Drawer — open, 4-column grid -->
<div class="fa-drawer">
<div class="fa-drawer-grid cols-4">
<!-- Col 1: Details -->
<div>
<div class="fa-drawer-col-head">Details</div>
<div class="fa-drawer-field">
<div class="fa-drawer-label">Datum</div>
<div class="fa-drawer-value">12. Juli 1938</div>
</div>
<div class="fa-drawer-field">
<div class="fa-drawer-label">Ort</div>
<div class="fa-drawer-value">Breslau</div>
</div>
<div class="fa-drawer-field">
<div class="fa-drawer-label">Status</div>
<div class="fa-status-badge"><span class="fa-status-dot"></span>Hochgeladen</div>
</div>
</div>
<!-- Col 2: Personen -->
<div>
<div class="fa-drawer-col-head">Personen</div>
<div class="fa-drawer-label" style="margin-bottom:4px;">Absender</div>
<div class="fa-person-row">
<div class="fa-p-av av-navy">FR</div>
<div class="fa-p-name">Franz Raddatz</div>
</div>
<div class="fa-drawer-label" style="margin-top:8px;margin-bottom:4px;">Empfänger</div>
<div class="fa-person-row">
<div class="fa-p-av av-purple">EM</div>
<div class="fa-p-name">Emma Müller</div>
</div>
</div>
<!-- Col 3: Schlagwörter -->
<div>
<div class="fa-drawer-col-head">Schlagwörter</div>
<div style="display:flex;flex-wrap:wrap;margin:-1.5px;">
<span class="fa-tag">Familie</span>
<span class="fa-tag">Krieg</span>
<span class="fa-tag">Breslau</span>
</div>
</div>
<!-- Col 4: Geschichten (NEW) -->
<div>
<div class="fa-drawer-col-head">Geschichten</div>
<!-- Story 1 -->
<div class="fa-story-entry">
<a class="fa-story-title" href="#">Der Sommer in Breslau</a>
<div class="fa-story-meta">von Maria Raddatz · 14. März 2025</div>
</div>
<!-- Story 2 -->
<div class="fa-story-entry">
<a class="fa-story-title" href="#">Die Hochzeit im Krieg</a>
<div class="fa-story-meta">von Gertrud Koch · 18. Okt. 2024</div>
</div>
<!-- "Alle anzeigen" link — shown when stories.length > 3 -->
<!-- (hidden in this example because only 2 stories) -->
<!-- "+ Geschichte anhängen" — BLOG_WRITE only -->
<div class="fa-story-link-turquoise">+ Geschichte anhängen</div>
</div>
</div><!-- /grid -->
</div><!-- /drawer -->
<!-- PDF viewer below drawer -->
<div class="fa-pdf" style="flex:1;min-height:200px;">
<div class="fa-paper" style="min-height:120px;">
<div style="font-size:7px;color:#8A8070;font-style:italic;margin-bottom:6px;opacity:.75;">Breslau, den 12. Juli 1938</div>
<div class="fa-pl" style="width:92%;"></div>
<div class="fa-ps" style="width:85%;"></div>
<div class="fa-ps" style="width:88%;"></div>
<div class="fa-pl" style="width:78%;"></div>
<div class="fa-ps" style="width:70%;"></div>
</div>
</div>
</div><!-- /desk -->
</div><!-- /prev-col -->
</div><!-- /previews -->
<!-- impl-ref table D-2 -->
<div class="agent">
<h4>impl-ref — D-2 · Geschichten-Spalte im Drawer</h4>
<table class="at">
<thead>
<tr>
<th>Element</th>
<th>Wert / Klasse</th>
<th>Hinweis</th>
</tr>
</thead>
<tbody>
<tr class="grp"><td colspan="3">Grid</td></tr>
<tr>
<td>Grid mit Geschichten</td>
<td><code>grid grid-cols-1 gap-6 lg:grid-cols-4</code></td>
<td>Konditional: <code>geschichten.length &gt; 0</code></td>
</tr>
<tr>
<td>Grid ohne Geschichten</td>
<td><code>grid grid-cols-1 gap-6 lg:grid-cols-3</code></td>
<td>Default — keine Änderung</td>
</tr>
<tr class="grp"><td colspan="3">Spalten-Heading</td></tr>
<tr>
<td>Col 4 Heading</td>
<td><code>mb-4 font-sans text-xs font-bold tracking-widest text-ink-3 uppercase</code></td>
<td>Identisch mit Col 13 Headings</td>
</tr>
<tr class="grp"><td colspan="3">Story-Einträge</td></tr>
<tr>
<td>Eintrags-Wrapper</td>
<td><code>space-y-3</code></td>
<td></td>
</tr>
<tr>
<td>Story-Titel-Link</td>
<td><code>block font-serif text-sm text-ink hover:text-primary hover:underline</code></td>
<td>Verlinkt auf <code>/geschichten/[id]</code></td>
</tr>
<tr>
<td>Story-Meta</td>
<td><code>font-sans text-[10px] text-ink-3</code></td>
<td>"von [author] · [date]"</td>
</tr>
<tr class="grp"><td colspan="3">Aktionslinks</td></tr>
<tr>
<td>"Alle anzeigen" Link</td>
<td><code>font-sans text-[10px] text-ink-3 hover:text-primary</code></td>
<td>Nur wenn <code>stories.length &gt; 3</code>; verlinkt auf <code>/geschichten?documentId=xxx</code></td>
</tr>
<tr>
<td>"+ Geschichte anhängen"</td>
<td><code>font-sans text-[10px] text-turquoise hover:underline</code></td>
<td>Nur sichtbar für BLOG_WRITE — via <code>canWrite</code>-Prop</td>
</tr>
<tr class="grp"><td colspan="3">Props</td></tr>
<tr>
<td>Neue Prop</td>
<td><code>geschichten: Geschichte[]</code></td>
<td>Wird von <code>+page.server.ts</code> an <code>DocumentMetadataDrawer</code> übergeben</td>
</tr>
<tr>
<td>Neue Prop</td>
<td><code>canWrite: boolean</code></td>
<td>Steuert Sichtbarkeit von "+ Geschichte anhängen"</td>
</tr>
</tbody>
</table>
</div>
</div><!-- /scr D-2 -->
<!-- ══ SCREEN D-3: MOBILE DRAWER ════════════════════════════════════════════ -->
<div class="scr" id="d3">
<div class="scr-head">
<h3>D-3 — Mobile · Drawer offen, Geschichten-Spalte gestapelt</h3>
<span class="scr-id">D-3</span>
</div>
<div class="scr-desc">Auf Mobile ist das Grid einspaltig (<code>grid-cols-1</code>). Alle vier Abschnitte stapeln sich vertikal. Die Geschichten-Spalte erscheint als letzter Block — unterhalb von Schlagwörtern. Layout und Typografie sind identisch mit Desktop, nur volle Breite.</div>
<div class="scr-var"><strong>Mobile &lt;640px</strong> — kein Person-Chip-Row, kebab statt einzelner Action-Buttons, alle Drawer-Spalten gestapelt.</div>
<div class="previews">
<div class="prev-col">
<div class="bp-lbl">Mobile · 320px · Drawer offen</div>
<div class="phone">
<!-- Status bar -->
<div class="pst">
<b>9:41</b>
<span>&#9679;&#9679;&#9679;</span>
</div>
<div class="pb">
<!-- Nav slim -->
<div class="fa-nav-slim">
<div class="fa-logo">FAMILIENARCHIV</div>
<div class="fa-nav-r"><div class="fa-av">MR</div></div>
</div>
<!-- TopBar mobile — compact -->
<div class="fa-topbar" style="height:40px;">
<div class="fa-accent" style="height:40px;"></div>
<div class="fa-back" style="font-size:8px;margin:0 3px;">&#8592;</div>
<div class="fa-tb-divider"></div>
<div class="fa-tb-title-block" style="padding:0 3px;">
<div class="fa-tb-title" style="font-size:9px;">Brief an Franz Raddatz…</div>
<div class="fa-tb-date" style="font-size:6px;">12. Juli 1938</div>
</div>
<!-- Details toggle active on mobile too -->
<button class="fa-tb-btn active" style="font-size:6px;height:19px;padding:0 6px;margin-right:4px;">Details ▴</button>
<div class="fa-tb-divider"></div>
<!-- Kebab menu on mobile replaces individual action buttons -->
<div class="fa-tb-kebab" style="margin-left:4px;">
<span></span><span></span><span></span>
</div>
</div>
<!-- Drawer — open, single column stacked -->
<div class="fa-drawer" style="padding:10px 12px;">
<!-- Col 1: Details -->
<div style="margin-bottom:14px;">
<div class="fa-drawer-col-head">Details</div>
<div class="fa-drawer-field">
<div class="fa-drawer-label">Datum</div>
<div class="fa-drawer-value" style="font-size:8.5px;">12. Juli 1938</div>
</div>
<div class="fa-drawer-field">
<div class="fa-drawer-label">Ort</div>
<div class="fa-drawer-value" style="font-size:8.5px;">Breslau</div>
</div>
<div class="fa-drawer-field">
<div class="fa-drawer-label">Status</div>
<div class="fa-status-badge"><span class="fa-status-dot"></span>Hochgeladen</div>
</div>
</div>
<!-- Col 2: Personen -->
<div style="margin-bottom:14px;">
<div class="fa-drawer-col-head">Personen</div>
<div class="fa-drawer-label" style="margin-bottom:3px;">Absender</div>
<div class="fa-person-row">
<div class="fa-p-av av-navy" style="width:15px;height:15px;font-size:5.5px;">FR</div>
<div class="fa-p-name" style="font-size:8.5px;">Franz Raddatz</div>
</div>
<div class="fa-drawer-label" style="margin-top:6px;margin-bottom:3px;">Empfänger</div>
<div class="fa-person-row">
<div class="fa-p-av av-purple" style="width:15px;height:15px;font-size:5.5px;">EM</div>
<div class="fa-p-name" style="font-size:8.5px;">Emma Müller</div>
</div>
</div>
<!-- Col 3: Schlagwörter -->
<div style="margin-bottom:14px;">
<div class="fa-drawer-col-head">Schlagwörter</div>
<div style="display:flex;flex-wrap:wrap;margin:-1.5px;">
<span class="fa-tag">Familie</span>
<span class="fa-tag">Krieg</span>
<span class="fa-tag">Breslau</span>
</div>
</div>
<!-- Col 4: Geschichten — full width on mobile, below Schlagwörter -->
<div>
<div class="fa-drawer-col-head">Geschichten</div>
<div class="fa-story-entry">
<a class="fa-story-title" href="#" style="font-size:8.5px;">Der Sommer in Breslau</a>
<div class="fa-story-meta" style="font-size:6px;">von Maria Raddatz · 14. März 2025</div>
</div>
<div class="fa-story-entry">
<a class="fa-story-title" href="#" style="font-size:8.5px;">Die Hochzeit im Krieg</a>
<div class="fa-story-meta" style="font-size:6px;">von Gertrud Koch · 18. Okt. 2024</div>
</div>
<div class="fa-story-link-muted" style="font-size:6px;">Alle Geschichten anzeigen →</div>
<div class="fa-story-link-turquoise" style="font-size:6px;">+ Geschichte anhängen</div>
</div>
</div><!-- /drawer -->
<!-- PDF viewer below drawer on mobile -->
<div class="fa-pdf" style="flex:1;min-height:100px;">
<div class="fa-paper" style="width:60%;min-height:70px;">
<div class="fa-pl" style="width:88%;"></div>
<div class="fa-ps" style="width:82%;"></div>
<div class="fa-ps" style="width:90%;"></div>
<div class="fa-pl" style="width:74%;"></div>
<div class="fa-ps" style="width:68%;"></div>
</div>
</div>
</div><!-- /pb -->
</div><!-- /phone -->
</div><!-- /prev-col -->
</div><!-- /previews -->
<div class="agent">
<h4>Hinweis — D-3 Mobile</h4>
<pre>Mobile erfordert keine eigene Komponente. Das Tailwind-Grid `grid-cols-1 lg:grid-cols-4` erledigt das Stacking automatisch. Die Geschichten-Spalte erscheint zuletzt im DOM (nach Schlagwörter) — das ist die korrekte Lesereihenfolge auf Mobile.</pre>
</div>
</div><!-- /scr D-3 -->
<!-- ══ LLM IMPLEMENTATION GUIDE ══════════════════════════════════════════════ -->
<div class="llm">
<h2>LLM-Implementierungsguide</h2>
<h3>Backend — Endpunkt-Erweiterung</h3>
<p>Der <code>GET /api/documents/{id}</code> Response (oder ein separater Endpunkt) muss ein <code>geschichten</code>-Array enthalten:</p>
<ul>
<li>Felder pro Eintrag: <code>id</code>, <code>title</code>, <code>author: { displayName }</code>, <code>publishedAt</code></li>
<li>Nur Geschichten mit Status <code>PUBLISHED</code> werden zurückgegeben — Filterung auf Service-Ebene, nicht im Controller</li>
<li>Maximale Anzahl im Response: unbegrenzt; das Frontend blendet einen "Alle anzeigen"-Link ein, wenn mehr als 3 vorhanden sind</li>
</ul>
<h3>Frontend — Änderungen</h3>
<ol>
<li><code>+page.server.ts</code>: <code>geschichten</code>-Array aus dem API-Response extrahieren und als Prop an <code>DocumentMetadataDrawer</code> übergeben</li>
<li><code>DocumentMetadataDrawer</code>: neue Prop <code>geschichten: Geschichte[]</code> und <code>canWrite: boolean</code> annehmen</li>
<li>Grid-Klasse dynamisch: <code>lg:grid-cols-{geschichten.length &gt; 0 ? 4 : 3}</code></li>
<li>4. Spalte konditional rendern: <code>{#if geschichten.length &gt; 0}</code></li>
<li>"Alle anzeigen"-Link: nur wenn <code>geschichten.length &gt; 3</code>, Link zu <code>/geschichten?documentId={id}</code></li>
<li>"+ Geschichte anhängen"-Link: nur wenn <code>canWrite</code>, Link zu <code>/geschichten/new?documentId={id}</code></li>
</ol>
<h3>Keine Änderungen erforderlich</h3>
<ul>
<li><strong>DocumentTopBar</strong>: kein neuer Button, kein neues Icon</li>
<li><strong>Andere Drawer-Spalten</strong>: Details, Personen, Schlagwörter bleiben unverändert</li>
<li><strong>Mobile Layout</strong>: das Grid-Stacking ist automatisch via Tailwind, keine separate Mobile-Logik</li>
</ul>
<h3>Implementierungsreferenz — Typedefinition</h3>
<table>
<thead>
<tr>
<th>Feld</th>
<th>Typ</th>
<th>Quelle</th>
</tr>
</thead>
<tbody>
<tr><td><code>id</code></td><td><code>string (UUID)</code></td><td>Backend</td></tr>
<tr><td><code>title</code></td><td><code>string</code></td><td>Backend</td></tr>
<tr><td><code>author.displayName</code></td><td><code>string</code></td><td>Backend</td></tr>
<tr><td><code>publishedAt</code></td><td><code>string (ISO date)</code></td><td>Backend</td></tr>
</tbody>
</table>
</div><!-- /llm -->
</div><!-- /doc -->
</body>
</html>