/* CycleScanner Cockpit — Frontend-Shell (Task 11).
 *
 * Design-Tokens 1:1 aus SignalBot cockpit/static/styles.css :root
 * (Zeilen 17-45) übernommen, OHNE die --cot-* Domänenfarben (COT-Konvention
 * gehört zu SignalBot, nicht zu CycleScanner — keine COT-Daten hier, Spec §1
 * Trim). Layout darunter ist CycleScanner-eigen: Topbar mit 6-Tab-Leiste
 * (N2 konsolidiert Spectrum/Harmonics/Projektion/Energy im festen
 * 2x2-Tab "Zyklen-Detail"),
 * feste linke Einstellungs-Spalte (#settings-col, schema-getrieben via
 * js/settings.js) + rechter View-Container mit einer <section class="view">
 * pro Tab.
 *
 * Task N4a (.superpowers/sdd/n4a-brief.md, Owner-Live-Test-Kritik: "kein
 * rechter Rand", "nicht das ganze Panel gefuellt", "Seite scrollt statt
 * eine Browserseite = ein volles Cockpit", "Navigationsspalte nicht
 * umrandet"): App-Rahmen + Vollbild-Disziplin 1:1 nach dem SignalBot-/
 * Backtester-Muster portiert (deren styles.css, jeweils Zeilen ~60-107 --
 * `.app`/`.cockpit-section`-Kommentar). Zwei dort real aufgetretene
 * Nutzer-Bugs gelten hier WOERTLICH mit:
 *   1. NIEMALS `max-content`-Breiten am App-Container (`.app` unten) --
 *      der Container bemisst sich sonst am Chart-Canvas, der sich per
 *      autoSize wieder am Container bemisst -> Endlos-Resize-Schleife (auf
 *      Retina divergiert die Pixel-Rundung: Flackern, explodierende Panes,
 *      weisse Charts). Breite ist DEFINIT: `width:100%` + festes `min-width`.
 *   2. `height:100vh` (nicht nur `min-height`) ist Pflicht fuer `.app` --
 *      nur mit DEFINITER Hoehe loesen sich `minmax(0,1fr)`-Grid-Zeilen und
 *      die flex/autoSize-Chart-Kette zu echten Pixelhoehen auf, sonst
 *      blaehen sich Panes auf ihre Intrinsic-Hoehe auf.
 * Anders als SignalBot/Backtester (die MEHRERE volle `.cockpit-section`s
 * untereinander stapeln und die Seite darueber vertikal scrollen lassen --
 * Markt-Rail bleibt sticky) ist CycleScanner tab-basiert: es existiert
 * IMMER nur EIN aktiver Inhalt (`.view.active`). Die Rolle von
 * `.cockpit-section` uebernimmt hier deshalb `.view.active` selbst (volle
 * Hoehe, `overflow-y:auto` als Puffer, falls gestapelte Report-Panels
 * -- Saisonalitaet/Tabelle/Audit -- mehr Platz brauchen als der Viewport
 * hergibt) statt eine zweite Scroll-Ebene einzufuehren. `.app`/`.layout`
 * selbst scrollen NIE (kein Seiten-Scroll als Normalzustand, Brief Punkt 3).
 * `min-height:0` an JEDER Stelle der Vollbild-Kette (`.layout` ->
 * `#view-container`/`#settings-col` -> `.view.active` -> `.scanner-grid` ->
 * Panels -> Chart-Container) ist zwingend -- dieselbe Grid-Blowout-Regel,
 * die diese Datei bereits horizontal fuer `.scanner-grid > div {min-width:0}`
 * nutzt (Task 12 Fix-Runde 1, Fund B), gilt VERTIKAL identisch: ein Grid-
 * /Flex-Item ohne explizites `min-height:0` hat einen automatischen
 * Mindestwert von `auto` (Intrinsic-/Content-Hoehe) -- bei hohem Inhalt
 * (z.B. der Audit-Tab) sprengt das sonst die `minmax(0,1fr)`-Zeile ueber
 * die feste `.app`-Hoehe hinaus, und WEIL nichts das abfaengt, blaeht sich
 * am Ende `.layout` selbst auf > 100vh auf -- exakt der gemeldete
 * Seiten-Scroll-Bug.
 *
 * Chart-Hoehe aus der Zelle: `charts.js` schreibt fuer jeden Chart-Container
 * `containerEl.style.flex = <Pane-Hoehen-Summe aus dem JSON> + " 1 0"`
 * statt einer absoluten `style.height`-Pixelsumme -- in einem Vollbild-Grid
 * gibt die umgebende Zelle die Hoehe vor, nicht eine feste Pixelzahl aus
 * den Settings (Task N4a, SignalBot-/Backtester-Vorbild: deren chart.js
 * `setStretchFactor`/`applyStretch` fuer PANES INNERHALB EINES Charts, s.
 * dortiger Kommentar "Pane-Hoehenverhaeltnis (Stretch, relativ wie
 * flex-grow)"). Jeder Tab hat GENAU EINEN Chart-Container pro Panel --
 * ein einzelnes `flex-grow`-Element ohne konkurrierende Geschwister fuellt
 * die Zeile immer vollstaendig, unabhaengig vom konkreten Gewicht.
 * (Bis Owner-Trim 2026-07-14 hatte der Scanner-Tab als einziger ZWEI
 * Flex-Geschwister in derselben Panel-Spalte -- `#scanner-chart` Preis +
 * ein `.scanner-spectrum-chart` genanntes eingebettetes Spektrum darunter,
 * Gewichtsverhaeltnis ~5.5:1 aus Settings/LAYOUT_DEFAULTS. Der Owner befand
 * live "Chart-Flaeche zu klein" und liess das eingebettete Spektrum
 * ersatzlos entfernen [.superpowers/sdd/spektrum-move-report.md] -- das
 * Spektrum lebt seit N2 im Spectrum-Quadranten des Zyklen-Detail-Tabs,
 * `#scanner-chart` ist seither das einzige Flex-Kind seiner
 * Panel-Spalte und fuellt sie komplett. Der Gewichts-Mechanismus selbst
 * bleibt unveraendert bestehen -- er ist generisch fuer ALLE Tabs, kennt
 * keine Tab-Unterscheidung und war schon vor der Spektrum-Einbettung die
 * Loesung fuer die 0-Hoehen-Falle bei leeren Containern, s. `:empty`-Floor-
 * Kommentare unten.)
 */

:root {
  --bg: #0E1117;
  --panel: #151A23;
  --panel-inset: #0B0F15;
  --border: #232B38;
  --hairline: #1B2230;

  --ink: #E8EDF4;
  --ink2: #939EB1;
  --ink3: #5A6577;

  --cyan: #00D4FF;
  --green: #10B981;
  --red: #EF4444;
  --purple: #9D7BFF;
  --amber: #FFB454;
  --gold: #F0B90B;
  --disabled: #5A6577;
  /* Text auf Cyan-Flaechen — gemeinsamer SB/BT-Wert (Design-Harmonisierung 2026-07-16) */
  --on-accent: #06222B;

  --font-ui: -apple-system, "Segoe UI", system-ui, sans-serif;
  --font-data: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
}

* { box-sizing: border-box; }
/* `overflow-y: hidden` erzwingt "kein Seiten-Scroll als Normalzustand"
   (Brief Punkt 3) -- `overflow-x: auto` bleibt bewusst offen: `.app` hat
   ein festes `min-width` (s.u., Falle 1) und schmalere Fenster sollen
   horizontal scrollen (kein Quetschen), exakt wie im SignalBot-/Backtester-
   Kommentar zu `.app` beschrieben. */
html, body { height: 100%; margin: 0; background: var(--bg); color: var(--ink); font-family: var(--font-ui); overflow-x: auto; overflow-y: hidden; }
/* Task N4a: Wurzel-Rahmen nach dem SignalBot-/Backtester-`.app`-Muster (s.
   Kopfkommentar) -- DEFINITE Breite (100% + festes min-width, niemals
   max-content) UND DEFINITE Hoehe (100vh), `padding:10px` gibt den zuvor
   fehlenden rechten/oberen/unteren Rand zurueck. `.layout` (s.u.) waechst
   per `flex:1 1 auto` in die verbleibende Hoehe nach der Topbar -- kein
   hartcodierter Topbar-Hoehen-Wert mehr noetig (vorher `calc(100vh-45px)`,
   eine Magic Number, CLAUDE.md §4.1). */
.app { display: flex; flex-direction: column; gap: 10px; padding: 10px; width: 100%; min-width: 1040px; height: 100vh; }
/* Audit-Fund M2 (n4a-report Nachtrag): `position:sticky` war ein Relikt aus
   der Zeit vor N4a, als `.app`/`.layout` noch Seiten-Scroll zulassen konnten
   -- seit N4a scrollt `.app` nie mehr (html/body: overflow-y:hidden), also
   ist sticky wirkungslos (totes CSS). Entfernt. Die Topbar-OPTIK (Rahmen/
   Radius passend zur neu gerundeten Navigationsspalte) ist bewusst NICHT
   Teil dieses Fixes -- das gehört nach N4b (Komponenten-Optik-Angleichung),
   nicht in diese Bugfix-Runde. */
/* Design-Harmonisierung 2026-07-16 (Fund 11): Topbar als Panel-Box (voller
   Rahmen + Radius 8px statt nur border-bottom), wie SB/BT-Topbar-Panels.
   Das Tab-Paradigma (unterstrichene Tabs) bleibt bewusst unangetastet. */
.topbar { display: flex; align-items: center; gap: 16px; padding: 8px 12px; border: 1px solid var(--border); border-radius: 8px; background: var(--panel); flex: 0 0 auto; }
.topbar .brand { font: 700 13px var(--font-ui); color: var(--cyan); white-space: nowrap; }
.tabbar { display: flex; gap: 2px; overflow-x: auto; }
.tabbar button { background: none; border: none; color: var(--ink2); padding: 8px 12px; cursor: pointer; font: 600 12.5px var(--font-ui); border-bottom: 2px solid transparent; white-space: nowrap; }
.tabbar button.active { color: var(--ink); border-bottom-color: var(--cyan); }
/* Task 16 (Owner-Feedback Live-Test): linke Spalte stufenlos per Splitter
   (js/splitter.js, --left-col) statt fixer 300px -- Fallback 300px bleibt
   der bisherige Default, solange nie gezogen wurde. `gap: 10px` zwischen
   den Spalten wie SignalBot/Backtester `.app` (Referenz-Cockpits, Step 4).
   Gilt fuer ALLE Tabs, nicht nur den Scanner -- #settings-col ist ein
   einziges, globales Grid-Item in `.layout` (kein Per-Tab-Duplikat).
   Task N4a: `height: calc(100vh - 45px)` (Magic Number, Topbar-Hoehe
   geraten) ersetzt durch `flex:1 1 auto` in der jetzt definit-hohen `.app`
   (s.o.) -- `grid-template-rows: minmax(0, 1fr)` macht die Zeile explizit
   (Brief Punkt 3), `min-height: 0` verhindert, dass hoher Tab-Inhalt (z.B.
   Audit) die Zeile ueber die verfuegbare Hoehe hinaus aufblaeht (dieselbe
   Grid-Blowout-Regel wie `.scanner-grid > div` unten, nur vertikal). */
.layout { display: grid; grid-template-columns: var(--left-col, 300px) minmax(0, 1fr); grid-template-rows: minmax(0, 1fr); flex: 1 1 auto; min-height: 0; gap: 10px; }
/* Task 16 Step 4: die Scroll-Zone ist jetzt #settings-col-body
   (position:absolute; inset:0 -- s.u.) -- #settings-col selbst bleibt
   overflow:visible (Default), sonst schneidet overflow-y:auto den
   linken Splitter-Griff ab (Step 3, an dieses Element angehaengt, ragt
   bei `right:-9px` ueber die eigene Box hinaus -- dieselbe Kopplungsfalle,
   die #scanner-right-col unten durch schlicht fehlendes overflow meidet).
   Task N4a (Owner-Live-Test: "die linke Navigation ist visuell komplett
   von oben bis unten ohne mit dunklem Hintergrund umrandet zu sein"):
   voller Rahmen (statt nur border-right) + border-radius -- eigenstaendige
   Panel-Box wie die Rail in SignalBot/Backtester. `min-height:0` als
   Grid-Item derselben Blowout-Vorsicht wie #view-container. */
#settings-col { position: relative; min-height: 0; background: var(--panel); border: 1px solid var(--border); border-radius: 8px; }
#settings-col-body { position: absolute; inset: 0; overflow-y: auto; padding: 10px; }
/* Task N4a: #view-container ist jetzt selbst ein Grid-Item mit definiter
   Hoehe (s. .layout) -- die Seiten-Scroll-Falle (Owner: "Seite scrollt
   statt eine Browserseite = ein volles Cockpit") war `overflow-y:auto`
   HIER kombiniert mit fehlendem `min-height:0`: ohne min-height:0 hat ein
   Grid-Item einen automatischen Mindestwert von `auto` (Content-Hoehe) --
   bei hohem Tab-Inhalt sprengte das die Grid-Zeile ueber `.layout` hinaus,
   und WEIL nichts das abfing, blaehte sich am Ende die ganze Seite auf.
   `overflow:hidden` hier ist die aeusserste Sicherung (nichts scrollt auf
   dieser Ebene mehr) -- der eigentliche Scroll-Puffer sitzt jetzt bei
   `.view.active` (s.u.), das als generisches Aequivalent zu
   SignalBot/Backtesters `.cockpit-section` fungiert. */
/* Owner-Korrektur 2026-07-14: Der aeussere `.app`-Rahmen und das `.layout`-
   Gap liefern bereits die einheitlichen 10px-Abstaende. Zusaetzliches
   Padding hier setzte Mittel- und Rechtsspalte oben/unten um je 10px ein
   und verdoppelte den Abstand zur linken Spalte. Ohne Innenabstand liegen
   alle drei Spalten wieder auf denselben oberen und unteren Kanten. */
#view-container { min-width: 0; min-height: 0; padding: 0; overflow: hidden; }
/* Task N4a (Brief Punkt 3, "Vollbild-Disziplin ... gilt fuer ALLE Tabs"):
   `.view.active` uebernimmt hier die Rolle von SignalBot/Backtesters
   `.cockpit-section` (volle verfuegbare Hoehe, definit) -- anders als dort
   (mehrere `.cockpit-section`s werden im Seiten-Scroll gestapelt) existiert
   bei uns immer nur EIN aktiver Tab-Inhalt, daher hier statt eines zweiten
   Grids ein Flex-Spalten-Container: Der Scanner gibt seinem dominanten
   Chart-Panel `flex:1 1 auto`; der Zyklen-Detail-Tab überschreibt dieses
   Grundlayout gezielt mit einem festen 2x2-Grid. Mehrgliedrige Report-Tabs
   (Saisonalitaet/Tabelle) teilen
   ihre Panels per `flex:1 1 0` gleichmaessig auf. Audit/Export bleiben
   bewusst bei natuerlicher Panel-Hoehe (sehr unterschiedliche, datengetrie-
   bene Panel-Groessen -- eine erzwungene Gleichverteilung waere dort selbst
   Leerraum/Stauchung, s. n4a-report). `overflow-y:auto` ist der Puffer, falls
   gestapelter Report-Inhalt trotzdem mehr Platz braucht als der Viewport
   hergibt -- "scrollt intern im Panel, nicht die Seite" (Brief Punkt 3):
   `.view.active` IST hier das Panel, das ggf. scrollt, die Seite
   (`html`/`body`, `.app`, `.layout`) scrollt nie. */
.view { display: none; }
.view.active { display: flex; flex-direction: column; gap: 10px; height: 100%; min-height: 0; overflow-y: auto; }
/* Task N2: vier vorher getrennte Chart-Tabs als feste, nicht einklappbare
   Quadranten. `display:grid` darf NUR den aktiven Detail-Tab treffen -- eine
   unbedingte ID-Regel würde `.view {display:none}` überstimmen und den Tab
   permanent sichtbar machen. `minmax(0,1fr)` + min-width/min-height:0 auf
   den Panels schützt die LWC-autoSize-Kette vor Grid-Blowout/0-Höhen. Der
   View selbst scrollt nicht; Überlauf bleibt in seinem jeweiligen Panel. */
#view-zyklen-detail.view.active {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  grid-template-rows: repeat(2, minmax(0, 1fr));
  gap: 10px;
  width: 100%;
  height: 100%;
  min-width: 0;
  min-height: 0;
  overflow: hidden;
}
.cycle-detail-panel {
  min-width: 0;
  min-height: 0;
  overflow: auto;
  display: flex;
  flex-direction: column;
}
.scanner-grid { display: grid; grid-template-columns: minmax(0, 1fr) var(--right-col, minmax(320px, 420px)); grid-template-rows: minmax(0, 1fr); gap: 10px; height: 100%; min-height: 0; }
/* Task 12 Fix-Runde 1 (Controller-Smoke, Fund B): Grid-Blowout — ein Grid-
   Item hat ohne explizites min-width:0 einen automatischen Mindestwert von
   max-content (hier: die breite Zyklen-Tabelle), wodurch die Spalte über
   ihre minmax()-Vorgabe hinauswächst und die ganze Seite horizontal
   scrollt statt nur das Panel. Beide direkten Grid-Kinder (Chart-Spalte +
   #scanner-right-col) bekommen min-width:0, damit sie an die Grid-Tracks
   gebunden bleiben; die Tabelle scrollt dann innerhalb ihres eigenen
   Containers (#cycle-table, s.u.). Task N4a: dieselbe Vorsicht jetzt auch
   VERTIKAL (min-height:0) + beide Kinder werden Flex-Spalten (display:flex;
   flex-direction:column), damit ihr jeweils EIN Panel-Kind (s.
   #scanner-chart-panel/#scanner-cycles-panel unten) per flex-grow die volle
   Zellenhoehe ausfuellen kann. */
.scanner-grid > div { min-width: 0; min-height: 0; display: flex; flex-direction: column; }
/* Design-Harmonisierung 2026-07-16 (Fund 4): Panel-Radius 8px + Padding
   14px 16px = SB/BT-Mehrheitsstandard (vorher 6px / 10px). */
.panel { background: var(--panel); border: 1px solid var(--border); border-radius: 8px; padding: 14px 16px; }
/* Task 16 Step 4: kompakter Panel-Abstand (10px) zwischen gestapelten
   Panels in derselben Spalte/Sektion -- SignalBot/Backtester-Muster
   (dort via flex+gap). Hier bewusst per Sibling-Selektor statt Umbau auf
   Flexbox, damit sich am Shrink-/Overflow-Verhalten der betroffenen
   Container (#settings-col-body, #scanner-right-col)
   nichts aendert. */
.panel + .panel { margin-top: 10px; }
/* Audit-Fund M1 (n4a-report Nachtrag): `.view.active` ist seit N4a selbst
   ein Flex-Container mit `gap: 10px` (s.o.) -- für Tabs, deren Panels
   DIREKTE Kinder von `.view.active` sind (Zyklen-Detail, Saisonalität,
   Tabelle, Audit), addierten sich `gap: 10px` UND `margin-top: 10px`
   (Regel oben) zu 20px zwischen gestapelten Panels. Diese Regel setzt den
   Margin-Anteil NUR für diesen Fall auf 0 zurück -- der Gap allein reicht.
   Andere `.panel + .panel`-Kontexte (z.B. `#settings-col-body`, dessen
   Settings-Gruppen KEIN Flex/Gap nutzen, s. Kommentar oben; `#scanner-
   right-col`, das ebenfalls kein eigenes `gap` definiert) bleiben
   unverändert auf den Margin angewiesen -- der `>`-Kombinator trifft nur
   Panels, die UNMITTELBARE Kinder von `.view.active` sind. */
.view.active > .panel + .panel { margin-top: 0; }
/* Task 16 Step 1 (Schriftgroessen-Paritaet): verbleibende <h3>-Panel-Header
   (Zyklen/Ausgewaehlte Zyklen -- die Settings-Gruppen selbst werden
   unten auf details/summary umgebaut) wie SignalBot .panel-header (12px/700). */
.panel > h3 { margin: 0 0 10px; font: 700 12px var(--font-ui); color: var(--ink); }

/* Task 9 (Phase 2): Scanner-KPI-Zeile + Regime-Badge -- Port von ui/
   tabs_main.py::render_kpi_row (assets/style.css:395-441 ist das Streamlit-
   Pendant, dort mit "positive"/"negative"/"neutral"-Farbcodierung nach
   `delta_type`). Die Brief-Interfaces-Form `{label, value, hint}`
   (cockpit/views.py::build_scanner_view) hat kein delta_type-Aequivalent
   (s. dortiger Modul-Docstring "Task 9") -- deshalb hier bewusst EINE
   einheitliche Hint-Farbe statt drei Varianten. `auto-fit`/`minmax` statt
   Streamlits starren 5 Spalten, damit die Zeile auch auf einer schmalen
   Scanner-Spalte (Splitter nach rechts gezogen) sauber umbricht statt zu
   ueberlaufen -- gleiches Muster wie SignalBot/Backtester .kpi-tiles. */
.kpi-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(130px, 1fr));
  gap: 8px;
  margin-bottom: 8px;
}
/* Design-Harmonisierung 2026-07-16 (Fund 13): Kachel-Metrik auf SB/BT-Standard
   (Padding 6px 8px, Label 700 9px, Wert 700 13px). */
.kpi-card {
  background: var(--panel-inset);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 6px 8px;
  min-width: 0;
}
.kpi-label {
  font: 700 9px var(--font-ui);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink3);
  margin-bottom: 4px;
}
.kpi-value {
  font: 700 13px var(--font-data);
  color: var(--ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.kpi-hint {
  font: 12px var(--font-data);
  color: var(--cyan);
  margin-top: 3px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* Task 12: Chart-Container braucht vor dem ersten renderChartStack() eine
   Mindesthöhe, sonst kollabiert der Panel-Bereich auf 0px (LWC autoSize
   übernimmt nur die Breite reaktiv). Task N4a: die Höhe setzt charts.js
   NICHT MEHR als absolute Pixelsumme (Inline-`style.height`) -- in einem
   Vollbild-Grid gibt die Zelle die Höhe vor, nicht eine Pixelsumme (Brief
   Punkt 4). Stattdessen setzt charts.js `style.flex` mit derselben Zahl
   als RELATIVES Gewicht (s. Kopfkommentar dieser Datei, Abschnitt
   "Chart-Höhe aus der Zelle") -- `min-height` hier bleibt unverändert die
   Floor-Sicherung gegen die 0-Höhen-Falle, unabhängig vom flex-Mechanismus. */
/* Audit-Fund I1 (n4a-report Nachtrag): die Floor-Regel griff bisher IMMER
   (auch mit gerendertem Chart) -- auf Laptop-Höhen (< ~830px Viewport) war
   sie damit eine harte Untergrenze GEGEN die per Brief geforderte
   Gewichtsverteilung: 300px (hier) + das feste Beiwerk im Panel (KPI-Zeile/
   Regime-Badge/Preset-Chips/Range-Controls/Paddings) übersteigt dann die
   verfügbare Panel-Höhe, wodurch `.view.active` intern zu scrollen beginnt
   statt die Fläche per flex-grow zu verteilen -- exakt das "nicht volles
   Cockpit", das N4a beheben sollte. `:empty` beschränkt den Floor auf genau
   den Zustand, für den er gedacht war: BEVOR der erste `renderChartStack()`-
   Aufruf lief (leeres `<div>` aus index.html) bzw. NACH einem
   Leerzustand-Rebuild (renderChartStack setzt bei leerem Panes-Array
   `containerEl.innerHTML = ""`, charts.js:397 -- dort UND NUR dort ist der
   Container tatsächlich `:empty`, ohne dass eine Floor-Sicherung gegen
   Kollaps auf 0px nötig wäre). Sobald LWC den Chart gebaut hat
   (Canvas-Kinder vorhanden), matcht `:empty` nicht mehr -- die Floor-Regel
   tritt dann nicht mehr gegen `flex-grow` an.
   (Bis Owner-Trim 2026-07-14 gab es hier eine zweite, analoge Floor-Regel
   fuer `.scanner-spectrum-chart` [eingebettetes Spektrum, 160px] -- diese
   Regel + der Selektor sind mit der Spektrum-Einbettung selbst entfallen,
   s. spektrum-move-report.md.) */
/* Owner-Fund 2026-07-16 ("zu hoch"): LWC autoSize schreibt eine explizite
   Pixelhoehe in den Chart-Wrapper -- ohne min-height:0 wird sie zum
   automatischen Flex-Minimum von #scanner-chart, das Element kann danach
   NIE mehr schrumpfen. Sichtbar seit dem Beispieldaten-Auto-Scan: der
   Preischart rendert zuerst (KPI-Zeile noch leer), die nachtraeglich
   gerenderte KPI-Zeile muesste den Chart um ihre Hoehe verkleinern --
   stattdessen lief das Panel ueber und die KPI-Kacheln wurden oben
   abgeschnitten. min-height:0 = dasselbe dokumentierte Muster wie bei den
   Detail-Quadranten (#spectrum-chart etc., Z. 609) und im SignalBot
   (styles.css:982 "min-height:0, damit lightweight-charts autoSize greift").
   Die :empty-Floor-Regel darunter gewinnt im Leerzustand weiterhin
   (hoehere Spezifitaet id+:empty). */
#scanner-chart { min-height: 0; }
#scanner-chart:empty { min-height: 300px; }

/* Task N4a: #scanner-chart-panel wird selbst eine Flex-Spalte -- `#scanner-
   chart` (das einzige Chart-Kind seit Owner-Trim 2026-07-14, s.
   Kopfkommentar dieser Datei) waechst per flex-grow (von charts.js gesetzt)
   auf die volle verbleibende Hoehe; KPI-Zeile/Range-Controls
   behalten ihre natürliche Höhe (Default flex:0 1 auto, hier nicht
   überschrieben). */
#scanner-chart-panel { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; }

/* Task 12: Spalten-Splitter zwischen Chart und rechter Zyklen-Spalte —
   1:1 aus SignalBot cockpit/static/styles.css (Zeilen 118-153)
   übernommen, s. js/splitter.js Kopfkommentar. `#scanner-right-col`
   braucht `position: relative` als Anker für den absolut positionierten
   Griff (`left: -9px`, sitzt über dem 10px-Grid-Gap). Task N4a: Flex-
   Spalten-Verhalten (display:flex etc.) kommt bereits generisch aus
   `.scanner-grid > div` (s.o.) -- hier nur noch der Anker. */
#scanner-right-col { position: relative; }

/* Task N4a: Zyklen-Panel wächst (Modul-Leiste/Banner und optionaler
   Rhythmus-Hinweis bleiben natürlich, #cycle-table s.u. wird der wachsende,
   intern scrollende Teil). */
#scanner-cycles-panel { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; }

.col-splitter {
  position: absolute;
  left: -9px;
  top: 0;
  bottom: 0;
  width: 8px;
  cursor: col-resize;
  z-index: 30;
}

/* Task 16 Step 3: linker Splitter (Einstellungs-Spalte <-> Mitte) sitzt auf
   der RECHTEN Kante von #settings-col statt der linken -- gleicher Griff,
   gespiegelte Seite (splitter.js: initSplitter({..., invert: true})). */
.col-splitter.col-splitter-left {
  left: auto;
  right: -9px;
}

.col-splitter::after {
  content: "";
  position: absolute;
  left: 3px;
  top: 0;
  bottom: 0;
  width: 2px;
  border-radius: 1px;
  background: transparent;
  transition: background 120ms ease;
}

.col-splitter:hover::after,
body.col-resizing .col-splitter::after {
  background: rgba(0, 212, 255, 0.45);
}

body.col-resizing {
  cursor: col-resize;
  user-select: none;
}

/* Task 12: Alert-Schweregrad-Akzente (engine/alerts.py Alert.severity:
   "signal"/"warning"/"info"). */
.alert-signal { color: var(--green); }
.alert-warning { color: var(--amber); }
.alert-info { color: var(--ink2); }
.field { margin-bottom: 8px; } .field label { display: block; color: var(--ink2); font-size: 11px; margin-bottom: 2px; }
/* Design-Harmonisierung 2026-07-16 (Fund 14): Input-Standard wie SB/BT
   (padding 3px 6px, font 500 11px var(--font-data)). */
.field input, .field select { width: 100%; background: var(--panel-inset); color: var(--ink); border: 1px solid var(--border); border-radius: 4px; padding: 3px 6px; font: 500 11px var(--font-data); }
.field input[type="checkbox"] { width: auto; }
.field input[type="file"] { padding: 3px; cursor: pointer; }
.field input[type="file"]::file-selector-button {
  margin-right: 8px;
  padding: 4px 8px;
  border: 1px solid var(--border);
  border-radius: 4px;
  background: var(--panel);
  color: var(--ink2);
  font: 600 10.5px var(--font-ui);
  cursor: pointer;
  transition: color .15s ease, border-color .15s ease, background .15s ease;
}
.field input[type="file"]::file-selector-button:hover {
  color: var(--cyan);
  border-color: var(--cyan);
  background: rgba(0, 212, 255, .08);
}

/* Task 16 Step 2 (Owner-Feedback Live-Test): einklappbare Settings-Gruppen
   -- natives <details>/<summary> statt eigenem JS-Zustand (settings.js
   groupPanelHtml()/bind()), Zustand pro Gruppe in localStorage
   ("cyclescanner.groupCollapsed.<gruppenkey>"), Default ausgeklappt. Die
   SPALTE selbst bleibt fest -- nur der Gruppen-INHALT klappt ein/aus.
   Typografie (10.5px/700 uppercase + letter-spacing) 1:1 nach
   SignalBot-Muster (.rail-title/.sector-name/.curation-label), Step 1. */
.settings-group > summary {
  cursor: pointer;
  user-select: none;
  list-style: none;
  display: flex;
  align-items: center;
  gap: 6px;
  font: 700 10.5px var(--font-ui);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink3);
}
.settings-group > summary::-webkit-details-marker { display: none; }
.settings-group > summary::before {
  content: "\25B8"; /* ▸ zu */
  display: inline-block;
  width: 0.7em;
  color: var(--ink3);
}
.settings-group[open] > summary::before { content: "\25BE"; /* ▾ offen */ }
.settings-group-body { margin-top: 8px; }

/* Design-Harmonisierung 2026-07-16 (Fund 4/19/20): Primary-Standard von SB/BT
   (Radius 6px, Padding 8px 16px, 700 12.5px, Text var(--on-accent));
   Disabled-Zustand im SB-Muster (inset + Border) statt Vollgrau. */
button.primary { background: var(--cyan); color: var(--on-accent); border: none; border-radius: 6px; padding: 8px 16px; font: 700 12.5px var(--font-ui); cursor: pointer; width: 100%; }
button.primary:disabled { background: var(--panel-inset); color: var(--disabled); border: 1px solid var(--border); cursor: not-allowed; }

/* Task 8 (Phase 2): Range-Reanalyse-Controls unter dem Scanner-Chart
   (#range-controls, cockpit/static/js/scanner.js::renderMeta/
   bindRangeControls). Eigener, kompakter Button-Stil statt button.primary
   (dessen width:100% waere hier fehl am Platz -- die Range-Buttons sitzen
   inline neben den Datums-/Zahlenfeldern, nicht allein in einer Spalte). */
.range-meta-text { color: var(--ink2); font-size: 11px; margin-bottom: 6px; }
.range-form { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 10px; margin-bottom: 6px; }
.range-field { display: flex; flex-direction: column; gap: 2px; color: var(--ink2); font-size: 11px; }
/* Design-Harmonisierung 2026-07-16 (Fund 14/4/19): Input-Standard + Button-Radius 6px. */
.range-field input { background: var(--panel-inset); color: var(--ink); border: 1px solid var(--border); border-radius: 4px; padding: 3px 6px; font: 500 11px var(--font-data); }
.range-btn { background: var(--panel-inset); color: var(--cyan); border: 1px solid rgba(0, 212, 255, 0.4); border-radius: 6px; padding: 6px 12px; font: 700 11px var(--font-ui); cursor: pointer; white-space: nowrap; }
.range-btn:hover { border-color: rgba(0, 212, 255, 0.7); }
.range-btn:disabled { color: var(--disabled); border-color: var(--border); cursor: wait; }
.range-btn.primary { background: var(--cyan); color: var(--on-accent); width: auto; }
.range-error { color: var(--red); }
/* Task 12 Fix-Runde 1 (Fund B): Tabelle darf ihre natürliche (breite) Größe
   behalten, scrollt aber innerhalb dieses Containers statt die Seite zu
   sprengen — zusammen mit `.scanner-grid > div { min-width: 0 }` oben.
   Task N4a: zusätzlich `overflow-y: auto` + `flex: 1 1 auto; min-height: 0`
   -- #cycle-table ist jetzt der wachsende Teil in #scanner-cycles-panel
   (s.o.), eine lange Zyklen-Liste scrollt intern statt das Panel/den Tab
   über die Viewport-Höhe hinaus zu strecken. */
#cycle-table { flex: 1 1 auto; min-height: 0; overflow: auto; }
/* Analysebereich (Von/Bis) als abgegrenzte, fixe Box unten in der Zyklen-Spalte
   -- entlastet das Chart-Panel; die Zyklen-Tabelle darueber schrumpft/scrollt. */
#scanner-range-box { flex: 0 0 auto; border-top: 1px solid var(--border); margin-top: 8px; padding-top: 8px; }
/* Design-Harmonisierung 2026-07-16 (Fund 3): var(--muted) war hier undefiniert. */
.range-box-title { font-size: 11px; color: var(--ink3); text-transform: uppercase; letter-spacing: .04em; margin-bottom: 6px; }
table.data { width: 100%; border-collapse: collapse; font-family: var(--font-data); font-size: 11px; }
table.data th, table.data td { border-bottom: 1px solid var(--hairline); padding: 4px 6px; text-align: right; white-space: nowrap; }
table.data th:first-child, table.data td:first-child { text-align: left; }
/* Design-Harmonisierung 2026-07-16 (Fund 12): Tabellen-Header sticky +
   uppercase im SB-Muster (.signal-table thead th); Padding/Align aus der
   gemeinsamen th/td-Regel oben bleiben erhalten. */
table.data th { position: sticky; top: 0; background: var(--panel); font: 700 9.5px var(--font-ui); letter-spacing: .06em; text-transform: uppercase; color: var(--ink3); border-bottom: 1px solid var(--border); }
/* Task 7 (Phase 2): Modul-Toggle-Leiste ueber der Zyklen-Tabelle (Ranking/
   KI/Konsens/Rhythmus/4 Best) -- Port der Streamlit-Button-Reihe
   (ui/tabs_main.py::render_cycle_scanner_tab, CYCLE_SELECTION_MODULES).
   `.active` = Modul ist Teil des aktuellen Toggle-Zustands (entspricht
   Streamlits `type="primary"`); `:disabled` = Modul ohne Perioden bzw.
   deaktiviert (z.B. KI aus, kein Konsens-Ergebnis, meta.module_available). */
.module-toggle-bar { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 10px; }
.module-toggle {
  flex: 1 1 auto;
  background: var(--panel-inset);
  color: var(--ink2);
  border: 1px solid var(--border);
  border-radius: 6px; /* Design-Harmonisierung 2026-07-16 (Fund 4): 4px -> 6px */
  padding: 6px 8px;
  font: 700 11px var(--font-ui);
  cursor: pointer;
  white-space: nowrap;
}
.module-toggle:hover:not(:disabled) { border-color: rgba(0, 212, 255, 0.5); color: var(--ink); }
.module-toggle.active { background: var(--cyan); color: var(--on-accent); border-color: var(--cyan); }
.module-toggle:disabled { color: var(--ink3); cursor: not-allowed; opacity: 0.6; }
/* Checkbox-Spalte der Zyklen-Tabelle (Task 7) -- Zellen sind sonst rechts-
   ausgerichtet (table.data th/td, s.u.); die Checkbox soll mittig sitzen. */
table.data td.cycle-sel-cell { text-align: center; }

/* Task 6 (Phase 2): Tabelle-View (zwei Detail-Tabellen, Top-Zyklen + alle
   erkannten Zyklen) -- Panel-interner Scroll statt Seiten-Scroll, gleiches
   Muster wie #cycle-table oben (overflow: auto + min-width: 0 auf dem
   umgebenden Grid-/Flow-Kontext). `table.data` (s.o.) liefert bereits die
   gemeinsame Zell-/Header-Optik -- table.js rendert seine beiden Tabellen
   mit derselben Klasse für visuelle Konsistenz mit der Scanner-Tabelle.
   Task N4a: #table-cycle-panel/#table-detected-panel (die zwei umgebenden
   .panel, s. index.html) teilen sich die Tabelle-View-Höhe zu gleichen
   Teilen (flex:1 1 0, kein Grund für eine andere Gewichtung zwischen "Top-
   Zyklen" und "Alle erkannten Zyklen") -- #table-cycle-frame/#table-detected
   wachsen darin (flex:1 1 auto) und scrollen intern bei zu vielen Zeilen. */
#table-cycle-panel, #table-detected-panel { flex: 1 1 0; min-height: 0; display: flex; flex-direction: column; }
.table-view { overflow: auto; min-width: 0; }
#table-cycle-frame, #table-detected { flex: 1 1 auto; min-height: 0; overflow: auto; min-width: 0; }

/* Design-Harmonisierung 2026-07-16 (Fund 17): Banner-Radius 4px -> 6px. */
.error-banner { background: rgba(239, 68, 68, .12); border: 1px solid var(--red); color: var(--ink); padding: 8px 10px; border-radius: 6px; margin: 8px 0; }
.ok-banner { background: rgba(16, 185, 129, .12); border: 1px solid var(--green); color: var(--ink); padding: 8px 10px; border-radius: 6px; margin: 8px 0; }
.field-help { cursor: help; opacity: .55; font-size: 12px; margin-left: 3px; }
.status-line { color: var(--ink2); font-size: 11px; margin-top: 6px; min-height: 16px; }

/* Mini-Task "Scanner-Rest": Auto-Composite-Banner oberhalb der Zyklen-
   Tabelle -- Port von ui/tabs_main.py::st.warning/st.info (Auto-Composite-
   Block, Zeilen 826-836). Gleiches Muster wie .error-banner oben (Info =
   Cyan statt Rot, Warnung = Amber), keine neue Farbe eingeführt. */
.scanner-banner { padding: 8px 10px; border-radius: 6px; margin: 0 0 10px; font-size: 12px; line-height: 1.4; } /* Radius 6px: Design-Harmonisierung 2026-07-16 (Fund 17) */
.scanner-banner-info { background: rgba(0, 212, 255, .10); border: 1px solid var(--cyan); color: var(--ink); }
.scanner-banner-warning { background: rgba(255, 180, 84, .12); border: 1px solid var(--amber); color: var(--ink); }
#scanner-auto-composite-banner .status-line { margin-top: -4px; margin-bottom: 8px; }

/* Task 12 (Phase 2): Export-View -- drei Download-Karten (CSV/JSON/XLSX).
   `.export-download-btn` ist ein eigener Anker-Button-Stil statt `button.
   primary` wiederzuverwenden -- Letzterer ist `button.primary` (Element+
   Klasse-Selektor), trifft also nie auf ein `<a>` (Downloads sind normale
   Browser-Navigation über `<a href download>`, kein `<button>`-Klick mit
   JS-Handler). `.disabled` (gesetzt von export.js, solange kein Analyse-
   Ergebnis vorliegt) blockt Klicks per `pointer-events: none` UND per
   `preventDefault()` im Klick-Handler (Defense-in-Depth, falls ein
   Screenreader/Tastatur-Fokus pointer-events umgeht). */
.export-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 10px; }
.export-card { display: flex; flex-direction: column; gap: 8px; }
.export-card-desc { color: var(--ink2); font-size: 11.5px; margin: 0; flex: 1; }
/* Design-Harmonisierung 2026-07-16 (Fund 4/19/20): Radius 6px, Text
   var(--on-accent); Disabled im SB-Muster (inset + Border) statt Vollgrau. */
.export-download-btn {
  background: var(--cyan); color: var(--on-accent); border: none; border-radius: 6px;
  padding: 8px 12px; font: 700 11.5px var(--font-ui); text-decoration: none;
  display: inline-flex; align-items: center; justify-content: center; cursor: pointer;
}
.export-download-btn.disabled { background: var(--panel-inset); color: var(--disabled); border: 1px solid var(--border); cursor: not-allowed; pointer-events: none; }

/* Task 10 (Phase 2): Audit-View -- Job-Buttons (Walk-Forward/Ablation),
   Guided-Workflow-Chips, Diagnostics-Liste, Walk-forward-Verdikt-Banner.
   Metriken-Grids (Übersicht/KI/Konsens/Walk-forward) nutzen bewusst die
   bestehenden `.kpi-row`/`.kpi-card`-Klassen (s.o., Task 9) statt eigener
   Regeln -- gleiche kompakte Kachel-Optik, keine neue Komponente nötig. Die
   Panel-/`.settings-group`-Optik der Detail-Abschnitte ist ebenfalls 1:1
   wiederverwendet (Task 16 Step 2) -- kein neuer Collapse-Mechanismus. */
.audit-actions-row { display: flex; align-items: center; gap: 10px; margin-bottom: 8px; }
.audit-actions-row:last-child { margin-bottom: 0; }
/* button.primary ist standardmäßig width:100% (Sidebar-Kontext, Selektor-
   Spezifität Element+Klasse) -- hier sitzt der Button inline neben seinem
   Status-Text, nicht allein in einer schmalen Spalte. `button.audit-job-btn`
   (nicht nur `.audit-job-btn`) noetig, um dieselbe Spezifität wie
   `button.primary` zu erreichen und die width:100% zuverlaessig zu
   ueberschreiben. */
button.audit-job-btn { width: auto; flex: 0 0 auto; }

.chip-row { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; margin-top: 6px; }
.chip { padding: 0.35rem 0.7rem; border-radius: 999px; border: 1px solid rgba(120, 180, 255, 0.25); background: var(--panel-inset); font-size: 11px; color: var(--ink2); white-space: nowrap; }

/* Task N4a/N2: Vollbild-Füllung der vier Chart-Panels im 2x2-Detail-Grid.
   Der Chart-/SVG-Container wächst per flex:1 1 auto, damit LWCs
   `autoSize:true` die volle Quadranten-Zelle übernehmen (kein Leerraum unter
   dem Chart). #harmonics-svg ist eine
   Ausnahme in der Praxis: der Server rendert dort ein SVG mit fester
   Pixelhöhe (utils/plotting.py::generate_harmonics_svg, Parameter `height`,
   Default 220) -- dieses Modul bleibt unverändert (bindende Projektregel,
   s. cockpit/views.py Modul-Docstring), der Container füllt die Zeile,
   das SVG selbst bleibt bei seiner definierten Höhe (dokumentierte
   Abweichung, s. n4a-report). Die ausgewählten Projektionszyklen sitzen seit
   N2 als begrenzter, intern scrollender Unterbereich im Projektionsquadrant,
   damit es exakt vier Panels bleibt. */
#spectrum-chart-panel, #harmonics-panel, #energy-chart-panel, #projection-chart-panel {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
}
#spectrum-chart, #harmonics-svg, #energy-chart, #projection-chart {
  flex: 1 1 auto;
  min-height: 0;
}
.cycle-detail-subsection {
  flex: 0 1 35%;
  min-height: 0;
  overflow: auto;
  margin-top: 8px;
  padding-top: 8px;
  border-top: 1px solid var(--hairline);
}
.cycle-detail-subtitle {
  margin-bottom: 4px;
  color: var(--ink3);
  font: 700 9.5px var(--font-ui);
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

/* Audit-Fund C1 (n4a-report Nachtrag, KRITISCH): der n4a-Report hatte den
   Leerraum unter dem Harmonics-Diagramm für unvermeidbar erklärt ("Server
   rendert fest 220px, plotting.py ist tabu") -- beides war falsch. Der
   Aufrufort (cockpit/views.py::build_harmonics_view) setzt bereits
   `height = max(int(settings.get("layout_spectrum_chart_height", 380)),
   260)` = 380, NICHT den 220px-Funktions-Default. Und `generate_harmonics_
   svg` (utils/plotting.py) liefert bereits `width="100%"` UND eine
   `viewBox="0 0 {width} {height}"` (verifiziert per curl gegen den
   isolierten Smoke-Server, Port 8768, GET /api/analysis/views/harmonics --
   die viewBox ist vorhanden). Eine viewBox skaliert mit jedem CSS-
   `width`/`height` auf dem WURZEL-`<svg>` -- das war schlicht nie gesetzt
   (das SVG behielt sein `height="380"`-Presentation-Attribut, weil KEINE
   CSS-Regel es überschrieb). Kein Server-Eingriff nötig, `utils/
   plotting.py` bleibt unangetastet (Projektregel). Live-Chrome-Verifiziert
   (Vorher/Nachher-Screenshot gegen Port 8768): vorher ca. 300px SVG in
   einem ~680px hohen Panel (Leerraum darunter), nachher füllt das SVG die
   Zelle vertikal (`preserveAspectRatio` bleibt Default `xMidYMid meet` --
   bei einem breiteren-als-hohen Panel füllt das die Höhe vollständig, was
   exakt die Brief-Anforderung "füllt die Zelle vertikal" ist). */
#harmonics-svg > svg { display: block; width: 100%; height: 100%; }

/* Task N4a: Saisonalität hat DREI gleichrangige Chart-Panels (Monat/Woche/
   Komponente) -- ohne im Brief vorgegebene Gewichtung ist eine
   Gleichverteilung (flex:1 1 0) der neutrale Default, kein neues Verhältnis
   erfunden. Die Metrik-Statuszeile (#seasonality-metrics-panel) bleibt bei
   natürlicher Höhe (kurzer Text, kein Chart). */
#seasonality-monthly-panel, #seasonality-weekly-panel, #seasonality-component-panel {
  flex: 1 1 0;
  min-height: 0;
  display: flex;
  flex-direction: column;
}
#seasonality-monthly-chart, #seasonality-weekly-chart, #seasonality-component-chart {
  flex: 1 1 auto;
  min-height: 0;
}
/* Owner-Korrektur 2026-07-14: Der optionale Leer-/Fehlertext ist im
   Erfolgszustand leer. Die globale `.status-line`-Regel reserviert trotzdem
   16px Mindesthoehe + 6px Margin; zusammen mit dem 10px View-Gap blieb damit
   unter dem letzten Chart-Panel eine sichtbare 32px-Luecke. Nur der leere
   Platzhalter wird entfernt. Sobald `seasonality.js` einen echten Hinweis
   einsetzt, greift `:empty` nicht mehr und die Meldung bleibt sichtbar. */
#seasonality-subtitle:empty { display: none; }

/* Task N1: kompakte Zyklen-Tabelle (Panel "Cycle Spectrum", Spalten exakt
   Sel|Len|Amp|Strg|Stab|R, cockpit/views.py::_build_cycle_spectrum_table).
   `Len` als farbiges Pill-Badge (Direction-Herkunft: engine/ranking.py:52/
   engine/adaptive_ranking.py:138), `Stab` fett ab FSC-Forecast-Grade
   (Stability >= 0.5, CLAUDE.md §5), Zeile gruen hervorgehoben, wenn
   "Detection Rank" gesetzt ist (Screenshot-Optik des WTT-Originals). */
.cycle-len-pill {
  display: inline-block;
  padding: 1px 7px;
  border-radius: 999px;
  font: 700 11px var(--font-data);
}
.cycle-len-pill.dir-up { background: rgba(16, 185, 129, 0.18); color: var(--green); }
.cycle-len-pill.dir-down { background: rgba(239, 68, 68, 0.18); color: var(--red); }
.cycle-len-pill.dir-neutral { background: rgba(148, 163, 184, 0.14); color: var(--ink2); }
table.data td.stab-forecast-grade { font-weight: 700; color: var(--ink); }
table.data tr.cycle-row-highlight { background: rgba(16, 185, 129, 0.07); }

.diag-item { padding: 6px 0; border-bottom: 1px solid var(--hairline); }
.diag-item:last-child { border-bottom: none; }
.diag-item-title { font-weight: 700; }
.diag-item.diag-critical .diag-item-title { color: var(--red); }
.diag-item.diag-warn .diag-item-title { color: var(--amber); }
.diag-item.diag-info .diag-item-title { color: var(--ink2); }
.diag-item-message { color: var(--ink2); font-size: 11px; margin-top: 2px; }

.wf-verdict { padding: 8px 10px; border-radius: 4px; margin: 8px 0; font-size: 12px; }
.wf-verdict.warn { background: rgba(255, 180, 84, 0.12); border: 1px solid var(--amber); color: var(--ink); }
.wf-verdict.success { background: rgba(16, 185, 129, 0.12); border: 1px solid var(--green); color: var(--ink); }

.audit-trace-block { margin-bottom: 10px; }
.audit-trace-block:last-child { margin-bottom: 0; }
.audit-trace-header { font-size: 12px; color: var(--ink2); margin-bottom: 4px; }
.audit-trace-header strong { color: var(--ink); }

/* --- Auth (Server-Betrieb, Port aus Backtester/SignalBot cockpit/static/
   auth.js — s. cockpit/static/auth.js Kopfkommentar). auth.js toggelt
   Sichtbarkeit ausschließlich über `classList.add/remove("hidden")` (nicht
   das `hidden`-Attribut) — deshalb eine globale Utility-Klasse statt
   `[hidden]`-Selektoren. !important verhindert, dass spätere, gleich
   spezifische Komponenten-Regeln (z.B. `.auth-overlay { display: flex }`)
   das Verstecken überschreiben, unabhängig von der Deklarationsreihenfolge
   im Stylesheet. */
.hidden { display: none !important; }

/* Overlay-Ton rgba(10,14,20,.96) = SB/BT-Standard (Design-Harmonisierung 2026-07-16, Fund 18) */
.auth-overlay { position: fixed; inset: 0; z-index: 1000; display: flex; align-items: center; justify-content: center; background: rgba(10, 14, 20, 0.96); }
.auth-card { display: flex; flex-direction: column; gap: 12px; min-width: 320px; padding: 28px; }
.auth-card .brand { font-weight: 700; color: var(--cyan); margin-bottom: 4px; }
.auth-card label { display: flex; flex-direction: column; gap: 4px; font-size: 12px; color: var(--ink2); }
#auth-password-login { display: flex; flex-direction: column; gap: 12px; }
.auth-error { color: var(--red); font-size: 12px; }
.auth-portal-link { color: var(--cyan); font-size: 12px; text-decoration: none; }
#auth-zone { display: inline-flex; align-items: center; gap: 8px; margin-left: auto; }
.auth-account { position: relative; }
.auth-menu { position: absolute; right: 0; top: 110%; z-index: 900; display: flex; flex-direction: column; gap: 6px; padding: 10px; background: var(--panel-inset); border: 1px solid var(--border); border-radius: 6px; min-width: 230px; box-shadow: 0 8px 24px rgba(0, 0, 0, .5); /* Schatten: Design-Harmonisierung 2026-07-16 (Fund 6) */ }
.auth-menu-email { font-size: 11px; color: var(--ink2); padding-bottom: 4px; border-bottom: 1px solid var(--border); }
.auth-btn { background: var(--panel-inset); color: var(--cyan); border: 1px solid rgba(0, 212, 255, 0.4); border-radius: 6px; padding: 6px 12px; font: 700 11.5px var(--font-ui); cursor: pointer; white-space: nowrap; text-decoration: none; display: inline-flex; align-items: center; gap: 4px; }
.auth-btn:hover { border-color: rgba(0, 212, 255, 0.7); }
/* Design-Harmonisierung 2026-07-16 (Fund 10): Topbar-Konto-/Portal-Buttons als
   28px-Icon-Box — 1:1 der vom Owner gelobte Stil aus Backtester index.html
   (.cockpit-right-topbar .auth-btn). Bewusst eng gescopet auf die Topbar-
   Ebene (#auth-zone-Direktkinder + Konto-Button); die Eintraege im .auth-menu
   bleiben bordered Buttons. */
#auth-zone > .auth-btn,
#auth-zone .auth-account > .auth-btn {
  border: none;
  background: transparent;
  width: 28px;
  height: 28px;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 15px;
  line-height: 1;
  border-radius: 6px;
  opacity: 0.85;
}
#auth-zone > .auth-btn:hover,
#auth-zone .auth-account > .auth-btn:hover {
  background: rgba(255, 255, 255, 0.08);
  opacity: 1;
}
/* Zurueck-Icon (Textglyph ⌂) rendert kleiner als das Emoji -- optisch angleichen. */
#auth-zone > #auth-tools-btn { font-size: 18px; }
/* Danger-Border auf var(--red)-Basis (Design-Harmonisierung 2026-07-16, Fund 18) */
.auth-btn-danger { color: var(--red); border-color: rgba(239, 68, 68, 0.45); }
/* Admin-Block flach im Flex-Column des Menüs (Buttons volle Breite). */
#auth-admin-slot { display: contents; }
/* Support-Modus-Banner (Impersonation): fixe Leiste oben, warnfarben. */
.impersonation-banner { position: fixed; top: 0; left: 0; right: 0; z-index: 999; padding: 6px 12px; text-align: center; background: #7a4a00; color: #fff; font-size: 13px; }
.impersonation-banner button { margin-left: 8px; }

/* --- Fokus-Stil (Design-Harmonisierung 2026-07-16, Fund 15) ------------
   Einheitlich in allen drei Cockpits: Border-Umfaerbung + dezenter Ring
   fuer Formularelemente; rahmenlose Buttons bekommen stattdessen einen
   sichtbaren Outline-Ring. */
input:focus-visible, select:focus-visible, textarea:focus-visible, button:focus-visible {
  outline: none;
  border-color: var(--cyan);
  box-shadow: 0 0 0 2px rgba(0, 212, 255, .12);
}
.tabbar button:focus-visible,
button.primary:focus-visible,
.export-download-btn:focus-visible,
#auth-zone > .auth-btn:focus-visible,
#auth-zone .auth-account > .auth-btn:focus-visible {
  outline: 2px solid rgba(0, 212, 255, .3);
  outline-offset: 1px;
}
