diff --git a/CLAUDE.md b/CLAUDE.md
index 7423c26..d6cacbc 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -136,7 +136,7 @@ src/:
static/:
index.html: "Login-Seite (Magic-Link), JS inline"
dashboard.html: "Hauptdashboard (Sidebar + Reiter-Ansicht + 8 Modals + Chat-Widget)"
- studio.html: "Studio (3-Spalten-Werkstatt, online nur fuer info@ sichtbar - Gating rein clientseitig). Reiter Einstellungen ersetzt das Bearbeiten-Modal der klassischen Ansicht und enthaelt die Wahl des KI-Wegs je Fall."
+ studio.html: "Studio (3-Spalten-Werkstatt, online nur fuer info@ sichtbar - Gating rein clientseitig). Reiter Einstellungen ersetzt das Bearbeiten-Modal der klassischen Ansicht und enthaelt die Wahl des KI-Wegs je Fall. Spalte 3 hat genau EINEN Startpunkt (#ls-btn), darunter stehen die vier Schritte als reine Anzeige. Einzelne Bausteine lassen sich aus der Oberflaeche nicht mehr starten, der Server kann es weiterhin."
css/:
style.css: "AegisSight Design System (Dark/Light Theme, alle Komponenten)"
studio.css: "Studio-spezifische Styles"
@@ -144,7 +144,7 @@ src/:
api.js: "REST-API-Client (fetch, Auth-Header, 30s Timeout, 403 -> Nur-Lese-Modus)"
app.js: "Hauptlogik: ThemeManager, NotificationCenter, App-Objekt (181 KB)"
components.js: "UI-Rendering: Sidebar, Faktenchecks, Toasts, Progress, Karte (geteilt mit Studio)"
- studio.js: "Kompletter Studio-Controller (116 KB), enthaelt auch den Einstellungen-Reiter je Fall"
+ studio.js: "Kompletter Studio-Controller, enthaelt den Einstellungen-Reiter je Fall und die Lauf-Anzeige (_renderLauf, _schrittZustaende, laufKnopf)"
chat.js: "Chat-Assistent Widget (Bedienfragen)"
pipeline.js: "Grafische Analysepipeline (11 Schritte, Live-Animation)"
layout.js: "Reiter-Umschalter je Lage (merkt letzten Reiter; enthaelt noch gridstack-Legacy-Stubs)"
@@ -171,6 +171,7 @@ tests/:
test_sammelaktionen.js: "Sammelaktionen der Seitenleiste im klassischen Dashboard, Auswahlmodus, Filterbeachtung, Loeschbestaetigung (Aufruf mit node)"
test_falleinstellungen.js: "Einstellungen-Reiter im Studio, Formular fuellen, nur Geaendertes speichern, KI-Weg umstellen samt Warnhinweis, Abo nachladen (Aufruf mit node)"
test_studio_struktur.py: "Strukturpruefung beider Oberflaechen ohne Browser, eindeutige IDs, jedes onclick zeigt auf eine vorhandene Methode, Cache-Buster, Design-Tokens"
+ test_lauf.js: "Lauf-Anzeige der Studio-Spalte, Zustand des Startknopfs, Kette, Schrittzustaende aus /freshness, Nachweis dass kein Schritt etwas startet (Aufruf mit node)"
test_quellenausgabe.py: "Ausgabetreue des Quellenverzeichnisses: Nummern aus dem Lagebild statt Zeilennummern, keine Kappung, ein Verlag ein Name, Statistik deckungsgleich mit dem Verzeichnis"
```
diff --git a/src/static/css/studio.css b/src/static/css/studio.css
index b183d15..9796bb8 100644
--- a/src/static/css/studio.css
+++ b/src/static/css/studio.css
@@ -1014,180 +1014,6 @@
es neu, die Zeile darunter meldet den Datenstand (aktuell / veraltet / laeuft). */
@keyframes st-spin { to { transform: rotate(360deg); } }
-/* Kompletter Lauf: orchestriert alles, daher als Primaerknopf ueber den Karten */
-.run-all {
- display: flex; align-items: center; justify-content: center; gap: 8px;
- width: 100%;
- padding: 9px 12px;
- margin-bottom: 12px;
- border-radius: var(--radius-md, 6px);
- border: 1px solid var(--accent);
- background: var(--accent);
- color: #fff;
- font-family: var(--font-title);
- font-weight: 600;
- font-size: 12.5px;
- cursor: pointer;
- transition: background .15s, opacity .15s;
-}
-.run-all:hover:not(:disabled) { background: var(--accent-hover); }
-.run-all:disabled { opacity: .45; cursor: not-allowed; }
-/* display:flex wuerde das hidden-Attribut ueberstimmen - explizit ausblenden */
-.run-all[hidden] { display: none; }
-.run-all .ra-icon { display: inline-flex; }
-.run-all .ra-state { display: none; }
-.run-all.running .ra-icon { display: none; }
-.run-all.running .ra-state {
- display: inline-flex; align-items: center; gap: 6px; font-weight: 400; font-size: 11.5px;
-}
-.run-all.running .ra-state::before {
- content: ''; width: 11px; height: 11px; flex-shrink: 0;
- border: 2px solid currentColor; border-right-color: transparent;
- border-radius: 50%; animation: st-spin .8s linear infinite;
-}
-
-/* Die Karte selbst */
-.art-card {
- background: var(--bg-card);
- border: 1px solid var(--border);
- border-radius: 10px;
- margin-bottom: 8px;
- overflow: hidden;
- transition: border-color .15s;
-}
-.art-card:hover { border-color: var(--accent); }
-.art-card:has(.studio-menu-item.active) { border-color: var(--accent); }
-.art-card.busy { border-color: var(--accent); }
-.art-card-soon { opacity: .6; }
-.art-row { display: flex; align-items: stretch; }
-
-/* Kartenkopf: uebernimmt die Rolle des alten Menue-Eintrags, aber ohne eigenen Rahmen */
-.studio-menu .art-head {
- flex: 1; min-width: 0;
- display: flex; align-items: center; gap: 10px;
- padding: 10px var(--sp-lg);
- margin: 0;
- background: none;
- border: none;
- border-radius: 0;
- text-align: left;
- cursor: pointer;
- color: var(--text-primary);
- font-family: var(--font-body);
-}
-.studio-menu .art-head:hover { background: var(--bg-hover); }
-.studio-menu .art-head.active { background: var(--tint-accent-faint); }
-.studio-menu .art-head-static { cursor: default; }
-.studio-menu .art-head-static:hover { background: none; }
-.art-head .mi-icon { flex-shrink: 0; width: 20px; height: 20px; display: inline-flex; align-items: center; justify-content: center; color: var(--accent); }
-.art-head .mi-text { flex: 1; min-width: 0; }
-.art-head .mi-title {
- display: block;
- font-family: var(--font-title);
- font-weight: 600;
- font-size: 13px;
- line-height: 1.25;
- white-space: normal;
- overflow: visible;
- text-overflow: clip;
-}
-.art-head .mi-meta { display: block; margin-top: 1px; font-size: 10.5px; color: var(--text-disabled); }
-
-/* Erzeuger-Aktion (rechnet das Ergebnis neu) */
-.art-run {
- flex-shrink: 0;
- width: 36px;
- display: flex; align-items: center; justify-content: center;
- background: none;
- border: none;
- border-left: 1px solid var(--border);
- color: var(--text-disabled);
- cursor: pointer;
- transition: color .15s, background .15s;
-}
-.art-run:hover:not(:disabled) { color: var(--accent); background: var(--bg-hover); }
-.art-run:disabled { opacity: .35; cursor: not-allowed; }
-
-/* Datenstand-Zeile */
-.art-note {
- display: none;
- align-items: center;
- gap: var(--sp-md);
- padding: 7px var(--sp-lg);
- border-top: 1px solid var(--border);
- font-size: 11px;
- line-height: 1.35;
-}
-.art-note.show { display: flex; }
-.art-note.fresh { background: var(--tint-success); color: var(--success); }
-.art-note.stale { background: var(--tint-warning); color: var(--warning); }
-.art-note.blocked { background: var(--bg-elevated); color: var(--text-disabled); }
-.art-note.running { background: var(--tint-accent); color: var(--accent); }
-.art-note .an-spin {
- width: 11px; height: 11px; flex-shrink: 0;
- border: 2px solid currentColor; border-right-color: transparent;
- border-radius: 50%; animation: st-spin .8s linear infinite;
-}
-.art-note button {
- margin-left: auto;
- white-space: nowrap;
- padding: 3px 9px;
- font-size: 10.5px;
- font-weight: 600;
- border-radius: var(--radius-md, 6px);
- border: 1px solid currentColor;
- background: transparent;
- color: inherit;
- cursor: pointer;
- font-family: var(--font-body);
-}
-.art-note button:hover { background: var(--tint-hover-subtle); }
-
-/* Unter-Ansichten: gehoeren zum selben Erzeuger (Analyse) */
-.art-subs { border-top: 1px solid var(--border); }
-.studio-menu .art-sub {
- display: flex; align-items: center; gap: 8px;
- width: 100%;
- padding: 6px var(--sp-lg);
- margin: 0;
- background: none;
- border: none;
- border-radius: 0;
- cursor: pointer;
- text-align: left;
- color: var(--text-secondary);
- font-family: var(--font-body);
- font-size: 11.5px;
-}
-.studio-menu .art-sub:hover { background: var(--bg-hover); color: var(--text-primary); }
-.studio-menu .art-sub.active { background: var(--tint-accent-faint); color: var(--accent); }
-.art-sub .mi-icon { flex-shrink: 0; color: var(--text-disabled); display: inline-flex; }
-.art-sub.active .mi-icon { color: var(--accent); }
-.art-sub .mi-title {
- flex: 1; min-width: 0;
- font-family: var(--font-body);
- font-weight: 500;
- font-size: 11.5px;
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
-}
-.art-sub .mi-meta { flex-shrink: 0; font-size: 10px; color: var(--text-disabled); }
-
-/* Export: Ausgabe-Aktion, kein Artefakt -> Fusszeile */
-.art-foot { margin-top: var(--sp-lg); padding-top: var(--sp-lg); border-top: 1px solid var(--border); }
-.studio-menu .art-export { justify-content: center; gap: 8px; margin-bottom: 0; padding: 8px; font-size: 12px; }
-.art-export .mi-title { font-family: var(--font-body); font-weight: 600; font-size: 12px; flex: 0 0 auto; }
-.art-export .mi-icon { color: var(--accent); display: inline-flex; }
-
-/* "Sammeln" in der Quellenspalte (sein Ergebnis landet dort) */
-.collect-btn:disabled { opacity: .45; cursor: not-allowed; }
-.collect-btn.running .cb-label { display: none; }
-.collect-btn.running::after {
- content: 'Sammelt …';
- display: inline-flex; align-items: center;
-}
-
/* Karten-Container fuellt den Karte-Tab */
#map-container {
width: 100%;
@@ -1486,3 +1312,221 @@
.case-ai-tag.ai-anthropic {
color: var(--text-disabled);
}
+
+/* === Der Lauf (Spalte 3) ============================================== */
+/* Genau ein Startpunkt. Die vier Schritte darunter sind Anzeige, sie starten
+ nichts. Frueher standen hier Artefakt-Karten, in denen ein Klick links das
+ Ergebnis oeffnete und einer rechts es neu rechnete. Das lag zu nah
+ beieinander und kostete im falschen Fall Geld. */
+
+.lauf-stand {
+ padding: 12px;
+ margin-bottom: 12px;
+ border: 1px solid var(--border);
+ border-radius: 8px;
+ background: var(--bg-card);
+}
+
+/* Vollstaendig umlaufender Rahmen, kein einseitiger Streifen */
+.lauf-stand.betont {
+ border: 2px solid var(--sidebar-active, #C8A851);
+ padding: 11px;
+}
+
+.ls-haupt {
+ font-size: 13px;
+ color: var(--text-primary);
+ margin-bottom: 2px;
+}
+
+.ls-haupt.warn { color: var(--warning); }
+
+.ls-neben {
+ font-size: 11px;
+ color: var(--text-tertiary);
+}
+
+.ls-btn {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 8px;
+ width: 100%;
+ margin-top: 12px;
+ padding: 10px 12px;
+ border-radius: 6px;
+ border: 1px solid var(--sidebar-active, #C8A851);
+ background: var(--sidebar-active, #C8A851);
+ color: #1A1A1A;
+ font-family: var(--font-title, inherit);
+ font-size: 13px;
+ font-weight: 600;
+ cursor: pointer;
+ transition: filter .15s, opacity .15s;
+}
+
+.ls-btn:hover:not(:disabled) { filter: brightness(1.08); }
+
+.ls-btn:disabled { opacity: .45; cursor: not-allowed; }
+
+.ls-btn.abbrechen {
+ background: transparent;
+ border-color: var(--error);
+ color: var(--error);
+}
+
+.ls-icon { display: inline-flex; flex-shrink: 0; }
+
+.ls-hinweis {
+ margin-top: 8px;
+ font-size: 11px;
+ color: var(--text-tertiary);
+ text-align: center;
+}
+
+.ls-hinweis[hidden] { display: none; }
+
+/* --- Die Kette --- */
+.lauf-kette {
+ display: flex;
+ align-items: center;
+ padding: 4px 6px 8px;
+}
+
+.kn {
+ width: 12px;
+ height: 12px;
+ flex-shrink: 0;
+ border-radius: 50%;
+ border: 2px solid var(--border);
+ background: var(--bg-elevated);
+}
+
+.kn-fertig { background: var(--success); border-color: var(--success); }
+.kn-veraltet { background: var(--warning); border-color: var(--warning); }
+.kn-laeuft {
+ background: var(--sidebar-active, #C8A851);
+ border-color: var(--sidebar-active, #C8A851);
+ animation: kn-puls 1.4s ease-in-out infinite;
+}
+.kn-gesperrt { opacity: .45; }
+
+.kn-linie { flex: 1; height: 2px; background: var(--border); }
+.kn-linie.aktiv { background: var(--success); }
+
+@keyframes kn-puls { 0%, 100% { opacity: 1; } 50% { opacity: .35; } }
+
+@media (prefers-reduced-motion: reduce) {
+ .kn-laeuft { animation: none; }
+}
+
+.lauf-fortschritt {
+ font-size: 11px;
+ color: var(--text-tertiary);
+ text-align: center;
+ margin-bottom: 12px;
+}
+
+/* --- Die Schritte --- */
+.lauf-schritte { margin-bottom: 4px; }
+
+.schritt {
+ border: 1px solid var(--border);
+ border-radius: 6px;
+ margin-bottom: 6px;
+ background: var(--bg-card);
+ overflow: hidden;
+}
+
+.schritt-laeuft, .schritt-veraltet {
+ border: 2px solid var(--sidebar-active, #C8A851);
+}
+
+.schritt-gesperrt { opacity: .55; }
+
+.schritt-kopf {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ width: 100%;
+ padding: 8px 10px;
+ background: none;
+ border: none;
+ color: var(--text-primary);
+ font-family: inherit;
+ text-align: left;
+ cursor: pointer;
+}
+
+.schritt-kopf:hover { background: var(--tint-hover-subtle, rgba(255,255,255,.03)); }
+
+.schritt-nr {
+ flex-shrink: 0;
+ width: 20px;
+ height: 20px;
+ border-radius: 50%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 11px;
+ font-weight: 700;
+ background: var(--bg-elevated);
+ color: var(--text-tertiary);
+}
+
+.schritt-fertig .schritt-nr { background: var(--tint-success); color: var(--success); }
+.schritt-veraltet .schritt-nr { background: var(--tint-warning); color: var(--warning); }
+.schritt-laeuft .schritt-nr { background: var(--tint-accent); color: var(--sidebar-active, #C8A851); }
+
+.schritt-txt { flex: 1; min-width: 0; display: flex; flex-direction: column; }
+.schritt-name { font-size: 12px; }
+.schritt-stand { font-size: 11px; color: var(--text-tertiary); }
+.schritt-fertig .schritt-stand { color: var(--success); }
+.schritt-veraltet .schritt-stand { color: var(--warning); }
+
+.schritt-pfeil {
+ flex-shrink: 0;
+ color: var(--text-tertiary);
+ display: inline-flex;
+ transition: transform .18s;
+}
+
+.schritt.offen .schritt-pfeil { transform: rotate(180deg); }
+
+.schritt-mehr {
+ display: none;
+ padding: 0 10px 10px 40px;
+ font-size: 11px;
+ line-height: 1.5;
+ color: var(--text-secondary);
+}
+
+.schritt.offen .schritt-mehr { display: block; }
+
+/* --- Bereichstrenner im Menue --- */
+.menu-bereich {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ margin: 20px 0 6px;
+ font-size: 10px;
+ font-weight: 700;
+ letter-spacing: .08em;
+ text-transform: uppercase;
+ color: var(--text-tertiary);
+}
+
+.menu-bereich::after {
+ content: "";
+ flex: 1;
+ height: 1px;
+ background: var(--border);
+}
+
+/* Untergeordnete Ansicht, gehoert zum Eintrag darueber */
+.studio-menu-item.mi-unter {
+ padding-left: 30px;
+ font-size: 12px;
+}
+
+.studio-menu-item.mi-unter .mi-title { font-size: 12px; }
diff --git a/src/static/dashboard.html b/src/static/dashboard.html
index 6b57189..9c16903 100644
--- a/src/static/dashboard.html
+++ b/src/static/dashboard.html
@@ -13,7 +13,7 @@
-
+