/* Honeypot XRPL – Whitehat-Dashboard
   Designbasis: weltbester-astra-6-designer (Astra 6), Profil lab_graphite,
   Referenzmodus inspired. Helle Bühne, graphitfarbene Primäraktion,
   feine Linien, klare Radien, Mono für Daten. */

/* Selbst-gehostete Schriften (Design-Audit 2026-10-06): Inter und JetBrains
   Mono waren nur als Token-Stacks deklariert und fielen auf System-Fallbacks
   (system-ui/Consolas) zurück. Latin-Subset-woff2 aus der offiziellen
   Google-Fonts-Distribution (Inter: SIL OFL 1.1; JetBrains Mono: SIL OFL 1.1 —
   Quellen, Versionen, Lizenzen: public/fonts/LICENSES.md). font-display:swap
   hält den Erstanstrich auf dem Fallback, bis die Datei geladen ist; die
   Token-Stacks --a6-font/--a6-mono bleiben unverändert. */
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/fonts/inter-latin-400.woff2") format("woff2");
}

@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("/fonts/inter-latin-600.woff2") format("woff2");
}

@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("/fonts/inter-latin-700.woff2") format("woff2");
}

@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 800;
  font-display: swap;
  src: url("/fonts/inter-latin-800.woff2") format("woff2");
}

@font-face {
  font-family: "JetBrains Mono";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/fonts/jetbrains-mono-latin-400.woff2") format("woff2");
}

@font-face {
  font-family: "JetBrains Mono";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("/fonts/jetbrains-mono-latin-700.woff2") format("woff2");
}

[data-a6] {
  --a6-bg: #f6f6f7;
  --a6-surface: #ffffff;
  --a6-surface-alt: #f0f0f2;
  --a6-ink: #141416;
  --a6-body: #484850;
  --a6-muted: #62626b;
  --a6-line: rgba(20, 20, 22, 0.12);
  --a6-line-strong: #17171b;
  --a6-control-line: #85858d;
  --a6-focus: #141416;
  --a6-error: #b3261e;
  --a6-error-soft: #fdeceb;
  --a6-warn: #9a5b00;
  --a6-warn-soft: #fdf3e3;
  --a6-success: #066348;
  --a6-success-soft: #e7f4ef;
  --a6-info: #1d4ed8;
  --a6-info-soft: #eef3fd; /* Badge-Info-Fläche (.badge-info) — vorher Hex im Badge-Regelblock */
  --a6-r-sm: 2px;
  --a6-r-md: 4px;
  --a6-r-lg: 6px;
  --a6-r-pill: 4px;
  --a6-r-xs: 3px; /* kleinster Radius (Swatches, Scrollbar-Thumbs) — Design P4 */
  --a6-font: Inter, system-ui, -apple-system, "Segoe UI", sans-serif;
  --a6-mono: "JetBrains Mono", ui-monospace, "Cascadia Code", Consolas, monospace;
  --a6-ease: cubic-bezier(0.2, 0.8, 0.2, 1);
  --a6-fast: 180ms;
  --a6-ui: 280ms;
  /* Inset-Glanzlicht der Hell-Elevation als eigener Token (Noir-Theme): die
     dunkle Bühne trägt kein Weiß-Inset — der Noir-Block setzt den Token auf
     transparent und definiert die Tiefe komplett neu. */
  --a6-inset-highlight: 0 1px 0 #ffffff inset;
  --a6-e1: var(--a6-inset-highlight), 0 14px 32px -26px rgba(20, 20, 22, 0.28);
  --a6-e2: var(--a6-inset-highlight), 0 28px 60px -42px rgba(20, 20, 22, 0.34);
  --a6-e3: var(--a6-inset-highlight), 0 44px 90px -54px rgba(20, 20, 22, 0.38);

  /* Hero-Bühne (.hx-stage) als Token (Noir-Theme): Verlaufsfläche und der
     EINE Radial-Glow der Seite sind Werte, keine Komponenten-Regeln — der
     Noir-Block tauscht beide gegen die dunkle Indigo-Verlaufsbühne mit
     Violett-/Teal-Glow (Sherlock-Richtung). */
  --a6-stage-bg: linear-gradient(155deg, #ffffff 0%, #fbfbfc 58%, #eeeef1 100%);
  --a6-stage-glow:
    radial-gradient(ellipse at 95% 0%, rgba(22, 48, 92, 0.10), transparent 55%),
    radial-gradient(ellipse at 0% 100%, rgba(15, 118, 110, 0.08), transparent 60%);

  /* Frost-/Glas-Schicht der Top-10-Box (Design-Tranche, Referenzmuster
     a6-hero__box/.phases — MUSTER, nicht kopierte Werte): halbtransparente
     Füllung + Blur über der Bühne, 1px-Rand in Frost-Tinte, Hairline-
     Verlauf und Sheen-Band. Kontraste per node nachgerechnet (WCAG):
     Frost-Fill komposit auf dem dunkelsten Bühnenendpunkt #eeeef1 → #fcfcfc,
     Ink darauf 17.93 ✓; Frost-Rand komposit #818184 vs #eeeef1 = 3.35 ✓ UI;
     Hairline-Mittelstop komposit #7e8da5 vs Frost-Fill = 3.34 ✓ (dekorativ,
     trotzdem ≥3). Der Sheen ist ein reines Lichtband (overlay) ohne Text-
     Funktion. */
  --a6-frost-fill: rgba(255, 255, 255, 0.80);
  --a6-frost-blur: blur(10px) saturate(150%);
  --a6-frost-border: rgba(20, 20, 22, 0.50);
  --a6-hairline-grad: linear-gradient(90deg, transparent, rgba(22, 48, 92, 0.55), transparent);
  --a6-sheen-grad: linear-gradient(115deg, transparent 32%, rgba(255, 255, 255, 0.55) 46%, transparent 58%);

  /* Shining-Word (Muster a6-blue.css .a6-title em / hero-file h1 em — der
     Referenz-Hell-Stop #ffb26b auf Weiß = 1.77:1 ist ein Kontrast-Blindpunkt
     und wird NICHT übernommen; #ec5b00 = 3.46 <4.5 ebenfalls verworfen).
     Beide Stops sind nachgerechnet: --a6-accent-text #a63c00 = 5.55 gegen
     den dunkelsten Bühnenendpunkt #eeeef1 (6.43 auf Weiß), Mittelstop
     #b8490a = 4.54 dort (5.26 auf Weiß) — jeder sichtbare Gradient-Pixel
     bleibt ≥4.5. --a6-shine-word-base ist der statische Fallback für
     reduced-motion/forced-colors (Wort bleibt in Akzent-Tinte lesbar). */
  --a6-shine-word-grad: linear-gradient(90deg, var(--a6-accent-text) 0%, #b8490a 50%, var(--a6-accent-text) 100%);
  --a6-shine-word-base: var(--a6-accent-text);

  /* Astra-6-Marke (lab_graphite): graphitfarbene Primäraktion, warmer Akzent
     nur auf der Hero-Bühne und für Status-Signale — kein Glow auf jeder Karte
     (SKILL lab_graphite.avoid). */
  --a6-accent: #ec5b00;
  --a6-accent-hi: #ff8a2e;
  --a6-accent-text: #a63c00;
  --a6-on-accent: #141416;
  --a6-accent-soft: #fff0e6;

  /* Neue Brand-Tokens (D3, nur ergänzt): Referenz-Palette Blau/Teal/Amber auf
     der weißen lab_graphite-Basis. --a6-accent bleibt für bestehende
     Konsumenten (.download-btn inkl. history-host.html #fh-refresh,
     forced-colors) unangetastet — keine globale Umbenennung. */
  --a6-brand-blue: #16305c;
  --a6-brand-teal: #0f766e;
  --a6-brand-amber: #b45309;

  /* Akzent der Leerzustands-Illustrationen (Design P0, public/icons.mjs):
     genau EIN Akzent-Element (.icon-accent) je Illustration — tiefe
     Brand-Tinte als eigener Kanal, kein Severity-Ton und kein neuer
     Farbwert; .empty-illu nutzt den Token mit Brand-Blau-Fallback. */
  --a6-icon-accent: var(--a6-brand-blue);

  /* Raster (Astra-6): Gutter skaliert mit der Viewportbreite; main,
     view-nav und Hero-Bühne teilen sich dieselbe Breitenformel —
     max 2400px (Nutzer-Vorgabe; überschreibt den Skill-Default 90rem). */
  --a6-gutter: clamp(12px, 2vw, 26px);
  --a6-container: 2400px;

  /* Graph-Bühne: weiß, Tintenlinie, dezente Grautöne (Palantir) */
  --a6-graph-canvas: #ffffff;
  --a6-graph-grid: rgba(20, 20, 22, 0.07);
  --a6-graph-line: #17171b;
  --a6-graph-line-soft: rgba(20, 20, 22, 0.12);

  /* Prismenflaechen des 3D-Profils: Schleier und Kante sind Theme-Werte, keine
     Konstanten im Profil. Light traegt eine dunkle Kante auf heller Buehne, Noir eine helle
     auf dunkler. Die alte Kante rgba(20,20,22,.24) war im ausgelieferten Noir-Theme auf
     #101317 nicht mehr von der Vorderflaeche zu trennen - die drei Flaechen lasen sich als
     ein einziger Fleck, und das Prisma wirkte schraeg statt kantig. */
  --a6-face-shade: rgba(12, 12, 14, 0.38);
  --a6-face-sheen: rgba(255, 255, 255, 0.24);
  --a6-face-edge: rgba(20, 20, 22, 0.24);
  --a6-face-seam: rgba(12, 12, 14, 0.30);

  /* Gemeinsame Bühnenhöhen-Leiter (Design-Audit 2026-10-06): #graph, #globe
     und .cluster-3d (drilldown.css) teilen sich diese Höhe — vorher sprang die
     Bühne beim Tab-Wechsel Live→Globe um 200 px. Basis 720 px; die Stufen
     640/560/480 px werden im Responsiv-Abschnitt je Breakpoint neu gesetzt. */
  --a6-stage-height: 720px;

  /* Cluster-Bubbles: weiß, 1px Tintenrand, Radius */
  --a6-cluster-fill: #ffffff;
  --a6-cluster-border: #17171b;
  --a6-cluster-hover: #f6f6f7;
  --a6-cluster-radius: 4px;
  --a6-grid-line: rgba(20, 20, 22, 0.05);

  /* Kanten: 1px, Tx-KATEGORIE-Codierung (txCategory, public/edge-colors.mjs)
     — exakt die EDGE_COLORS-Kategorien aus app.js (Audit 2026-10-07).
     payment = success-Familie ('grün=gut'-Bundlung, dokumentiert; 1.33:1
     Luminanzabstand zum Check-Teal); market = DEX+AMM ZUSAMMENGELEGT (die
     alten trustset/offer-Töne lagen bei 1.02:1 — Kritiker-Pflicht 5);
     nft nach Fuchsia geöffnet (Abstand zu escrow 1.12:1 -> 1.51:1, Pflicht 6);
     fraud = Severity-Rot als eigener Override-Token (Pflicht 4). */
  --a6-edge-payment: #066348;
  --a6-edge-market: #b45309;
  --a6-edge-escrow: #6d28d9;
  --a6-edge-check: #0f766e;
  --a6-edge-admin: #1d4ed8;
  --a6-edge-nft: #c026d3;
  --a6-edge-fraud: #b3261e;
  --a6-edge-neutral: #62626b;
  --a6-edge-ink: #141416;

  /* Rollen-Farbcodierung source/drainer/collector/relay — D7: identisch zu
     ROLE_COLORS in public/app.js (Fragmentierung behoben; JS-Werte =
     CSS-Werte). Drainer bleibt die Maliz-Signal-Tinte. */
  --a6-role-source: #16305c;
  --a6-role-source-border: #0f2445;
  --a6-role-drainer: #b3261e;
  --a6-role-drainer-border: #7f1d1d;
  --a6-role-collector: #b45309;
  --a6-role-collector-border: #7c3a06;
  --a6-role-relay: #0f766e;
  --a6-role-relay-border: #115e59;
  /* Unknown-Rolle (Vollrahmen-Umbau 2026-10-07): dieselbe Farbe für Legende,
     3D-Kugel und Kartenrahmen statt neutralgrau — 6.20 / 5.45:1 gegen beide
     Hell-Surfaces (WCAG 1.4.11, per node nachgerechnet). */
  --a6-role-unknown: #4a6478;

  /* Eine Akzentfarbe je Schweregrad — bestehende Semantik 1:1 weiterverwendet */
  --a6-sev-malicious: #b3261e;
  --a6-sev-suspect: #9a5b00;
  --a6-sev-info: #1d4ed8;
  --a6-sev-neutral: #62626b;
  --a6-sev-quorum: #6d28d9;

  /* Konsolidierte Tokens (Design-Audit 2026-10-06): Werte, die bisher als
     hartcodierte Hex mehrfach im Stylesheet standen — dieselben Farben, nur
     noch referenziert. --a6-ink-fill ist die graphitfarbene Primäraktion
     (SKILL buttons .a6-button--primary), --a6-card-line der Brand-Rand der
     Hero-KPI-Chips (.hx-kpi; die Cluster-Karten tragen seit dem Vollrahmen-
     Umbau die neutrale control-line-Basis plus Rollenfarbe). */
  --a6-ink-fill: #141416;
  /* Text auf der Ink-Primärfläche (Tabs/Buttons im Auswahlzustand, #check-go):
     ersetzt die früheren hartcodierten #fff — Noir dreht beide Kanäle. */
  --a6-on-ink: #ffffff;
  --a6-card-line: rgba(22, 48, 92, 0.30);
  --a6-swatch-payment: #066348; /* Payment-Kante ist GRÜN (Audit 2026-10-07):
                                   success-Familie, 7.28:1 auf der Karte */
  --a6-swatch-other: #3f3f46;
  /* Unknown-Rolle (3D-Farb-Audit 2026-10-07): dieselbe Farbe wie
     --a6-role-unknown — Legende, 3D-Kugel und Kartenrahmen teilen sich EINE
     Tinte. Vorher #f0f0f2 auf der weißen Bühne: 1.14:1 (FAIL, die randlose
     3D-Kugel war unsichtbar); Stahlblau #4a6478 = 6.20:1 gegen --a6-graph-
     canvas ✓ UI. Rand #dbe6f2 = 4.91:1 auf der Füllung ✓ UI. */
  --a6-swatch-unknown: #4a6478;
  --a6-swatch-unknown-line: #dbe6f2;
  --a6-swatch-cluster-line: #17171b;
  /* Flussketten-Chip: getönte Unknown-Familie statt Vollton — der Chip-Text
     (--a6-body) bleibt ≥4.5:1 lesbar (Vollton #4a6478 unter #484850 wäre
     <2:1); der Chip-RAND trägt den Vollton (.chain-node.chain-unknown). */
  --a6-unknown-fill: #e7edf3;

  color: var(--a6-ink);
  background: var(--a6-bg);
  font-family: var(--a6-font);
  line-height: 1.55;
  color-scheme: light;
}

/* ---------------- Noir-Theme (data-theme="noir", 2026-10-06) -------------
   Zweites VOLLTHEMA über dieselbe Token-SSOT: tiefes Indigo-Schwarz als
   Grundierung, Violett als EINZIGER Akzent, farbiger Glow ausschließlich auf
   der Hero-Bühne (--a6-stage-glow) — die Sherlock-DNA (Indigo-Tiefen-
   Surfaces #1a172f/#302c5b, Violett-Akzente, easeOutQuart-Ruhe) invertiert
   auf die dunkle Bühne. Bewusst KEIN einziger Komponenten-Selektor: nur Token-
   Overrides plus color-scheme: dark (Scrollbars/Form-Controls). Alle Paare
   sind nach WCAG 2.x relativer Luminanz gerechnet und als Regeltests in
   public/design-assets.test.mjs fixiert (Text ≥ 4,5:1, UI ≥ 3:1, beide
   Themes). Default über <body data-a6 data-theme="noir"> (alle drei HTML-
   Seiten), Hell über localStorage 'hx-theme' (i18n.mjs initThemeSwitcher;
   Bootstrap-Script direkt nach <body> verhindert den FOUC). */
[data-a6][data-theme="noir"] {
  color-scheme: dark;
  --a6-bg: #0a0c0f;
  --a6-surface: #101317;
  --a6-surface-alt: #161a20;
  --a6-ink: #eef2f6;
  --a6-body: #c3cbd4;
  --a6-muted: #9aa4b0;
  --a6-line: rgba(238, 242, 246, 0.10);
  --a6-line-strong: rgba(238, 242, 246, 0.20);
  --a6-control-line: #7b8591;
  --a6-focus: #a8cdeb;
  --a6-error: #ff7a70;
  --a6-error-soft: rgba(255, 122, 112, 0.12);
  --a6-warn: #fbbf24;
  --a6-warn-soft: rgba(251, 191, 36, 0.10);
  --a6-success: #4ade80;
  --a6-success-soft: rgba(74, 222, 128, 0.10);
  --a6-info: #9cc8f0;
  --a6-info-soft: rgba(156, 200, 240, 0.12);
  --a6-inset-highlight: 0 0 0 transparent;
  --a6-e1: 0 14px 32px -26px rgba(0, 0, 0, 0.60);
  --a6-e2: 0 28px 60px -42px rgba(0, 0, 0, 0.68);
  --a6-e3: 0 0 0 1px rgba(143, 184, 220, 0.14), 0 24px 70px -36px rgba(0, 0, 0, 0.70);
  --a6-accent: #8fb8dc;
  --a6-accent-hi: #b5d6ee;
  --a6-accent-text: #a8cdeb;
  --a6-on-accent: #0a0c0f;
  --a6-accent-soft: rgba(143, 184, 220, 0.12);
  --a6-brand-blue: #8fb8dc;
  --a6-brand-teal: #3ecfbb;
  --a6-brand-amber: #ffb45e;
  --a6-grid-line: rgba(238, 242, 246, 0.045);
  --a6-graph-canvas: #101317;
  --a6-graph-grid: rgba(238, 242, 246, 0.06);
  --a6-graph-line: #eef2f6;
  --a6-graph-line-soft: rgba(238, 242, 246, 0.14);
  --a6-face-shade: rgba(6, 8, 11, 0.42);
  --a6-face-sheen: rgba(255, 255, 255, 0.22);
  --a6-face-edge: rgba(238, 242, 246, 0.26);
  --a6-face-seam: rgba(6, 8, 11, 0.55);
  --a6-cluster-fill: #161a20;
  --a6-cluster-border: #eef2f6;
  --a6-cluster-hover: #1e242c;
  --a6-edge-payment: #4ade80;
  --a6-edge-market: #ffb45e;
  --a6-edge-escrow: #b79cff;
  --a6-edge-check: #3ecfbb;
  --a6-edge-admin: #8ab4ff;
  --a6-edge-nft: #d946ef;
  --a6-edge-fraud: #ff7a70;
  --a6-edge-neutral: #8c96a2;
  --a6-edge-ink: #eef2f6;
  --a6-role-source: #8fb8dc;
  --a6-role-source-border: #5f8fb8;
  --a6-role-drainer: #ff7a70;
  --a6-role-drainer-border: #d95f56;
  --a6-role-collector: #ffb45e;
  --a6-role-collector-border: #c98a45;
  --a6-role-relay: #3ecfbb;
  --a6-role-relay-border: #2fa393;
  --a6-role-unknown: #bfe3ff;
  --a6-sev-malicious: #ff7a70;
  --a6-sev-suspect: #fbbf24;
  --a6-sev-info: #9cc8f0;
  --a6-sev-neutral: #9aa4b0;
  --a6-sev-quorum: #c4b5fd;
  --a6-ink-fill: #eef2f6;
  --a6-on-ink: #0a0c0f;
  --a6-card-line: rgba(143, 184, 220, 0.28);
  --a6-swatch-payment: #4ade80;
  --a6-swatch-other: #7b8591;
  --a6-swatch-unknown: #bfe3ff;
  --a6-swatch-unknown-line: #46587e;
  --a6-swatch-cluster-line: #eef2f6;
  --a6-unknown-fill: #1a2333;
  --a6-stage-bg: linear-gradient(160deg, #141820 0%, #0f1217 60%, #0b0d11 100%);
  --a6-stage-glow:
    radial-gradient(ellipse at 100% 0%, rgba(143, 184, 220, 0.14), transparent 55%),
    radial-gradient(ellipse at 0% 100%, rgba(62, 207, 187, 0.06), transparent 60%);
  --a6-globe-atmosphere: rgba(143, 184, 220, 0.10);
  --a6-frost-fill: rgba(16, 19, 23, 0.74);
  --a6-frost-blur: blur(10px) saturate(140%);
  --a6-frost-border: rgba(238, 242, 246, 0.30);
  --a6-hairline-grad: linear-gradient(90deg, transparent, rgba(143, 184, 220, 0.60), transparent);
  --a6-sheen-grad: linear-gradient(115deg, transparent 32%, rgba(238, 242, 246, 0.08) 46%, transparent 58%);
  --a6-shine-word-grad: linear-gradient(90deg, #a8cdeb 0%, #8fb8dc 34%, #eef2f6 50%, #8fb8dc 66%, #a8cdeb 100%);
  --a6-shine-word-base: var(--a6-accent-text);
}

*,
*::before,
*::after { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  background: var(--a6-bg);
  color: var(--a6-ink);
  font-family: var(--a6-font);
  font-size: 15px;
}

:where(a, button, input, select, summary):focus-visible {
  outline: 3px solid var(--a6-focus);
  outline-offset: 3px;
}

/* Fokuslücke (Design-Audit 2026-10-06): Cluster-Karten sind interaktiv
   (li.cluster-card role="button" tabindex="0", app.js), werden vom :where-
   Selektor oben aber nicht erfasst — derselbe Ring wie bei Steuerlementen.
   Das Pendant für den Tabellen-Wrapper steht in drilldown.css. */
.cluster-card:focus-visible {
  outline: 3px solid var(--a6-focus);
  outline-offset: 3px;
}

/* ---------------- App-Kopfzeile (Console-Shell) ----------------
   Links Marke, rechts Status-Cluster in einer Zeile: Verbindungs-LED
   (#conn, app.js setConn), Netzwerk-Chip (#stat-network), Uhr (#stat-last) —
   angereiht die Kopf-KPIs (#stat-malicious/#stat-suspect/#stat-events, Mono
   tabular-nums, 3px-Severity-Band links), ganz rechts .lang-switch.
   Nicht sticky: die Kopfzeile verdeckt sonst die View-Anker
   (Skill-Nav-Regel; scroll-margin bleibt unten erhalten). */

.topbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px 18px;
  padding: 14px var(--a6-gutter);
  background: var(--a6-surface);
  border-bottom: 1px solid var(--a6-line-strong);
}

/* Marke (Design P1): Gravur-Logo .brand-mark (34×34 inline-SVG, Wabe ×
   Ledger-Knoten × Köder-Tropfen) neben der Textspalte. Markup identisch in
   index.html und history-host.html gepflegt; das Favicon ist die eigenständige
   Vereinfachung in public/favicon.svg. Der H1-Text trägt den Namen (genau
   eine H1 bleibt erhalten, das SVG ist aria-hidden). */
.brand {
  display: flex;
  align-items: center;
  gap: 12px;
}

.brand-text { min-width: 0; }

.brand-mark {
  display: block;
  flex: none;
  width: 34px;
  height: 34px;
  color: var(--a6-ink);
}

.brand-mark svg {
  display: block;
  width: 34px;
  height: 34px;
}

/* Köder-Tropfen — der EINE Akzent der Marke: Füllung ausschließlich über den
   Akzent-Token (kein Zweitfarbwert im Markup), Hover-Füllung nur bei echtem
   Zeiger, 180 ms fill (reduced-motion: Transition aus, unten). */
.brand-mark .hp-drop {
  fill: var(--a6-accent);
  transition: fill var(--a6-fast) var(--a6-ease);
}

@media (hover: hover) and (pointer: fine) {
  .brand:hover .brand-mark .hp-drop { fill: var(--a6-accent-hi); }
}

.brand h1 {
  margin: 0;
  /* 24 px (Design-Audit): H1 bleibt dominant — .hx-title wurde auf
     clamp max 1.25rem (20 px) gedeckelt, vorher drehte die Hierarchie um. */
  font-size: 24px;
  font-weight: 800;
  letter-spacing: -0.02em;
}

/* Wortmarke (Design P1) OHNE dritte Schriftfamilie: ab 900 px Versalen mit
   0.18-em-Tracking aus Inter 800; „XRPL“ als JetBrains-Mono-700-Anhang
   („gestempelte Daten“ als Kontrast zur Gravur). Darunter bleibt der
   Ist-Zustand (24 px/800, −0.02 em) unverändert. */
@media (min-width: 900px) {
  .brand h1 {
    text-transform: uppercase;
    letter-spacing: 0.18em;
  }

  .brand h1 .brand-xrpl {
    font-family: var(--a6-mono);
    font-weight: 700;
    font-size: 0.9em;
    letter-spacing: 0.14em;
  }
}

.brand .sub {
  margin: 3px 0 0;
  color: var(--a6-muted);
  font-size: 13px;
}

@media (prefers-reduced-motion: reduce) {
  .brand-mark .hp-drop { transition: none; }
}

.stats {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-left: auto;
}

.stat {
  display: flex;
  flex-direction: column;
  /* Linksbündig (Design-Audit): Wert und Label einer KPI auf gemeinsamer
     Achse — Scannbarkeit der Statuszeile statt Mittelachsen-Mix. */
  align-items: flex-start;
  text-align: left;
  min-width: 104px;
  padding: 8px 12px;
  background: var(--a6-surface);
  /* 1px-Vollrahmen in Severity-Farbe (alle vier Seiten) statt des früheren
     3px-Linksbändchens — Status nie nur durch Farbe: der Badge-/Labeltext
     trägt denselben Schweregrad; die Varianten unten setzen border-color. */
  border: 1px solid var(--a6-line-strong);
  border-radius: var(--a6-r-sm);
  box-shadow: var(--a6-e1);
}

/* Netzwerk und Uhr sind Status-Chips im Cluster, keine KPI-Karten: kompakt,
   flach, Haarlinie rundum statt Vollrahmen. */
.stat-network,
.stat-last {
  min-width: 0;
  padding: 6px 12px;
  border: 1px solid var(--a6-line);
  background: var(--a6-surface-alt);
  box-shadow: none;
}

.stat-value {
  font-family: var(--a6-mono);
  font-size: 20px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.stat-value-small { font-size: 14px; }

.stat-label {
  margin-top: 3px;
  font-size: 12px;
  color: var(--a6-muted);
  text-transform: uppercase;
  letter-spacing: 0.08em;
}

.stat-malicious .stat-value { color: var(--a6-error); }
.stat-suspect .stat-value { color: var(--a6-warn); }
.stat-events .stat-value { color: var(--a6-ink); }
.stat-network .stat-value { color: var(--a6-info); font-size: 15px; }
.stat-last .stat-value { color: var(--a6-muted); }

.stat-malicious { border-color: var(--a6-error); }
.stat-suspect { border-color: var(--a6-warn); }
.stat-events { border-color: var(--a6-brand-blue); }

.conn {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  color: var(--a6-body);
  padding: 8px 12px; /* 4-px-Raster (Design-Audit) */
  border: 1px solid var(--a6-line);
  border-radius: var(--a6-r-pill);
  background: var(--a6-surface);
}

.conn-dot {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--a6-muted);
}

.conn-dot.ok { background: var(--a6-success); }
.conn-dot.err { background: var(--a6-error); }

/* ---------------- Ansichts-Navigation ---------------- */

/* Layout-Container der Views-Umschaltung (Dashboard, Historie, Konto-Check).
   Markup kommt vom Verdrahtungs-Agenten: Die Buttons tragen die Doppelklasse
   'view-tab graph-tab' und erben deshalb den .graph-tab-Stil weiter unten —
   hier wird bewusst nur der Container definiert. */
.view-nav {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  padding: 14px var(--a6-gutter) 0;
  max-width: min(var(--a6-container), 100% - 2 * var(--a6-gutter));
  margin-inline: auto;
  width: 100%;
}

/* Text-Zentrierung in ALLEN View-Tabs (Layout-Fix 2026-10-06): Die <a>-Tabs
   (index.html „Flow-Host“, history-host.html „Dashboard“) laufen als
   blockifizierte Flex-Kinder des Nav-Containers und legen ihre vererbte
   Zeilenbox (line-height 1.55 → 20.15 px) an den oberen Rand — gemessen
   gapTop 11 / gapBottom 17 px (−6 px Abweichung) samt UA-Unterstreichung,
   während die <button>-Tabs über ihre UA-Grundeinstellung auf 14.5/14.5 px
   zentrieren (Referenzmesser „Konto-Check“). inline-flex + align-items:center
   stellt beide Tab-Arten auf dieselbe Zeilenmitte (verifiziert 13.9/14.1 px),
   text-decoration:none entfernt die UA-Unterstreichung des Anker-Tabs. Die
   Button-Tags ändern ihren Stand dadurch nicht — gemessen unverändert
   14.5/14.5 px. Ursachen-Fix auf Klassenebene statt Symptom-Patches je
   Element; legend-items/live-stats/chips sind Ink-Artefakte der Glyphen
   (line-box zentriert, kein Layout-Fehler) und bleiben unangetastet. */
.view-tab {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  text-decoration: none;
}

/* Anker-Ziele der Views nicht unter der Kopfzeile verschwinden lassen. */
#view-dashboard,
#view-history,
#view-check {
  scroll-margin-block-start: 84px;
}

/* Dashboard-View: Console-Raster — bewusst einspaltig in ALLEN Breakpoints:
   Feed- und Log-Panel oben, die Graph-Bühne full-width darunter. Die frühere
   Zweispaltig-Regel ab 1024 px ist entfernt; der Graph braucht die volle
   Breite. Die Panels bleiben Kinder neutraler Spaltencontainer — ohne eigene
   Rhythmus-Regel klebten ihre 1px-Ink-Borders aneinander (Astra-6
   lab_graphite: Borders dürfen nicht verschmelzen). Der View übernimmt
   denselben Achsabstand wie main (22 px), also denselben Token-Abstand, den
   main ohnehin nutzt. */
#view-dashboard { display: block; }

.console-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 22px;
}

.console-col {
  display: flex;
  flex-direction: column;
  gap: 22px;
  min-inline-size: 0; /* Grid-Kind darf schrumpfen, damit Inhalte umbrechen */
}

/* ---------------- Panels ---------------- */

main {
  padding: 22px var(--a6-gutter);
  display: flex;
  flex-direction: column;
  gap: 22px;
  max-width: min(var(--a6-container), 100% - 2 * var(--a6-gutter));
  margin: 0 auto;
  width: 100%;
}

.panel {
  background: var(--a6-surface);
  border: 1px solid var(--a6-line-strong);
  border-radius: var(--a6-r-lg);
  padding: clamp(1rem, 1.5vw, 1.5rem);
  box-shadow: var(--a6-e1);
}

.panel-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px;
  margin-bottom: 14px;
  /* Einheitliche Kopf-Trennlinie (Design-Audit): history.css/account-check.css
     hatten sie bereits scoped — jetzt tragen alle Panel-Köpfe dieselbe
     1-px-Ink-Linie (identische Deklaration, die Scoped-Regeln bleiben). */
  border-bottom: 1px solid var(--a6-line-strong);
  padding-bottom: 12px;
}

.panel-head h2 {
  margin: 0;
  font-size: 17px;
  font-weight: 700;
  letter-spacing: -0.01em;
}

/* ---------------- Frost-Panel: Top-10-Box (Design-Tranche) ----------------
   Die Box liegt als einzige Glas-Schicht auf der Bühne (Rest bleibt matte
   surface-Fläche — lab_graphite.avoid: kein Glow/Filter auf jeder Karte).
   Schichten: Frost-Füllung + backdrop-filter über der Bühne, 1px-Rand in
   Frost-Tinte, Hairline-::BEFORE als Border-Verlauf, der links/rechts
   sauber transparent ausläuft (12 % Einlauf, Muster hero-file .sheet::before
   + .rail), und Sheen-::AFTER als 115°-Lichtband mit minimalem ±12 %-Swing
   um die Mitte (11 s, Muster a6-blue.css .a6-hero__sheen — bewusst KEIN
   Dauer-Sweep). Bewusst NUR auf #exchange-outflows-panel: die übrigen
   Panels behalten .panel (surface + line-strong). */
#exchange-outflows-panel {
  position: relative;
  isolation: isolate;
  margin: 22px auto; /* Abstand zum Sektions-Rhythmus (Forensik 2026-10-07):
     das Panel ist direktes body-Kind außerhalb von main — ohne eigene
     Margin stießen die 1px-Ink-Borders von Bühne und view-nav aneinander
     (Astra-6 lab_graphite: Borders dürfen nicht verschmelzen). 22 px =
     der vierfach hartcodierte Panel-Rhythmus (console-grid/console-col/
     main, 647/653/660/663); auto wirkt horizontal erst, wenn das Panel
     je eingefasst wird (Vollbreite ist Design-Entscheidung, kein Fix). */
  background: var(--a6-frost-fill);
  backdrop-filter: var(--a6-frost-blur);
  border-color: var(--a6-frost-border);
  /* Sheen-Band clippen (Audit 2026-10-08): der a6-sheen-Sweep (inset:0,
     translateX ±12 %) ragt bei schmalen Viewports über die Panel-Kante und
     erzeugte horizontalen Scrollüberschuss (live belegt: 700 px Viewport
     scrollWidth 728 bei clientWidth 655; am Desktop trägt der breitere
     Container den Swing unauffällig). Dasselbe Muster wie .download-btn/
     .graph-tab (1d92459): overflow:hidden beschneidet das Band an der
     Panel-Grenze; Hairline (::before) und Frost liegen vollständig
     innerhalb, Box-Shadows der .panel-Basis bleiben unberührt. */
  overflow: hidden;
}

#exchange-outflows-panel::before {
  content: "";
  position: absolute;
  top: 0;
  left: 12%;
  right: 12%;
  height: 1px;
  background: var(--a6-hairline-grad);
  pointer-events: none;
}

#exchange-outflows-panel::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: var(--a6-sheen-grad);
  mix-blend-mode: overlay;
  transform: translateX(-12%);
  animation: a6-sheen 11s var(--a6-ease) infinite alternate;
  pointer-events: none;
}

@keyframes a6-sheen {
  from { transform: translateX(-12%); }
  to { transform: translateX(12%); }
}

/* ---------------- Top-10-Duo-Grid (Drainer-Tranche 2026-10-07) ----------------
   Zwei Top-10-Boxen nebeneinander: links Börsen-Zuflüsse (Frost-Panel oben),
   rechts Drainer-Rangliste (matte .panel-Fläche). Die frühere Einspaltig-
   Regel (style.css:634-641) gilt dem Console-Raster IN main — dieses Duo ist
   die begründete Ausnahme direkt unter der Bühne: zwei kompakte Ranglisten
   teilen sich die Breite, ohne dass der Graph (full-width in main) betroffen
   wäre. 22 px gap = der vierfach hartcodierte Panel-Rhythmus (647/653/660/
   710); die vertikalen 22 px bleiben bei den Panel-Margins (die Panel-Regeln
   selbst sind unverändert — Selektoren, Frost-Schicht und shine-spacing-
   Invarianten greifen weiter). minmax(0, 1fr) zwingend: ohne 0-Minimum
   sprengt eine 35-Zeichen-Adresse die Spalte (Hausmuster min-inline-size: 0,
   654/1605/1622/1649). Mobiler Stapel: ab 760 px einspaltig (Hausmuster
   .cluster-list), im 560-px-Block gap 0 — die Panel-Margins (22/14 px)
   liefern den Stapel-Abstand allein, ein zusätzlicher Gap würde ihn
   verdoppeln. */
.top-duo-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 22px;
  width: min(var(--a6-container), 100% - 2 * var(--a6-gutter)); /* Bühnen-Achsmaß (.hx-stage) */
  margin: 0 auto;
}

/* Listen-Rahmen BEIDER Duo-Boxen (Hausmuster .cluster-list :1421-1427):
   list-style none — der Rang steht schon in .exout-rank/.drout-rank, die
   decimal-Ziffern des <ol> wären eine Doppel-Zählung; padding 0 — ohne ihn
   ziehen die Browser-Defaults jede Zeile 40 px hinter panel-head/disclaimer
   ein; display grid + gap 16px — ohne Abstand klebten die 1-px-Borders
   zweier Zeilen zu einer 2px-Doppellinie (lab_graphite-Grundsatz
   style.css:636-638: Borders dürfen nicht verschmelzen). Der
   14-px-Oberabstand entspricht .cluster-list. */
.exchange-outflow-list,
.drainer-outflow-list {
  list-style: none;
  margin: 14px 0 0;
  padding: 0;
  display: grid;
  gap: 16px;
}

/* Ausblend-Guard (Audit 2026-10-08): Autor-Styles schlagen die UA-Regel
   [hidden]{display:none} — mit display:grid allein bleibt eine per
   hidden-Attribut ausgeblendete Liste laid out. Live belegt: #cluster-list
   hidden:true, aber 5249 px hoch gerendert, obwohl app.js:1890
   (listEl.hidden = !inClusterTab) die Karten nur im Cluster-Tab zeigen
   will; dasselbe gilt für die beiden Duo-Listen (app.js setzt hidden im
   Leerzustand). Attrib-Spezifität (0,2,0) schlägt die Basisregeln (0,1,0);
   bewusst nur diese drei Listen — keine globale [hidden]-Regel. */
.cluster-list[hidden],
.exchange-outflow-list[hidden],
.drainer-outflow-list[hidden] { display: none; }

.hint {
  color: var(--a6-muted);
  font-size: 12px;
}

/* ---------------- Live-Block-Feed ---------------- */

.live-stats {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-left: auto;
}

.livestat {
  display: inline-flex;
  align-items: baseline;
  gap: 6px;
  padding: 4px 12px; /* 4-px-Raster (Design-Audit) */
  background: var(--a6-surface-alt);
  border: 1px solid var(--a6-line);
  border-radius: var(--a6-r-pill);
  font-size: 12px;
  color: var(--a6-muted);
}

.livestat-value {
  font-family: var(--a6-mono);
  font-size: 14px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--a6-ink);
}

.livestat-malicious .livestat-value { color: var(--a6-error); }
.livestat-suspect .livestat-value { color: var(--a6-warn); }
.livestat-info .livestat-value { color: var(--a6-info); }

.block-feed {
  list-style: none;
  margin: 0 0 18px;
  padding: 0;
  display: grid;
  gap: 10px;
}

.block-card {
  padding: 12px 14px;
  background: var(--a6-surface);
  /* 1px-Vollrahmen in Bedeutungsfarbe (has-malicious/has-suspect setzen
     border-color) statt des früheren 3px-Linksbändchens. */
  border: 1px solid var(--a6-line-strong);
  border-radius: var(--a6-r-md);
}

.block-card.has-malicious { border-color: var(--a6-error); }
.block-card.has-suspect { border-color: var(--a6-warn); }

.block-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 12px;
  font-family: var(--a6-mono);
}

.block-height {
  font-size: 15px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.block-time {
  font-size: 13px;
  color: var(--a6-muted);
  font-variant-numeric: tabular-nums;
}

.block-txs {
  font-size: 13px;
  color: var(--a6-body);
  font-variant-numeric: tabular-nums;
}

.block-badges {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 9px;
}

/* ---------------- Feed-Modus-Auswahl + Fenster-Steuerung ----------------
   Modus-Tabs (Historie/Live) und Fensterwahl (24 h/3 d/7 d) direkt über dem
   Feed; alle interaktiven Elemente auf 44 px Zielhöhe (Astra-6-Contract,
   .graph-tab/.filter-select erben). */
.feed-controls {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  margin: 0 0 14px;
}

.feed-mode-tabs { margin-bottom: 0; }

.feed-more-row {
  display: flex;
  justify-content: center;
  margin: 2px 0 0;
}

/* 'Mehr laden': 44 px Zielhöhe erbt die .download-btn-Regel; eigene Breite,
   damit der Button nicht die volle Panelbreite aufreißt. */
.feed-more-btn { min-height: 44px; flex: 0 1 auto; }

/* ---------------- Fenster-Stundenchart (SVG, selbstständig) -------------
   Farblos bis auf die bestehende Maliziös-Semantik: Blöcke tonal,
   geflaggte Stunden in der bestehenden --a6-error-Tinte. Kein Glow, keine
   neue Farbe (lab_graphite). */
.feed-chart {
  margin-top: 14px;
  padding: 10px 12px;
  background: var(--a6-surface-alt);
  border: 1px solid var(--a6-line);
  border-radius: var(--a6-r-md);
}

.feed-chart svg { display: block; width: 100%; height: auto; }

.feed-chart-meta {
  margin: 6px 0 0;
  font-family: var(--a6-mono);
  font-size: 12px;
  color: var(--a6-muted);
  font-variant-numeric: tabular-nums;
}

.badge {
  display: inline-block;
  padding: 2px 10px;
  border-radius: var(--a6-r-pill);
  border: 1px solid var(--a6-line);
  background: var(--a6-surface-alt);
  color: var(--a6-body);
  font-family: var(--a6-mono);
  font-size: 12px;
}

/* Severity-Badges: Border = Severity-Vollton-Token (identisch zur Text-Farbe).
   Die früheren Alpha-Borders (z. B. rgba(154,91,0,.4)) komponierten über dem
   Soft-Fill auf <3:1 (Live-Audit: noir 1,77:1 / light 1,79:1) und verletzten
   WCAG 1.4.11 — der Vollton erbt die geprüfte Text-Paarung (≥4,5:1 gegen den
   Fill in beiden Themes) und liegt damit sicher über der UI-Grenze 3:1. */
.badge-malicious {
  background: var(--a6-error-soft);
  border-color: var(--a6-error);
  color: var(--a6-error);
}

.badge-suspect {
  background: var(--a6-warn-soft);
  border-color: var(--a6-warn);
  color: var(--a6-warn);
}

.badge-info {
  background: var(--a6-info-soft);
  border-color: var(--a6-info);
  color: var(--a6-info);
}

.badge-clean {
  background: var(--a6-success-soft);
  border-color: var(--a6-success);
  color: var(--a6-success);
}

.badge-partial {
  background: var(--a6-surface-alt);
  border-color: var(--a6-control-line);
  color: var(--a6-muted);
}

.badge-analyzing {
  background: var(--a6-surface-alt);
  border-color: var(--a6-line);
  color: var(--a6-muted);
}

/* ---------------- Analyse-Log ---------------- */

/* Log-Panel-Kopf: eigene Panel-Konsole (das Log ist kein Feed-Unterabschnitt
   mehr) — Kopf wie .panel-head, H2 statt H3, IDs unverändert. */
.log-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px;
  margin: 0 0 14px;
  /* Kopf-Trennlinie wie .panel-head (Design-Audit, einheitliche Console) */
  border-bottom: 1px solid var(--a6-line-strong);
  padding-bottom: 12px;
}

.log-head h2 {
  margin: 0;
  font-size: 17px;
  font-weight: 700;
  letter-spacing: -0.01em;
}

.live-controls {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  margin-left: auto;
}

.filter-label {
  font-size: 12px;
  color: var(--a6-muted);
  text-transform: uppercase;
  letter-spacing: 0.08em;
}

.filter-select {
  min-height: 44px; /* Astra-6-Touch-Mindestziel (44 px) — war 40 px (Befund 2026-09-29) */
  max-inline-size: 100%;
  min-inline-size: 0;
  padding: 8px 12px;
  background: var(--a6-surface-alt);
  border: 1px solid var(--a6-control-line);
  border-radius: var(--a6-r-sm);
  color: var(--a6-ink);
  font-size: 13px;
  transition: border-color var(--a6-fast), background-color var(--a6-fast);
}

.filter-select:focus {
  border-color: var(--a6-focus);
  background: var(--a6-surface);
  outline: none;
}

.download-btn {
  position: relative; /* Anker des Hover-Shines (::after unten) */
  isolation: isolate;
  overflow: hidden; /* Clipping des Shine-Bands (Forensik 2026-10-07):
     isolation erzeugt nur den Stacking-Kontext, schneidet nichts — ohne
     Clipping malt das inset:0-Band (-120 %→+120 %) außerhalb der Pille
     und parkt nach 700 ms statisch daneben. Haus-Pattern hidden (wie
     drilldown.css/globe.css); die Clip-Kante folgt dem border-radius.
     Fokus-Ring (outline + offset, 398-401) und box-shadow gehören zum
     Element selbst und bleiben unbeschnitten. */
  min-height: 44px; /* Astra-6-Touch-Mindestziel (44 px) — war 40 px (Befund 2026-09-29) */
  padding: 8px 18px;
  background: var(--a6-surface);
  border: 1px solid var(--a6-control-line);
  border-radius: var(--a6-r-pill);
  color: var(--a6-ink);
  font-size: 13px;
  font-weight: 650;
  cursor: pointer;
  box-shadow: var(--a6-e1);
  transition: transform var(--a6-ui) var(--a6-ease), background-color var(--a6-fast);
}

@media (hover: hover) and (pointer: fine) {
  /* Farbfeedback zusätzlich zum Hub (Design-Audit: reines translateY ließ den
     Button optisch kalt) */
  .download-btn:hover { background: var(--a6-surface-alt); transform: translateY(-2px); }
}

.download-btn:active { transform: translateY(0); }

/* Disabled-Zustand (Design-Audit): #check-go (account-check.js),
   #fh-refresh (history-host.html) und #feed-range (app.js) wurden disabled
   ohne jede visuelle Veränderung. Gilt für alle .download-btn/.filter-select;
   nach der Hover-Regel notiert, damit :disabled bei gleicher Spezifität gewinnt. */
.download-btn:disabled,
.filter-select:disabled {
  opacity: 0.55;
  cursor: not-allowed;
  box-shadow: none;
  transform: none;
}

.log-list {
  display: grid;
  gap: 6px;
  max-height: 440px;
  overflow-y: auto;
}

.log-row {
  display: grid;
  /* 4. Spalte (Adresse) schrumpfbar machen statt reiner auto-Track — die
     auto-Track nahm max-content an und drückte die volle Adresse einzeilig
     an den Panelrand; break-all greift erst, wenn die Spalte schrumpfen
     darf (Befund 2026-09-29). */
  grid-template-columns: auto auto minmax(0, 1fr) minmax(0, auto) minmax(0, 2fr) auto;
  align-items: baseline;
  gap: 12px;
  padding: 8px 12px;
  background: var(--a6-surface-alt);
  /* 1px-Vollrahmen in muted (6.04 / 7.27:1 — ≥3:1-tauglich) statt des
     früheren 3px-Linksbändchens; die sev-Varianten setzen border-color. */
  border: 1px solid var(--a6-muted);
  border-radius: var(--a6-r-sm);
  font-size: 13px;
}

.log-row > * { min-inline-size: 0; }

.log-row.sev-malicious { border-color: var(--a6-error); }
.log-row.sev-suspect { border-color: var(--a6-warn); }
.log-row.sev-info { border-color: var(--a6-info); }

.log-time {
  font-family: var(--a6-mono);
  font-size: 12px;
  color: var(--a6-muted);
  font-variant-numeric: tabular-nums;
}

.log-sev {
  font-family: var(--a6-mono);
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
}

.sev-text-malicious { color: var(--a6-error); }
.sev-text-suspect { color: var(--a6-warn); }
.sev-text-info { color: var(--a6-info); }

.log-rule { font-weight: 600; overflow-wrap: anywhere; }

.log-addr {
  font-family: var(--a6-mono);
  font-size: 12px;
  color: var(--a6-body);
  word-break: break-all;
  min-inline-size: 0;
  overflow-wrap: anywhere;
}

/* Utility für Volladressen in neuen Flächen (Historie, Konto-Check): wird dort
   nur referenziert, nicht doppelt definiert. Umbrechen statt Clippen; der
   umgebende Container braucht ≥12 px Inline-Padding zum Rand. */
.addr-full {
  font-family: var(--a6-mono);
  font-size: 12px;
  color: var(--a6-body);
  overflow-wrap: anywhere;
  word-break: break-all;
  min-inline-size: 0;
}

.log-note { color: var(--a6-body); overflow-wrap: anywhere; }

.log-ledger {
  font-family: var(--a6-mono);
  font-size: 12px;
  color: var(--a6-muted);
  font-variant-numeric: tabular-nums;
}

/* ---------------- Graph ---------------- */

#graph {
  width: 100%;
  /* Höhe über die gemeinsame Bühnen-Leiter --a6-stage-height (Token-Block
     oben): 720 px Basis → 640 (≤1024) → 560 (≤900) → 480 (≤560); #globe
     (globe.css) und .cluster-3d (drilldown.css) folgen derselben Leiter —
     Tab-Wechsel springen nicht mehr. */
  height: var(--a6-stage-height, 720px);
  background:
    radial-gradient(var(--a6-graph-grid) 1px, transparent 1px) 0 0 / 24px 24px,
    var(--a6-graph-canvas);
  border: 1px solid var(--a6-line);
  border-radius: var(--a6-r-md);
}

/* Flow-Host-Bühne (history-host.html #fh-graph): vorher ohne jede CSS-Regel —
   das SVG schwebte ohne Bühnenrahmen im Panel. Rahmen wie #graph
   (Punktraster, 1-px-Linie, Radius); das SVG skaliert selbst per viewBox
   (style-Attribut im Markup: width:100%;height:auto). */
#fh-graph {
  margin-top: 14px;
  padding: 10px 12px;
  background:
    radial-gradient(var(--a6-graph-grid) 1px, transparent 1px) 0 0 / 24px 24px,
    var(--a6-graph-canvas);
  border: 1px solid var(--a6-line);
  border-radius: var(--a6-r-md);
}

#fh-graph svg { display: block; width: 100%; height: auto; }

/* vis-network-Tooltip (Knoten-, Kanten- und Cluster-Bubble-Titel): Das Bundle
   injiziert sein Stylesheet zur Laufzeit als div.vis-tooltip mit
   white-space:nowrap — NACH style.css. Containerspezifische Selektoren mit
   höherer Spezifität gewinnen sonst keinen Konflikt; so brechen volle
   Adressen im Tooltip um, statt über den Graph-Rand zu laufen
   (Befund 2026-09-29). Der zweite Selektor deckt den 2D-Fallback im
   Drilldown-Modal (drilldown.js nutzt .cluster-3d als Container). */
#graph .vis-tooltip,
.cluster-3d .vis-tooltip {
  max-width: min(340px, 80vw);
  white-space: normal;
  overflow-wrap: anywhere;
  word-break: break-all;
  padding: 8px 10px;
  background: var(--a6-surface);
  color: var(--a6-ink);
  border: 1px solid var(--a6-line-strong);
  border-radius: var(--a6-r-sm);
  font-family: var(--a6-mono);
  font-size: 12px;
  box-shadow: var(--a6-e1);
}

.graph-note {
  margin: 10px 0 0;
  color: var(--a6-muted);
  font-size: 12px;
}

.graph-error {
  color: var(--a6-error);
  padding: 20px;
}

/* Tabs: Segment-Pill-Navigation, Stil konsistent zu .download-btn */
.graph-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: 10px;
}

.graph-tab {
  position: relative; /* Anker des Hover-Shines (::after unten) */
  isolation: isolate;
  overflow: hidden; /* Clipping des Shine-Bands — wie .download-btn oben
     (Forensik 2026-10-07); Kante folgt der Pillen-Rundung */
  min-height: 44px; /* Astra-6-Touch-Mindestziel (44 px) — war 40 px (Befund 2026-09-29) */
  padding: 8px 18px;
  background: var(--a6-surface);
  color: var(--a6-body);
  border: 1px solid var(--a6-control-line);
  border-radius: var(--a6-r-pill);
  font-size: 13px;
  font-weight: 650;
  cursor: pointer;
  box-shadow: var(--a6-e1);
  transition: background-color var(--a6-fast), color var(--a6-fast), transform var(--a6-ui) var(--a6-ease);
}

.graph-tab[aria-selected="true"] {
  background: var(--a6-ink-fill);
  border-color: var(--a6-ink-fill);
  color: var(--a6-on-ink);
}

/* Aktuelle Seite in Anker-Navigationen (about.html trägt aria-current="page"
   auf dem eigenen Tab — Muster .graph-tab[aria-selected], aber für Seiten-
   statt View-Wechsel; dieselbe Ink-Fläche, kein zweites Farbsystem). */
.view-tab[aria-current="page"] {
  background: var(--a6-ink-fill);
  border-color: var(--a6-ink-fill);
  color: var(--a6-on-ink);
}

@media (hover: hover) and (pointer: fine) {
  .graph-tab:not([aria-selected="true"]):hover { background: var(--a6-surface-alt); }
}

/* Hover-Shine der Console-Buttons (Muster hero-file .b::after — 105°-Band,
   -120 %→+120 %, EINMAL beim Hover, kein Dauerlauf): die Buttons behalten
   ihre 1px-control-line-Randführung (≥3.57:1 UI, beide Themes); der Shine
   ist reines Hover-Feedback auf der ungewählten Fläche. Ausgeschlossen:
   :disabled und die Ink-Flächen — die Auswahlflächen aria-selected/
   aria-pressed=true (Selektor unten) und die über account-check.css
   ink-gefüllte Primäraktion #check-go (Forensik 2026-10-07: der ID-
   Selektor account-check.css:74 (1,0,0) schlägt .download-btn:hover
   (0,2,0), die Ink-Füllung bleibt also auch im Hover — auf --a6-ink-fill
   wäre ein Akzent-Band ein Flickern auf der Primärfläche). Das Band wird
   durch overflow:hidden an den Basis-Klassen auf die Button-Box
   beschnitten; der aria-current-Tab (about.html) braucht KEINEN
   Ausschluss: .graph-tab:not([aria-selected="true"]):hover (0,3,0)
   schlägt .view-tab[aria-current="page"] (0,2,0), der Shine läuft dort
   über surface-alt, nicht über Ink. */
@media (hover: hover) and (pointer: fine) {
  .download-btn:not(:disabled):not(#check-go):hover::after,
  .graph-tab:not([aria-selected="true"]):not([aria-pressed="true"]):hover::after {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: inherit;
    background: linear-gradient(105deg, transparent 30%, var(--a6-accent-soft) 48%, transparent 66%);
    opacity: 0.6;
    transform: translateX(-120%);
    animation: a6-btn-shine 700ms var(--a6-ease);
    pointer-events: none;
  }
}

@keyframes a6-btn-shine {
  from { transform: translateX(-120%); }
  to { transform: translateX(120%); }
}

/* Verdichtungs-Umschalter der Cluster-Karten (Design P2, index.html
   #cluster-density): Toggle-Button-Gruppe im .graph-tab-Gewand — die
   Auswahlfläche läuft über aria-pressed (Muster .lang-btn), NICHT über
   aria-selected (keine role="tab"-Semantik; die Graph-Tabs bleiben die
   einzige Tablist). Basisklasse .graph-tab liefert 44-px-Ziel, Fokus-Ring,
   reduced-motion und forced-colors (Selektorliste unten) mit. */
.cluster-density {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

.density-btn {
  padding: 8px 14px; /* kompakter als Graph-Tabs (18 px) — drei Stufen, ein Gruppenzweck */
}

.density-btn[aria-pressed="true"] {
  background: var(--a6-ink-fill);
  border-color: var(--a6-ink-fill);
  color: var(--a6-on-ink);
}

@media (hover: hover) and (pointer: fine) {
  .density-btn:not([aria-pressed="true"]):hover { background: var(--a6-surface-alt); }
}

/* Fenster-Umschalter der Top-10-Börsen-Box (aria-pressed-Muster wie die
   Dichte-Stufen, eigene Klasse — der density-Test zählt genau drei
   .density-btn; Auswahlfläche über dieselben Tokens, kein neuer Wert). */
.exout-window-btn {
  padding: 8px 14px;
}

.exout-window-btn[aria-pressed="true"] {
  background: var(--a6-ink-fill);
  border-color: var(--a6-ink-fill);
  color: var(--a6-on-ink);
}

@media (hover: hover) and (pointer: fine) {
  .exout-window-btn:not([aria-pressed="true"]):hover { background: var(--a6-surface-alt); }
}

/* Fenster-Umschalter der Drainer-Box (Drainer-Tranche 2026-10-07): identisches
   aria-pressed-Muster wie die Börsen-Box — dieselben Tokens, keine neuen Werte. */
.drout-window-btn {
  padding: 8px 14px;
}

.drout-window-btn[aria-pressed="true"] {
  background: var(--a6-ink-fill);
  border-color: var(--a6-ink-fill);
  color: var(--a6-on-ink);
}

@media (hover: hover) and (pointer: fine) {
  .drout-window-btn:not([aria-pressed="true"]):hover { background: var(--a6-surface-alt); }
}

.graph-disclaimer {
  margin: 0 0 12px;
  color: var(--a6-muted);
  font-size: 12px;
}

.legend {
  display: flex;
  flex-wrap: wrap;
  /* 8-px-Gap (Design-Audit): 13 Einträge im panel-head — engerer Raster hält
     die Legende kompakt im Umbruch, ohne Markup zu ändern. */
  gap: 8px;
  margin-left: auto;
  font-size: 12px;
  color: var(--a6-muted);
}

.legend-item { display: inline-flex; align-items: center; gap: 6px; }

.swatch {
  width: 11px;
  height: 11px;
  border-radius: var(--a6-r-xs); /* kleinster Radius als Token (Design P4) */
  display: inline-block;
  border: 1px solid rgba(20, 20, 22, 0.25);
}

/* Rollen-Swatches (Legende + Chips) — D7: dieselben Werte wie ROLE_COLORS
   (app.js) und --a6-role-* (oben); unknown bleibt #f0f0f2 mit Randton. */
.swatch-source { background: var(--a6-role-source); border-radius: 50%; }
.swatch-drainer { background: var(--a6-role-drainer); border-radius: 50%; }
.swatch-collector { background: var(--a6-role-collector); border-radius: 50%; }
.swatch-relay { background: var(--a6-role-relay); border-radius: 50%; }
.swatch-unknown { background: var(--a6-swatch-unknown); border-color: var(--a6-swatch-unknown-line); border-radius: 50%; }
.swatch-cluster { background: var(--a6-cluster-fill); border: 1px solid var(--a6-swatch-cluster-line); }

/* Kantentyp-Swatches — seit dem Farb-Audit 2026-10-07 je Tx-KATEGORIE
   (txCategory, edge-colors.mjs): Payment GRÜN (success-Familie — die alte
   payment==drainer-Rot-Kollision ist behoben), market = DEX+AMM, admin =
   Protokoll-Verwaltung, nft = Fuchsia, fraud = Severity-Rot (Override).
   Farben ausschließlich über die --a6-edge- und --a6-swatch-Tokens
   (beide Themes). */
.swatch-payment { background: var(--a6-swatch-payment); }
.swatch-escrow { background: var(--a6-edge-escrow); }
.swatch-check { background: var(--a6-edge-check); }
.swatch-market { background: var(--a6-edge-market); }
.swatch-nft { background: var(--a6-edge-nft); }
.swatch-admin { background: var(--a6-edge-admin); }
.swatch-fraud { background: var(--a6-edge-fraud); }
.swatch-other { background: var(--a6-swatch-other); }

/* Kanten-Kategorien-Legende im Drilldown-Overlay (Audit 2026-10-07):
   dieselbe .legend-Basis wie im Dashboard, innerhalb der Graph-Sektion
   platziert; die Items sind inline-flex und umbrechen natürlich. */
.cluster-graph-legend { margin: 8px 0 0; }

/* Schweregrad-Swatches — Severity-Tokens, kein zweites Farbsystem */
.swatch-sev-malicious { background: var(--a6-sev-malicious); }
.swatch-sev-suspect { background: var(--a6-sev-suspect); }
.swatch-sev-info { background: var(--a6-sev-info); }

/* ---------------- Cluster-Zusammenfassung ---------------- */

/* Cluster-Karten: bewusst MAXIMAL zwei Spalten pro Zeile (Nutzer-Vorgabe).
   Track-Minimum = max(26rem, 50 % des Containers − halber Gap):
   — 2400 px Viewport: main-Inhalt 2296, Panel-Inhalt 2254 → Track-Min 1117;
     2·1117 + 16 Gap = 2250 ≤ 2254 → zwei Spalten;
     3·1117 + 32 = 3383 > 2254 → nie drei Spalten.
   — 1400 px Viewport: main-Inhalt 1296, Panel-Inhalt 1254 → Track-Min 617;
     2·617 + 16 = 1250 ≤ 1254 → zwei Spalten.
   — 900 px Viewport: main-Inhalt 822, Panel-Inhalt 780 → Track-Min 416;
     2·416 + 16 = 848 > 780 → eine Spalte.
   Der 10-px-Abschlag hält die Zwei-Spalten-Grenze auch bei Rundung
   (Browser-Auflösung) stabil; unter 760 px erzwingt der Override unten 1fr. */
.cluster-list {
  list-style: none;
  margin: 14px 0 0;
  padding: 0;
  display: grid;
  gap: 16px;
  grid-template-columns: repeat(auto-fit, minmax(max(26rem, min(100%, calc(50% - 10px))), 1fr));
}

.cluster-card {
  background: var(--a6-surface);
  /* Neutrale 1px-Basis in control-line (3.66 / 3.93:1 ✓) statt der früheren
     Alpha-card-line-Kante (< 2:1) mit 3px-Linksbändchen: die dominante Rolle
     trägt jetzt der VOLLRAHMEN in Rollenfarbe (Regeln unten) — jede Karte
     hat per app.js immer genau eine Rolle. --a6-card-line bleibt dem
     .hx-kpi-Rand vorbehalten. */
  border: 1px solid var(--a6-control-line);
  border-radius: var(--a6-r-md);
  padding: clamp(1rem, 1.5vw, 1.5rem);
  box-shadow: var(--a6-e1);
  cursor: pointer;
  min-inline-size: 0; /* Grid-Kind darf schrumpfen, damit Inhalte umbrechen */
  transition: transform var(--a6-ui) var(--a6-ease), box-shadow var(--a6-ui) var(--a6-ease),
    border-color var(--a6-ui) var(--a6-ease);
}

@media (hover: hover) and (pointer: fine) {
  .cluster-card:hover { transform: translateY(-2px); box-shadow: var(--a6-e2); }
}

.cluster-card:active { transform: translateY(0); }

/* Vollrahmen in der dominanten Rolle des Clusters — die Rollenfarben stehen
   als --a6-role-*-Tokens (Wertidentität mit ROLE_COLORS in app.js, D7);
   unknown nutzt das eigene --a6-role-unknown, damit „Unknown" in Legende,
   3D-Kugel und Kartenrahmen EINE Farbe ist (6.20 / 13.69:1 ✓ gegen beide
   Theme-Surfaces). */
.cluster-card.role-source { border-color: var(--a6-role-source); }
.cluster-card.role-drainer { border-color: var(--a6-role-drainer); }
.cluster-card.role-collector { border-color: var(--a6-role-collector); }
.cluster-card.role-relay { border-color: var(--a6-role-relay); }
.cluster-card.role-unknown { border-color: var(--a6-role-unknown); }

.cluster-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 10px;
}

/* Kopf-Tönung NUR für den Maliz-Träger (Design P2): SOLIDE error-soft-Fläche
   als Platte im Karten-Polster — bewusst kein Gradient und keine Rahmen-
   Verstärkung (Plan-Kritik 7a: keine Sonderdicke/Sonderfarbe je Schweregrad;
   der Vollrahmen bleibt für alle einheitlich 1 px). Die Tönung ist Flächen-
   Hinterlegung hinter Text (Textfarbe bleibt Ink, ≥ 4,5:1 auf error-soft)
   — das Schweregrad-Signal selbst trägt das .risk-badge-Textlabel, die
   Fläche verstärkt nur die Hierarchie. */
.cluster-card.sev-malicious .cluster-head {
  background: var(--a6-error-soft);
  padding: 6px 10px;
  border-radius: var(--a6-r-sm);
}

.cluster-label {
  font-family: var(--a6-mono);
  font-size: 15px; /* 14 → 15 px (Design-Audit): Karten-Titel über dem Body */
  font-weight: 700;
  letter-spacing: -0.01em;
  /* Server-sanitiste Labels sind bis 200 Zeichen lang und angreiferkontrolliert;
     ohne Umbruchregeln läuft ein unzerbrechlicher String über den Kartenrand
     hinaus (Befund 2026-09-29). Gleiches Muster wie .addr-full/.log-addr —
     gilt global, also auch für .cluster-label im Drilldown-Modal
     (drilldown.js Shell). */
  overflow-wrap: anywhere;
  word-break: break-all;
  min-inline-size: 0;
}

/* D2 (nur ergänzt): Label-Ton auf Brand-Blau (Ink statt Akzentfläche);
   Typografie und Umbruchregeln oben bleiben unverändert. */
.cluster-label { color: var(--a6-brand-blue); }

.cluster-roles {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 8px;
}

.role-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 2px 12px; /* 4-px-Raster (Design-Audit, war 2px 9px) */
  border: 1px solid var(--a6-line);
  border-radius: var(--a6-r-pill);
  background: var(--a6-surface-alt);
  color: var(--a6-body);
  font-size: 12px;
}

/* Drainer-Hervorhebung in der Rollen-Zeile (Design P2): der Drainer ist der
   Maliz-Träger der Rollen-Tafel —error-soft-Fläche + Rollen-Ton in Rand/Text
   (Rollen-Tokens D7, Text ≥ 4,5:1 auf error-soft). Dieselbe Behandlung wie
   .chain-node.chain-drainer — ein Signal, zwei Ebenen. */
.role-chip.role-drainer {
  border-color: var(--a6-role-drainer-border);
  background: var(--a6-error-soft);
  color: var(--a6-role-drainer);
  font-weight: 650;
}

/* XRPScan-Namens-Chips (nur ergänzt, Muster .role-chip/.risk-badge): Name
   ist zusätzliches Element, verdrängt nie die Adresse. --a6-accent bleibt
   unangetastet (Kommentar oben, style.css:46-49). */
.cluster-names {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 8px;
}

.name-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 2px 9px;
  border: 1px solid var(--a6-line);
  border-radius: var(--a6-r-pill);
  background: var(--a6-surface);
  color: var(--a6-brand-teal);
  font-size: 12px;
  max-inline-size: 100%;
  overflow-wrap: anywhere;
}

.name-chip-verified {
  color: var(--a6-brand-blue);
  font-weight: 700;
}

.name-chip-domain {
  font-family: var(--a6-mono);
  font-size: 12px;
  color: var(--a6-muted);
}

/* Destination-Tag-Chip (Exchange-Registry, Hosted-Account): Mono-Pill im
   Look der .name-chip, eigene Farbe zur Abgrenzung vom Namens-Badge. */
.tag-chip {
  display: inline-flex;
  align-items: center;
  padding: 2px 8px;
  border: 1px solid var(--a6-line);
  border-radius: var(--a6-r-pill);
  background: var(--a6-surface);
  color: var(--a6-brand-amber);
  font-family: var(--a6-mono);
  font-size: 12px; /* 11 → 12 px (Design-Audit): 12-px-Untergrenze des Projekts */
  font-variant-numeric: tabular-nums;
  max-inline-size: 100%;
  overflow-wrap: anywhere;
}

/* Adress-Chip-Zeile der Cluster-Karten (Coverage-Fix 2026-10-06, Live- und
   Flow-Host, public/cluster-chips.mjs): pro Mitglied ein Gruppen-Wrap mit
   Name-Chip plus zugehöriger Tag-Chips; '+'-Hinweis bei mehr als Cap 3
   Tags (Analogon .cluster-table-dash der Drilldown-Tabelle, die hier nicht
   geladen sein kann — history-host.html kennt nur style.css). */
.cluster-addr-row { gap: 6px 10px; }
.cluster-addr-chips {
  display: inline-flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px;
  max-inline-size: 100%;
}
/* '+N weitere Konten'-Hinweis (Design P2, Markup app.js moreChip): sitzt am
   Ende der Chip-Zeile, vertikal auf Chip-Höhe; title trägt die volle
   Mitgliederzahl. Der '+'-Hinweis aus tagChipsHtml (je Adresse, aria-hidden)
   teilt sich bewusst dieselbe Klasse — eine Hinweis-Sprache. */
.cluster-addr-more {
  align-self: center;
  color: var(--a6-muted);
  font-size: 12px;
  font-variant-numeric: tabular-nums;
}

.cluster-metrics {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 14px;
  margin-top: 10px;
}

.cluster-xrp {
  font-family: var(--a6-mono);
  font-size: 20px; /* 18 → 20 px (Design-Audit): Primärmetrik der Karte */
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--a6-ink);
}

.cluster-card.sev-malicious .cluster-xrp { color: var(--a6-error); }
.cluster-card.sev-suspect .cluster-xrp { color: var(--a6-warn); }

/* -------------------- Top-10 Börsen-Zuflüsse (2026-10-07) ----------------
   Client-Aggregation aus der bestehenden /api/flow-state-View (public/
   exchange-outflows.mjs + app.js renderExchangeOutflows) — kein neuer
   api/*-Endpoint. Vollrahmen-Muster der Cluster-Karten (1 px control-line,
   r-md), beide Themes laufen allein über die a6-Tokens (keine hartcodierten
   Hex außerhalb des Token-Blocks); Badges im Look der .name-chip/.tag-chip. */
.exout-row {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  gap: 2px 14px;
  align-items: baseline;
  border: 1px solid var(--a6-control-line);
  border-radius: var(--a6-r-md);
  padding: 10px 14px;
  background: var(--a6-surface);
  box-shadow: var(--a6-e1);
  min-inline-size: 0;
}

.exout-rank {
  font-family: var(--a6-mono);
  font-size: 13px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--a6-muted);
  min-inline-size: 2ch;
  text-align: right;
}

.exout-main {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-inline-size: 0; /* Grid-Kind darf schrumpfen — Umbruch vor Overflow */
}

.exout-name {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 6px;
  font-weight: 700;
  color: var(--a6-ink);
  overflow-wrap: anywhere;
  min-inline-size: 0;
}

.exout-domain {
  font-family: var(--a6-mono);
  font-size: 12px;
  font-weight: 400;
  color: var(--a6-muted);
}

.exout-addr {
  font-family: var(--a6-mono);
  font-size: 12px;
  color: var(--a6-body);
  overflow-wrap: anywhere;
  word-break: break-all;
  min-inline-size: 0;
}

.exout-amount {
  font-family: var(--a6-mono);
  font-size: 16px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--a6-ink);
  text-align: right;
}

.exout-meta {
  grid-column: 2 / -1;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 6px;
  font-size: 12px;
  color: var(--a6-muted);
  font-variant-numeric: tabular-nums;
}

.exout-badge {
  display: inline-flex;
  align-items: center;
  padding: 2px 8px;
  border: 1px solid var(--a6-line);
  border-radius: var(--a6-r-pill);
  background: var(--a6-surface);
  color: var(--a6-brand-teal);
  font-family: var(--a6-mono);
  font-size: 12px;
  font-variant-numeric: tabular-nums;
  max-inline-size: 100%;
  overflow-wrap: anywhere;
}

.exout-badge-wellknown { color: var(--a6-brand-blue); }

/* ------------------- /Top-10 Börsen-Zuflüsse ---------------------------- */

/* ------------------ Top-10 Drainer-Rangliste (2026-10-07) -----------------
   Rechte Duo-Box: Vollrahmen-Muster der .exout-Zeilen (1 px control-line,
   r-md), beide Themes laufen allein über die a6-Tokens (keine hartcodierten
   Hex außerhalb des Token-Blocks). Drainer-Signal NUR in Rang/Betrag über die
   bestehenden Rollen-Ton-Paare (--a6-role-drainer / --a6-role-drainer-border,
   style.css:202-203/324-325 — dieselbe Behandlung wie .role-chip.role-drainer);
   die Panel-Fläche selbst bleibt matte .panel (lab_graphite.avoid: kein Glow
   auf jeder Karte — die Frost-Schicht bleibt ausschließlich der Börsen-Box).
   Tag-Chips nutzen die bestehende .tag-chip-Klasse (Look der .name-chip).
   Panel-Rhythmus: dieselben Margins wie die Börsen-Box (22 px Sektions-
   Rhythmus, mobil 14 px) — im Stapel (unter 760 px) trägt NUR die linke
   Box die obere Margin (margin-top 0 der rechten Box + gap 0), sonst
   verdoppelten sich die Panel-Margins zum 44-/28-px-Abstand. */
#drainer-outflows-panel {
  margin: 22px auto;
}

.drout-row {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  gap: 2px 14px;
  align-items: baseline;
  border: 1px solid var(--a6-control-line);
  border-radius: var(--a6-r-md);
  padding: 10px 14px;
  background: var(--a6-surface);
  box-shadow: var(--a6-e1);
  min-inline-size: 0;
}

.drout-rank {
  font-family: var(--a6-mono);
  font-size: 13px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--a6-role-drainer);
  min-inline-size: 2ch;
  text-align: right;
}

.drout-main {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-inline-size: 0; /* Grid-Kind darf schrumpfen — Umbruch vor Overflow */
}

.drout-name {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 6px;
  font-family: var(--a6-mono);
  font-weight: 700;
  color: var(--a6-ink);
  overflow-wrap: anywhere;
  min-inline-size: 0;
}

.drout-amount {
  font-family: var(--a6-mono);
  font-size: 16px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--a6-role-drainer);
  text-align: right;
}

.drout-meta {
  grid-column: 2 / -1;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 6px;
  font-size: 12px;
  color: var(--a6-muted);
  font-variant-numeric: tabular-nums;
}

.drout-badge {
  display: inline-flex;
  align-items: center;
  padding: 2px 8px;
  border: 1px solid var(--a6-line);
  border-radius: var(--a6-r-pill);
  background: var(--a6-surface);
  color: var(--a6-brand-teal);
  font-family: var(--a6-mono);
  font-size: 12px;
  font-variant-numeric: tabular-nums;
  max-inline-size: 100%;
  overflow-wrap: anywhere;
}

/* Forwarded-Badge im Ton der Drainer-Rollen-Signalfläche (dieselbe
   Token-Kombination wie .role-chip.role-drainer / .chain-node.chain-drainer). */
.drout-badge-forwarded {
  border-color: var(--a6-role-drainer-border);
  background: var(--a6-error-soft);
  color: var(--a6-role-drainer);
}

/* '+'-Hinweis nach dem Tag-Chip-Cap 3 (Muster .cluster-addr-more, 1563). */
.drout-tag-more {
  align-self: center;
  color: var(--a6-muted);
  font-size: 12px;
  font-variant-numeric: tabular-nums;
}

/* ------------------- /Top-10 Drainer-Rangliste ---------------------------- */

.cluster-txs,
.cluster-accounts {
  font-family: var(--a6-mono);
  font-size: 12px;
  color: var(--a6-muted);
  font-variant-numeric: tabular-nums;
}

/* Mega-Cluster (Design P2, data-size="mega" — Mitglieder > 100 ODER
   distinctAccounts ≥ 100): zweiteiliger Metrik-Block statt langer Zeile —
   XRP prominent in Zeile 1, Tx/Konten als kombinierte 12-px-Mono-Zeile in
   Zeile 2. Keine Schrift unter 12 px. */
.cluster-metrics-mega {
  flex-direction: column;
  align-items: flex-start;
  gap: 4px;
}

.cluster-metrics-sub {
  display: inline-flex;
  flex-wrap: wrap;
  gap: 10px;
}

/* Mega-Meta-Zeile (app.js cluster.megaNote): der EINE Aggregations-Hinweis
   der Karte — ersetzt den '+N weitere'-Hinweis der Chip-Zeile, damit nie
   zwei konkurrierende Hinweise stehen (Plan-Kritik 10). */
.cluster-mega-note {
  margin: 8px 0 0;
  font-size: 12px;
  font-variant-numeric: tabular-nums;
  color: var(--a6-body);
}

/* Drainer→Kollektor-Kette: Start-bis-Ende-Nachverfolgung des Geldflusses */
.cluster-chain {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  margin-top: 10px;
  font-family: var(--a6-mono);
  font-size: 12px;
  min-inline-size: 0; /* Kette darf schrumpfen, Chips umbrechen statt überlaufen */
  overflow-wrap: anywhere; /* B7 (neue Zeile): volle Adressen brechen, Container wie Kette */
}

.chain-node {
  padding: 6px 12px;
  border: 1px solid var(--a6-line);
  border-radius: var(--a6-r-pill);
  background: var(--a6-surface-alt);
  color: var(--a6-body);
  /* Volle Base58-Adressen kontrolliert umbrechen lassen (anywhere statt
     break-all): bricht an der natürlichsten Stelle, nicht hart mitten im
     Wort — Adressen bleiben in den Karten vollständig lesbar. */
  overflow-wrap: anywhere;
  font-family: var(--a6-mono);
  font-variant-numeric: tabular-nums;
  /* Flex-Kind der Kette: darf schrumpfen, aber nie über die Karte hinaus
     wachsen (Befund 2026-09-29 — volle Base58-Adresse bricht im Chip um). */
  max-inline-size: 100%;
  flex: 0 1 auto;
}

.chain-node.chain-source {
  border-color: var(--a6-role-source-border);
  color: var(--a6-role-source);
}

.chain-node.chain-drainer {
  border-color: var(--a6-role-drainer-border);
  color: var(--a6-role-drainer);
  background: var(--a6-error-soft);
}

.chain-node.chain-collector {
  border-color: var(--a6-role-collector-border);
  color: var(--a6-role-collector);
  background: var(--a6-warn-soft);
}

.chain-node.chain-relay {
  border-color: var(--a6-role-relay-border);
  color: var(--a6-role-relay);
}

/* Unbekannte Rolle in der Flusskette (3D-Farb-Audit 2026-10-07): Füllung =
   getönte Unknown-Familie (--a6-unknown-fill, Text bleibt --a6-body und
   liest sich ≥4.5:1), Rand = der volle Unknown-Ton --a6-role-unknown —
   abgrenzbar von relay und surface-alt, derselbe Teller wie Legende und
   3D-Kugel. */
.chain-node.chain-unknown {
  border-color: var(--a6-role-unknown);
  color: var(--a6-body);
  background: var(--a6-unknown-fill);
}

.chain-arrow { color: var(--a6-muted); }

/* Trenner zwischen mehreren Flusspfaden (echte Kantenpfade, lib/cluster.mjs
   flowPaths) — bewusst KEIN Pfeil: '·' impliziert keine Transaktion. */
.chain-path-sep { color: var(--a6-muted); padding: 0 2px; }

.cluster-times {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  margin-top: 10px;
  font-family: var(--a6-mono);
  font-size: 12px;
  color: var(--a6-muted);
  font-variant-numeric: tabular-nums;
}

/* ---------------- Verdichtungsstufen der Cluster-Karten (Design P2) ----------------
   data-density am Listen-Container (#cluster-list; app.js setClusterDensity/
   renderClusterList, Segment-Control im panel-head, Persistierung
   localStorage 'hx-density'). Nur Abstände/Größen — keine Schrift unter
   12 px, keine Touch-Ziel-Verkleinerung (Karten bleiben klickbare Flächen
   weit über 44 px, Buttons laufen über .graph-tab mit 44-px-Mindesthöhe).
   Die Kappung von Chips (5→3 Adressen) und Kette (2→1 Pfade) passiert im
   Template (app.js clusterCardHtml), nicht im CSS. */
.cluster-list[data-density="compact"] .cluster-card {
  padding: 12px 14px;
}

.cluster-list[data-density="compact"] .cluster-metrics,
.cluster-list[data-density="compact"] .cluster-times {
  gap: 10px;
}

.cluster-list[data-density="compact"] .cluster-times {
  flex-wrap: nowrap; /* Zeiten einzeilig (Plan P2); kurze Uhrenwerte, umbarchbar in den Spans */
}

.cluster-list[data-density="dense"] .cluster-card {
  padding: 10px 12px;
}

.cluster-list[data-density="dense"] .cluster-xrp {
  font-size: 17px; /* 20 → 17 px (Plan P2): bleibt Primärmetrik über dem 12-px-Umfeld */
}

/* Transit-Hinweis (Markup app.js clusterCardHtml + drilldown.js, Klasse hatte
   zuvor keine Regel): Meta-Ton in 12 px statt vererbtrem 15-px-Ink-Body. Die
   drilldown-Variante trägt zusätzlich .graph-note (gleiche Typografie). */
.cluster-transit-note {
  margin: 8px 0 0;
  font-size: 12px;
  color: var(--a6-muted);
}

/* Feldkappe-Fußnote der Cluster-Karte (Kritik-Runde 3, Markup app.js
   clusterCardHtml): ehrliche Kennzeichnung, wenn die Write-Pfad-Feldkappe des
   Servers (300 Mitglieder) die Liste komprimiert hat — Meta-Ton wie der
   Transit-Hinweis, Warn-Haarlinie als Zweitkanal (der Text trägt den Inhalt,
   Farbe ist nie alleiniges Signal). */
.cluster-capped-note {
  margin: 8px 0 0;
  padding-top: 6px;
  border-top: 1px solid var(--a6-warn);
  font-size: 12px;
  color: var(--a6-muted);
}

#cluster-empty { margin-top: 14px; }

/* Leerzustände (Design-Audit): bisher reiner Fließtext — jetzt gestaltete
   Fläche (gestrichelte Kontroll-Linie, Surface-alt, zentriert). Betrifft
   #feed-empty/#log-empty/#cluster-empty (index.html) und #fh-status-msg/
   #fh-graph-empty/#fh-empty (history-host.html); der <p>-Textinhalt samt
   data-i18n-Keys bleibt unverändert — die Illustration wird per JS
   (public/icons.mjs mountEmptyIllu) als erstes Kind ergänzt. */
.empty {
  color: var(--a6-muted);
  font-size: 14px;
  background: var(--a6-surface-alt);
  border: 1px dashed var(--a6-control-line);
  border-radius: var(--a6-r-sm);
  padding: 16px;
  text-align: center;
}

/* Leerzustands-Illustration (Design P0, Unikat-SVGs aus public/icons.mjs):
   textlose Gravur über dem Text, 160×120, Grundtöne über currentColor in der
   Kontroll-Linie; der EINE Akzent (.icon-accent) läuft über den eigenen
   Token. Statisch — reduced-motion-neutral; forced-colors siehe unten. */
.empty-illu {
  display: block;
  margin: 0 auto 10px;
  width: 160px;
  height: 120px;
  color: var(--a6-control-line);
}

.empty-illu svg {
  display: block;
  width: 100%;
  height: 100%;
}

.empty-illu .icon-accent {
  color: var(--a6-icon-accent, var(--a6-brand-blue));
}

/* Panel-Signet Activity Graph (Design P0, app.js hängt es in die
   <h2 id="graph-title">): 20-px-Mini-Topologie in Ink, auf die H2-Baseline
   gestellt — reine Gravur ohne Akzent, bewusst kein Hexagon (das
   Waben-Motiv gehört exklusiv zur Marke .brand-mark). */
.panel-marker {
  display: inline-block;
  width: 20px;
  height: 20px;
  margin-right: 8px;
  vertical-align: -3px;
  color: var(--a6-ink);
}

.panel-marker svg {
  display: block;
  width: 100%;
  height: 100%;
}

@media (forced-colors: active) {
  .empty-illu { color: CanvasText; }
  .empty-illu .icon-accent { color: Highlight; }
}

.risk-badge {
  display: inline-block;
  padding: 1px 9px;
  border-radius: var(--a6-r-pill);
  font-size: 12px;
  font-family: var(--a6-mono);
}

/* Verdict-Badges: Border = Severity-Vollton-Token (gleiche Begründung wie
   .badge-* oben — Alpha-Borders komponieren <3:1, WCAG 1.4.11). */
.risk-malicious {
  background: var(--a6-error-soft);
  color: var(--a6-error);
  border: 1px solid var(--a6-error);
}

.risk-suspect {
  background: var(--a6-warn-soft);
  color: var(--a6-warn);
  border: 1px solid var(--a6-warn);
}

/* ---------------- Live-Statusleiste (Astra-6 .a6-stage, SKILL hero_system) --
   Zur Console-Kopfzone verdichtete Bühne: kurzer Titel/Lead links, KPI-Zähler
   als Tachometer-Zeile rechts (ab ~900px eine Zeile, darunter umbrechend).
   Eigene Breitenbegrenzung: die Bühne liegt außerhalb von main und erbt
   die main-Breite NICHT — identische Formel hält sie fluchtgleich zu den
   Panels. Panel-Hierarchie: Statusleiste mit e3 (höchste Erhebung), Panels
   mit e1 — der Glow (::before) wohnt auf GENAU dieser einen Bühne, nicht auf
   jeder Karte (lab_graphite.avoid: „Glow auf jeder Karte"). */
.hx-stage {
  position: relative;
  isolation: isolate;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 10px 24px;
  width: min(var(--a6-container), 100% - 2 * var(--a6-gutter));
  margin: 18px auto 0;
  padding: clamp(0.875rem, 1.4vw, 1.25rem) clamp(1rem, 2vw, 1.75rem);
  border: 1px solid var(--a6-line-strong);
  border-radius: var(--a6-r-lg);
  /* Verlauf + Glow über die Token --a6-stage-bg/--a6-stage-glow (Noir-Theme
     tauscht beide) — vorher hartcodierte Hell-Verläufe im Selektorblock. */
  background: var(--a6-stage-bg);
  box-shadow: var(--a6-e3);
}

.hx-stage::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  border-radius: inherit;
  pointer-events: none;
  background: var(--a6-stage-glow);
}

.hx-stage-copy {
  display: grid;
  gap: 0.25rem;
  min-inline-size: 0;
}

.hx-title {
  max-inline-size: 42ch;
  /* clamp max 1.25 rem (Design-Audit): Stage-Titel bleibt unter der Marke
     (.brand h1 = 24 px) — vorher drehte sich die Hierarchie um. */
  font-size: clamp(1.125rem, 0.95rem + 0.9vw, 1.25rem);
  line-height: 1.2;
  letter-spacing: -0.02em;
  font-weight: 800;
  overflow-wrap: anywhere;
  hyphens: auto;
  text-wrap: balance;
}

/* Shining-Word (Muster hero-file h1 em / a6-blue.css .a6-title em — das
   „Wien"-Muster, NICHT kopiert): ein heller Gradient-Streifen oszilliert
   minimal durch die Wortmitte (background-size 200 %, Position 42 %↔58 %,
   11 s alternate — kein 250 %-Durchlauf, kein Dauer-Sweep). Angewandt auf
   die drei Schlüssel-Überschriften der Console: .hx-title (Radar-Titel),
   #exout-title (Top-10-Box), #graph-title (Aktivitäts-Graph). Bewusst
   NICHT auf .role-chip/.legend: 12-px-Typo mit background-clip:text ist
   ein Lesbarkeitsrisiko (Glyphen-Abschneidung), und die Rollen-Wörter
   sind Labels, keine Überschriften.
   Kontrast: jeder sichtbare Gradient-Pixel ≥4.5 in BEIDEN Themes —
   Hell #a63c00 5.55 / #b8490a 4.54 gegen den dunkelsten Bühnenendpunkt
   #eeeef1; Noir #b9a8ff 9.15 / #8fb8dc 5.83 / #e4dcff 14.46 gegen #100f1a
   (Token-Kommentare oben, per node nachgerechnet).
   background-clip:text frisst Unterlinien — text-decoration:none.
   Die Padding-Zugabe (Muster elementor-head-startseite.html:102-103)
   verhindert Glyphen-Abschneidung am Gradient-Rand. */
.hx-shine-word {
  background-image: var(--a6-shine-word-grad);
  background-size: 200% 100%;
  background-position: 50% 0;
  background-repeat: no-repeat;
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  -webkit-text-fill-color: transparent;
  text-decoration: none;
  padding: 0 0.09em 0.02em 0.03em;
  animation: a6-shine-word 11s var(--a6-ease) infinite alternate;
}

@keyframes a6-shine-word {
  from { background-position: 42% 0; }
  to { background-position: 58% 0; }
}

.hx-lead {
  max-inline-size: 72ch;
  color: var(--a6-body);
  font-size: 0.875rem;
  line-height: 1.55;
}

/* Tachometer-Zeile: zeigt ausschließlich bereits live gemessene Werte
   (dieselben Zähler wie die Kopfstatistik) — keine erfundenen Zahlen, keine
   duplizierten IDs (SKILL honesty: „Keine erfundenen Bewertungen,
   Projektzahlen"). */
.hx-kpis {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 0;
}

.hx-kpi {
  display: inline-flex;
  align-items: baseline;
  gap: 8px;
  padding: 6px 12px;
  background: var(--a6-surface);
  /* Eine Regel statt Doppel-Deklaration (Design-Audit, ehemals D1-Folgeblock):
     1px Rand in Brand-Tinte (--a6-card-line, Noir: Violett-Tinte) + dezenter
     Karten-Schatten — IDs (#hx-kpi-malicious/-suspect/-txs) bleiben. */
  border: 1px solid var(--a6-card-line);
  border-radius: var(--a6-r-pill);
  box-shadow: var(--a6-e1), 0 1px 2px rgba(22, 48, 92, 0.06);
}

.hx-kpi-value {
  font-family: var(--a6-mono);
  font-size: 16px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--a6-ink);
}

.hx-kpi-label {
  font-size: 12px;
  color: var(--a6-muted);
  text-transform: uppercase;
  letter-spacing: 0.08em;
}

.hx-kpi-malicious .hx-kpi-value { color: var(--a6-error); }
.hx-kpi-suspect .hx-kpi-value { color: var(--a6-warn); }
.hx-kpi-txs .hx-kpi-value { color: var(--a6-ink); }

/* ---------------- Sprachumschalter (initLangSwitcher in i18n.mjs) --------
   Zwei native Buttons EN/DE, aria-pressed spiegelt die aktuelle Sprache;
   Zielhöhe 44 px (Astra-6-Button-Contract), Stil konsistent zu .graph-tab. */
.lang-switch {
  display: inline-flex;
  gap: 6px;
  align-items: center;
}

/* Theme-Umschalter-Container (index.html #theme-switch + Pendants in
   about.html/history-host.html): identische Zeilenform wie .lang-switch —
   ein EINZIGER Button (.theme-btn, Komposition auf .lang-btn-Basis) sitzt
   darin. */
.theme-switch {
  display: inline-flex;
  gap: 6px;
  align-items: center;
}

.lang-btn {
  min-height: 44px;
  min-width: 44px;
  padding: 8px 16px;
  background: var(--a6-surface);
  color: var(--a6-body);
  border: 1px solid var(--a6-control-line);
  border-radius: var(--a6-r-pill);
  font-size: 13px;
  font-weight: 650;
  cursor: pointer;
  box-shadow: var(--a6-e1);
  transition: background-color var(--a6-fast), color var(--a6-fast), transform var(--a6-ui) var(--a6-ease);
}

/* Theme-Umschalter (initThemeSwitcher in i18n.mjs): EIN Button, gleiche Pille
   wie .lang-btn — die Zusatzklasse .theme-btn trägt nur die Identität im
   Markup/Tests, den Stil liefert die .lang-btn-Regelgruppe darüber
   (Kompositionsmuster wie .density-btn auf .graph-tab-Basis). */
.theme-btn { letter-spacing: 0.02em; }

.lang-btn[aria-pressed="true"] {
  background: var(--a6-ink-fill);
  border-color: var(--a6-ink-fill);
  color: var(--a6-on-ink);
}

@media (hover: hover) and (pointer: fine) {
  .lang-btn:not([aria-pressed="true"]):hover { background: var(--a6-surface-alt); }
}

.lang-btn:active { transform: translateY(0); }

/* ---------------- Fußzeile ---------------- */

.footbar {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
  justify-content: space-between;
  /* Gutter-Token statt fixer 26 px (Design-Audit): Fußzeile fluchtet mit
     topbar/main; vertikal aufs 4-px-Raster. */
  padding: 12px var(--a6-gutter);
  border-top: 1px solid var(--a6-line-strong);
  background: var(--a6-surface);
  color: var(--a6-muted);
  font-size: 12px;
}

#last-update { font-family: var(--a6-mono); }

/* Footer-Verweise (Info-Seite prominently, Muster .footbar): schlichte
   Text-Links in Body-Tinte, Hover auf Ink — keine zweite Button-Sprache in
   der Fußzeile. */
.foot-link {
  color: var(--a6-body);
  text-decoration: underline;
  text-decoration-color: var(--a6-line-strong);
  text-underline-offset: 3px;
}

.foot-link:hover {
  color: var(--a6-ink);
}

.foot-link:focus-visible {
  outline: 3px solid var(--a6-focus);
  outline-offset: 3px;
}

/* ---------------- Info-Seite (about.html, 2026-10-06) ----------------
   Eigenständige Seite im App-Shell-Look (Muster history-host.html: nur
   style.css, keine zweite CSS-Datei). Panels, panel-head, panel-marker und
   .graph-note kommen aus dem Bestand; hier stehen nur die Eigenanteile:
   Lead-Absätze, Begriffs-Definitionen (dl) und die Grenzen-Liste. */

.about-lead {
  max-inline-size: 72ch;
  margin: 0 0 10px;
  color: var(--a6-body);
}

/* Begriffs-Raster: Term-Spalte fix getaktet (min 11 rem, max 16 rem),
   Beschreibung folgt in der Restspalte — Präzisions-Look der Konten-Tabelle
   ohne deren Mono-Dichte; ein Aufzählungsraum unter 760 px. */
.about-defs {
  display: grid;
  grid-template-columns: minmax(11rem, 16rem) minmax(0, 1fr);
  gap: 10px 22px;
  margin: 0 0 4px;
}

.about-defs dt {
  font-weight: 650;
  color: var(--a6-ink);
  overflow-wrap: anywhere;
}

.about-defs dd {
  margin: 0;
  color: var(--a6-body);
}

/* Abschnitt innerhalb eines Panels (Muster .check-section, account-check.css):
   Haarlinien-Trennung, 14-px-Titel über Body. */
.about-subsection {
  margin-top: 14px;
  padding-top: 12px;
  border-top: 1px solid var(--a6-line);
}

.about-subsection h3 {
  margin: 0 0 10px;
  font-size: 14px;
  font-weight: 700;
  letter-spacing: 0.02em;
  color: var(--a6-ink);
}

/* Grenzen-Liste: geordnete Aufzählung mit Ink-Markern (kein Emoji-Ersatz),
   Zeilenabstand im Lesetakt. */
.about-limits {
  margin: 0;
  padding-left: 1.2rem;
  display: grid;
  gap: 8px;
  max-inline-size: 90ch;
  color: var(--a6-body);
}

.about-limits li::marker {
  color: var(--a6-ink);
}

@media (max-width: 760px) {
  .about-defs {
    grid-template-columns: minmax(0, 1fr);
    gap: 2px 0 10px;
  }

  .about-defs dt {
    margin-top: 8px;
  }

  .about-defs dt:first-child {
    margin-top: 0;
  }
}

/* ---------------- Responsiv ---------------- */

/* Konsistente Bühnen-Höhenleiter (Design-Audit 2026-10-06): gemeinsamer Token
   --a6-stage-height für #graph, #globe (globe.css) und .cluster-3d
   (drilldown.css) — 720 px (Basis) → 640 px (≤1024) → 560 px (≤900) →
   480 px (≤560); die Bühnen folgen alle derselben Leiter. */
@media (max-width: 1024px) {
  [data-a6] { --a6-stage-height: 640px; }
}

@media (max-width: 900px) {
  .stats { margin-left: 0; width: 100%; }
  .stat { flex: 1 1 100px; min-width: 96px; }
  /* Status-Chips bleiben kompakt und wachsen nicht auf KPI-Kartenbreite. */
  .stat-network,
  .stat-last { flex: 0 1 auto; }
  [data-a6] { --a6-stage-height: 560px; } /* Leiter: 720 → 640 → 560 */
  .legend { margin-left: 0; }
  .live-stats { margin-left: 0; width: 100%; }
  .live-controls { margin-left: 0; width: 100%; }
  .log-row {
    grid-template-columns: auto auto minmax(0, 1fr);
    grid-template-areas:
      "sev time ledger"
      "rule rule rule"
      "note note note"
      "addr addr addr";
  }
  .log-sev { grid-area: sev; }
  .log-time { grid-area: time; }
  .log-ledger { grid-area: ledger; justify-self: end; }
  .log-rule { grid-area: rule; }
  .log-note { grid-area: note; }
  .log-addr { grid-area: addr; }
}

@media (max-width: 760px) {
  .cluster-list { grid-template-columns: 1fr; }
  /* Top-10-Duo: mobiler Stapel unterhalb des bestehenden Breakpoints
     (Hausmuster .cluster-list) — Börsen-Box oben, Drainer-Box darunter.
     gap 0 + margin-top 0 der rechten Box: die Panel-Margins allein liefern
     den Rhythmus (22 px), ohne Verdopplung. */
  .top-duo-grid { grid-template-columns: minmax(0, 1fr); gap: 0; }
  #drainer-outflows-panel { margin: 0 auto 22px; }
}

@media (max-width: 560px) {
  main { padding: 12px; }
  .topbar { padding: 12px 14px; }
  .panel { padding: 14px; }
  .hx-stage { margin-top: 14px; }
  #exchange-outflows-panel { margin: 14px auto; } /* mobiler Rhythmus wie .hx-stage */
  #drainer-outflows-panel { margin: 0 auto 14px; } /* Stapel: obere Margin trägt die Börsen-Box allein */
  [data-a6] { --a6-stage-height: 480px; } /* Leiter-Endstufe: 720 → 640 → 560 → 480 */
  .filter-select { flex: 1 1 140px; }
  .download-btn { flex: 1 1 100%; }
  .log-list { max-height: 340px; }
}

/* ---------------- Reduced Motion & Forced Colors ---------------- */

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation: none !important;
    transition: none !important;
  }
  .graph-tab,
  .cluster-card,
  .lang-btn,
  .download-btn { transform: none !important; }

  /* Shining-Word reduced-motion: das Wort bleibt STATISCH in Akzent-Tinte
     lesbar (Muster a6-blue.css:161-166) — animation:none allein ließe den
     transparenten Text auf dem Gradient-Standbild; der Base-Fallback
     (--a6-shine-word-base = --a6-accent-text, 5.55/8.85:1) stellt die
     Vollfarbe her. */
  .hx-shine-word {
    background-image: none;
    color: var(--a6-shine-word-base);
    -webkit-text-fill-color: var(--a6-shine-word-base);
  }

  /* Das Sheen-Band steht ohne Animation als statischer Schrägstrich —
     ohne Bewegung ist es Design-Rauschen und tritt zurück. Die Hairline
     (::before) bleibt: sie ist statisch. */
  #exchange-outflows-panel::after { display: none; }
}

@media (forced-colors: active) {
  .panel,
  .stat,
  .conn,
  .block-card,
  .badge,
  .livestat,
  .log-row,
  .filter-select,
  .download-btn,
  .graph-tab,
  .cluster-card,
  .role-chip,
  .chain-node,
  .vis-tooltip,
  .lang-btn,
  .hx-stage,
  .hx-kpi,
  .feed-chart,
  .empty,
  #fh-graph,
  #graph,
  #exchange-outflows-panel {
    border: 1px solid CanvasText;
    box-shadow: none;
  }

  /* Der Radial-Glow der Hero-Bühne tritt in erzwungenen Farbschemata
     vollständig zurück (SKILL forced_colors_css: .a6-stage::before). */
  .hx-stage::before {
    display: none;
  }

  /* Frost-Schicht der Top-10-Box: Hairline und Sheen sind reine Dekoration
     und treten zurück (Muster .hx-stage::before oben); der Rand steht schon
     in der CanvasText-Liste. */
  #exchange-outflows-panel::before,
  #exchange-outflows-panel::after {
    display: none;
  }

  /* Shining-Word in erzwungenen Farben: background-clip:text mit
     transparentem Text wäre unsichtbar — zurück auf CanvasText. */
  .hx-shine-word {
    background-image: none;
    color: CanvasText;
    -webkit-text-fill-color: CanvasText;
  }
}

/* ---------------- Palantir-Operations-Layer (Redesign 2026-10-08) ----------
   Designstrategie, eine Stelle, ein Regelwerk:
   1. Graphit statt Violett: neutrale Flächen; EIN kühler Stahl-Akzent für
      Auswahl und Fokus; Farbe nur als Signal (Bedrohung, Status, Kategorie).
   2. Präzision: 1-px-Haarlinien, Radien 2–6 px, keine Pillen; Mono für Daten
      und Labels; Zahlen tabular.
   3. Sichtbare Struktur: Module nummeriert (01–05), Rastergrund, Fadenkreuz-
      Marken auf dem Lagebild.
   4. Ein Glow pro Seite (Lagebild), Shine nur auf Überschriften.
   5. Keine Bewegung auf Panels (transform würde Fixed-Overlays brechen).
   Alle Element-IDs, data-i18n-Schlüssel und Handler bleiben unverändert. */

body[data-a6] {
  background-image:
    linear-gradient(var(--a6-grid-line) 1px, transparent 1px),
    linear-gradient(90deg, var(--a6-grid-line) 1px, transparent 1px);
  background-size: 56px 56px;
  background-attachment: fixed;
}

.topbar {
  background: color-mix(in srgb, var(--a6-bg) 88%, transparent);
  backdrop-filter: blur(12px);
  border-bottom: 1px solid var(--a6-line-strong);
}

.brand h1 {
  text-transform: uppercase;
  letter-spacing: 0.14em;
  font-weight: 700;
}

.brand .sub,
.stat-label,
.hx-kpi-label,
.livestat {
  letter-spacing: 0.06em;
}

.stat-label,
.hx-kpi-label {
  font-family: var(--a6-mono);
  font-size: 11px;
  text-transform: uppercase;
}

.view-nav {
  counter-reset: hp-module;
  gap: 0;
  padding-top: 0;
  border-bottom: 1px solid var(--a6-line);
  margin-top: 0;
}

.view-nav .view-tab {
  counter-increment: hp-module;
  min-height: 48px;
  padding: 12px 18px;
  background: transparent;
  border: 0;
  border-radius: 0;
  box-shadow: none;
  color: var(--a6-muted);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.view-nav .view-tab::before {
  content: "0" counter(hp-module);
  margin-right: 10px;
  font-family: var(--a6-mono);
  font-size: 11px;
  font-weight: 400;
  color: var(--a6-accent-text);
  opacity: 0.7;
}

.view-nav .view-tab:hover { color: var(--a6-ink); background: var(--a6-surface); }

.view-nav .view-tab[aria-selected="true"],
.view-nav .view-tab[aria-current="page"] {
  color: var(--a6-ink);
  background: transparent;
  box-shadow: inset 0 -2px 0 var(--a6-accent);
}

.panel {
  border-radius: var(--a6-r-md);
  border-color: var(--a6-line);
  box-shadow: none;
}

.panel-head {
  border-bottom-color: var(--a6-line);
}

.panel-head h2 {
  font-size: 13px;
  font-weight: 650;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.hx-stage {
  border-radius: var(--a6-r-md);
  box-shadow: var(--a6-e3);
}

.hx-stage::after {
  content: "";
  position: absolute;
  inset: 8px;
  pointer-events: none;
  background:
    linear-gradient(var(--a6-accent), var(--a6-accent)) left top / 14px 1px no-repeat,
    linear-gradient(var(--a6-accent), var(--a6-accent)) left top / 1px 14px no-repeat,
    linear-gradient(var(--a6-accent), var(--a6-accent)) right top / 14px 1px no-repeat,
    linear-gradient(var(--a6-accent), var(--a6-accent)) right top / 1px 14px no-repeat,
    linear-gradient(var(--a6-accent), var(--a6-accent)) left bottom / 14px 1px no-repeat,
    linear-gradient(var(--a6-accent), var(--a6-accent)) left bottom / 1px 14px no-repeat,
    linear-gradient(var(--a6-accent), var(--a6-accent)) right bottom / 14px 1px no-repeat,
    linear-gradient(var(--a6-accent), var(--a6-accent)) right bottom / 1px 14px no-repeat;
  opacity: 0.6;
}

.graph-tab,
.download-btn {
  letter-spacing: 0.02em;
}

.hx-kpi {
  border-color: var(--a6-line-strong);
  box-shadow: none;
}

@media (prefers-reduced-motion: reduce) {
  .view-nav .view-tab { transition: none; }
}

.graph-tab[aria-selected="true"],
.graph-tab[aria-pressed="true"],
.density-btn[aria-pressed="true"],
.exout-window-btn[aria-pressed="true"],
.drout-window-btn[aria-pressed="true"],
.lang-btn[aria-pressed="true"],
.lang-switch button[aria-pressed="true"],
.theme-switch button[aria-pressed="true"] {
  background: color-mix(in srgb, var(--a6-accent) 18%, var(--a6-surface));
  border-color: var(--a6-accent);
  color: var(--a6-ink);
}

.stat-malicious,
.stat-suspect,
.stat-events {
  border-color: var(--a6-line-strong);
}

.stat-malicious { box-shadow: inset 0 2px 0 var(--a6-error); }
.stat-suspect { box-shadow: inset 0 2px 0 var(--a6-warn); }
.stat-events { box-shadow: inset 0 2px 0 var(--a6-accent); }

@media (max-width: 760px) {
  .stats {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    width: 100%;
    margin-left: 0;
  }
  .stat { min-width: 0; }
  .stat-network { grid-column: 1 / -1; }
}

/* ---------------- Sidebar-Shell (Redesign 2026-10-08, gr8r-Muster) ----------
   Ab 900 px wird die Modul-Navigation zur fixierten Glas-Sidebar links; der
   Seiteninhalt rückt per body-Padding auf. Darunter bleibt die Leiste oben
   (mobil ohne Hover-Abhängigkeit). Gilt identisch für index, about und
   history-host, weil alle drei dieselbe .view-nav-Struktur tragen. Die
   "liquid"-Wirkung ist rein CSS: Glas-Füllung mit Blur, weiche Akzentlichter
   in den Ecken, Auswahl als Verlauf mit Akzent-Kante. */
@media (min-width: 900px) {
  body[data-a6] {
    padding-left: 248px;
  }

  .view-nav {
    position: fixed;
    inset: 0 auto 0 0;
    width: 248px;
    height: 100vh;
    height: 100dvh;
    margin: 0;
    max-width: none;
    padding: 22px 14px;
    flex-direction: column;
    flex-wrap: nowrap;
    gap: 4px;
    overflow-y: auto;
    overflow-x: hidden;
    border-bottom: 0;
    border-right: 1px solid var(--a6-line-strong);
    background: color-mix(in srgb, var(--a6-surface) 74%, transparent);
    backdrop-filter: blur(22px) saturate(140%);
    -webkit-backdrop-filter: blur(22px) saturate(140%);
    z-index: 20;
    isolation: isolate;
  }

  .view-nav::before {
    content: "";
    position: absolute;
    inset: 0;
    z-index: -1;
    pointer-events: none;
    background:
      radial-gradient(180px 220px at 0% 0%, color-mix(in srgb, var(--a6-accent) 16%, transparent), transparent 70%),
      radial-gradient(200px 240px at 100% 100%, color-mix(in srgb, var(--a6-brand-teal) 10%, transparent), transparent 70%);
  }

  .view-nav .view-tab {
    width: 100%;
    justify-content: flex-start;
    min-height: 44px;
    padding: 10px 12px;
    border-radius: var(--a6-r-md);
    transition: background-color var(--a6-fast) var(--a6-ease), color var(--a6-fast) var(--a6-ease);
  }

  .view-nav .view-tab:hover {
    background: color-mix(in srgb, var(--a6-accent) 10%, transparent);
  }

  .view-nav .view-tab[aria-selected="true"],
  .view-nav .view-tab[aria-current="page"] {
    background: linear-gradient(90deg, color-mix(in srgb, var(--a6-accent) 24%, transparent), transparent 92%);
    box-shadow: inset 2px 0 0 var(--a6-accent);
  }

  .topbar,
  .hx-stage,
  main,
  .footbar {
    min-width: 0;
  }
}

@media (min-width: 900px) and (prefers-reduced-motion: reduce) {
  .view-nav .view-tab { transition: none; }
}

/* ---------------- Live-Ticker + Muster-Monitor (Redesign 2026-10-08) -------
   Ticker neben Globe/Graph (ab 1200 px Spalte, darunter gestapelt); beide
   Module nutzen ausschließlich Tokens, keine eigenen Farben. */
.graph-live {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 14px;
}
.graph-live-view { min-width: 0; }
@media (min-width: 1200px) {
  .graph-live { grid-template-columns: minmax(0, 1fr) 300px; }
}

.drain-ticker {
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-width: 0;
  max-height: var(--a6-stage-height);
  padding: 12px;
  background: var(--a6-surface);
  border: 1px solid var(--a6-line-strong);
  border-radius: var(--a6-r-md);
  overflow: hidden;
}
.drain-ticker h3 {
  margin: 0;
  font-size: 12px;
  font-weight: 650;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.drain-ticker .hint { margin: 0; font-size: 12px; color: var(--a6-muted); }

.ticker-list {
  list-style: none;
  margin: 0;
  padding: 0;
  flex: 1;
  min-height: 0;
  overflow: auto;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.tk-row {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 2px 10px;
  padding: 7px 9px;
  border: 1px solid var(--a6-line);
  border-radius: var(--a6-r-sm);
  font-size: 12px;
}
.tk-time { font-family: var(--a6-mono); font-size: 11px; color: var(--a6-muted); }
.tk-rule { font-weight: 600; color: var(--a6-ink); text-align: right; }
.tk-addr { grid-column: 1 / -1; font-family: var(--a6-mono); font-size: 11px; color: var(--a6-body); overflow-wrap: anywhere; }
.tk-note { grid-column: 1 / -1; font-size: 11.5px; color: var(--a6-muted); }
.tk-malicious { border-color: var(--a6-sev-malicious); }
.tk-suspect { border-color: var(--a6-sev-suspect); }

.pattern-chart svg {
  display: block;
  width: 100%;
  height: 120px;
  background: var(--a6-graph-canvas);
  border: 1px solid var(--a6-line);
  border-radius: var(--a6-r-sm);
}
.pm-line {
  fill: none;
  stroke: var(--a6-accent);
  stroke-width: 1;
  vector-effect: non-scaling-stroke;
}
.pm-mark { fill: var(--a6-surface); stroke-width: 1.2; vector-effect: non-scaling-stroke; }
.pm-mark-load { stroke: var(--a6-sev-malicious); }
.pm-mark-gap { stroke: var(--a6-sev-suspect); }
.pm-mark-burst { stroke: var(--a6-sev-info); }

.pattern-list {
  list-style: none;
  margin: 10px 0 0;
  padding: 0;
  display: grid;
  gap: 4px;
}
.pm-row {
  display: grid;
  grid-template-columns: 170px 130px 90px 1fr;
  gap: 10px;
  padding: 6px 8px;
  border: 1px solid var(--a6-line);
  border-radius: var(--a6-r-sm);
  font-family: var(--a6-mono);
  font-size: 12px;
}
@media (max-width: 700px) {
  .pm-row { grid-template-columns: 1fr 1fr; }
}

.tk-name {
  margin-left: 6px;
  padding: 1px 6px;
  border: 1px solid var(--a6-line-strong);
  border-radius: var(--a6-r-xs);
  font-family: var(--a6-font);
  font-size: 11px;
  color: var(--a6-brand-blue);
}

/* Validator-Panel: Watchlist-Zeilen und Alarme nach Ampel-Tokens */
.vl-list, .vl-alerts { list-style: none; margin: 0; padding: 0; display: grid; gap: 4px; }
.vl-row {
  display: grid;
  grid-template-columns: minmax(120px, 1.4fr) minmax(90px, 1fr) repeat(4, minmax(90px, 1fr));
  gap: 8px 14px;
  align-items: baseline;
  padding: 7px 9px;
  border: 1px solid var(--a6-line);
  border-radius: var(--a6-r-sm);
  font-size: 12px;
}
.vl-row small { display: block; font-size: 10px; letter-spacing: 0.08em; text-transform: uppercase; color: var(--a6-muted); font-family: var(--a6-mono); }
.vl-domain { font-weight: 600; color: var(--a6-ink); overflow-wrap: anywhere; }
.vl-key { font-family: var(--a6-mono); font-size: 11px; color: var(--a6-muted); }
.vl-metric { font-family: var(--a6-mono); font-variant-numeric: tabular-nums; color: var(--a6-ink); }
.vl-ok { border-color: var(--a6-line); }
.vl-warn { border-color: var(--a6-sev-suspect); }
.vl-bad { border-color: var(--a6-sev-malicious); }
.vl-alerts { margin-top: 10px; }
.vl-alert {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  padding: 6px 9px;
  border: 1px solid var(--a6-line);
  border-radius: var(--a6-r-sm);
  font-size: 12px;
}
.vl-alert.vl-bad { border-color: var(--a6-sev-malicious); }
.vl-alert.vl-warn { border-color: var(--a6-sev-suspect); }
.vl-attr { margin: 10px 0 0; font-size: 11px; }
@media (max-width: 760px) {
  .vl-row { grid-template-columns: 1fr 1fr; }
}

.vl-cov { margin: 0 0 8px; font-size: 12px; color: var(--a6-muted); font-family: var(--a6-mono); }

.pm-rule { grid-column: 1 / -1; font-family: var(--a6-font); font-size: 11px; color: var(--a6-muted); }

/* Validator-Trace-Overlay: Dialog über dem Dashboard, Ampelfarben aus Tokens, 3D nur per CSS. */
.vt-overlay { position: fixed; inset: 0; z-index: 60; display: grid; place-items: center; padding: 18px; background: color-mix(in srgb, var(--a6-bg) 72%, transparent); backdrop-filter: blur(6px); }
.vt-panel { width: min(1040px, 100%); max-height: 92vh; overflow: auto; background: var(--a6-surface); border: 1px solid var(--a6-line-strong); border-radius: var(--a6-r-md); padding: 18px; box-shadow: var(--a6-e3); }
.vt-head { display: flex; justify-content: space-between; align-items: center; gap: 12px; border-bottom: 1px solid var(--a6-line); padding-bottom: 10px; }
.vt-head h2 { margin: 0; font-size: 15px; letter-spacing: 0.06em; text-transform: uppercase; overflow-wrap: anywhere; }
.vt-close { min-height: 40px; padding: 6px 14px; background: transparent; color: var(--a6-ink); border: 1px solid var(--a6-control-line); border-radius: var(--a6-r-sm); cursor: pointer; }
.vt-tabs { display: flex; gap: 6px; margin: 12px 0; flex-wrap: wrap; }
.vt-tab { min-height: 40px; padding: 6px 12px; background: transparent; color: var(--a6-body); border: 1px solid var(--a6-control-line); border-radius: var(--a6-r-sm); cursor: pointer; font-size: 12px; }
.vt-tab[aria-pressed="true"] { border-color: var(--a6-accent); color: var(--a6-ink); background: color-mix(in srgb, var(--a6-accent) 18%, var(--a6-surface)); }
.vt-cards { display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); gap: 8px; }
.vt-card { padding: 10px; border: 1px solid var(--a6-line); border-radius: var(--a6-r-sm); }
.vt-card small { display: block; font-family: var(--a6-mono); font-size: 10px; text-transform: uppercase; color: var(--a6-muted); }
.vt-card b { font-family: var(--a6-mono); font-size: 20px; font-variant-numeric: tabular-nums; }
.vt-card.vt-missed { border-color: var(--a6-sev-suspect); }
.vt-card.vt-wrong-hash, .vt-card.vt-partial { border-color: var(--a6-sev-malicious); }
.vt-h { margin: 18px 0 8px; font-size: 12px; letter-spacing: 0.08em; text-transform: uppercase; color: var(--a6-muted); font-weight: 650; }
.vt-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.vt-chip { font-size: 11px; padding: 3px 8px; border: 1px solid var(--a6-line-strong); border-radius: var(--a6-r-xs); font-family: var(--a6-mono); }
.vt-timeline { list-style: none; margin: 0; padding: 0; display: grid; gap: 4px; }
/* vt-ev ist zwei Dinge: eine Timeline-Zeile mit vier Spalten und ein Belegtext in einer
   Hypothese. Ohne Trennung bekam der Belegtext das Spaltenraster der Timeline - seine
   Zeile stand in einer 180-px-Spalte, während 763 px daneben leer blieben. */
.vt-timeline .vt-ev { display: grid; grid-template-columns: 180px 150px 130px 1fr; gap: 8px; padding: 6px 8px; border: 1px solid var(--a6-line); border-radius: var(--a6-r-sm); font-size: 12px; font-family: var(--a6-mono); }
.vt-timeline .vt-ev.vt-missed { border-color: var(--a6-sev-suspect); }
.vt-timeline .vt-ev.vt-wrong-hash, .vt-timeline .vt-ev.vt-partial { border-color: var(--a6-sev-malicious); }
.vt-reasons { display: flex; flex-wrap: wrap; gap: 4px; }
.vt-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 4px; }
.vt-row, .vt-rec { display: flex; justify-content: space-between; gap: 10px; padding: 6px 8px; border: 1px solid var(--a6-line); border-radius: var(--a6-r-sm); font-size: 12px; font-family: var(--a6-mono); }
.vt-key { color: var(--a6-muted); }
.vt-empty { color: var(--a6-muted); font-size: 13px; }
.vt-note { margin: 14px 0 0; font-size: 11px; color: var(--a6-muted); }
.vt-key-full { margin: 0 0 6px; font-size: 11px; color: var(--a6-muted); word-break: break-all; }
.vt-key-full small, .vt-ref small, .vt-coverage small, .vt-scope small { color: var(--a6-muted); text-transform: uppercase; letter-spacing: .06em; margin-right: 6px; font-size: 10px; }
.vt-ref, .vt-coverage, .vt-scope { margin: 0 0 6px; font-size: 12px; color: var(--a6-ink); }
/* Ground-Truth-Zeile: das Urteil ueber die eigene Erfassung, nicht ueber den Knoten.
   Richtung der Gegenprobe als Vollrahmen-Farbe (Design P2: kein Linksbändchen). */
.vt-truth { margin: 0 0 8px; font-size: 12px; line-height: 1.45; padding: 7px 10px; border: 1px solid var(--a6-control-line); border-radius: var(--a6-r-md); color: var(--a6-body); }
.vt-truth small { color: var(--a6-muted); text-transform: uppercase; letter-spacing: .06em; font-size: 10px; }
.vt-truth-contradicted { border-color: var(--a6-error); background: var(--a6-error-soft); }
.vt-truth-consistent { border-color: var(--a6-success); background: var(--a6-success-soft); }
.vt-truth-none, .vt-truth-not-comparable { border-style: dashed; background: var(--a6-surface-alt); }
.vt-attr { margin: 0 0 10px; font-size: 12px; line-height: 1.45; padding: 7px 10px; border: 1px solid var(--a6-control-line); border-radius: var(--a6-r-md); color: var(--a6-body); }
.vt-attr small { color: var(--a6-muted); text-transform: uppercase; letter-spacing: .06em; font-size: 10px; }
.vt-attr-contradicted { border-color: var(--a6-error); background: var(--a6-error-soft); }
.vt-attr-covered { border-color: var(--a6-success); background: var(--a6-success-soft); }
.vt-attr-open { border-style: dashed; background: var(--a6-surface-alt); }
/* Beobachtungsliste: ein Urteil ueber ein Fenster, nicht ueber einen Tag. quiet ist die
 * einzige Fuellung mit Erfolgsgruen; partial-coverage und not-evaluated sind gestrichelt,
 * weil sie eine offene Frage sind, keine Entlastung. */
.vt-watch { margin: 0 0 10px; padding: 8px 10px; border: 1px solid var(--a6-control-line); border-radius: var(--a6-r-md); color: var(--a6-body); font-size: 12px; line-height: 1.45; }
.vt-watch h3 { margin: 0 0 4px; font-size: 10px; letter-spacing: .06em; text-transform: uppercase; color: var(--a6-muted); font-family: var(--a6-mono); font-weight: 600; }
.vt-watch p { margin: 0 0 4px; }
.vt-watch small { color: var(--a6-muted); font-family: var(--a6-mono); font-size: 11px; }
.vt-watch-rows { margin: 4px 0 0; padding: 0; list-style: none; display: grid; gap: 3px; font-size: 11px; }
.vt-watch-ledger { font-family: var(--a6-mono); font-variant-numeric: tabular-nums; color: var(--a6-ink); }
.vt-watch-new-cohort { border-color: var(--a6-error); background: var(--a6-error-soft); }
.vt-watch-new-incidents { border-color: var(--a6-warn); background: var(--a6-warn-soft); }
.vt-watch-quiet { border-color: var(--a6-success); background: var(--a6-success-soft); }
.vt-watch-partial-coverage, .vt-watch-not-evaluated, .vt-watch-no-coverage, .vt-watch-bad-entry, .vt-watch-error { border-style: dashed; background: var(--a6-surface-alt); }
.vt-watch-first { border-color: var(--a6-info); background: var(--a6-info-soft); }
.vt-scope { color: var(--a6-muted); }
[data-master] { cursor: pointer; }
[data-master]:focus-visible { outline: 3px solid var(--a6-focus); outline-offset: 3px; }
@media (max-width: 760px) { .vt-cards { grid-template-columns: repeat(2, minmax(0, 1fr)); } .vt-timeline .vt-ev { grid-template-columns: 1fr 1fr; } .vt-note.vt-table-hint { display: block; } .vt-table { font-size: 10px; } .vt-table th, .vt-table td { padding: 5px 6px; } }
/* Incident-Profil (3D): ein Prisma je Zeitintervall.
   Geometrie ist abgeleitet statt paarweise gesetzt: --skew = 45deg, --d = max. Tiefe des
   Profils, --dz = --d * tan 45deg (1) = Höhenversatz dieser Tiefe. Die Tiefenfläche ist
   --dc breit und mit -skew geschert (Höhenversatz bei x = --dc: dc * tan 45deg = --dzc). Die
   Deckfläche ist --dzc hoch und mit (skew - 90deg) geschert (Breitenversatz bei y = --dzc:
   dzc * cot 45deg = dc). Beide Kanten treffen sich deshalb bei jeder Breite exakt; ein
   Wertepaar wie 15/13 wäre nur für eine einzige Breite geschlossen und öffnet das Prisma um
   Bruchteile eines Pixels.

   Warum 45deg und nicht 51deg (gemessen 2026-10-16 im ausgelieferten Overlay, Content 961 px,
   24-h-Fenster, peak 50, Zelle 30,87 px): eine waagerechte Deckflaeche kann in einer Ansicht von
   oben nie hoeher sein als sie tief ist. Der Hoehenversatz der Tiefe --dc ist --dc * tan(--skew),
   und genau dieser Versatz ist die Hoehe der Deckflaeche. Bei 51deg (tan 1,2349) war die Kappe
   8,38 px hoch an einer Tiefe von 6,78 px - die Kappe war hoeher als die Tiefe breit, also ein
   Koerper, den keine Ansicht von oben zeigen kann. Das, nicht die Zellenbreite, ist der Grund
   des schraegen Eindrucks. 45deg ist die obere Grenze einer gueltigen Ansicht und zugleich die
   kanonische Kavaliere-Projektion: Kappe 6,79 px = Tiefe 6,79 px an 24,07 px Vorderflaeche,
   28 % Scheranteil. Der Scheranteil (Scherflaeche / Vorderflaeche) haengt an --d / --col-w
   (0,22 -> 28 %) und nicht am Winkel; der Winkel entscheidet nur Kappenhoehe und Rampenneigung.
   Verworfen: 36deg bei 0,30 * Zelle (Tiefenflaeche 9,26 px an 21,61 px Vorderflaeche - 43 %
   Scheranteil, die Scherflaeche lehnte 54deg aus der Lotrechten, das Feld las sich als nach
   rechts kippende Nadeln, und die Kappe von 6,72 px war zu niedrig, um als Flaeche gelesen zu
   werden) und 30deg (Kappe 3,92 px - deutlich unter dieser Grenze).

   Prisma in eigener Zelle (--dc <= 0,22 * --col-w): die Vorderfläche ist --col-w - --dc
   breit, die Tiefenfläche füllt den Rest derselben Zelle (left: 100% der Vorderfläche,
   Breite --dc), die Deckfläche schert um genau --dc nach rechts und endet an der
   Zellenkante. Ein Prisma ragt damit nie in die Nachbarspalte - die frühere Klammer
   --dc <= --gap war nur nötig, weil die Tiefenfläche außerhalb der Säule lag
   (left: 100% der Säule) und in den Abstand hineinragte.

   Warum die Zelle breiter und der Abstand kleiner sein muss (gemessen 2026-10-16 live im
   ausgelieferten Overlay, 7d-Fenster: Aussenbreite 987 px = Content 961 px, Plot 909,48 px,
   Abstand 11,04 px, Säule 27,31 px, Tiefe 8,16 px, Deckfläche 7,09 px): die Zelle war nur
   2,5-mal so breit wie ihr Abstand, und die Tiefenfläche war eine 8-px-Scheibe an einer
   27-px-Säule - das Profil las sich als Kamm aus dünnen Stäben, nicht als Säulenfeld.
   Neu: --gap 7,69 px, --col-w 30,86 px, --dc 6,79 px, Vorderfläche 24,07 px. Dieselbe
   Projektion, dieselbe Achse - aber als Körper lesbar, weil die Zelle 4-mal so breit ist
   wie der Abstand und gut ein Fünftel davon Tiefe zeigt.

   Tiefe ist je Profil konstant: --dc = --d für jede Säule, auch für eine mit einem einzigen
   Vorfall. Die begrenzte Fassung --dc = min(--d, --bh) machte die Säulen unterschiedlich
   dick - gemessen 2026-10-16 im 24-h-Fenster (peak 50) 10,48 px Tiefenfläche an der
   7-Vorfall-Säule gegen 3,52 px an der 1-Vorfall-Säule. Dieselbe Projektion, zwei
   Körperbreiten nebeneinander: das Profil las sich schräg statt als Säulenfeld.

   Unter jeder Säule steht dieselbe Bodenplatte (--base-h, volle Zellenbreite, Farbe des
   Intervall-Zustands). Vorher war der Grundriss je Säule verschieden hoch (6,81 px unter
   einer 5-Vorfall-Säule gegen 4 px unter einer leeren) und ragte seitlich unter der
   Tiefenfläche hervor - ein angeschnittener Sockel statt einer Ebene. Jetzt ist die
   Null-Linie für alle 24 Intervalle dieselbe Kante, und ein leeres Intervall ist eine
   Platte ohne Körper statt einer Halbkachel.

   --col-w ist nachgerechnet, nicht gesetzt: .vt-plot zieht links --gutter und rechts 3px ab,
   .vt-bars teilt den Rest in 24 Säulen und 23 Abstand. Kontrolle 2026-10-16 (Content 961 px,
   altes --gutter 40,36 px, altes --d 8,16 px, altes --gap 11,05 px):
   (961 - 40,36 - 11,16 - 254,18) / 24 = 27,30 px - gegen gemessene 27,3 px Säulenbreite.
   Mit dem neuen --gap 7,69 px: 30,86 px. Invariante --dc <= 0,22 * --col-w gilt im Band,
   in dem die Tiefe aus der Zelle kommt. Nachgerechnet über c = Content in px
   (gap = clamp(4, 0,008c, 11), gutter = clamp(20, 0,042c, 42)):
   col-w = (0,774c - 3) / 24 für 476 <= c <= 1000 und col-w = (0,816c - 45) / 24 für
   1000 <= c <= 1375. Die 4-px-Untergrenze greift unter c = 568 px (col-w = 18,19 px),
   der 8,5-px-Deckel über c = 1192 px (col-w = 38,65 px). Im Mittelband ist die Vorderfläche
   genau 78 % der Zelle; unterhalb davon ist die Tiefe die Untergrenze und die Vorderfläche
   wird schmaler - bei 460 px Content: Zelle 14,38 px, Tiefe 4 px, Vorderfläche 10,38 px (72 %).
   Unter 115 px Content trägt der Raster nicht - 24 Säulen und 23 * 4 px Abstand brauchen
   115 px ohne jede Säulenbreite; der schmalste real gemessene Container sind 260 px Content
   (390 px Viewport, 286 px Aussenbreite), und unter 380 px Content geht das Profil auf eine
   flache Ebene zurück (Container-Query unten).

   Beleuchtung (Licht von oben): drei getrennte Toene, nicht drei Ueberlagerungen
   derselben Farbe: Vorderflaeche die Typ-Farbfolge, Deckflaeche dieselbe Farbe mit 16-18 %
   Weiss, Tiefenflaeche sie mit 38-42 % Schwarz. Die Schleier und die Kante sind Theme-Werte
   (--a6-face-shade / --a6-face-sheen / --a6-face-edge), keine Konstanten im Profil: auf
   #101317 traegt jede Flaeche eine helle Kante, weil eine dunkle Kante im Noir-Theme die
   drei Flaechen nicht mehr trennte. Die alten Schleier 45 % Weiss und 55 % Schwarz wuschen
   die Typ-Farbe auf der Deckflaeche aus (bernsteinfarbene missed-Saeulen endeten als graues
   Blech) und machten die Tiefenflaeche zu einem schwarzen Keil.
   Die Saulentiefe --d ist eine Rendering-Groesse mit einer Achsen-Grenze:
   min(max(4 px, 0,22 * Zellenbreite), 8,5 px, Plot-Hoehe * --dmax). Die ersten beiden Grenzen
   geben das Volumen - die Tiefe haengt an der Zelle, nicht an einem Datenwert, und alle
   24 Intervalle behalten dieselbe Projektion. Die dritte Grenze ist die Geometrie selbst:
   die Kappe ist --dc * tan(45deg) hoch, also passt sie nur auf einen Koerper, der mindestens
   so hoch ist. --dmax kommt aus profile3d und ist (kurzeste gezeichnete Saeulenhoehe /
   Achsenmaximum) / 1 - als Anteil der Plot-Hoehe, damit die kleinere Mobile-Plot-Hoehe
   mitgeht.
   Der fruehere Daten-Deckel (--dcap = kurzste Saeule / Plot-Hoehe, ohne tan) hing an einer
   linearen Achse bis peak 50: im live gezeichneten 24-h-Profil (Plot 176 px) sank die Tiefe auf
   3,52 px und die Kappe auf 2,28 px - alle Saeulen flach, ein Prisma als Haarlinie. Die
   Wurzel-Achse ab peak 25 macht denselben Fall gut: 1 Vorfall ist dort 24,89 px hoch, die
   Grenze 24,89 px, die Tiefe bleibt bei vollen 6,79 px. Bei peak 5000 (7 Tage, 4.502 Vorfaelle
   in einem Intervall) ist die kurzeste Saeule 2,49 px und die Grenze 2,49 px - dort wird das
   Profil duenn, weil die Daten duenn sind. Eine Untergrenze an der
   Saeulenhoehe selbst (max(--dzc, ...) auf --bh) waere die Alternative gewesen: sie hebt den
   Balken ueber seinen Achsenwert und macht eine Skala unlesbar, die vergleichen soll.
   Kein WebGL, keine neue Abhaengigkeit.
   Balkenhöhe und Achse teilen sich ein Maß: --u = Plot-Höhe / Achsenmaximum, ein Balken mit
   --n Vorfällen ist deshalb zwingend so hoch, wie die Gitterlinie bei n sagt.
   Kein WebGL, keine neue Abhängigkeit. */
.vt-profile { container-type: inline-size; --skew: 45deg; --dz-k: 1; --dc: var(--d); --dzc: calc(var(--d) * var(--dz-k)); --gap: clamp(4px, 0.8cqw, 11px); --gutter: clamp(20px, 4.2cqw, 42px); --plot-h: 208px; --col-w: calc((100cqw - var(--gutter) - 3px - 23 * var(--gap)) / 24); --d: min(max(4px, calc(var(--col-w) * 0.22)), 8.5px, calc(var(--plot-h) * var(--dmax, 1))); --dz: calc(var(--d) * var(--dz-k)); --u: calc(var(--plot-h) / var(--peak, 1)); --vt-missed: var(--a6-sev-suspect); --vt-partial: var(--a6-sev-info); --vt-wrongHash: var(--a6-sev-malicious); --vt-voteNotObserved: var(--a6-sev-neutral); --vt-noQuorum: var(--a6-sev-quorum); --vt-shade: var(--a6-face-shade); --vt-sheen: var(--a6-face-sheen); --vt-edge: var(--a6-face-edge); --vt-seam: var(--a6-face-seam); --vt-edge-side: color-mix(in srgb, var(--a6-face-edge) 55%, transparent); position: relative; border: 1px solid var(--a6-line); border-radius: var(--a6-r-sm); background: var(--a6-graph-canvas); padding: 10px 12px 8px; }
.vt-profile-top { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 8px; margin: 0 0 10px; }
.vt-profile-meta, .vt-readout { margin: 0; font-family: var(--a6-mono); font-size: 11px; color: var(--a6-muted); }
.vt-readout { color: var(--a6-ink); }
.vt-profile { --base-h: 6px; --label-h: 15px; }
.vt-plot { position: relative; height: calc(var(--plot-h) + var(--dz) + var(--base-h) + var(--label-h)); margin: 0 3px 0 var(--gutter); }
.vt-grid, .vt-bars { position: absolute; left: 0; right: 0; bottom: var(--base-h); height: var(--plot-h); }
.vt-gridline { position: absolute; left: 0; right: 0; height: 1px; background: var(--a6-graph-grid); }
/* Null-Linie = Groundline: Oberkante der Bodenplatte, auf der alle Prismensohlen sitzen.
   Eine Säule ohne sichtbaren Boden wirkt schwebend, deshalb eine Stufe stärker als die
   Gitterlinien. */
.vt-gridline.vt-zero { background: var(--a6-graph-line); }
.vt-gridlabel { position: absolute; right: calc(100% + 6px); top: -6px; font-family: var(--a6-mono); font-size: 10px; color: var(--a6-muted); }
.vt-bars { display: flex; align-items: stretch; gap: var(--gap); }
@keyframes vt-rise { from { opacity: 0; transform: translateY(7px); } to { opacity: 1; transform: none; } }
.vt-col { position: relative; flex: 1 1 0; min-width: 0; height: 100%; animation: vt-rise 300ms ease-out backwards; animation-delay: calc(var(--i, 0) * 16ms); }
/* Bodenplatte = Grundfläche des Intervalls: dieselbe Grundfläche wie das Prisma darüber
   (Zellenbreite minus Tiefe --dc), konstante Höhe, eine Platte
   unter jeder Säule (sie ragt in den Plattenstreifen unterhalb der Null-Linie). Sie trägt
   den Intervall-Zustand und ist in jeder Säule gleich sichtbar - nicht nur dort, wo kein
   Balken steht. Zustände: clean (beobachtet, keine Abweichung), solo / cohort / network
   (Abweichung, nach Zahl betroffener dUNL-Validatoren), unknown (Abweichung, Kohorte nicht
   erfasst), none (kein Ledger aufgezeichnet). Ein leeres Intervall bleibt als Platte stehen,
   damit die Zeitachse sich durchgehend liest. */
/* Der Zustand ist ein durchgehendes Band, nicht 24 gefasste Platten. Je Saule ein eigener
   Rundum-Rand bei 24,08 px Platte und 14,46 px Loch (Abstand 7,68 px + Tiefe 6,78 px) malt 24
   Fliesen mit 23 Fugen unter eine durchgehende schere Deckflaeche - das liest sich als Kante
   um jede Saule und unten als Zacken. Die Platte fuellt jetzt die ganze Zelle und traegt nur
   eine Haarlinie oben; die Grundflaeche selbst ist der eine Koerper .vt-floor / .vt-floor-top. */
.vt-base { position: absolute; left: 0; right: 0; bottom: calc(-1 * var(--base-h)); height: var(--base-h); background: color-mix(in srgb, var(--a6-graph-grid) 80%, transparent); box-shadow: inset 0 1px 0 var(--a6-line); }
.vt-col.vt-state-clean .vt-base { background: var(--a6-success-soft); box-shadow: inset 0 1px 0 color-mix(in srgb, var(--a6-success) 70%, transparent); }
.vt-col.vt-state-solo .vt-base { background: color-mix(in srgb, var(--a6-ink) 12%, transparent); box-shadow: inset 0 1px 0 var(--a6-line-strong); }
.vt-col.vt-state-cohort .vt-base { background: var(--a6-accent-soft); box-shadow: inset 0 1px 0 var(--a6-accent); }
.vt-col.vt-state-network .vt-base { background: color-mix(in srgb, var(--a6-accent) 45%, transparent); box-shadow: inset 0 1px 0 var(--a6-accent); }
.vt-col.vt-state-unknown .vt-base { background: repeating-linear-gradient(135deg, transparent 0 3px, var(--a6-muted) 3px 4px); box-shadow: inset 0 1px 0 var(--a6-line); }
.vt-col.vt-state-none .vt-base { background: repeating-linear-gradient(135deg, transparent 0 3px, var(--a6-line) 3px 4px); box-shadow: none; opacity: 0.6; }
/* Die Bodenplatte ist eine Ebene, kein Streifen: sie traegt dieselbe Deckflaeche wie die Prismen
   (Hoehe --dzc, geschert um --skew) und dieselbe Tiefe --dc. Ohne sie hat die Scherung keinen
   Grund, auf dem sie steht - dann ist die Tiefe ein Kippen der ganzen Grafik, kein Volumen, und
   die saemtlichen Kappen sind gleich grosse Flecken ueber unterschiedlich hohen Koerpern. Mit ihr
   sitzt jede Saeule auf einem Koerper, und die Luecken zwischen den Saeulen zeigen, wie weit die
   Ebene nach hinten reicht. Die Zustand-Farbe der Ebene ist dieselbe wie die des Streifens. */
/* Die Grundebene ist EIN Koerper, nicht 24 Platten. Der fruehere Zustand gab je Saule eine
   eigene Scherflaeche - und nur dort, wo ein Koerper stand. Im 24-h-Fenster (6 Saulen, 18 leere
   Intervalle) standen 6 Sockel mit 6,78 px Scherung neben 18 Sockeln ohne Scherung: der Fuss
   wechselte seine Form von Intervall zu Intervall, und das Auge liest das als Kippung der
   ganzen Grafik - der "schrage" Eindruck, nicht als Volumen. Eine durchgehende Ebene
   (Vorderband --base-h, Deckflaeche --dzc, ueber die ganze Plot-Breite, dieselbe Tiefe --dc
   wie jedes Prisma) gibt der Scherung der Kappen erst den Grund, auf dem sie steht: alle 24
   Intervalle liegen auf derselben Flaeche, die Luecken zeigen, wie weit die Ebene nach hinten
   reicht, und die Saulen stehen aufrecht darauf. Die Zustand-Farbe bleibt je Intervall am
   Vorderband; die Deckflaeche ist neutral und wird von den Balken verdeckt, wo ein Koerper
   steht. */
.vt-floor { position: absolute; left: 0; right: var(--dc); bottom: 0; height: var(--base-h); background: color-mix(in srgb, var(--a6-ink) 8%, transparent); box-shadow: inset 0 0 0 1px var(--a6-line-strong); }
.vt-floor-top { position: absolute; left: 0; right: var(--dc); bottom: var(--base-h); height: var(--dzc); transform-origin: 0 100%; transform: skewX(calc(var(--skew) - 90deg)); background: linear-gradient(to top, color-mix(in srgb, var(--a6-ink) 7%, transparent), color-mix(in srgb, var(--a6-ink) 2%, transparent)); box-shadow: inset 0 0 0 1px var(--a6-line), inset 0 1px 0 color-mix(in srgb, var(--a6-line) 55%, transparent); }.vt-col { --bh: calc(var(--u) * var(--n, 0)); --dc: var(--d); --dzc: calc(var(--dc) * var(--dz-k)); --vt-cap-face: var(--vt-cap, var(--a6-sev-neutral)); }
/* Die Invariante "Kappe <= Koerper" wird nicht an der Saeulenhoehe geheilt - ein
   max(--dzc, ...) auf --bh wuerde den Balken ueber seinen Achsenwert heben und eine Skala
   unlesbar machen, die vergleichen soll - sondern ueber die Tiefe: --d ist durch
   Plot-Hoehe * --dmax begrenzt, und --dmax ist genau so gewaehlt, dass die Kappe hoechstens
   CAP_RATIO - ein Drittel - der kurzesten gezeichneten Saeulenhoehe ist. Die Balkenhoehe bleibt damit exakt der Achsenwert:
   Gitterlinie und Balken zeigen dieselbe Zahl. Die exakte Zahl steht zusaetzlich als
   Wertemarke ueber dem Prisma sowie in Readout und aria-label. */
/* Die Bodenplatte ist keine Achsenregel: sie ist in jeder Säule gleich hoch, ob ein Balken
   darauf steht oder nicht. Damit entfällt die frühere je Säule verschiedene Grundriss-Höhe
   (6,81 px gegen 4 px) - der Grund, warum das Profil unten zackig wirkte. */
/*    Unter 380 px Content-Breite wird das Prisma flach. Nachgerechnet mit den neuen Werten
   (cqw = Content / 100; .vt-plot zieht --gutter links und 3px rechts ab; 24 Säulen, 23
   Abstand): bei 380 px greifen --gap 4 px und --gutter 20 px (Untergrenzen), die Zelle ist
   11,04 px und --dc 4,00 px - gut ein Drittel der Zelle als Tiefe, die Tiefenfläche ist eine
   eigene Kante. Bei 260 px Content sind es 6,04 px Zelle, 4,00 px Tiefe an einer 2,04 px
   Vorderfläche und 4,00 px Deckfläche: die Extrusion wäre ein Fleck auf der Säule, kein
   Volumen, und eine 11-px-Ziffer passt nicht in eine 6-px-Spalte. Eine schmale Säule ohne
   Scherflächen ist eine lesbare Spalte; eine breite Säule mit ihnen ist ein Prisma. Die
   Typ-Farbe bleibt in Vorderfläche, Bodenplatte, Kohortenstreifen und Legende. */
@container (max-width: 380px) { .vt-side, .vt-top, .vt-value { display: none; } .vt-floor-top { display: none; } .vt-col { --dc: 0px; --dzc: 0px; } }
/* Wertemarken-Duennung: die Marke sitzt mittig ueber der Zelle (left: 0, right: 0) und ragt bei
   langen Zahlen ueber die Zelle hinaus. Zwei Nachbarmarken beruehren sich, sobald ihre
   Textbreite den Abstand ihrer Zentren uebersteigt, und der ist --col-w + --gap. Mono hat
   0,60 em Ziffernvorschub (gemessen 2026-10-16: zwei Ziffern 12 px bei 10 px Schrift), also
   sind vier Ziffern bei 11 px 26,4 px breit. Im Band 500..1000 px Content sind --gap 0,8cqw
   und --gutter 4,2cqw, damit --col-w + --gap = 0,04025 * Content - 0,125: vier Ziffern
   brauchen ab 659 px Content keinen Verzicht. Unter der Kante faellt jede zweite Marke, der
   Pitch verdoppelt sich auf 2 * (--col-w + --gap) und traegt vier Ziffern bis 331 px und
   fuenf Ziffern bis 414 px Content. Bekannte Grenze: fuenfstellige Bucket-Zahlen (33,0 px)
   beruehren sich unterhalb von etwa 414 px Content auch geduennt. Kein Informationsverlust:
   jede Spalte traegt tabindex="0", und Readout (aria-live) und aria-label nennen dieselbe
   Zahl wie die Marke selbst. */
@container (max-width: 660px) { .vt-col:nth-child(even) .vt-value { display: none; } }
/* Beleuchtungs-Hierarchie des Prismas (Design-Korrektur 2026-10-17, nachgerechnet).
   Gemessen im ausgelieferten Overlay: die Kappe hatte background-image: none - ein
   color-mix-Anteil von 130 % liegt ausserhalb des gueltigen Bereichs (0..100 %), die ganze
   Deklaration fiel, und die Kappe zeigte damit genau die Farbe der Vorderflaeche. Ohne
   Tonstufen zwischen Vorder-, Tief- und Deckflaeche ist ein Prisma ein flacher Balken mit
   schraeger Oberkante - genau der schraege Eindruck. Die Stufen sind jetzt aus der
   Grundfarbe abgeleitet, nicht ueber halbtransparente Auflicht-Schichten.
   Nachgerechnet am Beispiel neutral #9aa4b0 = rgb(154,164,176) auf Canvas #101317:
     Vorderflaeche  Grundfarbe                        rgb(154,164,176)
     Deckflaeche    cap 74 % + weiss an der Vorderkante  rgb(180,188,197)  +26
     Deckflaeche    cap 88 % + weiss an der Hinterkante  rgb(166,175,185)  +11
     Tiefflaeche    shade 62 % an der Vorderkante        rgb(115,123,133)  -39
     Tiefflaeche    shade 100 % an der Hinterkante       rgb(92,98,107)    -62
   Die Kappe ist an der gemeinsamen Kante mit dem Koerper im Mittel mindestens 12 heller und an
   der hinteren Kante mindestens 6 - die hintere Kante grenzt an das Canvas, die gemeinsame an
   die Vorderflaeche, und dort muss der Unterschied sitzen,
   die Tiefflaeche mindestens 39 dunkler, und beide fallen nach hinten ab: die Tiefe wird
   rueckwaerts dunkler, wie im Licht. Die Naht zwischen Kappe und Koerper ist eine eigene
   Token-Stufe (--a6-face-seam), weil sie in beiden Themen dunkel sein muss - die Canvasfarbe
   selbst waere im hellen Thema eine Aufhellung gewesen. */
.vt-bar { position: absolute; left: 0; right: var(--dc); bottom: 0; height: var(--bh); background-image: var(--vt-grad); box-shadow: inset 0 0 0 1px var(--vt-edge), 0 1px 3px color-mix(in srgb, var(--a6-ink) 16%, transparent); transition: transform 160ms ease-out; }
.vt-side { position: absolute; left: 100%; top: 0; width: var(--dc); height: 100%; transform-origin: 0 100%; transform: skewY(calc(-1 * var(--skew))); background-image: linear-gradient(to right, color-mix(in srgb, var(--vt-shade) 62%, transparent), var(--vt-shade)), var(--vt-grad); box-shadow: inset 0 0 0 1px var(--vt-edge-side); }
.vt-top { position: absolute; left: 0; bottom: 100%; width: 100%; height: var(--dzc); transform-origin: 0 100%; transform: skewX(calc(var(--skew) - 90deg)); background-color: var(--vt-cap-face); background-image: linear-gradient(to top, color-mix(in srgb, var(--vt-cap-face) 74%, white), color-mix(in srgb, var(--vt-cap-face) 88%, white)); box-shadow: inset 0 0 0 1px var(--vt-edge), inset 0 -1px 0 var(--vt-seam); }
/* Wertemarke: die exakte Zahl über dem Prisma. Die Balkenhöhe ist eine Sichtbarkeits-
   Regelung (Untergrenze 2 px, Achsenrunde), die Zahl ist die Messung - deshalb steht sie
   dort, wo sie auch bei einem 2-px-Balken lesbar bleibt. Sie ist für Screenreader doppelt
   (aria-label nennt dieselbe Zahl), also aria-hidden. */
.vt-value { position: absolute; left: 0; right: 0; bottom: calc(var(--bh) + var(--dzc) + 2px); text-align: center; font-family: var(--a6-mono); font-size: 11px; line-height: 1; color: var(--a6-ink); font-variant-numeric: tabular-nums; pointer-events: none; }
/* Signatur-Marker: sitzt auf der Oberkante des Prismas (Hoehe --bh) am rechten Zellenrand
   und zeigt, dass die Transaktions-Forensik an einem Ausfall-Ledger dieses Intervalls eine
   Form-Signatur gemessen hat. Er ist schmucklos und absichtlich klein - die Aussage liegt
   im Readout und im Streifen. Am Rand, damit die Wertemarke in derselben Höhe steht. */
.vt-mark { position: absolute; right: 0; bottom: calc(var(--bh) + var(--dzc) - 6px); width: 6px; height: 6px; border-radius: 50%; background: var(--a6-accent); box-shadow: 0 0 0 1.5px var(--a6-graph-canvas); }
.vt-axis { position: relative; height: 14px; margin: 4px 3px 0 var(--gutter); }
.vt-axis span { position: absolute; top: 0; transform: translateX(-50%); font-family: var(--a6-mono); font-size: 10px; color: var(--a6-muted); white-space: nowrap; }
/* Kohortenstreifen: eine Zeile unter der Zeitachse, exakt auf den Intervallen der Säulen
   (gleiche Ränder, gleicher Abstand, gleiche Flex-Basis). Er zeigt, wie viele dUNL-Validatoren
   im selben Intervall ausfielen – die Frage, die zwischen "mein Knoten" und "Netzereignis"
   trennt. Ohne Aufzeichnung ist die Kachel schraffiert, nicht grau. */
.vt-heat { display: flex; gap: var(--gap); margin: 3px 3px 0 var(--gutter); }
.vt-heat-cell { flex: 1 1 0; min-width: 0; height: 11px; border-radius: var(--a6-r-xs); background: color-mix(in srgb, var(--a6-graph-grid) 55%, transparent); }
.vt-heat-cell.vt-state-clean { background: var(--a6-success-soft); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--a6-success) 70%, transparent); }
.vt-heat-cell.vt-state-solo { background: color-mix(in srgb, var(--a6-ink) 12%, transparent); box-shadow: inset 0 0 0 1px var(--a6-line-strong); }
.vt-heat-cell.vt-state-cohort { background: var(--a6-accent-soft); box-shadow: inset 0 0 0 1px var(--a6-accent); }
.vt-heat-cell.vt-state-network { background: var(--a6-accent); }
.vt-heat-cell.vt-state-unknown { background: repeating-linear-gradient(135deg, transparent 0 3px, var(--a6-muted) 3px 4px); }
.vt-heat-cell.vt-state-none { background: repeating-linear-gradient(135deg, transparent 0 3px, var(--a6-line) 3px 4px); opacity: 0.6; }
/* Signaturstreifen: eine Zeile unter dem Kohortenstreifen, exakt auf denselben Intervallen
   (gleiche Raender, gleicher Abstand, gleiche Flex-Basis). Er zeigt, ob die Transaktions-
   Forensik an den Ausfall-Ledern dieses Validators eine Form-Signatur gemessen hat. */
.vt-sign { display: flex; gap: var(--gap); margin: 3px 3px 0 var(--gutter); }
.vt-sign-cell { flex: 1 1 0; min-width: 0; height: 11px; border-radius: var(--a6-r-xs); background: color-mix(in srgb, var(--a6-graph-grid) 55%, transparent); }
.vt-sign-cell.vt-sign-hit { background: var(--a6-accent); }
.vt-sign-cell.vt-sign-clear { background: var(--a6-success-soft); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--a6-success) 70%, transparent); }
.vt-sign-cell.vt-sign-unknown { background: repeating-linear-gradient(135deg, transparent 0 3px, var(--a6-muted) 3px 4px); }
.vt-sign-cell.vt-sign-none { background: repeating-linear-gradient(135deg, transparent 0 3px, var(--a6-line) 3px 4px); opacity: 0.6; }
.vt-legend { list-style: none; display: flex; flex-wrap: wrap; gap: 12px; margin: 8px 0 0 var(--gutter); padding: 0; }
.vt-heat-legend { margin-top: 4px; }
.vt-sign-legend { margin-top: 4px; }
.vt-sign-note { margin: 6px 0 0 var(--gutter); font-size: 11px; }
.vt-legend li { display: flex; align-items: center; gap: 6px; font-family: var(--a6-mono); font-size: 11px; color: var(--a6-body); }
.vt-swatch { width: 12px; height: 12px; border: 1px solid var(--a6-line-strong); border-radius: var(--a6-r-xs); }
.vt-swatch.vt-c-missed { background: var(--a6-sev-suspect); }
.vt-swatch.vt-c-partial { background: var(--a6-sev-info); }
.vt-swatch.vt-c-wrongHash { background: var(--a6-sev-malicious); }
.vt-swatch.vt-c-voteNotObserved { background: var(--a6-sev-neutral); }
/* Die Quorum-Absage ist die schwerste Abwesenheits-Art: sie verneint, dass das Netz
   ueberhaupt schloss. Sie bekommt eine eigene Farbe, damit sie im Stapel nicht mit der
   Erfassungs-Luecke auf einem bewiesen geschlossenen Ledger verwechselt wird. */
.vt-swatch.vt-c-noQuorum { background: var(--a6-sev-quorum); }
.vt-heat-key { width: 12px; height: 11px; border-radius: var(--a6-r-xs); background: color-mix(in srgb, var(--a6-graph-grid) 55%, transparent); }
.vt-heat-key.vt-state-clean { background: var(--a6-success-soft); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--a6-success) 70%, transparent); }
.vt-heat-key.vt-state-solo { background: color-mix(in srgb, var(--a6-ink) 12%, transparent); box-shadow: inset 0 0 0 1px var(--a6-line-strong); }
.vt-heat-key.vt-state-cohort { background: var(--a6-accent-soft); box-shadow: inset 0 0 0 1px var(--a6-accent); }
.vt-heat-key.vt-state-network { background: var(--a6-accent); }
.vt-heat-key.vt-state-unknown { background: repeating-linear-gradient(135deg, transparent 0 3px, var(--a6-muted) 3px 4px); }
.vt-heat-key.vt-state-none { background: repeating-linear-gradient(135deg, transparent 0 3px, var(--a6-line) 3px 4px); opacity: 0.6; }
.vt-sign-key { width: 12px; height: 11px; border-radius: var(--a6-r-xs); background: color-mix(in srgb, var(--a6-graph-grid) 55%, transparent); }
.vt-sign-key.vt-sign-hit { background: var(--a6-accent); }
.vt-sign-key.vt-sign-clear { background: var(--a6-success-soft); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--a6-success) 70%, transparent); }
.vt-sign-key.vt-sign-unknown { background: repeating-linear-gradient(135deg, transparent 0 3px, var(--a6-muted) 3px 4px); }
.vt-sign-key.vt-sign-none { background: repeating-linear-gradient(135deg, transparent 0 3px, var(--a6-line) 3px 4px); opacity: 0.6; }
/* Zustände: Hover und Tastaturfokus heben das Prisma aus der Ebene (nur transform, kein
   Layout-Sprung). Der Fokusrahmen umfasst die ganze Intervallspalte, weil Readout und
   Screenreader dieselbe Spalte lesen. */
.vt-col:hover .vt-bar, .vt-col:focus-visible .vt-bar { transform: translateY(-3px); }
.vt-col:focus { outline: none; }
.vt-col:focus-visible { outline: 2px solid var(--a6-focus); outline-offset: 2px; }
.vt-sub { margin: 0 0 8px; font-size: 11px; color: var(--a6-muted); }
.vt-rc-list { display: grid; gap: 8px; }
.vt-rc-rest { display: grid; gap: 8px; margin-top: 8px; }
.vt-rc-rest[hidden] { display: none; }
.vt-rc-more { margin-top: 8px; padding: 6px 12px; border: 1px solid var(--a6-line); border-radius: var(--a6-r-sm); background: transparent; color: var(--a6-ink); font-family: var(--a6-mono); font-size: 11px; cursor: pointer; }
.vt-rc-more:hover { border-color: var(--a6-accent); color: var(--a6-accent-text); }
.vt-rc { border: 1px solid var(--a6-line); border-radius: var(--a6-r-sm); padding: 10px 12px; background: var(--a6-surface-alt); }
.vt-rc-head { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; font-family: var(--a6-mono); font-size: 12px; }
.vt-rc-led { color: var(--a6-ink); font-weight: 650; }
.vt-rc-when { color: var(--a6-muted); }
.vt-badge { font-family: var(--a6-mono); font-size: 10px; text-transform: uppercase; letter-spacing: 0.06em; padding: 2px 7px; border: 1px solid var(--a6-line-strong); border-radius: var(--a6-r-xs); }
.vt-badge.vt-type-missed { border-color: var(--a6-sev-suspect); color: var(--a6-sev-suspect); }
.vt-badge.vt-type-wrong-hash, .vt-badge.vt-type-partial { border-color: var(--a6-sev-malicious); color: var(--a6-sev-malicious); }
.vt-badge.vt-type-no-quorum { border-color: var(--a6-sev-neutral); color: var(--a6-sev-neutral); }
.vt-badge.vt-scope-network { background: var(--a6-ink); color: var(--a6-surface); border-color: var(--a6-ink); }
.vt-badge.vt-scope-cohort { border-color: var(--a6-accent); color: var(--a6-accent-text); }
.vt-badge.vt-scope-capture-cohort { border-color: var(--a6-sev-neutral); color: var(--a6-sev-neutral); }
.vt-rc-metrics { list-style: none; margin: 8px 0 0; padding: 0; display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 6px; }
.vt-rc-metrics li { border: 1px solid var(--a6-line); border-radius: var(--a6-r-sm); padding: 6px 8px; }
.vt-rc-metrics small { display: block; font-family: var(--a6-mono); font-size: 10px; text-transform: uppercase; letter-spacing: 0.06em; color: var(--a6-muted); }
.vt-rc-metrics b { font-family: var(--a6-mono); font-size: 12px; font-weight: 600; }
/* Mit-Betroffene je Ledger: erst die Namen machen die Kohorten-Zahl nachvollziehbar. */
.vt-rc-peers { margin: 8px 0 0; font-size: 11px; color: var(--a6-muted); }
.vt-rc-peers small { display: block; font-family: var(--a6-mono); font-size: 10px; text-transform: uppercase; letter-spacing: 0.06em; }
.vt-peer-group { display: block; }
.vt-peer-group + .vt-peer-group { margin-top: 6px; }
.vt-rc-peerstruth { margin: 5px 0 0; font-size: 11px; color: var(--a6-muted); }
.vt-peer { display: inline-block; border: 1px solid var(--a6-line); border-radius: var(--a6-r-sm); padding: 2px 6px; margin: 3px 4px 0 0; font-family: var(--a6-mono); font-size: 10px; color: var(--a6-ink); }
.vt-peer-more { display: inline-block; border: 1px dashed var(--a6-line); border-radius: var(--a6-r-sm); background: none; padding: 2px 6px; margin: 3px 0 0 0; font-family: var(--a6-mono); font-size: 10px; color: var(--a6-muted); cursor: pointer; }
.vt-peer-more:hover, .vt-peer-more:focus-visible { color: var(--a6-ink); border-color: var(--a6-ink); outline: none; }
.vt-peer-none { color: var(--a6-muted); border-style: dashed; }
/* Die Gegenliste der Abgestimmten: eine Beobachtung, kein Vorwurf. Ihr Etikett bekommt
   eine eigene Farbe, damit es in derselben Zelle nicht wie die angeklagten Knoten aussieht. */
.vt-cq-voters > small { color: var(--a6-success); }
.vt-cq-voters-none > small { color: var(--a6-muted); }
/* Kohorten-Chips mit Gegenurteil: ein widerlegter Knoten darf nicht wie ein betroffener
   aussehen. Der Titel tragt die Zahl, die das Urteil belegt. */
.vt-peer-contradicted { border-color: var(--a6-error); color: var(--a6-error); background: var(--a6-error-soft); }
.vt-peer-consistent { border-color: var(--a6-success); color: var(--a6-success); background: var(--a6-success-soft); }
.vt-peer-not-comparable { border-style: dashed; color: var(--a6-muted); }
.vt-cq-peer-truth { margin: 6px 0 0; font-size: 12px; line-height: 1.45; padding: 7px 10px; border: 1px solid var(--a6-control-line); border-radius: var(--a6-r-md); color: var(--a6-body); }
.vt-cq-peer-truth small { color: var(--a6-body); }
.vt-cq-affected td { text-align: left; background: var(--a6-surface-alt); }
.vt-cq-affected-label { display: inline-block; margin-right: 6px; color: var(--a6-muted); font-size: 10px; text-transform: uppercase; letter-spacing: 0.06em; }
.vt-est-note { color: var(--a6-muted); }
.vt-atk-peers { display: block; }
/* Breite Tabellen: auf schmalen Screens quetscht nichts die Zellen, es wird horizontal gelesen. */
.vt-tablewrap { overflow-x: auto; }
.vt-table-hint { display: none; }
.vt-sens-head { margin: 0 0 8px; font-size: 12px; }
.vt-sens-fam { margin: 8px 0 0; font-size: 11px; color: var(--a6-muted); }
.vt-sensv { font-weight: 600; }
.vt-sensv-signature-specific { color: var(--a6-sev-suspect); }
.vt-sensv-signature-network { color: var(--a6-sev-info); }
.vt-sensv-signature-blind { color: var(--a6-success); }
.vt-sensv-no-link { color: var(--a6-muted); }
.vt-sensv-thin-sample, .vt-sensv-no-exposure { color: var(--a6-muted); font-style: italic; }
/* Zahl und Zaehler stammen aus verschiedenen Populationen: die Rate ist keine duenne Messung,
   sie ist gar keine Messung. Das ist eine Warnung ueber die Engine, nicht ueber den Knoten. */
.vt-sensv-count-mismatch { color: var(--a6-sev-malicious); font-weight: 600; }
.vt-h4 { margin: 10px 0 4px; font-size: 10px; text-transform: uppercase; letter-spacing: 0.08em; color: var(--a6-muted); font-weight: 650; }
.vt-hyps, .vt-next { list-style: none; margin: 0; padding: 0; display: grid; gap: 4px; }
.vt-hyp { display: grid; grid-template-columns: minmax(140px, 1fr) auto; gap: 4px 10px; align-items: baseline; border: 1px solid var(--a6-line); border-radius: var(--a6-r-sm); padding: 6px 8px; font-size: 12px; }
.vt-strength { font-family: var(--a6-mono); font-size: 10px; text-transform: uppercase; letter-spacing: 0.06em; color: var(--a6-muted); }
.vt-ev, .vt-lift { grid-column: 1 / -1; font-family: var(--a6-mono); font-size: 11px; color: var(--a6-body); }
.vt-hyp .vt-ev { display: block; }
.vt-ev-peers { margin-top: 3px; }
.vt-hyp.vt-st-measured { border-color: var(--a6-line-strong); background: var(--a6-surface); }
.vt-hyp.vt-st-measured .vt-strength { color: var(--a6-accent-text); }
.vt-hyp.vt-st-none { border-style: dashed; }
.vt-hyp.vt-st-none .vt-strength { color: var(--a6-sev-neutral); }
.vt-lift { color: var(--a6-accent-text); }
.vt-next li { font-size: 12px; padding: 5px 8px; border: 1px solid var(--a6-accent); border-radius: var(--a6-r-sm); background: var(--a6-accent-soft); }
.vt-rc-gaps { margin: 8px 0 0; font-size: 11px; color: var(--a6-muted); font-family: var(--a6-mono); }
.vt-table { width: 100%; border-collapse: collapse; font-family: var(--a6-mono); font-size: 11px; }
.vt-table th, .vt-table td { border-bottom: 1px solid var(--a6-line); padding: 6px 8px; text-align: right; font-weight: 400; }
.vt-table th[scope="row"], .vt-th-l { text-align: left; }
.vt-table thead th { border-bottom-color: var(--a6-line-strong); color: var(--a6-muted); font-weight: 650; text-transform: uppercase; letter-spacing: 0.06em; font-size: 10px; }
.vt-table th[scope="row"] { font-weight: 600; color: var(--a6-ink); }
/* Serien-Zeile in der Retrace-Karte: die Erklaerung ist die Zeitfolge, nicht der Ledger - sie
   steht deshalb direkt unter dem Ledger-Kopf und trägt dieselbe Akzentkante wie die Karte. */
.vt-rc-series { margin: 6px 0 0; padding: 6px 8px; border: 1px solid var(--a6-accent); border-radius: var(--a6-r-xs); background: var(--a6-accent-soft); font-family: var(--a6-mono); font-size: 11px; color: var(--a6-body); }
.vt-rc-series small { font-size: 10px; text-transform: uppercase; letter-spacing: 0.06em; color: var(--a6-muted); }
/* Knoten-Signatur: Messgroesse (dt) und Wert (dd) als Definitionspaar in einem Raster, das bei
   schmalem Container umbricht, ohne die Lesereihenfolge zu drehen. */
.vt-fp { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 6px; margin: 0; }
.vt-fp-row { border: 1px solid var(--a6-line); border-radius: var(--a6-r-sm); padding: 6px 8px; }
.vt-fp-row dt { font-family: var(--a6-mono); font-size: 10px; text-transform: uppercase; letter-spacing: 0.06em; color: var(--a6-muted); }
.vt-fp-row dd { margin: 3px 0 0; font-family: var(--a6-mono); font-size: 12px; color: var(--a6-ink); }
/* Last-Ausfall-Kurve: die Normalbetriebsstufe ist der Vergleichspunkt jedes Last-Urteils. Sie
   muss in der Tabelle selbst zu sehen sein, nicht erst in der Zeile darunter. */
.vt-table tr.vt-load-base th[scope="row"], .vt-table tr.vt-load-base td { background: var(--a6-accent-soft); border-bottom-color: var(--a6-accent); }
/* Urteils-Badges: die Farbe trägt das Urteil, damit das Auge vor dem Text weiß, ob ein Befund
   ein Kapazitätsproblem, ein Netzproblem oder kein Problem ist. */
.vt-badge.vt-fpv-chronic-outage, .vt-badge.vt-fpv-outlier-node { border-color: var(--a6-sev-suspect); color: var(--a6-sev-suspect); }
.vt-badge.vt-fpv-network-driven, .vt-badge.vt-fpv-clean { border-color: var(--a6-accent); color: var(--a6-accent-text); }
.vt-badge.vt-fpv-solo-only, .vt-badge.vt-fpv-intermittent, .vt-badge.vt-fpv-mixed { border-color: var(--a6-sev-neutral); color: var(--a6-sev-neutral); }
.vt-badge.vt-loadv-load-limited { border-color: var(--a6-sev-suspect); color: var(--a6-sev-suspect); }
.vt-badge.vt-loadv-always-failing { border-color: var(--a6-sev-malicious); color: var(--a6-sev-malicious); }
.vt-badge.vt-loadv-load-resilient { border-color: var(--a6-success); color: var(--a6-success); }
.vt-badge.vt-loadv-unknown { border-color: var(--a6-sev-neutral); color: var(--a6-sev-neutral); }
/* Angriffs-Wirkungs-Urteil: dasselbe Farbversprechen wie die anderen Urteils-Badges. */
.vt-badge.vt-impv-attack-linked { border-color: var(--a6-sev-malicious); color: var(--a6-sev-malicious); }
.vt-badge.vt-impv-no-link { border-color: var(--a6-success); color: var(--a6-success); }
.vt-badge.vt-impv-thin-overlap, .vt-badge.vt-impv-unknown { border-color: var(--a6-sev-neutral); color: var(--a6-sev-neutral); }
.vt-badge.vt-impv-no-overlap { border-color: var(--a6-sev-suspect); color: var(--a6-sev-suspect); }
.vt-badge.vt-impv-node-driven { border-color: var(--a6-sev-info); color: var(--a6-sev-info); }
.vt-badge.vt-peerv-peer-baseline { border-color: var(--a6-success); color: var(--a6-success); }
.vt-badge.vt-peerv-elevated { border-color: var(--a6-sev-suspect); color: var(--a6-sev-suspect); }
.vt-badge.vt-peerv-outlier { border-color: var(--a6-sev-malicious); color: var(--a6-sev-malicious); }
.vt-badge.vt-peerv-insufficient-sample, .vt-badge.vt-peerv-not-observed { border-color: var(--a6-sev-neutral); color: var(--a6-sev-neutral); }
/* Vote-Timing: "nie signiert" ist der harte Befund, "spaet" einer mit anderer naechster Pruefung,
   "Netz selbst war spaet" entlastet den Knoten, und "nicht erfasst" darf nie als Entlastung aussehen. */
.vt-badge.vt-latv-latency-not-the-cause, .vt-badge.vt-latv-no-deviations { border-color: var(--a6-success); color: var(--a6-success); }
.vt-badge.vt-latv-node-side-late, .vt-badge.vt-latv-mixed { border-color: var(--a6-sev-suspect); color: var(--a6-sev-suspect); }
.vt-badge.vt-latv-never-signed { border-color: var(--a6-sev-malicious); color: var(--a6-sev-malicious); }
.vt-badge.vt-latv-network-side { border-color: var(--a6-sev-info); color: var(--a6-sev-info); }
.vt-badge.vt-latv-capture-only { border-color: var(--a6-sev-info); color: var(--a6-sev-info); }
.vt-badge.vt-latv-closure-unproven, .vt-badge.vt-latv-partially-proven { border-color: var(--a6-sev-neutral); color: var(--a6-sev-neutral); }
.vt-badge.vt-latv-not-recorded, .vt-badge.vt-latv-insufficient-sample { border-color: var(--a6-sev-neutral); color: var(--a6-sev-neutral); }
.vt-badge.vt-divv-clean { border-color: var(--a6-success); color: var(--a6-success); }
.vt-badge.vt-divv-tip-lag { border-color: var(--a6-sev-info); color: var(--a6-sev-info); }
.vt-badge.vt-divv-deterministic-divergence, .vt-badge.vt-divv-feature-linked { border-color: var(--a6-sev-suspect); color: var(--a6-sev-suspect); }
.vt-badge.vt-divv-shared-lineage { border-color: var(--a6-sev-malicious); color: var(--a6-sev-malicious); }
.vt-badge.vt-divv-transient-divergence, .vt-badge.vt-divv-insufficient-sample, .vt-badge.vt-divv-not-recorded { border-color: var(--a6-sev-neutral); color: var(--a6-sev-neutral); }
.vt-badge.vt-causek-cause { border-color: var(--a6-sev-suspect); color: var(--a6-sev-suspect); }
.vt-badge.vt-causek-localization { border-color: var(--a6-sev-info); color: var(--a6-sev-info); }
.vt-badge.vt-causek-consequence, .vt-badge.vt-causek-category { border-color: var(--a6-sev-neutral); color: var(--a6-sev-neutral); }
.vt-badge.vt-causek-exoneration { border-color: var(--a6-success); color: var(--a6-success); }
.vt-badge.vt-causek-none { border-color: var(--a6-sev-neutral); color: var(--a6-sev-neutral); border-style: dashed; }
.vt-badge.vt-surfv-quorum-bound, .vt-badge.vt-surfv-node-bound { border-color: var(--a6-sev-suspect); color: var(--a6-sev-suspect); }
.vt-badge.vt-surfv-load-bound { border-color: var(--a6-sev-info); color: var(--a6-sev-info); }
.vt-badge.vt-surfv-both-bound { border-color: var(--a6-sev-malicious); color: var(--a6-sev-malicious); }
.vt-badge.vt-surfv-mixed, .vt-badge.vt-surfv-unknown { border-color: var(--a6-sev-neutral); color: var(--a6-sev-neutral); }
/* Die Kopplungs-Ebene entscheidet, ob eine Netzaktion oder ein Knotenfehler vorliegt.
   Die Badge-Farbe ist Teil dieser Aussage, nicht Dekoration. */
.vt-badge.vt-coupl-stress-coupled { border-color: var(--a6-sev-malicious); color: var(--a6-sev-malicious); }
.vt-badge.vt-coupl-stress-linked-weak { border-color: var(--a6-sev-suspect); color: var(--a6-sev-suspect); }
.vt-badge.vt-coupl-node-local-under-stress { border-color: var(--a6-sev-info); color: var(--a6-sev-info); }
.vt-badge.vt-coupl-no-coupling-measured { border-color: var(--a6-success); color: var(--a6-success); }
.vt-badge.vt-coupl-insufficient-sample, .vt-badge.vt-coupl-not-recorded { border-color: var(--a6-sev-neutral); color: var(--a6-sev-neutral); }
// Die Dosis-Achse urteilt in dieselben vier Abstufungen: ein gemessener Anstieg ist ein
// Signal, ein gemessener Rueckgang ist eines, ein flacher Verlauf keins, und ein Urteil ohne
// Nenner ist offen. Offene Urteile sehen hier offen aus.
.vt-badge.vt-load-load-dose-rising { border-color: var(--a6-sev-suspect); color: var(--a6-sev-suspect); }
.vt-badge.vt-load-load-dose-falling { border-color: var(--a6-sev-info); color: var(--a6-sev-info); }
.vt-badge.vt-load-load-dose-flat { border-color: var(--a6-sev-neutral); color: var(--a6-sev-neutral); }
.vt-badge.vt-load-not-comparable, .vt-badge.vt-load-insufficient-sample { border-color: var(--a6-sev-neutral); color: var(--a6-sev-neutral); }
.vt-code { font-family: var(--a6-mono); font-size: 10px; color: var(--a6-muted); }
.vt-table tr.vt-hot th[scope="row"], .vt-table tr.vt-hot td { background: var(--a6-warn-soft); border-bottom-color: var(--a6-sev-suspect); }
.vt-table tr.vt-thin th[scope="row"], .vt-table tr.vt-thin td { border-bottom-style: dashed; color: var(--a6-muted); }
.vt-table td.vt-imp-rules { text-align: left; }
.vt-imp-rules .vt-chip { margin: 0 4px 3px 0; }

.vt-small { display: block; color: var(--a6-muted); font-size: 10px; text-transform: none; letter-spacing: 0; font-weight: 400; }
.vt-atk { display: grid; grid-template-columns: 150px 170px 150px 1fr; gap: 8px; align-items: baseline; }
.vt-atk-when, .vt-atk-n { color: var(--a6-muted); }
.vt-atk-split, .vt-atk-split-open { color: var(--a6-muted); font-family: var(--a6-mono); font-size: 11px; }
@media (prefers-reduced-motion: reduce) {
  .vt-col { animation: none; }
  .vt-bar { transition: none; }
}
@media (max-width: 760px) {
  /* Tiefe, Säulenabstand und Achsen-Gutter folgen der Containerbreite (cqw) – hier braucht nur
     die Plot-Höhe ein Mobilmaß; die Kantenbeziehung dz = d · tan 45° gilt unverändert. */
  .vt-profile { --plot-h: 156px; }
  .vt-rc-metrics { grid-template-columns: 1fr 1fr; }
  .vt-fp { grid-template-columns: 1fr 1fr; }
  .vt-atk { grid-template-columns: 1fr 1fr; }
  .vt-table { font-size: 10px; }
  .vt-table th, .vt-table td { padding: 5px 4px; }
}
/* Datenvisualisierung: die Farbcodierung trägt die Aussage (Vorfallstyp und Kohorten-Breite).
   In erzwungenen Kontrastfarben bleiben Prismen- und Streifenflächen erhalten, Text und Achsen
   folgen Systemfarben. */
@media (forced-colors: active) {
  .vt-bar, .vt-side, .vt-top, .vt-base, .vt-floor, .vt-floor-top, .vt-swatch, .vt-heat-cell, .vt-heat-key, .vt-sign-cell, .vt-sign-key, .vt-mark { forced-color-adjust: none; }
  .vt-col:focus-visible { outline: 3px solid CanvasText; outline-offset: 2px; }
  .vt-hyp.vt-st-measured { border-color: CanvasText; }
}
