﻿/**
 * Databula - Interactive Table, Pivot, KPI, Sparkline & Widget Styles
 * (c) Glück IT - https://glueck-it.de
 */

:root {
  --pvt-bg: var(--bg-panel, #ffffff);
  --pvt-bg-even: rgba(0, 0, 0, 0.015);
  --pvt-bg-odd: transparent;
  --pvt-hover: rgba(59, 130, 246, 0.08);
  --pvt-marked: #e0f2fe;
  --pvt-lvl0-bg: #f1f5f9;
  --pvt-lvl0-txt: #0f172a;
  --pvt-lvl1-bg: #f8fafc;
  --pvt-lvl1-txt: #1e293b;
  --pvt-lvl2-bg: #ffffff;
  --pvt-lvl2-txt: #334155;
  --border-color: #e2e8f0;
  --border-strong: #cbd5e1;
  --text-main: #0f172a;
  --text-muted: #64748b;
  --accent: #3b82f6;
  --snap-up-text: #16a34a;
  --snap-down-text: #dc2626;
  --status-warning-text: #d97706;
  --status-success-text: #16a34a;
  --status-error-text: #dc2626;
  --dl-sb-h: 28px;
}

[data-theme="dark"] {
  --pvt-bg: var(--bg-panel, #1e293b);
  --pvt-bg-even: rgba(255, 255, 255, 0.02);
  --pvt-bg-odd: transparent;
  --pvt-hover: rgba(59, 130, 246, 0.15);
  --pvt-marked: #0369a1;
  --pvt-lvl0-bg: #0f172a;
  --pvt-lvl0-txt: #f8fafc;
  --pvt-lvl1-bg: #1e293b;
  --pvt-lvl1-txt: #f1f5f9;
  --pvt-lvl2-bg: #1e293b;
  --pvt-lvl2-txt: #cbd5e1;
  --border-color: #334155;
  --border-strong: #475569;
  --text-main: #f8fafc;
  --text-muted: #94a3b8;
  --accent: #60a5fa;
  --snap-up-text: #4ade80;
  --snap-down-text: #f87171;
  --status-warning-text: #fbbf24;
  --status-success-text: #4ade80;
  --status-error-text: #f87171;
}

/* --- UNIFIED DATA-TABLE SYSTEM --- */
.data-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.875rem;
    text-align: left;
}
.data-table th,
.data-table td {
    padding: 8px 12px;
    border-bottom: 1px solid var(--border-color);
    color: var(--text-main);
}
.data-table th {
    font-weight: 600;
    color: var(--text-muted);
    background: var(--bg-panel);
}
/* Compact (Help-Tabellen, Revision) */
.data-table--compact th,
.data-table--compact td { padding: 4px 8px; font-size: 0.8rem; }
/* Small (CMS-Listen) */
.data-table--sm th,
.data-table--sm td { padding: 6px 10px; font-size: 0.85rem; }
/* Striped rows */
.data-table--striped tbody tr:nth-child(even) td { background-color: var(--pvt-bg-even); }
.data-table--striped tbody tr:nth-child(odd) td  { background-color: var(--pvt-bg-odd); }
/* Row hover */
.data-table--hover tbody tr:hover td { background-color: var(--pvt-hover); }
/* Sticky header */
.data-table--sticky thead th { position: sticky; top: 0; z-index: 1; background: var(--pvt-lvl1-bg); color: var(--pvt-lvl1-txt); border-bottom: 1px solid var(--border-strong); }
/* Key-Value Layout (Label | Wert) */
.data-table--kv th { width: 180px; white-space: nowrap; background: var(--bg-panel); }
/* Header uppercase (CMS-Style) */
.data-table--caps th { text-transform: uppercase; letter-spacing: 0.03em; font-size: 0.8rem; }
/* No-wrap (Daten-Tabellen) */
.data-table--nowrap { white-space: nowrap; }
/* Code in Tabellen */
.data-table code { background: var(--bg-panel); padding: 1px 5px; border-radius: var(--radius-xs); font-size: 0.8rem; }
.text-right { text-align: right; }


/* --- PIVOT TABLE CORE --- */

/* 6. PIVOT TABLE STRUKTUR */
table.pivot {
    border-collapse: separate; /* Wichtig für Sticky Headers */
    border-spacing: 0;
    width: 100%; min-width: 100%;
    table-layout: auto;
    white-space: nowrap;
    font-size: 0.8rem;
    margin-top: 0;
}

table.pivot.pivot-layout-frozen td,
table.pivot.pivot-layout-frozen th {
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Column Resize */
table.pivot th .col-resize-handle {
    position: absolute;
    right: 0; top: 0; bottom: 0;
    width: 5px;
    cursor: col-resize;
    z-index: 310;
}
table.pivot th .col-resize-handle:hover {
    background-color: var(--md-sys-color-primary, #3b82f6);
    opacity: 0.5;
}
table.pivot.col-resizing {
    cursor: col-resize;
    user-select: none;
}
.col-resize-guide {
    position: fixed;
    top: 0; bottom: 0;
    width: 2px;
    background: var(--md-sys-color-primary, #3b82f6);
    opacity: 0.7;
    z-index: 9999;
    pointer-events: none;
}

table.pivot th, table.pivot td {
    padding: 4px 8px;
    border-right: 1px solid var(--border-color);
    border-bottom: 1px solid var(--border-color);
    background-clip: padding-box;
    box-sizing: border-box;
    /* Tabellenziffern: gleich breite Ziffern, damit rechtsbuendige Zahlenspalten
       und Tausender-/Dezimaltrenner vertikal fluchten (wie .detail-table .col-num).
       Wirkt nur auf Ziffern — Text-Spalten (Aktie/Sektor/Signal) bleiben unberuehrt. */
    font-variant-numeric: tabular-nums;
}

/* Schwellen-Marker (Pivot\NumberFormatterMarker): kleines Symbol hinter dem Wert.
   Form UND Farbe unterscheiden die Stufen (Farbsehschwäche / kleine Flächen):
   Dreieck hoch = positiv, Balken = neutral, Dreieck runter = negativ. Farben aus
   base.css (--signal-buy/neutral/sell, je Light/Dark). Feste Größe -> die
   Marker bilden eine saubere rechte Spalte. */
.pvt-marker {
    display: inline-block;
    width: 10px;
    height: 10px;
    margin-left: 6px;
    vertical-align: middle;   /* mittig zur Zahl statt auf der Grundlinie */
}
.pvt-marker--buy     { background: var(--signal-buy);     clip-path: polygon(50% 0, 100% 100%, 0 100%); }
.pvt-marker--neutral { background: var(--signal-neutral); height: 3px; border-radius: 1px; }
.pvt-marker--sell    { background: var(--signal-sell);    clip-path: polygon(0 0, 100% 0, 50% 100%); }
/* Windows-Kontrastmodus forced background auf Canvas -> geclippte Formen wären
   unsichtbar; CanvasText hält die Form sichtbar (Farbe trägt dort eh nicht). */
@media (forced-colors: active) {
    .pvt-marker { background: CanvasText; }
}

/* 7. STICKY HEADERS & COLUMNS
   Z-Index-Skala (aufsteigend):
     100  Header (thead, 1. Zeile)
     101  Filter-Row (thead tr.filter-row)
     110  Tree-Col + Fixed-Col im tbody
     200  Fixed-Col in Filter-Row
     201  Filter-Row erste Zelle (sticky left)
     299  Fixed-Col im Header
     300  Super-Ecke (oben links, thead + first col)
     310  Column-Resize-Handle (über Corner, damit greifbar)
     320  DataList-Statusleiste oben (.dl-statusbar--top) — muss über JEDER sticky
          Kopfzelle liegen, auch der Super-Ecke, sonst scrollt der Kopf über sie hinweg
     2000 Cross-Hover-Overlay (muss über Sticky liegen)
     2005 Collapse-Icons
     9999 Resize-Guide-Line (während Drag, über allem)
   Am UNTEREN Rand (konkurriert mit nichts aus dem thead, deshalb eigene kleine Stufe):
     5    DataList-Statusleiste unten (.dl-statusbar--bottom) — über dem statischen tbody,
          unter .admin-pagination (base.css, z-index 10). Trägt nur, solange DataList keine
          fixierten Spalten rendert (die lägen im tbody auf 110). */

/* A) Standard Header */
table.pivot thead th, table.pivot thead td {
    position: sticky; top: 0;
    z-index: 100;
    background-color: var(--pvt-header-bg);
    color: var(--pvt-header-txt);
    font-weight: 600;
    font-size: 0.8rem;
    border-bottom: 1px solid var(--border-strong);
}
/* Discoverability: Spalten mit Erklärungs-Tooltip (data-title) bekommen einen dezenten
   gepunkteten Unterstrich als Hover-Hinweis — sonst bleibt der erklärende Tooltip ungesehen.
   text-underline-offset haelt ihn vom border-bottom getrennt. */
table.pivot thead th[data-title] {
    text-decoration: underline dotted;
    text-decoration-thickness: 1px;
    text-underline-offset: 3px;
    text-decoration-color: color-mix(in srgb, var(--pvt-header-txt) 45%, transparent);
}

/* Sort-Animation */
@keyframes pivot-sort-spin {
    to { transform: rotate(360deg); }
}
table.pivot thead th .sort-spinner {
    display: inline-block;
    width: 10px; height: 10px;
    border: 2px solid transparent;
    border-top-color: currentColor;
    border-radius: 50%;
    animation: pivot-sort-spin 0.4s linear infinite;
    vertical-align: middle;
    margin-left: 4px;
    opacity: 0.75;
}
table.pivot tbody.is-sorting tr {
    opacity: 0.4;
    transition: opacity 0.1s;
}

/* B) Filter Row (Muss über Header, aber UNTER Tree Spalte) */
table.pivot thead tr.pivot-filter-row td,
table.pivot thead tr.pivot-filter-row th {
    background-color: var(--bg-panel);
    z-index: 101;
    padding: 0px; /* FIX: Inputs sollen Zelle füllen */
}

/* Sticky-Blöcke C-F: !important nötig — Hintergrund + z-index der fixierten Zellen
   müssen die Zebra-/Level-/Heatmap-Regeln gleicher Spezifität sicher überschreiben. */
/* C) Sticky Spalte (Tree) */
table.pivot tbody th.tree,
table.pivot tbody td.tree {
    position: sticky; left: 0;
    z-index: 110;
    background-color: var(--bg-app);
    border-right: 2px solid var(--border-strong);
}

/* D) Super-Ecke (oben links) - nur erste Zeile, nicht Filterzeile. Wichtig: NUR wenn die
   erste Spalte auch fixiert ist (Tree- bzw. fixedColumns-Modus → pivot-col-fixed). Sonst
   (flache Liste ohne fixierte Spalte) bliebe die Header-Zelle beim Horizontal-Scroll links
   stehen, während ihre Body-Zellen (position:static) wegscrollen — sichtbarer Versatz. */
table.pivot thead tr:first-child th.tree,
table.pivot thead tr:first-child th.pivot-col-fixed:first-child {
    position: sticky; left: 0;
    z-index: 300 !important; /* Ganz oben */
    background-color: var(--pvt-header-bg) !important;
    border-right: 2px solid var(--border-strong) !important;
    border-bottom: 1px solid var(--border-strong);
}

/* E) Filterzeile erste Zelle (Muss auch sticky sein!). Wie D: NUR wenn die erste Spalte
   auch fixiert ist (Tree- bzw. fixedColumns-Modus). Sonst (flache Liste ohne fixierte
   Spalte, z.B. admin\LogViewer) bekäme die leere erste Filterzelle unconditioneell
   sticky+Rand, während Header-# und Body-PK-Spalte normal scrollen — Versatz. */
table.pivot tr.pivot-filter-row th.tree:first-child,
table.pivot tr.pivot-filter-row th.pivot-col-fixed:first-child {
    position: sticky; left: 0;
    z-index: 201 !important; /* Höher als normale Filterzellen (101) */
    background-color: var(--bg-panel) !important;
    border-right: 2px solid var(--border-strong);
}

/* F) Generisch fixierte Spalten (pivot-col-fixed) */
table.pivot th.pivot-col-fixed,
table.pivot td.pivot-col-fixed {
    position: sticky !important;
    z-index: 110 !important;
    background-color: var(--bg-app);
}
table.pivot th.pivot-col-fixed-last,
table.pivot td.pivot-col-fixed-last {
    border-right: 2px solid var(--border-strong) !important;
}

table.pivot thead th.pivot-col-fixed {
    z-index: 299 !important;
    background-color: var(--pvt-header-bg) !important;
}

tr.pivot-filter-row th.pivot-col-fixed,
tr.pivot-filter-row td.pivot-col-fixed {
    z-index: 200 !important;
    background-color: var(--bg-panel) !important;
}

/* 8. ZEILEN & SPALTEN STYLES (Color Fixed) */

/* Level 0 */
table.pivot tr.pvtlvl0 th, table.pivot tr.pvtlvl0 td {
    background-color: var(--pvt-header-bg) !important;
    color: var(--pvt-header-txt) !important;
    font-weight: bold;
}
/* Level 1 */
table.pivot tr.pvtlvl1 th, table.pivot tr.pvtlvl1 td {
    background-color: var(--pvt-lvl1-bg) !important;
    color: var(--pvt-lvl1-txt) !important;
    border-top: 1px solid var(--border-strong);
}

table.pivot th.text,
table.pivot td.text { 
  text-align: left !important; 
}

/* Custom column classes */
.mono-font {
    font-family: "Courier New", Courier, monospace;
    font-weight: bold;
}

/* --- Summenspalten Farben --- */
/* Prinzip: Gesamt (ttl_line/ttl_share) dunkelst → clevel0 → clevel1 → clevel2 hellst */
/* !important durchgängig nötig: überschreibt die Zebra-/Level-Hintergründe von
   table.pivot td (tr-even/tr-odd + clevelN), die gleiche Spezifität haben. */

/* A) BODY-Zellen (td) — volle Spaltenfarbe */

table.pivot td.ttl_line {
    background-color: var(--pvt-col-total) !important;
    color: var(--text-main) !important;
    border-left: 2px solid var(--border-strong) !important;
    font-weight: bold;
}
table.pivot td.ttl_share {
    background-color: var(--pvt-col-total) !important;
    color: var(--text-main) !important;
    font-weight: bold;
}
table.pivot td.ttl_col.clevel0 {
    background-color: var(--pvt-col-0-bg) !important;
    color: var(--text-main) !important;
    border-left: 2px solid var(--border-strong) !important;
    font-weight: bold;
}
table.pivot td.ttl_col.clevel1 {
    background-color: var(--pvt-col-1-bg) !important;
    color: var(--text-main) !important;
    border-left: 1px solid var(--border-strong) !important;
    font-weight: 600;
}
table.pivot td.ttl_col.clevel2 {
    background-color: var(--pvt-col-2-bg) !important;
    color: var(--text-main) !important;
    border-left: 1px solid var(--border-color) !important;
    font-weight: normal;
}

/* B) HEADER-Zellen (thead th) — Header-Farbe bleibt, Spaltenfarbe als Overlay */
/* box-shadow inset mischt die Spaltenfarbe halbtransparent über die Header-Farbe */

table.pivot thead th.ttl_line {
    background-color: var(--pvt-header-bg) !important;
    color: var(--pvt-header-txt) !important;
    box-shadow: inset 0 0 0 9999px var(--pvt-col-total-overlay) !important;
    border-left: 2px solid var(--border-strong) !important;
}
table.pivot thead th.ttl_share {
    background-color: var(--pvt-header-bg) !important;
    color: var(--pvt-header-txt) !important;
    box-shadow: inset 0 0 0 9999px var(--pvt-col-total-overlay) !important;
}
table.pivot thead th.ttl_col.clevel0 {
    background-color: var(--pvt-header-bg) !important;
    color: var(--pvt-header-txt) !important;
    box-shadow: inset 0 0 0 9999px var(--pvt-col-0-overlay) !important;
    border-left: 2px solid var(--border-strong) !important;
}
table.pivot thead th.ttl_col.clevel1 {
    background-color: var(--pvt-header-bg) !important;
    color: var(--pvt-header-txt) !important;
    box-shadow: inset 0 0 0 9999px var(--pvt-col-1-overlay) !important;
    border-left: 1px solid var(--border-strong) !important;
}
table.pivot thead th.ttl_col.clevel2 {
    background-color: var(--pvt-header-bg) !important;
    color: var(--pvt-header-txt) !important;
    box-shadow: inset 0 0 0 9999px var(--pvt-col-2-overlay) !important;
    border-left: 1px solid var(--border-color) !important;
}

/* Tree Padding & Alignment */
table.pivot tr.pvtlvl2 th.tree { padding-left: 20px !important; }
table.pivot tr.pvtlvl3 th.tree { padding-left: 35px !important; }
table.pivot tr.pvtlvl4 th.tree { padding-left: 50px !important; }
table.pivot td { text-align: right; }
table.pivot th.text { text-align: left; }
/* Explizit rechtsbündige Detail-/Spare-Spalten (addGroupRowColumns 'align'=>'right',
   z.B. BrokerPortfolio Stück/Kurs/Delta): diese Spalten rendern als <th> (Teil der
   Zeilen-Header-Region), wo ohne eigene Klasse nur die UA-Default-Zentrierung von <th>
   greift. Bewusst eine EIGENE Klasse statt eines globalen `table.pivot th`-Defaults —
   Reports mit unmarkierten Text-Spalten in <th> (Namen/Ticker/Labels) sollen nicht
   plötzlich rechtsbündig werden. */
table.pivot th.num { text-align: right; }

/* Zebra Striping – Klassen statt :nth-child (verhindert O(n²) Selector-Matching beim Sortieren) */
table.pivot tr.tr-even td { background-color: var(--pvt-bg-even); }
table.pivot tr.tr-odd  td { background-color: var(--pvt-bg-odd); }

/* Details Spalten (Normalisierung) */
th.pivot-tree-details, td.pivot-tree-details {
    font-weight: normal; position: static; /* Scrollt mit */
}

/* 9. INTERAKTION & DETAILS TOGGLE */

/* Hover */
.pivot_over { background-color: var(--pvt-hover) !important; cursor: crosshair; }

/* Selection */
table.pivot tr.pivot_marked th,
table.pivot tr.pivot_marked td {
    background-color: var(--pvt-marked) !important;
    color: var(--pvt-marked-txt) !important;
}
/* Cross-Hover Highlight */
table.pivot td.pivot_over, table.pivot th.pivot_over {
    background-color: var(--pvt-marked) !important;
    color: var(--pvt-marked-txt) !important;
    position: relative; z-index: 2000 !important; /* Über Sticky Header */
    outline: 1px solid rgba(0,0,0,0.1); outline-offset: -1px;
}

/* Bereichs-Selektion (Copy) */
table.pivot td.pivot-sel,
table.pivot th.pivot-sel {
    background-color: rgba(59, 130, 246, 0.15) !important;
    outline: 1px solid rgba(59, 130, 246, 0.4);
    outline-offset: -1px;
}
[data-theme="dark"] table.pivot td.pivot-sel,
[data-theme="dark"] table.pivot th.pivot-sel {
    background-color: rgba(96, 165, 250, 0.2) !important;
    outline-color: rgba(96, 165, 250, 0.5);
}
.pivot-copy-btn {
    display: inline-flex; align-items: center; justify-content: center;
    width: 28px; height: 28px; padding: 0; border: 1px solid var(--border-color, #ccc);
    border-radius: 4px; background: var(--bg-panel, #fff); color: var(--text-main, #333);
    cursor: pointer; box-shadow: 0 2px 6px rgba(0,0,0,0.15);
    transition: background 0.15s, color 0.15s;
}
.pivot-copy-btn:hover { background: var(--pvt-hover, #e0e7ff); }
.pivot-copy-btn.pivot-copy-ok {
    background: #22c55e !important; color: #fff !important;
    border-color: #16a34a;
}

/* DETAILS AUSBLENDEN (FIX: !important zwingend erforderlich) */
.pivot-tree-hide {
    display: none !important;
    visibility: collapse !important;
}

/* Breitenbegrenzte Tree-/Detail-Zelle: harte max-width + Ellipsis, Volltext im
   title-Tooltip. Gesetzt vom Renderer (Pivot::clipCell) für lange Labels (z. B.
   ausgeschriebene Wertpapiernamen), damit sie die (sticky) Spalte nicht sprengen.
   Die inline max-width:<px> aus dem Renderer überschreibt das 100%-Fallback. */
.pivot-clip {
    display: inline-block;
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    vertical-align: bottom;
}

/* Pivot-Toolbar-Button während einer laufenden Aktion (Details/Alle auf/zu):
   nur die Eieruhr (.btn-spinner) zeigen, das statische +/-/Icon kurz ausblenden. */
.pivot-toolbar .btn.pv-busy > [aria-hidden="true"] { display: none; }

/* 10. ICONS (SVG Data URI + Click Fix) */
span.pivot-collapse {
    width: 14px; height: 14px; display: inline-block; margin-right: 8px;
    vertical-align: middle;
    cursor: pointer; 
    
    /* FIX: Hoher Z-Index damit Klick funktioniert */
    position: relative; 
    z-index: 2005 !important; 
    
    background-size: contain; background-repeat: no-repeat; background-position: center;
    opacity: 0.7; transition: opacity 0.2s;
}
span.pivot-collapse:hover { opacity: 1; transform: scale(1.1); }

.pivot-collapse-minus {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23475569' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='5' y1='12' x2='19' y2='12'%3E%3C/line%3E%3C/svg%3E");
}
.pivot-collapse-plus {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23475569' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='12' y1='5' x2='12' y2='19'%3E%3C/line%3E%3Cline x1='5' y1='12' x2='19' y2='12'%3E%3C/line%3E%3C/svg%3E");
}
/* Dark Mode Invertierung */
[data-theme="dark"] .pivot-collapse-minus, [data-theme="dark"] .pivot-collapse-plus { filter: invert(1); }

/* 11. POPOVER & PANEL (Fixed Positioning) */
.app-popover, .pivot-popover-panel {
    display: none;
    position: fixed; /* Fix: Panel bleibt am Viewport */
    z-index: 9000;
    
    background-color: var(--bg-panel);
    border: 1px solid var(--border-strong);
    box-shadow: 0 4px 6px -1px rgba(0,0,0,0.1), 0 2px 4px -2px rgba(0,0,0,0.1);
    border-radius: 8px;
    min-width: 300px; max-width: 450px;
    
    opacity: 0; transform: translateY(-8px);
    transition: opacity 0.2s ease, transform 0.2s ease;
}
.app-popover.visible, .app-popover.show { opacity: 1; transform: translateY(0); }

.popover-header {
    padding: 10px 15px; font-weight: 600; 
    border-bottom: 1px solid var(--border-color); background: var(--bg-app);
    display: flex; justify-content: space-between; align-items: center;
}
.popover-body { padding: 15px; max-height: 60vh; overflow-y: auto; }
.popover-footer {
    padding: 10px 15px; border-top: 1px solid var(--border-color);
    text-align: right; background: var(--bg-app);
}
/* Fokussierbarer Ersatz für den früheren <span onclick> — BFSG/WCAG 2.1 AA
   (2.1.1 Tastatur, 4.1.2 Name/Rolle/Wert). Browser-Fokusring bleibt bewusst
   erhalten, kein outline:none. */
.popover-close {
    background: none; border: none; padding: 2px 4px; margin: 0;
    font: inherit; line-height: 1; cursor: pointer; color: inherit;
}

/* Form Elements im Popover */
.form-grid { display: flex; flex-direction: column; gap: 12px; }
.form-grid.horizontal {
    flex-direction: row;
    flex-wrap: wrap;
    align-items: flex-start;
}
.form-group label { display: block; font-size: 0.8rem; font-weight: 600; margin-bottom: 4px; color: var(--text-muted); }
.form-control, input.pivot_input_filter {
    width: 100%; box-sizing: border-box; padding: 6px;
    border: 1px solid var(--border-color); border-radius: 3px;
    background-color: var(--bg-app); color: var(--text-main);
}


/* --- FILTER ROW & MULTISELECT --- */

/* =========================================
   12. FILTER ZEILE (UNIFORMITY FIX)
   ========================================= */
/* !important durchgängig nötig: jquery.multiselect bringt eigenes Stylesheet +
   Inline-Styles am .ms-options-wrap mit — Input/Select/MultiSelect-Button werden
   nur durch Override dieser Drittanbieter-Optik einheitlich. */

/* A) Container für MultiSelect auf 100% zwingen */
.pivot-filter-row .ms-options-wrap {
    width: 100% !important;
    height: 100% !important;
    display: block !important; 

}
table.pivot tr.pivot-filter-row th {
    padding: 0px;
}

/* B) DIE "GLEICHMACHUNG" (Input, Select & MultiSelect Button) */
.pivot-filter-row input.pivot_input_filter,
.pivot-filter-row select.pivot_input_filter,
.pivot-filter-row .ms-options-wrap > button {
    /* 1. Layout & Größe */
    width: 100% !important;
    height: 100% !important;
    min-height: 26px !important; /* Fixe Höhe für alle */
    max-height: 26px !important; 
    
    display: block ; /* FIX: Fill cell width */
    box-sizing: border-box !important;
    
    /* 2. Reset der Optik (Rahmen weg, Zellen-Rahmen nutzen) */
    background-color: var(--bg-app) !important;
    color: var(--text-main) !important;
    border: none !important; 
    border-radius: 0 !important;
    box-shadow: none !important;
    
    /* 3. Typografie & Ausrichtung */
    padding: 0 6px !important;   /* Einheitlicher Innenabstand */
    font-size: 0.9em !important;
    line-height: 26px !important; /* Vertikal zentrieren */
    font-family: inherit !important;
    text-align: left !important;
}

/* C) Speziell für den MultiSelect Button (Flexbox für Text + Pfeil) */
.pivot-filter-row .ms-options-wrap > button {
    display: flex !important;
/*    align-items: center !important; */
    justify-content: space-between !important; /* Text links, Pfeil rechts */
}
/* Den Text im Button beschneiden, falls zu lang */
.pivot-filter-row .ms-options-wrap > button > span {
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* D) Fokus Effekt (Blauer Rahmen INNEN) */
.pivot-filter-row input.pivot_input_filter:focus,
.pivot-filter-row select.pivot_input_filter:focus,
.pivot-filter-row .ms-options-wrap > button:focus,
.pivot-filter-row .ms-options-wrap.ms-active > button {
    outline: none !important;
    box-shadow: inset 0 0 0 2px #3b82f6 !important; /* Fokus sichtbar machen */
    background-color: var(--bg-app) !important;
    z-index: 2; /* Damit der Rahmen über Nachbarn liegt */
}

/* Dropdown-Navigation → base.css (app-nav-dropdown-*) */

/* MultiSelect Styles → siehe Abschnitt 12b */

/* Dashboard */
/* box-sizing: ohne border-box läuft width:100% + Padding rechts über und wird
   vom overflow-x:hidden des .app-workspace abgeschnitten (rechter Rand fehlt). */
.dashboard-container { box-sizing: border-box; width: 100%; margin: 0 auto; padding: clamp(12px, 2vw, 28px); font-size: 1rem; }
.dashboard-section { margin-bottom: 30px; }
.dashboard-section h2 { font-size: 1.25rem; margin-bottom: 10px; border-bottom: 1px solid var(--border-color); padding-bottom: 6px; }
.dashboard-section p { font-size: 0.95rem; line-height: 1.6; color: var(--text-muted); padding: 0; }
/* Gruppen-Ueberschrift der Bericht-Uebersicht (gruppiert nach Top-Level-Kategorie) */
.dashboard-report-group { font-size: 1.05rem; margin: 18px 0 8px; color: var(--text-main); }
/* erste Gruppe sitzt direkt unter der h2 „Verfügbare Berichte" — engerer Abstand */
.dashboard-section h2 + .dashboard-report-group { margin-top: 4px; }
.dashboard-report-group + .cms-card-grid { margin-bottom: 6px; }
/* Kachel-Beschreibung auf einheitlich 2 Zeilen normieren: lange Texte werden mit
   Ellipsis gekuerzt, kurze reservieren via min-height denselben Platz — so wirken
   die Kacheln trotz unterschiedlich langer DB-Texte gleich hoch/ausgewogen. */
.dashboard-container .cms-card-text p {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    overflow: hidden;
    line-height: 1.4;
    min-height: 2.8em;
}
/* KPI-Tiles (Dashboard + Stock-Detail Kennzahlen) */
.kpi-row { display: flex; flex-wrap: wrap; gap: 1.5rem; margin-bottom: 1rem; }
.dashboard-kpi {
    flex: 1 1 200px; min-width: 180px;
    text-align: center; padding: 1rem 1.5rem;
    background: var(--surface-container-low);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-md);
}
.dashboard-kpi-value { font-size: 1.8rem; font-weight: bold; color: var(--primary-container); line-height: 1.1; }
.dashboard-kpi-label { font-size: 0.9rem; margin-top: 0.4rem; color: var(--text-muted); }
.dashboard-kpi-meta { font-size: 0.7rem; }
/* Datenquellen-Tabelle — Ampel-Pille für Freshness-Status. Tokens
   --status-success/draft/hold-*/--text-muted sind in base.css mit
   Dark-Mode-Parity definiert. */
.dashboard-freshness-cell { font-variant-numeric: tabular-nums; white-space: nowrap; }
.dashboard-freshness {
    display: inline-block;
    padding: 2px 10px;
    border-radius: var(--radius-full);
    font-size: 0.75rem;
    font-weight: 600;
    white-space: nowrap;
}
.dashboard-freshness--ok       { background: var(--status-success-bg); color: var(--status-success-text); }
.dashboard-freshness--warn     { background: var(--status-draft-bg);   color: var(--status-draft-text); }
.dashboard-freshness--late     { background: var(--status-hold-bg);    color: var(--status-hold-text); }
.dashboard-freshness--ondemand { background: var(--surface-container); color: var(--text-muted); }
.dashboard-freshness--unknown  { background: var(--surface-container); color: var(--text-muted); }

/* admin\SystemDashboard — Modul-/Sichtbarkeits-Badges */
.modules-badges { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
.module-badge, .visibility-badge {
    display: inline-block;
    padding: 2px 8px;
    border-radius: var(--radius-full);
    font-size: 0.72rem;
    font-weight: 600;
    background: var(--surface-container);
    color: var(--text-main);
}
.module-badge { font-family: var(--font-mono, monospace); }
/* visibility-badge: --surface-container-high statt --bg-panel, damit der
   Kontrast im Dark-Mode erhalten bleibt (bg-panel und surface-container sind
   im Dark-Mode beide #1d1e25, surface-container-high #272830 hebt ab). */
.visibility-badge { background: var(--surface-container-high); color: var(--text-muted); }

/* Filter Tooltip */
.pivot-filter-tooltip {
    position: fixed; display: none; background-color: var(--bg-panel); color: var(--text-main);
    border: 1px solid var(--border-strong); box-shadow: var(--shadow-md);
    padding: 10px; border-radius: 4px; font-size: 0.85em; z-index: 10000;
    width: max-content; min-width: 150px; max-width: 300px;
    line-height: 1.4em; pointer-events: none;
}
.pivot-filter-tooltip strong { display: block; margin-bottom: 5px; border-bottom: 1px solid var(--border-color); }
.pivot-filter-tooltip code { background: var(--bg-panel); padding: 0 4px; border-radius: 2px; font-family: monospace; font-weight: bold; }

/* 13. HEATMAP */
table.pivot.show-heatmap {
  --heat-bull: 34, 197, 94;
  --heat-bear: 239, 68, 68;
  --heat-bull-solid: #22c55e;
  --heat-bear-solid: #ef4444;
}
table.pivot.show-heatmap.heatmap-cb {
  --heat-bull: 59, 130, 246;
  --heat-bear: 249, 115, 22;
  --heat-bull-solid: #3b82f6;
  --heat-bear-solid: #f97316;
}
/* !important nötig: der box-shadow-Heatmap-Layer muss die Zebra-/Level-Backgrounds
   von table.pivot td (tr-even/tr-odd, clevelN) gleicher Spezifität überlagern. */
table.pivot.show-heatmap .heat-bull-1 { box-shadow: inset 0 0 0 9999px rgba(var(--heat-bull), 0.1) !important; }
table.pivot.show-heatmap .heat-bull-2 { box-shadow: inset 0 0 0 9999px rgba(var(--heat-bull), 0.2) !important; }
table.pivot.show-heatmap .heat-bull-3 { box-shadow: inset 0 0 0 9999px rgba(var(--heat-bull), 0.3) !important; }
table.pivot.show-heatmap .heat-bull-4 { box-shadow: inset 0 0 0 9999px rgba(var(--heat-bull), 0.4) !important; }
table.pivot.show-heatmap .heat-bull-5 { box-shadow: inset 0 0 0 9999px rgba(var(--heat-bull), 0.5) !important; color:#fff!important; text-shadow: 0 1px 2px rgba(0,0,0,0.4); }
table.pivot.show-heatmap .heat-bull-6 { box-shadow: inset 0 0 0 9999px rgba(var(--heat-bull), 0.6) !important; color:#fff!important; }
table.pivot.show-heatmap .heat-bull-7 { box-shadow: inset 0 0 0 9999px rgba(var(--heat-bull), 0.7) !important; color:#fff!important; }
table.pivot.show-heatmap .heat-bull-8 { box-shadow: inset 0 0 0 9999px rgba(var(--heat-bull), 0.8) !important; color:#fff!important; }
table.pivot.show-heatmap .heat-bull-9 { box-shadow: inset 0 0 0 9999px rgba(var(--heat-bull), 0.9) !important; color:#fff!important; }
table.pivot.show-heatmap .heat-bull-10{ box-shadow: inset 0 0 0 9999px rgba(var(--heat-bull), 1.0) !important; color:#fff!important; }

table.pivot.show-heatmap .heat-bear-1 { box-shadow: inset 0 0 0 9999px rgba(var(--heat-bear), 0.1) !important; }
table.pivot.show-heatmap .heat-bear-2 { box-shadow: inset 0 0 0 9999px rgba(var(--heat-bear), 0.2) !important; }
table.pivot.show-heatmap .heat-bear-3 { box-shadow: inset 0 0 0 9999px rgba(var(--heat-bear), 0.3) !important; }
table.pivot.show-heatmap .heat-bear-4 { box-shadow: inset 0 0 0 9999px rgba(var(--heat-bear), 0.4) !important; }
table.pivot.show-heatmap .heat-bear-5 { box-shadow: inset 0 0 0 9999px rgba(var(--heat-bear), 0.5) !important; color:#fff!important; text-shadow: 0 1px 2px rgba(0,0,0,0.4); }
table.pivot.show-heatmap .heat-bear-6 { box-shadow: inset 0 0 0 9999px rgba(var(--heat-bear), 0.6) !important; color:#fff!important; }
table.pivot.show-heatmap .heat-bear-7 { box-shadow: inset 0 0 0 9999px rgba(var(--heat-bear), 0.7) !important; color:#fff!important; }
table.pivot.show-heatmap .heat-bear-8 { box-shadow: inset 0 0 0 9999px rgba(var(--heat-bear), 0.8) !important; color:#fff!important; }
table.pivot.show-heatmap .heat-bear-9 { box-shadow: inset 0 0 0 9999px rgba(var(--heat-bear), 0.9) !important; color:#fff!important; }
table.pivot.show-heatmap .heat-bear-10{ box-shadow: inset 0 0 0 9999px rgba(var(--heat-bear), 1.0) !important; color:#fff!important; }

/* Tree Mini-Bars */
table.pivot td.tree { padding-right: 8px; position: relative; }
.mini-heat-bar {
  position: absolute; top: 4px; bottom: 4px; right: 0; display: flex; gap: 1px; z-index: 10; pointer-events: none;
}
.mini-heat-bar i { width: 4px; height: 100%; display: block; border-radius: 1px; }
.mini-heat-bar .heat-bull-10 { background-color: var(--heat-bull-solid, #22c55e); }
.mini-heat-bar .heat-bear-10 { background-color: var(--heat-bear-solid, #ef4444); }

.sort-indicator { display: inline-flex; align-items: center; vertical-align: middle; margin-left: 3px; }
.sort-indicator svg { width: 14px; height: 14px; }
/* Prioritäts-Nummer bei Mehrspalten-Sort (Shift+Klick): 1 = primär, 2 = sekundär … */
.sort-prio { font-size: 0.62em; font-weight: 700; line-height: 1; vertical-align: super; margin-left: 1px; opacity: 0.85; }

.heat-sort-icons {
    position: absolute;
    right: 4px;
    top: 50%;
    transform: translateY(-50%);
    display: flex;
    gap: 2px;
    pointer-events: auto;
    z-index: 11;
}
.heat-sort-icon,
.heat-trend-icon {
    cursor: pointer;
    opacity: 0.45;
    pointer-events: auto;
    transition: opacity 0.15s;
    white-space: nowrap;
    display: inline-flex;
    align-items: center;
}
.heat-sort-icon svg,
.heat-trend-icon svg {
    width: 14px;
    height: 14px;
}
.heat-sort-icon:hover,
.heat-trend-icon:hover {
    opacity: 1;
}
.heat-sort-icon.active,
.heat-trend-icon.active {
    opacity: 1;
}
.heat-sort-icon .sort-dir,
.heat-trend-icon .sort-dir {
    display: inline-flex;
}
.heat-sort-icon .sort-dir svg,
.heat-trend-icon .sort-dir svg {
    width: 10px;
    height: 10px;
}

.ms-options-wrap.ms-active > .ms-options {
    visibility: visible;
    text-align: left;
    font-weight: normal;
}

/* =========================================
   12b. MULTISELECT DROPDOWN LISTE (FIXED)
   ========================================= */

/* Der Container der Liste */
.ms-options { 
    /* WICHTIG: Scrollbar X töten */
    overflow-x: hidden !important;
    overflow-y: auto !important; /* Vertikal scrollen erlaubt */
    
    /* Layout */
    position: absolute; top: 100%; left: 0; z-index: 1001 !important;
    min-width: 220px; max-width: 400px; /* Begrenzte Breite verhindert Riesen-Menüs */
    
    background: var(--bg-panel) !important;
    border: 1px solid var(--border-strong) !important;
    box-shadow: var(--shadow-md);
    border-radius: 4px; 
    padding: 5px 0;
    margin-top: 1px;
}

/* Die Liste selbst */
.ms-options ul { 
    list-style: none !important; 
    padding: 0 !important; 
    margin: 0 !important; 
}

/* Das einzelne Item (Zeile) */
.ms-options li { 
    padding: 4px 8px !important; 
    cursor: pointer; 
    margin: 0 !important;
    list-style: none !important;
}

.ms-options li:hover {
    background-color: var(--pvt-lvl1-bg) !important;
}

/* Ausgewaehlte Zeile: jquery.multiselect.css setzt hartes #efefef aufs Label
   (li.selected label) — im Dark-Mode steht der helle --text-main dann auf
   hellem Grund (ausgewaschen). CI-Token statt Fixwert haelt beide Themes lesbar. */
.ms-options li.selected label {
    background-color: var(--surface-container-high) !important;
}
/* Hover-Feedback auch auf selektierten Zeilen: das opake Label ueberdeckt sonst
   den li:hover — gleicher Ton wie unselektierte Zeilen, in beiden Themes lesbar. */
.ms-options li.selected:hover label {
    background-color: var(--pvt-lvl1-bg) !important;
}

/* Das Label (Text + Checkbox) - Hier passiert der Fix für den Scrollbalken */
.ms-options li label {
    display: flex !important;
    align-items: center;
    gap: 6px;
    width: 100% !important;
    cursor: pointer;
    color: var(--text-main) !important;
    white-space: nowrap !important;
    overflow: hidden !important;
    text-overflow: ellipsis !important;
    line-height: 1.1
}

.ms-options li label input {
    flex-shrink: 0;
    vertical-align: middle;
}

/* Select All Link */
.ms-selectall { 
    color: var(--btn-primary-bg) !important; 
    padding: 5px 10px; 
    display: block; 
    font-weight: bold; 
    text-decoration: none; 

/* --- DATALIST EXTENSIONS --- */

table.dl.detail-table tbody tr:nth-child(even) td,
table.dl.detail-table tbody tr:nth-child(odd)  td { background-color: inherit; }
table.dl.detail-table tr.tr-even td { background-color: var(--pvt-bg-even); }
table.dl.detail-table tr.tr-odd  td { background-color: var(--pvt-bg-odd); }

/* Pillen-/Badge-Zelle (Pivot\Cell\Pill) — Farbe kommt datengetrieben inline, Form aus der Klasse. */
.dl-pill { display: inline-block; color: #fff; font-weight: 600; padding: 2px 9px; border-radius: 999px; font-size: 11px; white-space: nowrap; }

/* Hinweis-Bänder über der Tabelle (Gating/Coverage/Overflow). */
.dl-notice        { font-size: 12px; margin: 0 0 6px; color: var(--text-muted, #424754); }
.dl-notice--info  { color: var(--accent, #3b82f6); }
.dl-notice--warn  { color: var(--status-warning-text, #d97706); }
.dl-notice--good  { color: var(--status-success-text, #16a34a); }

/* Spaltenfarben (col_class je Spalte). */
.dl-col--accent { color: var(--accent, #3b82f6); }
.dl-col--warn   { color: var(--status-warning-text, #d97706); }
.dl-col--good   { color: var(--status-success-text, #16a34a); }
.dl-col--muted  { color: var(--text-muted, #424754); }

/* Zeilenfarben (rowClass, datengetrieben) — dezenter Hintergrund-Tint.
   Als Layer-Ebene, NICHT als background: die frühere Fassung ((0,1,1)) verlor gegen das Zebra
   (table.pivot tr.tr-even td, (0,2,3)) und färbte faktisch gar nichts. Als Ebene liegt sie
   sauber ÜBER dem Zebra — kein Spezifitäts-Wettlauf, kein !important. */
.dl--layered tr.dl-row--err  > td { --dl-layer-row: color-mix(in srgb, var(--status-error-text, #dc2626) 8%, transparent); }
.dl--layered tr.dl-row--warn > td { --dl-layer-row: color-mix(in srgb, var(--status-warning-text, #d97706) 10%, transparent); }
.dl--layered tr.dl-row--good > td { --dl-layer-row: color-mix(in srgb, var(--status-success-text, #16a34a) 8%, transparent); }
.dl-row--muted td { color: var(--text-muted, #424754); }

/* Statusleiste: links Zähler · mitte Freitext/Chips · rechts Busy-Zone · ganz rechts Info-Symbol.
   line-height:1 gibt der Top-Variante die Höhen-Reserve, die ihre feste Höhe sonst nicht hat:
   16px Content-Box (28 − 2×6 Padding) nehmen das 16px-Icon exakt auf, ein normal umbrechender
   12px-Text überragte sie. */
.dl-statusbar          { display: flex; align-items: center; gap: 12px; font-size: 12px; line-height: 1; color: var(--text-muted, #64748b); padding: 6px 2px; }

/* Top-Variante: klebt beim Scrollen oben, der sticky Tabellenkopf (table.pivot thead th,
   top:0/z-index:100) setzt exakt darunter an. Dafür braucht die Leiste eine BEKANNTE Höhe —
   --dl-sb-h ist die eine Quelle: die Leiste bezieht daraus ihre Höhe, der Kopf seinen
   top-Offset. Ohne Offset schöbe sich der hochscrollende Kopf unter die Leiste; ohne z-index
   über 100 läge er darüber. Kein margin zwischen beiden — eine Lücke würde beim Scrollen
   Datenzeilen durchscheinen lassen.
   Der Kopf-Offset hängt an der Marker-Klasse .dl--sb-top, die DataList::render() genau dann an
   die Tabelle schreibt, wenn die Leiste oben steht — NICHT an einem Sibling-Selektor auf die
   Leiste: die Reihenfolge im Markup ist Zufall, kein Vertrag, und ein dazwischenrutschendes
   Element ließe den Offset still abreißen. Als Klasse auf table.pivot trägt sie auch, wenn die
   StatusBar später an der Pivot-Engine hängt (die kein .dl trägt).
   z-index 320: über JEDER sticky Kopfzelle inkl. Fixed-Col (299) und Super-Ecke (300) —
   siehe Z-Index-Skala im Kopf dieser Datei.
   ACHTUNG bei künftigem top auf der Filterzeile (tr.pivot-filter-row): sie erbt diesen Offset
   heute mit (thead th/td), was korrekt ist, weil sie wie der Kopf auf top:0 sass. Bekommt sie
   je einen eigenen top (Kopfhöhe), muss sie hier ausgenommen werden. */
.dl-statusbar--top,
table.dl--sb-top       { --dl-sb-h: 28px; }
.dl-statusbar--top     { height: var(--dl-sb-h); box-sizing: border-box; position: sticky; top: 0; z-index: 320; background: var(--bg-panel); }
table.dl--sb-top thead th,
table.dl--sb-top thead td { top: var(--dl-sb-h); }

/* Mobil läuft die Leiste im Fluss mit, statt zu kleben: .app-workspace scrollt hier auch
   HORIZONTAL (base.css, ab 768px) — eine sticky Leiste ist aber nur so breit wie ihr
   Containing-Block (Client-Breite, nicht Scroll-Breite) und wandert beim Rechts-Scrollen aus
   dem Bild. Der 28px-Streifen über dem versetzten Kopf gäbe dann die durchscrollenden
   tbody-Zeilen frei. --dl-sb-h: 0 nimmt dem Kopf denselben Offset wieder ab — eine Quelle,
   zwei Zustaende. */
@media (max-width: 768px) {
  .dl-statusbar--top { position: static; height: auto; }
  table.dl--sb-top   { --dl-sb-h: 0px; }
}
/* sticky statt normalem Fluss: bei LAZY_SCROLL waechst <tbody> laufend (code.js
   enableLazyScroll hängt neue Zeilen an), die Leiste liegt aber IM Fluss direkt
   nach der Tabelle (DataList.php render()) — ohne sticky wandert sie mit jedem
   Nachladen aus dem Viewport und blitzt nur kurz auf. .app-workspace ist der
   scrollende Container (overflow-y:auto, base.css) — dasselbe sticky-bottom:0-
   Muster wie .admin-pagination (base.css), die dort denselben Containing-Block
   nutzt und trotz .app-workspace { contain:layout style } korrekt stickt.
   ACHTUNG: kollidiert mit .admin-pagination (auch sticky bottom:0), falls je ein
   DataList-Report MODE_PAGED (Pagination) MIT StatusBar::BOTTOM kombiniert (aktuell
   kein Konsument — LogViewer nutzt ausschließlich lazyScroll()). Vor einer solchen
   Kombination hier einen vertikalen Offset ergänzen statt beide auf bottom:0. */
.dl-statusbar--bottom  { margin-top: 6px; position: sticky; bottom: 0; z-index: 5; background: var(--bg-panel); }
.dl-sb-count           { font-weight: 600; white-space: nowrap; }
.dl-sb-mid             { flex: 1 1 auto; display: flex; align-items: center; gap: 8px; min-width: 0; }
.dl-sb-text            { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dl-sb-chip            { background: color-mix(in srgb, var(--accent, #3b82f6) 14%, transparent); color: var(--accent, #3b82f6); border-radius: 10px; padding: 1px 8px; white-space: nowrap; }
.dl-sb-busy            { flex: 0 0 auto; display: flex; align-items: center; min-width: 16px; }
.dl-sb-spinner, .dl-sb-progress { display: none; }

/* Zone für das Info-Symbol am rechten Rand der Leiste (das Symbol selbst ist .info-icon,
   siehe Tooltip-Block weiter oben — es steht auch außerhalb von DataList). */
.dl-sb-info            { flex: 0 0 auto; display: flex; align-items: center; }

/* Spinner bei Filter-Operationen (pivot:busy/idle). */
.dl-statusbar.is-busy .dl-sb-spinner {
  display: inline-block; width: 12px; height: 12px; border-radius: 50%;
  border: 2px solid color-mix(in srgb, var(--accent, #3b82f6) 30%, transparent);
  border-top-color: var(--accent, #3b82f6);
  animation: dl-spin 0.6s linear infinite;
}
@keyframes dl-spin { to { transform: rotate(360deg); } }

/* Progressbar beim Nachladen (LAZY_SCROLL, Ausbaustufe 6 — is-loading). */
.dl-statusbar.is-loading .dl-sb-progress {
  display: block; width: 80px; height: 3px; border-radius: 2px; overflow: hidden; position: relative;
  background: color-mix(in srgb, var(--accent, #3b82f6) 18%, transparent);
}
.dl-statusbar.is-loading .dl-sb-progress::after {
  content: ""; position: absolute; top: 0; bottom: 0; left: 0; width: 40%; border-radius: 2px;
  background: var(--accent, #3b82f6);
  animation: dl-progress 1s ease-in-out infinite;
}
/* transform statt left animieren — kein Layout-Recalc pro Frame. */
@keyframes dl-progress { 0% { transform: translateX(-100%); } 100% { transform: translateX(250%); } }

/* ── DataList: datengetriebene Farb-Ebenen ────────────────────────────────
   Vier Quellen wollen denselben Zell-Hintergrund: Zebra, Spalten-Band, Zeilen-/Zell-Tint
   (Identitätsfarbe aus der DB) und Ampel/Heatmap. Sie werden NICHT über Spezifität
   gegeneinander ausgespielt (das endet bei !important), sondern GESTAPELT:

     Zebra          → background-color  (unterste Ebene, unverändert)
     Band/Tint/Zell → background-image  (drei halbtransparente Ebenen darüber)

   Reihenfolge im background-image: der erste Layer liegt oben. Damit gilt
   Ampel/Heatmap > Zeilen-/Zell-Tint > Band > Zebra, und jede Ebene bleibt einzeln
   schaltbar. Nicht gesetzte Ebenen sind transparent und damit wirkungslos.

   Die Ebenen liegen nur auf Tabellen, die sie brauchen (.dl--layered, gesetzt von
   DataList::hasColorLayers) — eine gewöhnliche Liste zahlt keine Paint-Kosten dafür. */
.dl--layered tbody td {
  --dl-tint-eff:   var(--dl-tint, transparent);
  --dl-layer-cell: transparent;
  --dl-layer-band: transparent;
  /* Tint-Lasur: 12 % der Identitätsfarbe über dem Zeilenhintergrund. Bewusst eine Lasur und
     keine Vollfläche — so bleibt die Schrift ohne Kontrastrechnung lesbar, und im Dark-Mode
     ergibt dieselbe Farbe automatisch eine dunkle, entsättigte Variante. */
  --dl-layer-row:  color-mix(in srgb, var(--dl-tint-eff) 12%, transparent);
  background-image:
    linear-gradient(var(--dl-layer-cell), var(--dl-layer-cell)),
    linear-gradient(var(--dl-layer-row),  var(--dl-layer-row)),
    linear-gradient(var(--dl-layer-band), var(--dl-layer-band));
}
/* Dark-Override der DB-Farbe: nur für Ausreisser, die im Dark-Mode absaufen. Ohne
   --dl-tint-dark gilt weiter die kanonische Farbe (nullable DB-Spalte). */
[data-theme="dark"] .dl--layered tbody td { --dl-tint-eff: var(--dl-tint-dark, var(--dl-tint, transparent)); }

/* Ampel (ColumnDef::$tone) — diskrete Bewertung, semantische Tokens statt Hex aus dem Report. */
.dl--layered tbody td.dl-tone--good    { --dl-layer-cell: color-mix(in srgb, var(--status-success-text, #16a34a) 14%, transparent); }
.dl--layered tbody td.dl-tone--warn    { --dl-layer-cell: color-mix(in srgb, var(--status-warning-text, #d97706) 16%, transparent); }
.dl--layered tbody td.dl-tone--bad     { --dl-layer-cell: color-mix(in srgb, var(--status-error-text,   #dc2626) 14%, transparent); }
.dl--layered tbody td.dl-tone--neutral { --dl-layer-cell: color-mix(in srgb, var(--text-muted,          #64748b) 10%, transparent); }

/* Heatmap (ColumnDef::$heat) — stetige Skala. Die Zelle liefert nur eine ZAHL (--dl-t, 0..1),
   die Farbe entsteht hier aus einem Token. Kein Farbwert aus PHP → Dark-Mode bleibt intakt.
   Marker-KLASSE, kein Attribut-Selektor: `[style*="--dl-t"]` würde als Substring auch `--dl-tint`
   treffen und einer Zelle mit Tint+Ampel die Ampel überschreiben (gleiche Spezifität, spätere
   Quelle) — und ein style-Substring-Match ist bei tausenden Zellen der teuerste Selektor. */
.dl--layered tbody td.dl-heat {
  --dl-layer-cell: color-mix(in srgb, var(--accent, #3b82f6) calc(var(--dl-t, 0) * 55%), transparent);
}

/* Spalten-Bänder (Themenblöcke): bewusst neutrales Grau, nicht bunt — bunte Bänder würden
   mit Zeilen-Tint und Ampel um dieselbe Aussage konkurrieren. Nur jede zweite Gruppe wird
   getönt, die Trennlinien tragen den Rest. */
.dl--layered .dl-band-a { --dl-layer-band: color-mix(in srgb, var(--text-main, #111) 4%, transparent); }
.dl th.dl-stub, .dl td.dl-stub      { border-left: 2px solid var(--border-strong, #94a3b8); }
.dl th.dl-grp-end, .dl td.dl-grp-end { border-right: 2px solid var(--border-strong, #94a3b8); }

/* Hintergründe auch im Druck/PDF ausgeben — Chrome unterschlägt sie sonst. */
.dl--layered { print-color-adjust: exact; -webkit-print-color-adjust: exact; }

/* ── DataList: Gliederung (Excel-Outline) ─────────────────────────────────
   Spalten-Gruppen: Kopfzeile mit Label + [−]-Box; eingeklappt bleibt nur die schmale
   Stub-Spalte mit der [+]-Box stehen (wie in Excel). Zeilen-Gruppen: Kopfzeile mit
   Toggle, Kinder hängen per data-parent darunter. */
.dl-hidden { display: none; }

/* Spezifität (0,2,1): `table.pivot thead th` und `.detail-table thead th` (beide (0,1,2))
   würden Padding/Font sonst überstimmen und von der kompakten Gliederungszeile bliebe nichts. */
.dl .dl-grp-row th { padding: 2px 6px; font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.04em; }

/* Gruppen-Kopfzelle. WICHTIG — zwei Fallen, die hier zusammenkommen:
   1. `background:` als KURZFORM würde background-image mit zurücksetzen und damit den
      Layer-Stack der Tabelle killen. Deshalb ausschließlich background-color.
   2. Die Zelle erbt von `table.pivot thead th` sowohl `position: sticky` als auch die HELLE
      Header-Schrift (--pvt-header-txt auf dunklem --pvt-header-bg). Eine (fast) transparente
      Fläche ergäbe im Light-Mode weiße Schrift auf hellem Grund — unsichtbar — und die
      Datenzeilen scrollten sichtbar durch die stehende Zelle hindurch. Also: OPAKE Fläche
      plus passendes Textfarb-Token (--pvt-lvl1-* schaltet mit dem Theme mit). */
.dl th.dl-grp {
  text-align: left;
  background-color: var(--pvt-lvl1-bg);
  color: var(--pvt-lvl1-txt);
  border-bottom: 1px solid var(--border-strong, #94a3b8);
}
.dl th.dl-grp-gap { background-color: var(--pvt-header-bg); border-bottom: 0; }
.dl-grp-label { margin-left: 6px; vertical-align: middle; }
/* Eingeklappt schrumpft die Kopfzelle auf die Stub-Breite — das Label passt nicht mehr,
   die Box bleibt. */
.dl th.dl-grp--collapsed .dl-grp-label { display: none; }
.dl-col-stub { width: 26px; }

/* Die [+]/[−]-Box — Excel-Optik: kleines Quadrat mit Rahmen, klickbar. */
.dl-outline-toggle {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 16px;
  height: 16px;
  padding: 0;
  border: 1px solid var(--border-strong, #94a3b8);
  border-radius: 3px;
  background-color: var(--bg-panel, #fff);
  color: var(--text-main, #111);
  font-size: 12px;
  line-height: 1;
  font-weight: 700;
  cursor: pointer;
  vertical-align: middle;
}
.dl-outline-toggle:hover { border-color: var(--accent, #3b82f6); color: var(--accent, #3b82f6); }
/* Touch-Ziel vergrössern, ohne das Kästchen optisch aufzublasen. VERTIKAL grosszügig, horizontal
   knapp: die Stub-Spalte ist nur 26 px breit — ein symmetrisches Overlay ragte in die benachbarte
   (sortierbare) Kopfzelle und finge deren Klicks ab, die Spalte liesse sich nicht mehr sortieren. */
.dl-outline-toggle::after { content: ""; position: absolute; inset: -8px -2px; }

/* Ebenenschalter über der Tabelle (Excel: [1] nur Gruppen · [2] alle Details). */
.dl-outline-bar { display: flex; align-items: center; gap: 4px; margin: 0 0 6px; }
.dl-outline-bar__label { font-size: 0.72rem; color: var(--text-muted, #64748b); margin-right: 4px; }
/* min 32 px: Touch-Ziel-Untergrenze des Projekts (die Leiste steht frei, hier ist Platz dafür). */
.dl-outline-level {
  min-width: 32px;
  min-height: 32px;
  padding: 0;
  border: 1px solid var(--border-strong, #94a3b8);
  border-radius: 3px;
  background-color: var(--bg-panel, #fff);
  color: var(--text-main, #111);
  font-size: 11px;
  font-weight: 700;
  cursor: pointer;
}
.dl-outline-level:hover { border-color: var(--accent, #3b82f6); color: var(--accent, #3b82f6); }

/* Zeilen-Gruppen (DataList::groupRows).
   table.dl-Präfix (0,3,3): das Zebra von .detail-table (table.dl.detail-table tr.tr-even td)
   hat dieselbe Spezifität und stünde sonst später in der Quelle → der Gruppenkopf sähe aus wie
   eine Datenzeile. background-COLOR (keine Kurzform), sonst fiele der Tint-Layer der Zeile mit. */
table.dl tr.dl-rowgrp > td.dl-rowgrp-cell {
  background-color: var(--surface-container-low, #f0f3ff);
  border-top: 1px solid var(--border-strong, #94a3b8);
  font-weight: 600;
  text-align: left;
  padding: 5px 10px;
}
.dl-rowgrp-label { margin-left: 8px; }
/* Kontrast: Accent-Text auf Accent-Lasur läge bei ~3:1 (unter WCAG AA, BFSG-relevant) —
   deshalb kräftigere Fläche und die reguläre Textfarbe. */
.dl-rowgrp-count {
  margin-left: 8px;
  padding: 0 7px;
  border-radius: var(--radius-full, 999px);
  background-color: color-mix(in srgb, var(--accent, #3b82f6) 22%, transparent);
  color: var(--text-main);
  font-size: 0.72rem;
  font-weight: 600;
}
/* Eingeklappte Kinder: per KLASSE verstecken, nicht per inline display — der Client-Filter
   setzt bei Treffern style.display='' und würde eine Inline-Regel plätten. So bleiben
   Gliederung und Filter unabhängig voneinander. */
.dl tr.dl-collapsed { display: none; }


/* ── Kategorie-Archiv ──────────────────────────────────────────────────────

/* --- WIDGETS: DATABAR & CELL CHARTS --- */

/* Daten-Balken (\layout\bar\TrackBar) — Track + prozentualer Fill als Markup. Für HTML-
   Kontexte, in denen kein Gradient-Background auf der Zelle möglich ist (Zell-Escaping).
   Variable Teile (Höhe, Track-/Fill-Farbe, Breite) kommen inline; der Rest ist geteilt. */
.databar { display: block; width: 100%; border-radius: 3px; overflow: hidden; }
.databar__fill { display: block; height: 100%; border-radius: 3px; }
/* Analysten-Verteilung (StockDetail): \layout\spark\SparkStacked als voll-breiter, gerundeter
   Balken — preserveAspectRatio="none" + width:100% streckt die Segmente, overflow:hidden rundet
   die scharfen SVG-Rect-Kanten. */
.analyst-dist { max-width: 480px; border-radius: 3px; overflow: hidden; }
.analyst-dist > .spark { display: block; width: 100%; height: 18px; }
/* Dip-Radar (reports\DipRadar): Tendenz-Text farblich an die Trend-Sparkline koppeln —
   ↗ aufwärts, ↘ abwärts, gedämpft → seitwärts; dieselben Snap-Tokens wie .spark
   (Projekt-Konvention Blau/Orange statt Grün/Rot wegen Farbsehschwäche). */
.trend-cell { white-space: nowrap; font-weight: 600; }
.trend-cell .trend-meta { font-weight: 400; color: var(--text-muted); }
.trend--up   { color: var(--snap-up-text); }
.trend--down { color: var(--snap-down-text); }
.trend--flat { color: var(--text-muted); }
/* Spark-Pivot-Zellen: das SVG hat eine feste Eigenbreite (72px) und „schwimmt" sonst in der
   von table-layout:auto auf 100% gestreckten Spalte (wirkt abgeschnitten / zu schmal). Spalte
   report-übergreifend auf eine einheitliche Breite fixieren und das SVG sie füllen lassen
   (preserveAspectRatio="none" streckt die Linie horizontal mit). Greift nur auf echte
   Spark-Zellen, nicht auf den „—"-Platzhalter (.spark-empty) zu junger Reihen. StockDetail nutzt
   .spark in .kpi-hist-spark (nicht in table.pivot) → unberührt. Ausgenommen die Top-Fondshalter-
   Verlauf-Spalte (.fonds-verlauf-cell): dort steht vor dem SVG das Verweildauer-Badge
   (.fonds-tenure) inline in derselben Zelle — ein block-gestrecktes SVG würde es auf eine
   eigene Zeile über den Chart drücken statt daneben zu stehen. */
table.pivot td:has(> svg.spark):not(.fonds-verlauf-cell) { width: 96px; min-width: 96px; max-width: 96px; }
table.pivot td:has(> svg.spark):not(.fonds-verlauf-cell) > .spark { display: block; width: 100%; }
/* Zeilenhöhe: die Zeile richtet sich nach ihrer höchsten Zelle — bei einer Spark-Spalte ist
   das immer sie selbst, weil die SVG-Eigenhöhe (18–28px je Report) über der Textzeilenhöhe
   liegt. Der volle Text-Padding (4px oben/unten, s. o.) kommt on top und macht dadurch JEDE
   Zeile der Tabelle unnötig hoch, nicht nur die Chart-Spalte. Reduziert auf 1px — das Chart
   selbst trägt bereits genug visuelles Gewicht, ein Luftpolster wie bei Text braucht es nicht. */
table.pivot td:has(> svg.spark):not(.fonds-verlauf-cell) { padding-top: 1px; padding-bottom: 1px; }
/* KPI-Glyph-Zellen (\Pivot\Cell\Kpi): Form-Symbole behalten ihr Eigen-Seitenverhältnis
   (preserveAspectRatio="meet") → Spalte nur auf eine ruhige Breite fixieren und das Glyph
   zentrieren, nicht strecken. Greift nur auf bare Glyph-Zellen (Stars/Bars/Pie …), nicht auf
   inline im Text stehende Glyphen (DipRadar-Pfeile in .trend-cell) und nicht auf „—". */
table.pivot td:has(> svg.kpi) { width: 92px; min-width: 92px; max-width: 92px; text-align: center; }

/* StockDetail Score-Verlauf: voll-breiter Tristate-Konviktions-Streifen über dem

/* --- WIDGETS: SPARKLINES --- */

/* SparkChart-Familie (\layout\spark\SparkChart) — Duplikat der Regeln aus
   app.css: dort für Report-/Dashboard-Widgets, hier für dieselbe Komponente
   in der Editorial-Artikel-Zone (WebsiteLayout lädt app.css nicht). Diverging-
   Balken (SparkBars/SparkWinLoss) tragen ihre Füllung selbst über .bar-*. */
.spark { display: inline-block; vertical-align: middle; }
.spark.is-positive { color: var(--snap-up-text); }
.spark.is-negative { color: var(--snap-down-text); }
.spark.is-neutral  { color: var(--text-muted); }
.spark.is-accent   { color: var(--accent); }
.spark.is-muted    { color: var(--text-muted); }
.spark .bar-pos     { fill: var(--snap-up-text); }
.spark .bar-neg     { fill: var(--snap-down-text); }
.spark .bar-neutral { fill: var(--text-muted); }
.spark .spark-base    { stroke: var(--text-muted); stroke-width: 0.5; stroke-opacity: 0.5; }
.spark .spark-mark    { stroke: var(--text-muted); stroke-width: 1; stroke-dasharray: 1.5 1.5; }
.spark .bullet-target { stroke: var(--text-main); stroke-width: 1.5; }
.spark .spark-hit     { fill: transparent; }
.spark-empty { color: var(--text-muted); }


/* --- WIDGETS: HEATMAP --- */

/* heatmap.css — Styles für das generische Heatmap-/Treemap-Control (heatmap.js).
 * Konsumenten: reports\CongressOverview, reports\MarketHeatmap. Farb-Swatches der
 * Legende kommen per --swatch-Custom-Property vom Server (datenagnostisch). */

.heatmap-canvas {
  width: 100%;
  height: 420px;
  position: relative;
  overflow: hidden;
  background: var(--bg-secondary, #f8fafc);
  border-radius: 6px;
}

.heatmap-tooltip {
  position: absolute;
  pointer-events: none;
  display: none;
  z-index: 100;
  max-width: 240px;
  padding: 8px 12px;
  border-radius: 6px;
  background: rgba(15, 23, 42, 0.92);
  color: #f8fafc;
  font-size: 12px;
  line-height: 1.5;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.35);
}

.heatmap-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 16px;
  margin-bottom: 8px;
  font-size: 0.8em;
}

.heatmap-legend__item {
  display: flex;
  align-items: center;
  gap: 6px;
}

.heatmap-legend__item::before {
  content: '';
  display: inline-block;
  width: 12px;
  height: 12px;
  border-radius: 2px;
  background: var(--swatch, rgba(100, 116, 139, 0.55));
}

/* ── Bucket-gestapeltes Layout (reports\MarketHeatmap): je Segment-Bucket eine
 *    Zeile mit Gewinner|Verlierer-Paar. Kacheln gleich gross → Kürzel lesbar. */
.heatmap-bucket {
  margin: 0 0 18px;
}

.heatmap-bucket__head {
  display: flex;
  align-items: baseline;
  gap: 10px;
  margin: 0 0 6px;
}

.heatmap-bucket__name {
  font-weight: 700;
  font-size: 1.02em;
}

.heatmap-bucket__sub {
  font-size: 0.78em;
  color: var(--text-muted, #64748b);
}

/* Titel + zugehöriger Chart als EIN Grid-Item je Seite (.heatmap-pair__col) — nicht mehr
 * zwei getrennte Grids (Titel-Zeile, Chart-Zeile). Die brachen beim Umbruch auf schmalen
 * Viewports unabhängig voneinander um: beide Titel stapelten sich VOR beiden Charts, statt
 * dass jeder Titel über seinem eigenen Chart bleibt. */
.heatmap-pair {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 16px;
}

.heatmap-pair__title {
  display: block;
  margin-bottom: 4px;
  font-size: 0.78em;
  font-weight: 700;
}

/* Gewinner/Verlierer-Label in der Tile-Palette (Heatmap::COLOR_POSITIVE/NEGATIVE = Emerald/Rose) —
 * semantisch theme-unabhängig grün/rot, lesbar auf hellem wie dunklem Card-Hintergrund. */
.heatmap-pair__title.l { color: #10b981; }
.heatmap-pair__title.r { color: #f43f5e; }

/* Bucket-Maps füllen die Höhe aus — bewusst gross, damit die proportionalen Kacheln Luft haben.
 * Verkleinert wird erst, wenn das Paar gestapelt ist (< 768px) UND der Schirm wirklich schmal wird (< 480px). */
.heatmap-canvas--bucket {
  height: 440px;
}

.heatmap-disclaimer {
  font-size: 0.78em;
  color: var(--text-muted, #64748b);
  margin-top: 20px;
  padding-top: 12px;
  border-top: 1px solid var(--border-color, #e5e7eb);
}

@media (max-width: 768px) {
  .heatmap-pair {
    grid-template-columns: 1fr;
  }
}

/* Erst wenn das Paar längst gestapelt ist UND der Schirm wirklich schmal wird (Phone), Höhe reduzieren. */
@media (max-width: 480px) {
  .heatmap-canvas {
    height: 300px;
  }
  .heatmap-canvas--bucket {
    height: 320px;
  }
}
