diff --git a/docs/specs/dashboard-expansion-patterns.html b/docs/specs/dashboard-expansion-patterns.html new file mode 100644 index 00000000..0907ca9e --- /dev/null +++ b/docs/specs/dashboard-expansion-patterns.html @@ -0,0 +1,1122 @@ + + +
+ + ++ Die rechte Spalte der Startseite ist mit DropZone + „Metadaten fehlen" bereits ausgelastet. + Issue #240 möchte zwei weitere Karten ergänzen: Transkription ausstehend und Lesefertig. + Direkt gestapelt scrollt die Spalte sofort aus dem sichtbaren Bereich — ein 67-jähriger Nutzer auf einem kleinen + Display sieht die unteren Karten nie. +
++ Der Nutzer schlug Tabs vor (Neueste Aktivität / Transkription fehlt / Metadaten fehlen / Lesefertig). + Diese Spec bewertet diesen Vorschlag und zeigt drei weitere Muster im Vergleich. + Alle Muster werden bei 320 px (Mobiltelefon) und Desktop (~55 % Skalierung) gezeigt. +
++ Der ursprüngliche Vorschlag nannte vier Tabs für die gesamte Seite (Neueste Aktivität / Transkription / Metadaten / Lesefertig). + Das ist ein UX-Antipattern: „Neueste Aktivität" ist der häufigste Anwendungsfall — ihn hinter einem Klick zu verstecken erhöht den Aufwand für jeden Dashboard-Besuch. + Tabs sollten ausschließlich auf die drei To-do-Widget-Kategorien in der rechten Spalte angewendet werden, nicht auf die gesamte Seite. +
+
+ Accessibility: Tab-Elemente müssen role="tablist", role="tab", aria-selected und
+ role="tabpanel" tragen. Jeder Tab braucht min-h-[44px] (WCAG 2.2 Touchziel).
+ Drei Tabs in einer 300-px-Spalte = ~100 px pro Tab — grenzwertig auf Deutsch mit langen Wörtern.
+
| Element | Tailwind-Klassen | Wert | Hinweis |
|---|---|---|---|
| Tab-Strip | flex border-b border-line | — | ARIA: role="tablist" |
| Tab inaktiv | px-3 py-3 text-xs font-medium text-gray-500 border-b-2 border-transparent whitespace-nowrap -mb-px | min-h 44 px ✓ | role="tab" aria-selected="false" |
| Tab aktiv | px-3 py-3 text-xs font-semibold text-ink border-b-2 border-ink -mb-px | 2 px navy Unterlinie | aria-selected="true" |
| Tab-Panel | pt-3 focus:outline-none | — | role="tabpanel" tabindex="0" |
| Aufgaben-Karte | rounded-sm border border-line bg-white p-4 flex-1 | padding 16 px | Ersetzt 3 separate Karten |
| Zeile mit Kontext | flex flex-col py-2 border-b border-line last:border-0 | min-h ~44 px | z. B. „3 von 8 Blöcken geprüft" |
+ Jede Sektion zeigt eine klickbare Kopfzeile (Pfeil + Label + Anzahl). Server-seitig wird die Sektion mit der
+ höchsten Anzahl als <details open> gerendert. Kein Client-JS nötig — native
+ <details>/<summary>-Elemente liefern ARIA-Accessibility gratis.
+ Sortierung der offenen Sektion nach Dringlichkeit: Metadaten → Transkription → Lesefertig.
+
| Element | HTML / Tailwind-Klassen | Wert | Hinweis |
|---|---|---|---|
| Accordion-Wrapper | <details> nativ | — | Accessibility gratis, kein JS |
| Accordion-Header | <summary class="flex items-center justify-between min-h-[44px] cursor-pointer list-none"> | min-h 44 px ✓ | WCAG 2.2 Touchziel |
| Pfeil-Icon | transition-transform group-open:rotate-90 | w-4 h-4 | CSS-only; kein JS |
| Zähler-Badge | ml-auto font-mono text-xs text-gray-400 | — | z. B. „(5)" |
| Dringlichste Sektion | <details open> | — | Server-seitig rendern: if incompleteDocs.length >= needsTrans.length |
| Accordion-Inhalt | pt-1 pb-2 direkt nach <summary> | — | Keine overflow:hidden-Animation nötig |
+ Der Ist-Zustand der rechten Spalte bleibt vollständig erhalten. Die zwei neuen Karten des Issue #240 werden
+ nicht in die rechte Spalte gepackt, sondern in einen neuen vollbreiten Abschnitt direkt unterhalb des
+ bestehenden Zwei-Spalten-Gitters. Der Abschnitt ist nur sichtbar, wenn mindestens eine der beiden Kategorien
+ Einträge hat ({#if needsTranscription.length > 0 || readyToRead.length > 0}).
+
+ Die „Lesefertig"-Spalte erhält einen mint-gefärbten Hintergrund (bg-mint/10 border-mint) als positives Signal — kein
+ neutrales To-do, sondern eine Einladung zum Lesen. Leere Zustände zeigen eine kurze Erfolgsmeldung in
+ bg-mint/5, nicht eine tote weiße Box.
+
| Element | Tailwind-Klassen | Wert | Hinweis |
|---|---|---|---|
| Streifen-Wrapper | mt-4 bg-white border border-line rounded-sm p-6 | padding 24 px | Direkt nach bestehendem div.mt-4.grid |
| Streifen-Titel | text-xs font-bold uppercase tracking-widest text-gray-400 mb-4 | 12 px / 700 | Standard-Section-Title-Muster |
| 3-Spalten-Grid | grid grid-cols-1 gap-4 sm:grid-cols-3 | gap 16 px | Mobil: 1 Spalte, sm+: 3 |
| Transkription-Spalte | bg-surface rounded-sm border border-line p-4 | — | Neutral — es ist eine Aufgabe |
| Lesefertig-Spalte | bg-mint/10 rounded-sm border border-mint p-4 | — | Mint-Ton = positives Signal |
| Leerer Zustand | flex flex-col items-center justify-center text-center bg-mint/5 border border-dashed border-mint rounded-sm p-6 min-h-[80px] | min-h 80 px | Niemals leere graue Box |
| Untertext-Zeile | text-xs text-gray-400 mt-0.5 | 12 px | z. B. „3 von 8 Blöcken geprüft" |
| Sichtbarkeit | {#if needsTranscription.length > 0 || readyToRead.length > 0} | — | Streifen komplett ausgeblendet wenn leer |
+ Alle drei Kategorien werden in einer einzigen sortierten Liste zusammengeführt. Sortierung: + Metadaten fehlen (blockiert Suche) → Transkription fehlt → Lesefertig. Jede Zeile trägt ein farbkodiertes Label; + Farbe darf niemals der einzige Indikator sein — Icon und Text sind Pflicht (WCAG 1.4.1). +
++ Kontrast-Check: Orange-700 auf Weiß = 5,4:1 ✓ AA. Navy auf Weiß = 14,5:1 ✓ AAA. Green-800 auf Weiß = 9,7:1 ✓ AAA. +
+ +| Element | Tailwind-Klassen | Wert | Hinweis |
|---|---|---|---|
| Listen-Wrapper | rounded-sm border border-line bg-white p-4 flex-1 | — | Ersetzt separate 3 Karten |
| Prioritäts-Zeile | flex items-start gap-3 py-2 border-b border-line last:border-0 min-h-[44px] | min-h 44 px ✓ | WCAG touch target |
| Typ-Punkt | w-2 h-2 rounded-full mt-1.5 shrink-0 | 8 × 8 px | Nie allein — Label ist Pflicht |
| Label orange | text-xs text-orange-700 | 12 px | Kontrast 5,4:1 ✓ AA |
| Label navy | text-xs text-ink | 12 px | Kontrast 14,5:1 ✓ AAA |
| Label grün | text-xs text-green-800 | 12 px | Kontrast 9,7:1 ✓ AAA |
| Merge-Service | findWhatsNext(int size) auf DashboardController | — | Sortierung: Metadaten → Trans → Lesefertig; per Markus: Threshold als @Param |
| Kriterium | +A — Tabs | +B — Accordion | +C — Mission Control ★ | +D — Priority Queue | +
|---|---|---|---|---|
| Alle 3 Kategorien sofort sichtbar | +Nein | +Nur Überschriften | +Ja | +Nein (gemischt) | +
| Neueste Aktivität bleibt Primärinhalt | +Ja | +Ja | +Ja | +Ja | +
| 60+ Usability (Discovery ohne Klick) | +Mittel | +Überschriften sichtbar | +Sehr gut | +Mittel — gemischte Liste | +
| JS-Zustand nötig | +Ja (aktiver Tab) | +Nein | +Nein | +Nein | +
| WCAG 2.2 compliance (out of the box) | +tablist + aria-selected nötig | +details/summary nativ | +Keine neuen Anforderungen | +Farbe + Icon + Label alle Pflicht | +
| Mobile 320 px | +3 Tabs zu schmal | +Gut | +Sehr gut — stapelt natürlich | +Gut | +
| „Lesefertig" als visueller Applaus | +Nur wenn Tab aktiv | +Nur wenn offen | +Ja — eigene mint-Karte | +Nein — gleichwertig mit Aufgaben | +
| Backend-Merge-Komplexität | +Gering | +Gering | +Gering (2 separate Queries) | +Mittel (Merge + Sortierung) | +
| Implementierungsaufwand Frontend | +Mittel | +Gering | +Gering | +Gering | +
+ Der Mission-Control-Streifen ist das einzige Muster, das alle drei Kategorien gleichzeitig sichtbar macht, + ohne den Primärinhalt zu verstecken oder JS-Zustand zu erzeugen. Scrollen nach unten ist kein Fehler — + versteckter Inhalt schon. +
++ Quick win: Wenn C abgelehnt wird — Muster B (Accordion) als Zweitstimme. Kein Refactoring der rechten Spalte, kein JS, alle Kategorien-Überschriften immer sichtbar. +
+SELECT … WHERE annotation_count = 0text IS NULL OR LENGTH(text) < threshold). Kurrent-Kenntnisse empfohlen.
+ annotation_count > 0 AND reviewed < 75 %reviewed_pct >= 0.90 (bestehend)COUNT(*) WHERE created_at > NOW() - INTERVAL '7 days'| Element | Tailwind-Klassen / Logik | Wert | Hinweis |
|---|---|---|---|
| Skill-Pill „Ohne Vorkenntnisse" | inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-xs font-semibold bg-green-50 border border-green-200 text-green-800 | Kontrast 9,7:1 ✓ | Klärung für 60+ und Neueinsteiger |
| Skill-Pill „Kurrent hilfreich" | inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-xs font-semibold bg-surface border border-line text-ink | neutral — kein Schreckpunkt | Nicht „Experten nötig" sondern „hilfreich" |
| Wochenpuls (Segmentierung + Transkription) | text-xs font-semibold text-green-700 (Seg.) / text-ink (Trans.) | 12 px | Query: COUNT WHERE created_at > NOW() - INTERVAL '7 days'; kein globaler Balken |
| Per-Dokument-Balken Track | flex-1 h-1 bg-navy/20 rounded-full overflow-hidden | h: 4 px | Nur in Transkription-Spalte, nur wenn annotation_count > 0 |
| Per-Dokument-Balken Füllstand | h-full bg-navy rounded-full + style="width:{pct}%" | — | pct = textedBlocks / totalBlocks * 100; Guard: totalBlocks = 0 → width 0 |
| Lesefertig-Prozentzahl | text-xs font-semibold text-green-800 | 12 px | Kein Balken — die mint-Spalte selbst ist das Erfolgssignal |
| Contributor-Avatar | w-6 h-6 rounded-full flex items-center justify-center text-[10px] font-bold text-white | 24 × 24 px | Farbe per User-ID deterministisch (kein API-Feld nötig) |
| „Starte hier"-CTA | block w-full text-center text-xs font-semibold text-white bg-ink rounded-sm py-1.5 mt-2 hover:bg-ink-2 transition-colors focus-visible:ring-2 focus-visible:ring-ink | min-h 36 px | Link: /enrich?filter=NEEDS_SEGMENTATION&next=1 |
| Lesefertig-Leerstand CTA | inline-flex items-center text-xs font-semibold text-ink border border-ink rounded-sm px-3 py-1 hover:bg-ink hover:text-white transition-colors | — | Link springt zur Segmentierungs-Ansicht |
| Contributor-API-Feld | GET /api/documents/needs-segmentation → DTO enthält lastContributors: [{initials, colorSeed}] | max 3 Avatare | Neues DTO-Feld — beachte Nora: nur Initialen, keine Namen |
| Segmentierung-Query | WHERE NOT EXISTS (SELECT 1 FROM document_annotations WHERE document_id = d.id) | — | Index auf document_annotations.document_id prüfen (Tobias) |
| Transkription-Query | EXISTS annotation AND (no blocks OR reviewed_pct < 0.75) | — | Guard gegen Division durch 0 (Sara) |
initials + einen deterministischen colorSeed (z. B. Hash der User-ID mod 6 Farben),
+ keine E-Mail-Adressen oder echten Namen. Das @RequirePermission(READ_ALL) auf den neuen Endpoints gilt auch hier.
+ + Der bestehende Dashboard-Aufbau (Neueste Aktivität links, DropZone + Metadaten-Widget rechts) bleibt unverändert. + Unterhalb des Zwei-Spalten-Gitters erscheint ein neuer vollbreiter Mission-Control-Streifen mit drei + gleichwertigen Spalten: Rahmen einzeichnen (Segmentierung, kein Vorwissen nötig), + Text eintippen (Transkription, Kurrent hilfreich), Lesefertig ✓ (Belohnungsbereich). +
++ Die „Transkription fehlt"-Spalte aus Issue #240 wird in Segmentierung + Transkription aufgeteilt, um + eine klare Beitragspyramide zu schaffen: Jeder kann Rahmen einzeichnen — nicht jeder kann Kurrent lesen. + Ein wöchentlich rotierender Sort mit Experten-gesucht-Escape-Hatch verhindert, dass schwer lesbare + Dokumente dauerhaft die Spalte blockieren. +
+Dokumente ohne Annotationsrahmen. Kein Kurrent nötig — Textblöcke markieren reicht.
+Bedingung: annotation_count = 0
Sort: Wöchentliche Rotation (seeded shuffle, s. u.)
+Fortschritt: Wochenpuls „↑ +5 diese Woche", kein globaler Balken
+Annotationen vorhanden, aber Text fehlt oder reviewed < 90 %. Kurrent-Kenntnisse hilfreich.
+Bedingung: annotation_count > 0 AND reviewed_pct < 0.90
Sort: Teilfortschritt zuerst, dann wöchentliche Rotation; needsExpert-Flagge schiebt nach hinten
Fortschritt: Per-Dokument-Balken „3 / 8 Blöcke"
+Reviewed ≥ 90 %. Keine Aufgabe — Einladung zum Lesen.
+Bedingung: reviewed_pct >= 0.90
Sort: Neueste zuerst
+Fortschritt: „94 % geprüft" als Text — kein Balken, die mint-Spalte ist das Signal
+Leerstand: Cross-Column-Redirect zu Spalte 1
+updated_at
+ können dieselben 3 besonders schwer lesbaren Dokumente dauerhaft die Spalte blockieren.
+ Jeder öffnet sie, gibt auf, und die Spalte wird zur Sackgasse.Document.needsExpert = true (1 Boolean, keine Migration wenn Flyway-Migration V{n} hinzugefügt wird).
+ In der Transkriptions-Spalte zeigen flagged Dokumente einen lila Badge und werden hinter unflagged Dokumenten einsortiert.
+ Kein Leaderboard, keine Scham — nur ein ehrliches Signal an die Community.
+ | Element | SQL / Tailwind | Wert | Hinweis |
|---|---|---|---|
| Sort Transkription | ORDER BY textedBlocks DESC, HASHTEXT(id::text || EXTRACT(WEEK FROM NOW())::int::text) | — | Kein neues Feld nötig; ändert sich automatisch jede Woche |
needsExpert-Flag | ALTER TABLE documents ADD COLUMN needs_expert BOOLEAN NOT NULL DEFAULT FALSE | Flyway V{n}__add_needs_expert.sql | Flagged Docs ans Ende: ORDER BY needs_expert ASC, ... |
| Experten-Badge | inline-flex items-center px-2 py-0.5 rounded text-xs font-semibold bg-purple-50 border border-purple-200 text-purple-700 | Kontrast 6,8:1 ✓ | Nur wenn doc.needsExpert === true |
| „Zu schwer"-Button (Enrich) | text-xs text-gray-400 hover:text-gray-600 underline underline-offset-2 | — | Unscheinbar — kein roter Knopf, keine Scham |
| Endpoint (Flagge setzen) | PATCH /api/documents/{id}/needs-expert | @RequirePermission(READ_ALL) | Jeder angemeldete Nutzer darf flaggen |
+ Die rechte Spalte (DropZone + Metadaten) erscheint auf Mobil zuerst im DOM (lg:order-last schiebt sie auf Desktop nach rechts).
+ Der Streifen stapelt seine drei Spalten vertikal. Jede Spalte hat volle Breite — keine Overflow-Probleme.
+
min-h-[44px] (WCAG 2.2).
+ Dokument-Zeilen in den Spalten: min-h-[44px] py-2.
+ Der „Zu schwer"-Button auf der Enrich-Seite: min-h-[44px] als Icon-Button mit aria-label.
+ Unter jedem Spaltentitel. „Ohne Vorkenntnisse" (grün) vs. „Kurrent hilfreich" (navy-neutral). + Senkt die Hemmschwelle — Neueinsteiger sehen sofort, was ohne Kurrent-Kenntnisse möglich ist.
+bg-green-50 border-green-200 text-green-800 / bg-surface border-line text-ink
„↑ +5 diese Woche · 1 480 offen" statt globalem Fortschrittsbalken. + Zeigt Schwung, nicht den Berg. Psychologisch: 0,8 %-Balken ist demotivierender als kein Balken.
+SELECT COUNT(*) WHERE created_at > NOW() - INTERVAL '7 days'
Nur in Spalte 2, nur wenn annotation_count > 0. Richtiger Maßstab:
+ 8 Blöcke sind in einer Sitzung abschließbar. Zeigt auch, welche Dokumente „fast fertig" sind.
width: {textedBlocks / totalBlocks * 100}%; Guard: totalBlocks === 0 → width: 0
Max. 3 Initialen-Bubbles der letzten Beitragenden pro Spalte. Kein Leaderboard (Wettbewerb) — + soziale Sichtbarkeit (Zugehörigkeit). Farbe deterministisch aus User-ID-Hash.
+DTO: lastContributors: [{initials, colorIndex}] — nur Initialen, keine Namen (Nora)
Ein einziger opinionated Button je Aufgaben-Spalte, der direkt zum nächsten Dokument springt. + Entscheidungslähmung ist der Hauptgrund für Non-Participation bei Familienprojekten.
+/enrich?filter=NEEDS_SEGMENTATION&next=1 (Segmentierung)/enrich?filter=NEEDS_TRANSCRIPTION&next=1 (Transkription)
Wenn Spalte 3 leer ist (frühe Phase), erscheint kein toter Endpunkt sondern: + „Erscheint hier, sobald die Transkription abgeschlossen ist — jetzt mithelfen →". + Der Link springt zu Spalte 1.
+{#if readyToRead.length === 0} → DashboardReadyToReadEmpty.svelte
| Element | Tailwind-Klassen | Pixel / Wert | Hinweis |
|---|---|---|---|
| Streifen-Wrapper | mt-4 bg-white border border-line rounded-sm p-6 | padding 24 px | Direkt nach bestehendem div.mt-4.grid |
| Streifen-Titel | text-xs font-bold uppercase tracking-widest text-gray-400 mb-4 | 12 px / 700 | Standard-Section-Title-Muster |
| 3-Spalten-Grid | grid grid-cols-1 gap-4 sm:grid-cols-3 | gap 16 px | sm = 640 px; darunter stapeln |
| Segmentierung-Spalte | bg-surface rounded-sm border border-line p-4 flex flex-col gap-3 | — | Neutral |
| Transkription-Spalte | bg-surface rounded-sm border border-line p-4 flex flex-col gap-3 | — | Neutral — es ist eine Aufgabe |
| Lesefertig-Spalte (gefüllt) | bg-mint/10 rounded-sm border border-mint p-4 flex flex-col gap-3 | — | Mint-Ton = Erfolg |
| Lesefertig-Spalte (leer) | flex flex-col items-center justify-center text-center bg-mint/5 border border-dashed border-mint rounded-sm p-6 min-h-[120px] | min-h 120 px | Kein toter Endpunkt |
| Skill-Pill easy | inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-xs font-semibold bg-green-50 border border-green-200 text-green-800 | Kontrast 9,7:1 ✓ AAA | — |
| Skill-Pill kurrent | inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-xs font-semibold bg-surface border border-line text-ink | Kontrast 14,5:1 ✓ AAA | Neutral — kein Abschreck-Signal |
| Wochenpuls-Zahl | text-xs font-semibold text-green-700 (Seg.) / text-ink (Trans.) | 12 px | Kein globaler Balken |
| Per-Dokument-Track | flex-1 h-1 bg-navy/20 rounded-full overflow-hidden | h 4 px | Nur wenn annotation_count > 0 |
| Per-Dokument-Fill | h-full bg-ink rounded-full transition-all + style="width:{pct}%" | — | Guard: totalBlocks === 0 → 0% |
| Lesefertig-Prozent | text-xs font-semibold text-green-800 | 12 px | Kein Balken — mint-Spalte ist das Signal |
| Contributor-Avatar | w-6 h-6 rounded-full flex items-center justify-center text-[10px] font-bold text-white shrink-0 | 24 × 24 px | Farbe: 6 Werte, Index = userIdHash % 6 |
| CTA-Button (primär) | block w-full text-center text-xs font-semibold text-white bg-ink rounded-sm py-2 mt-2 hover:bg-ink-2 transition-colors focus-visible:ring-2 focus-visible:ring-ink focus-visible:ring-offset-1 | min-h 36 px | aria-label mit Dokumenttitel falls nötig |
| CTA-Button (ghost, Leerstand) | inline-flex items-center text-xs font-semibold text-ink border border-ink rounded-sm px-3 py-2 hover:bg-ink hover:text-white transition-colors | min-h 36 px | — |
| Experten-gesucht-Badge | inline-flex items-center px-2 py-0.5 rounded text-xs font-semibold bg-purple-50 border border-purple-200 text-purple-700 | Kontrast 6,8:1 ✓ AA | Nur wenn doc.needsExpert === true |
| Sichtbarkeit Streifen | {#if needsSegmentation.length > 0 || needsTranscription.length > 0 || readyToRead.length > 0} | — | Streifen verschwindet wenn alle drei Buckets leer |
| Dokument-Zeile Mindesthöhe | min-h-[44px] flex items-start py-2 | 44 px ✓ WCAG 2.2 | Gilt für alle klickbaren Zeilen |
| Endpoint / Query | Bedingung | Sort | Auth |
|---|---|---|---|
GET /api/documents/needs-segmentation?size=3 | NOT EXISTS (SELECT 1 FROM document_annotations WHERE document_id = d.id) | HASHTEXT(id::text || week::text) | READ_ALL |
GET /api/documents/needs-transcription?size=3 | EXISTS annotation AND (no blocks OR reviewed_pct < 0.90) | textedBlocks DESC, needs_expert ASC, HASHTEXT(...) | READ_ALL |
GET /api/documents/ready-to-read?size=3 | reviewed_pct >= 0.90 | updated_at DESC | READ_ALL |
PATCH /api/documents/{id}/needs-expert | Setzt needs_expert = true | — | READ_ALL (jeder Nutzer darf flaggen) |
GET /api/stats/strip-activity | Wochenpuls: COUNT(*) WHERE created_at > NOW() - INTERVAL '7 days' pro Bucket | — | READ_ALL |
| Flyway-Migration | ALTER TABLE documents ADD COLUMN needs_expert BOOLEAN NOT NULL DEFAULT FALSE | — | V{n}__add_needs_expert_flag.sql |
| Index prüfen (Tobias) | document_annotations(document_id), transcription_blocks(document_id, reviewed) | — | EXPLAIN ANALYZE vor Merge |
| Division durch 0 (Sara) | Alle reviewed_pct-Queries: CASE WHEN COUNT(*) = 0 THEN 0 ELSE SUM(...)::float / COUNT(*) END | — | — |
DashboardMissionControl.svelteWrapper für den vollbreiten Streifen. Props: needsSegmentation, needsTranscription,
+ readyToRead, weeklyActivity. Rendert die drei Spalten und ist komplett unsichtbar wenn alle Arrays leer sind.
DashboardSegmentationCol.svelteSpalte 1: Skill-Pill, Wochenpuls, Avatare, Dokumentliste, CTA. Keine Balken — keine Dokument-Metadaten vorhanden.
+DashboardTranscriptionCol.svelteSpalte 2: Skill-Pill, Wochenpuls, Avatare, per-Dokument-Balken, Experten-Badge bei needsExpert, CTA.
DashboardReadyToReadCol.svelteSpalte 3: Zeigt gefüllten Zustand (Liste mit %-Text) oder leeren Zustand (Cross-Column-Redirect zu Segmentierung).
+DashboardNeedsMetadata.svelte ist unverändert —
+ sie lebt weiterhin in der rechten Spalte. Der Mission-Control-Streifen ist vollständig additiv und ändert nichts am bestehenden Layout.
+