
    /* === Tabellenansicht: spezifische Styles === */
    /* A4 (Audit 2026-08, F9): Kasten des <noscript>-Hinweises weiter unten im
       Body. Die Regel steht hier statt im <noscript> selbst, weil <style> dort
       nur als Kind von <head> HTML-konform waere; im Body meldet der Validator
       es an (ohne Rendering-Folge). Sie kostet nichts, wenn JS laeuft — dann
       rendert der Browser den <noscript>-Inhalt gar nicht erst.
       Die Farb-Variablen sind im Kontrast-Hell-Modus umdefiniert und
       serverseitig gesetzt, der Kasten passt sich also auch ohne JS beiden
       Modi an. Fallbacks fuer den Fall, dass der Block je ausserhalb des
       Dashboard-Scopes landet. rem statt em: die em-Kaskade rechnete gegen
       die Container-Schrift. */
    .vergleich-noscript-hinweis {
        margin: 0 0 1em;
        padding: 0.75em 1em;
        border: 1px solid var(--border, rgba(0,0,0,0.2));
        border-radius: 0.4em;
        background: var(--bg-card, rgba(0,0,0,0.04));
        color: var(--text-secondary, inherit);
        font-size: 0.875rem;
        line-height: 1.5;
    }
    /* Filter-Section-Styles aus Listen-Core spiegeln (Anpassen-Overlay etc.) */
    /* 50.01em statt 50em (Audit A-7.2 / Phase K6 2026-08-12): zusammen mit dem
       `max-width: 50em`-Block darunter traf exakt 800px BEIDE Blocks — Panel gestapelt
       UND mit Desktop-Links-Border. Der Offset sitzt bewusst auf der min-width-Seite,
       damit 800px zur schmalen Variante gehoert; folgt dem einzigen Bestands-Vorbild
       `min-width: 72.01em` weiter unten. `.01em` statt `.0625em`, weil letzteres exakt
       801px waere und damit eine volle Pixel-Breite unversorgt liesse. */
    @media (min-width: 50.01em) {
        .filter-row-separator[style] .filter-column-separator { border-top: none; padding-top: 0; margin-top: 0; border-left: 1px solid var(--border); padding-left: var(--space-lg); margin-left: var(--space-md); }
    }
    @media (max-width: 50em) {
        .filter-row-separator[style] { grid-template-columns: 1fr !important; }
    }
    .filter-checkbox {
        display: flex; align-items: center; gap: 0.5em;
        padding: 0.375em 0; min-height: 2.25rem;
        cursor: pointer; font-size: 0.85rem;
        color: var(--text-secondary, #aab);
        transition: color 150ms ease; line-height: 1.35;
    }
    .filter-checkbox:hover { color: var(--text-primary, #fff); }
    .filter-checkbox input[type="checkbox"] {
        width: 1.25rem; height: 1.25rem; min-width: 1.25rem;
        accent-color: #00528d; cursor: pointer;
    }
    .filter-panel-titel { display: block; font-size: 0.7rem; font-weight: 600; color: rgba(255, 255, 255, 0.7); text-transform: uppercase; letter-spacing: 0.045em; padding-bottom: 0.5em; margin-bottom: 0.75em; border-bottom: 1px solid rgba(255, 255, 255, 0.1); }
    /* Politur 2026-05-14: Anlagebetrag-Buttons ("bis EUR 4.999" / "EUR 5.000
       bis EUR 9.999" / "ab EUR 10.000") bleiben einzeilig — konsistent zum
       gleichnamigen Patch in der Listenansicht (`festgeld-vergleich-core.
       inc.php`, im 52em-Block). Hier global gesetzt, weil der
       Anwender den Fix "fuer alle Displaygroessen" angefordert hat. Der
       Ausland-Filter (`.ausland-filter-group`) bleibt ausgenommen — dort
       sind zwei laengere Strings ("Ja" / "Nein") in einem schmalen
       2-Button-Layout, die kein nowrap brauchen.
       max-width: 34em am Gruppen-Container greift nur im 52em-Block,
       damit nowrap auf schmaleren Viewports nicht ueber den Filter-
       Container hinausragt. */
    .vergleich-dashboard .filter-section .filter-group:not(.ausland-filter-group) .toggle-btn {
        white-space: nowrap;
    }
    @media (max-width: 52em) {
        .vergleich-dashboard .filter-section .filter-group:not(.ausland-filter-group) {
            max-width: 34em;
        }
    }
    /* Tooltip am Sortierbutton "Vergleichswert" — erklaert, was der Modus tut.
       Gleiches data-tooltip/::after-Muster wie der Zinszellen-Tooltip und die
       Listen-Schwester: reines CSS, kein JS, kein natives title. Das Attribut
       kommt mit jedem Server-Render mit und uebersteht damit sowohl den
       Clone-and-Replace der Sort-Buttons (kloneUndBinde in
       festgeld-tabelle-race.js) als auch den Cross-Fade-Tausch. Attribut-
       Selektor statt eigener Klasse: nur der Vergleichswert-Button traegt
       data-tooltip, Anbieter und Zinssatz bleiben bewusst ohne. */
    .vergleich-dashboard .sort-btn[data-tooltip],
    .vergleich-dashboard .sort-tooltip-btn { position: relative; }
    .vergleich-dashboard .sort-btn[data-tooltip]::after,
    .vergleich-dashboard .sort-tooltip-btn::after {
        content: attr(data-tooltip);
        position: absolute;
        top: calc(100% + 0.5em);
        right: 0;
        width: max-content;
        max-width: 22em;
        padding: 0.5em 0.75em;
        background: rgba(15, 20, 30, 0.95);
        color: #fff;
        /* font-weight zuruecksetzen: .sort-btn.active erbt sonst 700 in den Tooltip. */
        font-weight: 400;
        /* rem statt em: em rechnet gegen die .sort-btn-Schrift (--text-xs = 11px)
           und ergaebe winzige 8.25px. 0.75rem = 12px, identisch zur Zinszellen-
           Tooltip-Basis in tabelle.min.css und zur Listen-Schwester. */
        font-size: 0.75rem;
        line-height: 1.4;
        border-radius: 0.375em;
        box-shadow: 0 0.25em 0.75em rgba(0, 0, 0, 0.3);
        backdrop-filter: blur(8px);
        white-space: normal;
        text-align: left;
        pointer-events: none;
        opacity: 0;
        transform: translateY(-0.25em);
        transition: opacity 0.15s ease, transform 0.15s ease;
        z-index: 100;
    }
    .vergleich-dashboard .sort-btn[data-tooltip]:hover::after,
    .vergleich-dashboard .sort-btn[data-tooltip]:focus::after,
    .vergleich-dashboard .sort-btn[data-tooltip]:focus-visible::after {
        opacity: 1;
        transform: translateY(0);
    }
    /* Stacking: der Tooltip ragt ueber die Tabelle darunter. Deren Sticky-
       Zellen liegen bei z-index 3 (.ab-zelle-Header) bzw. 4 (.vp-spalte), der
       per JS an den body gehaengte Info-Tooltip bei 200. Der z-index sitzt am
       Button selbst (position: relative), damit der ::after mit ihm nach oben
       wandert. */
    .vergleich-dashboard .sort-btn[data-tooltip]:hover,
    .vergleich-dashboard .sort-btn[data-tooltip]:focus,
    .vergleich-dashboard .sort-btn[data-tooltip]:focus-visible,
    .vergleich-dashboard .sort-tooltip-btn.is-tooltip-open,
    .vergleich-dashboard .sort-tooltip-btn:focus,
    .vergleich-dashboard .sort-tooltip-btn:focus-visible {
        z-index: 300;
    }
    /* ⓘ-Trigger fuer Touch. Default unsichtbar — nur der (hover: none)-Block
       weiter unten blendet ihn ein, Desktop bleibt damit unveraendert.
       Sichtbar bleibt das Glyph klein; die Trefferflaeche vergroessert ein
       unsichtbares ::before (Bestandsmuster, siehe .ab-zins-balken::before) —
       padding wuerde das Glyph mitvergroessern. Der Slop waechst bewusst
       asymmetrisch nach oben/unten/rechts: nach links liegt der
       Vergleichswert-Button, dort wuerde eine breite Zone Sortier-Taps
       abfangen. */
    .vergleich-dashboard .sort-tooltip-btn {
        display: none;
        align-items: center;
        justify-content: center;
        padding: 0;
        border: 0;
        background: transparent;
        color: var(--text-secondary, rgba(255,255,255,0.7));
        font-size: 1.5em;
        line-height: 1;
        cursor: pointer;
    }
    .vergleich-dashboard .sort-tooltip-btn::before {
        content: '';
        position: absolute;
        top: -0.55em;
        bottom: -0.55em;
        left: -0.15em;
        right: -0.55em;
    }
    .vergleich-dashboard .sort-tooltip-btn.is-tooltip-open { color: var(--accent, #A9F6DB); }
    .vergleich-dashboard .sort-tooltip-btn.is-tooltip-open::after {
        opacity: 1;
        transform: translateY(0);
    }
    /* Geraete ohne :hover — ⓘ einblenden und den Hover-Tooltip des
       Sortierbuttons abschalten. Sonst zeigt "sticky hover" nach einem
       Sortier-Tap beide Tooltips gleichzeitig. Bewusst nur (hover: none)
       ohne pointer-Zusatz: die Bedingung "Hover funktioniert nicht" ist
       genau das Kriterium, an dem der Ersatz haengt. */
    @media (hover: none) {
        .vergleich-dashboard .sort-tooltip-btn { display: inline-flex; }
        .vergleich-dashboard .sort-btn[data-tooltip]::after { content: none; }
    }
    @media (max-width: 48em) {
        .vergleich-dashboard .sort-btn[data-tooltip]::after,
        .vergleich-dashboard .sort-tooltip-btn::after {
            max-width: 16em;
            font-size: 0.7rem;
        }
    }
    @media (max-width: 48em) {
        /* Politur 2026-06-11d: EINZEILIGER Header auf Mobile. Der
           Treffer-Zaehler ("X Treffer · Laufzeit Y") ist ausgeblendet —
           Anwender-Entscheidung: Treffer-Anzahl steckt bereits im Pager
           ("von N"), die gewaehlte Laufzeit in den Einstellungen.
           display:none entfernt nur die Darstellung; die Trefferzahl
           bleibt fuer Crawler im separaten meta-Element daneben erhalten
           (K7 2026-08-13 - der sichtbare Zaehler traegt die Auszeichnung
           seitdem nicht mehr).
           Zeile: Pager links (kompakt — Skala-Track ausgeblendet, Platz
           reicht neben den Sort-Buttons nicht; Chevron-Paar bleibt) +
           `.sort-buttons` rechts (margin-left: auto). Site-CSS-Default
           (`flex-direction: column; align-items: stretch`) wird mit
           `row` + `flex-wrap: wrap` ueberschrieben — Spezifizitaet
           `.vergleich-dashboard.tabelle-dashboard .results-header`
           (0,3,0) schlaegt das Site-Default sauber. */
        .vergleich-dashboard.tabelle-dashboard .results-header {
            flex-direction: row;
            flex-wrap: wrap;
            align-items: center;
            row-gap: 0.4em;
        }
        .vergleich-dashboard.tabelle-dashboard .result-count {
            display: none;
        }
        .vergleich-dashboard.tabelle-dashboard .sort-buttons {
            align-self: center;
            margin-left: auto;
            /* 11h: 2.5%-Desktop-Randabstand auf Mobile opfern —
               jeder Pixel zaehlt fuer den Einzeiler. */
            margin-right: 0;
        }
        .vergleich-dashboard.tabelle-dashboard .tabelle-pager {
            flex: 0 1 auto;
            margin-inline: 0;
        }
        .tabelle-pager-track { display: none; }
        .tabelle-pager-leiste { flex: 0 0 auto; gap: 0; }
        /* 11h: Kurz-Range "X–Y / N" — "Anbieter" und "von" ausgeblendet,
           Slash an. Pfeil-Flaechen schmaler (2.25em breit, Hoehe 3em
           bleibt als Touch-Ziel), Glyph entsprechend kompakter, Gap enger. */
        .tabelle-pager { gap: 0.5em; }
        .tabelle-pager-prefix,
        .tabelle-pager-von-wort { display: none; }
        .tabelle-pager-slash { display: inline; }
        .tabelle-pager-btn { width: 2.25em; height: 3em; }
        .tabelle-pager-glyph { width: 2em; height: 0.7em; }
    }
    /* Zweizeiliger Sort-Pill (Cross-View 2026-06-26): wie Festgeld-AT-Liste.
       Richtung (`.sort-richtung`) bricht im Inline-Wrapper (.sort-kurz/.sort-label)
       auf Zeile 2 um — groessere Touch-Flaeche, einheitlich mit Tagesgeld/Girokonto. */
    @media (max-width: 38em) {
        .vergleich-dashboard.tabelle-dashboard .sort-btn .sort-richtung { display: block; }
    }
    /* Sortierbutton-Touchgroesse bis 48em (Cross-View 2026-06-26b): aus 38em-Block ausgelagert, Rollout des Tagesgeld-Patches */
    @media (max-width: 48em) {
        .vergleich-dashboard.tabelle-dashboard .sort-btn { font-size: 0.75rem; line-height: 1.2; text-align: center; }
    }
    .vergleich-dashboard .laufzeit-nav {
        flex-wrap: nowrap; overflow-x: auto;
        -webkit-overflow-scrolling: touch; scrollbar-width: none;
        scroll-snap-type: x mandatory;
        /* Fallback-Deklaration zuerst (Audit A-7.1 / Phase K6 2026-08-12): Engines ohne
           das `safe`-Keyword (WebKit/Safari, Chrome < 115) verwerfen die naechste
           Deklaration komplett. Ohne diese Zeile greift dort das zentrale `center` aus
           vergleich.min.css — bei Overflow werden die linken Laufzeit-Pills dann
           unerreichbar, weil scrollLeft nicht negativ werden kann. In Chromium aendert
           die Zeile nichts: dort gewinnt weiterhin das gueltige `safe center` danach. */
        justify-content: flex-start;
        justify-content: safe center; gap: 0.25em;
    }
    .vergleich-dashboard .laufzeit-nav::-webkit-scrollbar { display: none; }
    .vergleich-dashboard .laufzeit-nav .laufzeit-pill {
        min-height: 2.75em; scroll-snap-align: center;
        flex-shrink: 0; padding-left: 0.75em; padding-right: 0.75em;
    }
    /* Auto-Margins als Zentrier-Fallback (Audit A-7.1b / Phase A-7.1b 2026-08-12).
       NICHT ENTFERNEN und nicht mit der Fallback-Zeile oben "aufraeumen" - die
       beiden gehoeren zusammen. Auf Engines ohne `safe`-Keyword (WebKit/Safari,
       Chrome < 115) gewinnt oben die `flex-start`-Fallbackzeile; dort stuende
       die Leiste auch dann linksbuendig, wenn alle Pills in eine Zeile passen.
       (Der Deklarationstext wird hier bewusst NICHT woertlich zitiert, damit
       Zaehl-Sweeps ueber die Fallbackzeile eindeutig bleiben.)
       Auto-Margins zentrieren genau in diesem Fall und kollabieren bei Overflow
       auf 0 - die K6-Wirkung (linke Pills per scrollLeft erreichbar) bleibt
       damit erhalten. Live gemessen 2026-08-12 (Chromium 151, dpr 1, Alt-Engine
       simuliert per erzwungenem flex-start): bei 1200px Offset der ersten Pill
       8,00px -> 45,52px; bei Overflow (800/640/375px) computed margin exakt 0px
       und erste Pill erreichbar. Sweep 1260->1100px in 5px-Schritten: Kipppunkt
       bei ~1135px - oberhalb zentriert (margin > 0), unterhalb margin exakt 0,
       erste Pill in jeder Stufe erreichbar.
       In modernen Engines unveraendert bis auf 1/64px - und das nur in Chromium,
       Firefox und WebKit messen 0,000px: `center` legt die ungerade
       Rest-LayoutUnit rechts ab, die Auto-Margins links - sichtbar nur als
       Antialiasing-Nuance, `gap` und Snap-Punkte bleiben identisch.
       `:first-child`/`:last-child` sind zwingend - `:first-of-type` traefe wegen
       des gemischten Markups (aktive Pill = <span>, uebrige = <a>) ZWEI Elemente
       und risse die Leiste auseinander. */
    .vergleich-dashboard .laufzeit-nav .laufzeit-pill:first-child { margin-inline-start: auto; }
    .vergleich-dashboard .laufzeit-nav .laufzeit-pill:last-child { margin-inline-end: auto; }
    @media (hover: hover) and (pointer: fine) {
        .vergleich-dashboard .laufzeit-nav { cursor: grab; }
        .vergleich-dashboard .laufzeit-nav.is-dragging { cursor: grabbing; scroll-behavior: auto; user-select: none; }
        .vergleich-dashboard .laufzeit-nav.is-dragging .laufzeit-pill { pointer-events: none; }
    }
    .nav-scroll-wrapper {
        -webkit-mask-image: linear-gradient(to right, transparent, black 1.5em, black calc(100% - 1.5em), transparent);
        mask-image: linear-gradient(to right, transparent, black 1.5em, black calc(100% - 1.5em), transparent);
    }
    .nav-scroll-wrapper.scroll-start {
        -webkit-mask-image: linear-gradient(to right, black, black calc(100% - 1.5em), transparent);
        mask-image: linear-gradient(to right, black, black calc(100% - 1.5em), transparent);
    }
    .nav-scroll-wrapper.scroll-end {
        -webkit-mask-image: linear-gradient(to right, transparent, black 1.5em, black);
        mask-image: linear-gradient(to right, transparent, black 1.5em, black);
    }
    .nav-scroll-wrapper.scroll-start.scroll-end { -webkit-mask-image: none; mask-image: none; }
    /* Anlagebetrag-Toggle: interner Scroller analog Laufzeit-Nav. Verhindert
       horizontalen Dokument-Overflow auf schmalen Viewports (z.B. 390px): die drei
       nicht-umbrechenden Stufen-Buttons scrollen intern, statt das Dokument zu
       weiten. min-width:0 gibt dem Flex-Kind frei, unter die Inhaltsbreite zu
       schrumpfen (sonst erzwingen die nowrap-Buttons die volle Min-Content-Breite). */
    .vergleich-dashboard .filter-group:not(.ausland-filter-group) .toggle-scroll-wrapper { min-width: 0; }
    /* "Anlagebetrag"-Label + Band durchgaengig linksbuendig (Label buendig zum Band-Anfang in ALLEN
       Groessen): align-items:stretch hebt die geteilte Mobile-Regel `.filter-group{align-items:center}` auf,
       sodass der Band-Wrapper die filter-group fuellt statt zentriert/eingerueckt zu werden, wenn die fg
       breiter als das Band ist (z.B. ~700px, Breakpoint mit umgebrochenem Ausland-Toggle). min-width:0 +
       interner Scroll des Wrappers bleibt erhalten (kein Dokument-Overflow auf sehr schmalen Viewports).
       text-align:left rueckt den Label-Text linksbuendig. Ausland-balken-toggle (.ausland-filter-group)
       ausgenommen (Label+Toggle zentriert gewollt). Cross-Produkt-Sync 2026-06-28 (Folge-Politur). */
    .vergleich-dashboard .filter-group:not(.ausland-filter-group) { align-items: stretch; }
    .vergleich-dashboard .filter-group:not(.ausland-filter-group) .filter-label { text-align: left; }
    .vergleich-dashboard .filter-group:not(.ausland-filter-group) .toggle-group {
        flex-wrap: nowrap;
        min-width: 0;
        overflow-x: auto;
        -webkit-overflow-scrolling: touch;
        scrollbar-width: none;
        scroll-snap-type: x mandatory;
    }
    .vergleich-dashboard .filter-group:not(.ausland-filter-group) .toggle-group::-webkit-scrollbar { display: none; }
    .vergleich-dashboard .filter-group:not(.ausland-filter-group) .toggle-btn {
        flex-shrink: 0;
        scroll-snap-align: center;
        white-space: nowrap;
        /* Mehr horizontales Padding als die min.css-Basis (mobil nur 4-6px),
           damit die Stufen-Texte im Scroller nicht ineinanderlaufen. */
        padding-left: var(--space-md);
        padding-right: var(--space-md);
    }
    @media (hover: hover) and (pointer: fine) {
        .vergleich-dashboard .filter-group:not(.ausland-filter-group) .toggle-group { cursor: grab; }
        .vergleich-dashboard .filter-group:not(.ausland-filter-group) .toggle-group.is-dragging {
            cursor: grabbing;
            scroll-behavior: auto;
            user-select: none;
        }
        .vergleich-dashboard .filter-group:not(.ausland-filter-group) .toggle-group.is-dragging .toggle-btn { pointer-events: none; }
    }
    .toggle-scroll-wrapper {
        -webkit-mask-image: linear-gradient(to right, transparent, black 1.5em, black calc(100% - 1.5em), transparent);
        mask-image: linear-gradient(to right, transparent, black 1.5em, black calc(100% - 1.5em), transparent);
    }
    .toggle-scroll-wrapper.scroll-start {
        -webkit-mask-image: linear-gradient(to right, black, black calc(100% - 1.5em), transparent);
        mask-image: linear-gradient(to right, black, black calc(100% - 1.5em), transparent);
    }
    .toggle-scroll-wrapper.scroll-end {
        -webkit-mask-image: linear-gradient(to right, transparent, black 1.5em, black);
        mask-image: linear-gradient(to right, transparent, black 1.5em, black);
    }
    .toggle-scroll-wrapper.scroll-start.scroll-end {
        -webkit-mask-image: none;
        mask-image: none;
    }
    .vergleich-dashboard .filter-section { position: relative; margin-top: -1.1em; margin-bottom: 5em; overflow: visible; }
    /* Kontrast-Politur 2026-06-12b: Die Footer-Link-Farben (Ruhe #1FD99B /
       Hover #8FF6D5) sind ZENTRAL in vergleich.min.css definiert
       (`.vergleich-dashboard .footer-line a` bzw. `a:hover` — dort lag auch
       das fruehere Neon-Gelb #c8e600/#e0ff00, das der Anwender beim Hover
       bemaengelte). Anwender-Entscheidung gegen verteilte Definitionen:
       min.css darf dafuer angefasst werden, die frueheren Core-Overrides
       (.vergleich-footer a:link/:visited/:hover) sind entfernt. Bei
       min.css-Aenderungen den ?v=-Parameter am <link> oben bumpen. */

    /* === Eigentliches Tabellen-Layout === */
    /* Full-Bleed ab Tablet aufwaerts: Sortier-Bar, Tabelle und "mehr anzeigen"
       brechen aus dem zentrierten Site-Container aus und nutzen die volle
       Viewport-Breite. Schalterleiste und Filter-Bar bleiben oben im normalen
       Container — visuell ein klarer Datenbereich.
       Klassisches "left: 50%; margin-left: -50vw"-Pattern.
       Politur 2026-06-11 (Doc-Overflow-Fix): 100vw umfasst per CSS-Spec die
       vertikale Scrollbar — sobald die Seite vertikal scrollt, war der
       Container um die Scrollbar-Breite (6-17 px je nach Browser) zu breit
       und erzeugte horizontalen Doc-Overflow. `--viewport-w` wird per IIFE
       (tabelleViewportBreiteIIFE) auf documentElement.clientWidth gesetzt
       (= Viewport OHNE Scrollbar) und an :root vererbt; 100vw bleibt nur
       als No-JS-Fallback. */
    /* Vier-Stufen-Layout: gleichbreite Slots ueber den gesamten Nicht-Mobile-Bereich
       (analog DE-Hauptnav `#nav li { width: 11.111% }` = 100/9). Drei prozentuale
       Stufen, monoton fallende Anbieter-Anzahl ohne Spruenge zwischen den Schwellen
       (Politur 2026-05-09: untere Schwelle von 72em auf 38.0625em vorgezogen — der
       frueher dazwischenliegende Default-12em-Bereich 52-72em liess die Spaltenzahl
       beim Verkleinern wieder auf 4-5 anwachsen, weil weder min-width:72em noch
       max-width:52em griff. Folge: max-width:52em-Block setzt die Variablen nicht
       mehr, dort nur noch Font-/Padding-Politur):
         - >= 124em (4K + ueberbreit, ~1984px+):     9 Slots = 1 VP + 8 Anbieter à 11.111%
         -  76-124em (Full-HD-Standard, ~1216px):    6 Slots = 1 VP + 5 Anbieter à 16.667%
         -  38.0625-76em (Tablet-/Notebook-Bereich): 4 Slots = 1 VP + 3 Anbieter à 25%
         -  <= 38em:                                 Mobile-Block (3 Anbieter à 30vw, VP unsichtbar)
       Cascade: bei jedem Viewport greifen alle min-width-Regeln, die <= der
       Viewport-Breite sind — die letzte gewinnt (= naechstgroesserer Modus).
       Container-Innenbreite = 100vw - 1.5em links - 1.5em rechts = 100vw - 3em.
       Politur 2026-06-11 (Doc-Overflow-Fix): 100vw in den Stufen-Formeln durch
       var(--viewport-w, 100vw) ersetzt — selbe Begruendung wie am Full-Bleed-
       Block oben (100vw enthaelt die vertikale Scrollbar; die Slots haetten
       sonst weiter ueber den korrigierten Container hinausgehangen). */
    /* Politur 2026-05-11 (Sticky-Mini-Header): --slot-breite und --vp-breite
       jetzt am .tabelle-wrapper statt am .tabelle-scroller. Grund: CSS-Custom-
       Properties vererben nur an Descendants, nicht an Geschwister. Der
       Sticky-Mini-Header (.tabelle-sticky-mini) ist Sibling des Scrollers und
       konnte vorher die Variablen nicht lesen — Mini-Spalten waren auf den
       12em/14em-Fallback gefangen, Misalignment auf Desktop-mittel/breit.
       Migration auf den gemeinsamen Wrapper loest das. Der Scroller bleibt
       Descendant des Wrappers und erbt die Werte weiter. */
    .tabelle-wrapper {
        margin-top: 1em;
        position: relative;
        /* Default-Werte fuer --slot-breite/--vp-breite: greifen praktisch nur,
           wenn keine Media-Query matcht (siehe Z. ~840+). Migration vom
           .tabelle-scroller auf .tabelle-wrapper im Zuge der Sticky-Mini-
           Header-Einfuehrung (Politur 2026-05-11), damit der Mini-Track als
           Sibling des Scrollers die Variablen erbt. */
        --slot-breite: 12em;
        --vp-breite: 14em;
    }
    /* Scroll-Ende-Indikator: schmaler heller Schimmer-Gradient am rechten Rand
       der Tabelle deutet an, dass nach dem letzten sichtbaren Anbieter weitere
       folgen — der naechste Anbieter ist gerade ausserhalb des Viewports, sein
       Vorhandensein wird durch einen weichen Hell-Verlauf an der Container-Kante
       visuell signalisiert. Pseudo-Element sitzt auf dem nicht-scrollenden
       .tabelle-wrapper — bleibt beim horizontalen Scrollen am Container-Rand
       fixiert. Wird per JS-Klasse `.is-scroll-ende` ausgeblendet, sobald der
       Nutzer das Scroll-Ende erreicht (= alle Anbieter sichtbar gewesen) oder
       die Tabelle gar nicht horizontal scrollen muss (alle Anbieter passen rein).
       2026-05-07d: schmaler (3em → 2em) und in helle Farbe gewechselt
       (rgba(0,40,90,0.65) → rgba(255,255,255,0.20)) — Schimmer statt Schatten. */
    /* Phase 14 (A9): Linker Schimmer als Pendant zum rechten ::after.
       Default unsichtbar (User ist am Anfang = scrollLeft 0); sobald nach
       rechts gescrollt wurde, wird die Klasse `.is-scroll-anfang` per JS
       gesetzt und der Schimmer fadet ein — Hinweis darauf, dass es nach
       links zurueck noch Inhalt gibt. */
    .tabelle-scroller {
        /* Politur 2026-05-11 (Folgepatch): --hdr-logo-h von 11rem auf 12rem
           erhoeht. Begruendung: mit `flex-shrink: 0` am .ab-logo-wrap (siehe
           Z. ~1244) wird das Logo bei langen Anbieter-/Produktnamen nicht mehr
           gestaucht. Damit der Stack `Logo (max 8em) + Anbieter (max 2 Zeilen
           per line-clamp) + Produkt (1 Zeile per line-clamp) + Padding/Gap`
           ohne Ueberlauf in die Badge-Reihe passt, braucht die Reihe 12rem
           statt 11rem (Rechnung: 128 + 30 + 13 + 16 = 187 px ≤ 192 px). */
        --hdr-logo-h: 12rem;
        --hdr-badge-h: 2rem;
        --hdr-zins-h: 3rem;
        --row-data-h: 2.75rem;
        /* Politur 2026-05-10e: eigene Mindesthoehe fuer Bereichstitel-Reihen
           (.ab-gruppe-spacer + button.vp-gruppentitel), getrennt von
           --row-data-h. Auf Desktop deckt 2.75rem (~44 px) den VP-Gruppentitel-
           Button (padding 1.125em + line-height 1.15) — _vpRowSync zwingt sonst
           die VP-Tracks auf einen zu kleinen Wert und der Button overflowt.
           Auf Mobile ist die VP-Spalte display:none, der gleiche 2.75rem-Wert
           gibt den Bereichsbezeichnungen Atemluft ueber dem Toggle-Overlay
           (~30 px Inhalt) und bleibt deutlich kompakter als --row-data-h: 5rem
           (= 80 px Daten-Reihen). */
        --row-titel-h: 2.75rem;
        /* Politur 2026-05-13: Eigene Mindesthoehe fuer CTA-Footer-Reihen
           (Anbieterlinks). 80% von --row-data-h — Anwender-Wunsch: Felder
           mit Anbieterlinks niedriger als die Daten-Reihen, weil sie weniger
           Inhalt tragen und ohne Mini-Label oben keinen extra Padding-Raum
           brauchen. `minmax(--row-cta-h, auto)` laesst die Reihe weiter
           auto-wachsen, falls der Linktext laenger ist als 2.2rem (35 px). */
        --row-cta-h: 2.2rem;
        /* Vergleichswert-Sortierung: die Zins-Zelle traegt dann eine zusaetzliche
           Nominal-Zeile (.ab-zins-nominal). Die feste Track-Hoehe --hdr-zins-h
           (3rem) reicht dafuer nicht.
           Rechnung Desktop (Zellenschrift 1.5rem = 24 px aus tabelle.min.css,
           gap 0.375em = 9 px, Balken 4 px, Padding 0.25em = 6 px; Nominal-Zeile
           0.6875rem * 1.2 = 13,2 px, absolut gesetzt mit bottom: 0.25em = 2,75 px):
             Fluss-Inhalt  = Zahl 24 + Gap 9 + Balken 4 = 37 px (wie JEDE andere
                             Spalte — die Nominal-Zeile ist aus dem Fluss raus)
             bei T = 80 px  zentriert -> Zahl-Oberkante 21,5 / Balken-Unterkante 58,5
             Nominal        = Oberkante 64,05 / Unterkante 77,25
             Abstand Balken -> Nominal = 5,6 px, kollisionsfrei.
           Die Klasse .is-vergleichswert hebt die Variable nur in diesem Modus an.
           Sie sitzt bewusst an .vp-spalte und .tabelle-grid — den beiden einzigen
           Konsumenten der Variable — und NICHT am .tabelle-scroller: der Scroller-
           Knoten bleibt beim Cross-Fade dauerhaft im DOM (tauscheInnerenScroller-
           Inhalt ersetzt nur die zwei Grids plus die Sticky-Mini und kopiert eine
           feste Liste von CSS-Vars), ein Flag an ihm wuerde beim Sortierwechsel per
           AJAX also weder ankommen noch je wieder verschwinden. An den Grids kommt
           es mit jedem Server-Render frisch mit. Klasse statt Inline-Style, damit
           die zwei Mobile-Stufen (1.25rem / 1.05rem Zellenschrift, siehe 52em- und
           38em-Block) eigene, kleinere Werte setzen koennen. */
        /* --slot-breite/--vp-breite definiert jetzt der .tabelle-wrapper
           (Politur 2026-05-11, Sticky-Mini-Migration). Default 12em/14em
           und Media-Query-Stufen (Z. ~840+ und Z. ~1700+) wirken via
           Inheritance auf den Scroller, der weiterhin grid-template-columns
           damit baut. Die calc-Logik ist analog zur DE-Hauptnav
           `#nav li { width: 11.111% }` (= 100/9). */
    }
    .vp-spalte.is-vergleichswert,
    .tabelle-grid.is-vergleichswert { --hdr-zins-h: 5rem; }
    /* Vergleichswert-Sortierung: Nominalzins klein unter der Hauptzahl der
       Zinszelle — kompakter als in der Liste, weil die Anbieter-Spalte schmal
       ist. */
    .ab-zelle.ab-zins .ab-zins-nominal {
        /* Absolut positioniert statt im Flex-Fluss: im Fluss schiebt die Zeile in
           der vertikal zentrierten Zelle (justify-content: center) die Zinszahl
           nach oben und den Balken nach unten — in DE gemessen 11 px Versatz
           gegenueber den Nachbarspalten. Absolut positioniert traegt sie nichts
           mehr zur Flow-Hoehe bei: im Fluss bleiben Zahl + Balken mit genau einem
           Gap dazwischen, also exakt die Geometrie jeder anderen Spalte — auch in
           per Lazyload nachgelieferten Spalten, die dieselbe Zellstruktur erben.
           Containing Block ist die Zinszelle selbst (ihr position: sticky zaehlt
           als positioniert). padding-right: inherit uebernimmt den COMPUTED
           px-Wert des Zellen-Paddings; dadurch sitzt die Nominal-Zeile auf allen
           drei Breakpoints buendig auf der rechten Kante von Zahl und Balken,
           ohne die drei Padding-Stufen (0.625em/0.5em/0.4375em je eigener
           Zellenschrift) hier spiegeln zu muessen.
           font-size in rem statt em — die em-Kaskade ergaebe auf 375px nur rund
           7,6 px fuer die Kernaussage. 0.6875rem = 11 px auf allen Stufen. */
        position: absolute;
        right: 0;
        bottom: 0.25em;
        padding-right: inherit;
        font-size: 0.6875rem;
        font-weight: 400;
        line-height: 1.2;
        white-space: nowrap;
        color: rgba(255, 255, 255, 0.72);
    }
    /* Vergleichspunkt-Spalte: Flex-Item mit Sticky-Left, eigenes Grid (nicht mehr Subgrid) */
    .vp-spalte {
        flex-shrink: 0;
        width: var(--vp-breite, 14em);
        position: sticky;
        left: 0;
        z-index: 4;
        background: rgba(9, 87, 143, 0.92);
        border-right: 1px solid rgba(255,255,255,0.18);
        box-shadow: 0.25em 0 0.5em rgba(0,0,0,0.2);
        display: grid;
        /* Single-Track-Spalte, die der Outer-Width folgt. Ohne explizite Track-
           Definition fällt der Track auf grid-auto-columns: auto (= max-content)
           zurück — lange Labels weiten den Inner-Track dann über die Outer-
           Width aus. Mobile-Overrides setzen nur `width`, nicht den Track —
           Folge wäre VP-Zellen-Schimmer in die erste Anbieter-Spalte. minmax(0, 1fr)
           bindet den Track an den verfügbaren Platz und cappt min-content. */
        grid-template-columns: minmax(0, 1fr);
        /* Politur 2026-05-10e: --tabelle-data-rows enthaelt die explizite
           Daten-Track-Liste pro sichtbarer Reihe (Server: PHP-Loop ueber $reihen,
           JS-Toggle: updateGridTracks). Fallback ist die alte uniforme repeat()-
           Definition, falls die Variable mal nicht gesetzt ist. */
        grid-template-rows:
            var(--hdr-logo-h)
            var(--hdr-badge-h)
            var(--hdr-zins-h)
            var(--tabelle-data-rows, repeat(var(--anzahl-reihen-rest, 30), minmax(var(--row-data-h), auto)));
    }
    .tabelle-grid {
        flex-shrink: 0;
        display: grid;
        grid-template-columns: repeat(var(--anzahl-anbieter, 8), var(--slot-breite, 12em));
        grid-template-rows:
            var(--hdr-logo-h)
            var(--hdr-badge-h)
            var(--hdr-zins-h)
            var(--tabelle-data-rows, repeat(var(--anzahl-reihen-rest, 30), minmax(var(--row-data-h), auto)));
        align-items: stretch;
    }
    /* Header-Stapel-Reihen — sticky oben, opaker Hintergrund.
       Logo-Reihe hat seit 2026-05-07 einen einfarbigen Hintergrund #0070C0
       (Anwender-Wunsch: Logo-Raender sollen nicht mehr sichtbar sein) —
       siehe eigene Regel weiter unten. badge/zins/balken behalten den
       semitransparenten Block-Background. */
    .vp-zelle.vp-header,
    .ab-zelle[class*="ab-badge"], .ab-zelle[class*="ab-zins"] {
        background: rgba(9, 87, 143, 0.92);
    }
    /* === Phase 8: Sticky-Header oben ===
       position: sticky stand urspruenglich fuer Vertikal-Scrolling im
       Scroll-Container (max-height + overflow-y: auto). Seit Politur
       2026-05-04 / 08l ist der Container ohne eigenen Vertikal-Scrollport
       (overflow-y: visible) — die top-Werte sind also funktional inaktiv.
       position: sticky bleibt trotzdem stehen, weil das ::after-Tooltip an
       .ab-zelle.ab-zins darauf als Containing-Block-Anker angewiesen ist
       (siehe Tooltip-Block ~Z. 1280). Stolperstein-Pflege fuer kuenftige
       top-Werte: rem-Variablen, niemals em (Phase-2-Stolperstein 3). */
    .ab-zelle.ab-logo,
    .ab-zelle.ab-badge,
    .ab-zelle.ab-zins,
    .vp-zelle.vp-header {
        position: sticky;
        z-index: 3;
    }
    .ab-zelle.ab-badge,
    .vp-zelle.vp-header.vp-badge  { top: var(--hdr-logo-h); }
    .ab-zelle.ab-zins,
    .vp-zelle.vp-header.vp-zins   { top: calc(var(--hdr-logo-h) + var(--hdr-badge-h)); }

    /* Phase 8 / 14e Sticky-Mini-Header (.tabelle-scroller.is-mini) wurde
       am 2026-05-11 durch den externen, fixed-positionierten Sticky-Mini-
       Header (siehe .tabelle-sticky-mini am Ende dieser Datei)
       abgeloest. Die alte Mechanik war auf einen vertikal scrollenden
       .tabelle-scroller angewiesen (max-height + overflow-y: auto), die
       seit Politur 2026-05-04 / 08l aus dem Container raus sind. Dadurch
       triggerte die is-mini-Klasse nie mehr. Die zugehoerigen CSS-Bloecke
       sind entfernt, ebenso die IIFE stickyHeaderSetup und die race.js-
       Hooks tabelleStickyHeaderReset/Setup. */
    /* Politur 2026-05-13: kompakter Meta-Strip oben links — Default Desktop
       unsichtbar. Mobile-Block (siehe @media (max-width: 38em) weiter unten)
       schaltet ihn sichtbar und positioniert ihn an der oberen linken Ecke
       der Logo-Zelle. Auf Desktop laeuft die Positionsnummer seit Politur
       2026-05-22 ueber den textuellen `.ab-pos-badge` (#NN-Notation)
       direkt am Logo; die Inland-/Ausland-Pille bleibt in der separaten
       Badge-Reihe. */
    .ab-meta-strip { display: none; }
    .ab-zelle.ab-badge { padding-top: 0.25em; padding-bottom: 0.25em; }
    /* Politur 2026-05-07h: Trennlinie zwischen Badge-Reihe und Zins-Reihe
       entfaellt — Inland-/Auslandsanlage-Pille und Zinszahl fliessen optisch
       ineinander. Generelle `.vp-zelle, .ab-zelle { border-bottom: 1px ... }`
       (Z. ~750) bleibt fuer alle anderen Reihen aktiv. */
    .ab-zelle.ab-badge,
    .vp-zelle.vp-header.vp-badge {
        border-bottom: 0;
    }
    /* Vergleichspunkt-Spalte: spezifische Header-Zellen */
    .vp-zelle.vp-header.vp-logo, .vp-zelle.vp-header.vp-badge { font-size: 0.62rem; }
    /* Phase 14 (A7): Truncate-Spans — Default Desktop sichtbar
       der Volltext, Kurz-Variante ausgeblendet. Mobile-Switch
       weiter unten im 38em-Block. */
    .ab-merkmal-voll { display: inline; }
    .ab-merkmal-kurz { display: none; }
    /* Geschenk-Icon Neukundenaktion (cardgift-g.svg): wie Tagesgeld-Schwester —
       Hover haelt Tuerkis (Site-Default `a.cardgifta:hover img` schoebe das gruene
       Icon per hue-rotate ins Gelbe), Icon +10% (2.97em ggue. Site-Default 2.7em
       aus style.css). Hoehere Spezifitaet als die Site-Defaults, Position unkritisch. */
    .ab-merkmal-html a.cardgifta:hover img { filter: brightness(1.12); }
    .ab-merkmal-html a.cardgifta img { width: 2.97em; }
    /* Tooltip fuer Info-Icon (Mobile-Truncate). Wird per JS-IIFE
       `infoTooltipSetup()` an document.body angehaengt. */
    .ab-info-tooltip {
        display: none;
        position: absolute;
        z-index: 200;
        max-width: 18em;
        padding: 0.5em 0.75em;
        background: rgba(15, 20, 30, 0.95);
        color: #fff;
        font-size: 0.75rem;
        font-weight: 400;
        line-height: 1.4;
        border-radius: 0.4em;
        box-shadow: 0 0.25em 0.75em rgba(0, 0, 0, 0.35);
        pointer-events: auto;
    }
    .ab-info-tooltip.is-visible { display: block; }
    /* Footer-CTA */
    /* CTA-Link selbst (`.anbieter-link` + `.link-arrow`) erbt vollstaendig
       aus vergleich.min.css (`.vergleich-dashboard .anbieter-link…`).
       Dort ist auch `::after { content:none !important }` definiert, das
       das vom globalen Theme injizierte Linksymbol fuer externe Links unterdrueckt.
       Phase 14 (A6): Externer-Link-Icon Default unsichtbar, Mobile-Switch
       weiter unten im 38em-Block. Spezifizitaet `.vergleich-dashboard …`
       schlaegt das Listen-globale `.anbieter-link`-Styling. */
    /* ── Focus-Visible fuer Tastatur-Navigation: Laufzeit-Pills + CTA-Link ──
       Beide sind <a> und erben sonst nur den globalen Ring aus style.css
       (`a:focus-visible { outline: 2px solid var(--mint-400) }`, --mint-400
       = #4DE5B4). Der ist auf dem Seitenblau mit 3,94:1 (Pill) bzw. 3,56:1
       (CTA-Zelle) zwar knapp WCAG-1.4.11-konform, weicht aber von der
       Akzentfarbe aller uebrigen Fokus-Ringe des Vergleichs ab. #A9F6DB
       hebt zugleich auf 5,05:1 bzw. 4,57:1 (live gemessen 2026-08-06).
       Kontrast-Hell-Variante weiter unten im Kontrast-Block. */
    .vergleich-dashboard .laufzeit-pill:focus-visible,
    .vergleich-dashboard .anbieter-link:focus-visible {
        outline: 2px solid var(--accent, #A9F6DB);
        outline-offset: 2px;
    }
    /* Mobile: schmaler padden, kleinere Schrift. */
    @media (max-width: 38em) {
        .tabelle-fussnoten {
            padding: 0.875em 1em;
            font-size: 0.8rem;
        }
    }
    /* Responsive */
    @media (max-width: 52em) {
        /* Politur 2026-05-09: --slot-breite und --vp-breite werden ab dem
           38.0625em-Block (oben) per calc((100vw - 3em) / 4) gesetzt — hier
           nur Font-Size/Padding-Politur fuer Tablet/kleinere Notebooks.
           Vorher: --slot-breite: 10em und --vp-breite: 12em fix; das fuehrte
           zu 2-4 sichtbaren Anbietern je nach Bruchteil und nicht zur klaren
           3-Anbieter-Stufung. */
        .vp-zelle, .ab-zelle { font-size: 0.78rem; padding: 0.4375em 0.5em; }
        .ab-zelle.ab-zins { font-size: 1.25rem; }
        /* Vergleichswert-Modus, Rechnung wie im Default-Block (Zellenschrift
           1.25rem = 20 px, Padding 0.25em = 5 px, Nominal absolut 13,2 px +
           bottom 2,75 px):
             Fluss-Inhalt = Zahl 20 + Gap 7,5 + Balken 4 = 31,5 px
             bei T = 72 px -> Balken-Unterkante 51,75 / Nominal-Oberkante 56,05
             Abstand 4,3 px, kollisionsfrei. Die Nominal-Zeile schrumpft nicht
             mit der Zellenschrift (feste 11 px), deshalb faellt der Wert nicht
             proportional zur Default-Stufe. */
        .vp-spalte.is-vergleichswert,
        .tabelle-grid.is-vergleichswert { --hdr-zins-h: 4.5rem; }
    }
    /* Mobile-Hauptblock (Politur 2026-05-08, Phase 14, weiter angepasst in
       Folgepatch 14c):
       Drei Anbieter-Spalten mit je 1/3 Viewport. VP-Spalte ist auf Mobile
       komplett unsichtbar (`display: none`) — die Reihen-Labels uebernehmen
       die Mini-Labels, die Bereichsbezeichnungen rendern in der ersten
       Anbieter-Spalte (siehe Mini-Label-Block plus mb-gruppentitel-Block
       weiter unten). `--vp-breite: 0px` bleibt fuer die Variable, weil
       `.tabelle-scroller { scroll-padding-left: var(--vp-breite, 14em); }`
       sie verwendet — `0px` vermeidet ein vp-Breite-grosses Snap-Padding,
       das sonst die linkste Spalte zu weit nach rechts wegschneiden wuerde
       (Stolperstein 13). --hdr-logo-h auf 8rem statt 11rem reduziert die
       Logo-Reihe. font-size/padding-Werte aus dem alten 38em-Block uebernommen.
       Die alte 29em-Stufe (3-Slot-Layout fuer Smartphones im Hochkant) entfaellt,
       weil dieses Mobile-Layout ohnehin durchgaengig 3 Spalten zeigt.

       Vertikal-Scroll-Port (Phase 14, A2) entfaellt im Folgepatch 08l
       (2026-05-08): Auf Anwender-Wunsch hat die Tabelle auf Mobile keinen
       eigenen Y-Scrollbalken mehr — `max-height`, `overflow-y: auto` und
       `overscroll-behavior-y` wurden entfernt. Die Tabelle wird in voller
       Hoehe sichtbar und ueber den Page-Scrollbar erreicht.
       Politur 2026-05-11: Der frueher hier verankerte Mini-Sticky-Header
       (`.tabelle-scroller.is-mini`) ist durch den externen, fixed-positio-
       nierten Sticky-Mini-Header (.tabelle-sticky-mini) ersetzt — siehe
       eigener CSS-Block am Ende dieser Datei. */
    @media (max-width: 38em) {
        .tabelle-wrapper {
            /* Politur 2026-05-08h (B1): Anbieter-Spalten auf 30vw reduziert
               (vorher calc((100vw - 0.5em) / 3) ≈ 33.3vw). Bei 375 px
               Viewport ergibt 30vw = 112.5 px je Spalte; 3 Spalten = 337.5 px.
               Plus Site-Container-Offset (~21 px) plus rechte Schimmer-Gradient/
               Scrollbar (~12 px) bleiben rund 5 px Atemluft — drei Anbieter-
               Spalten passen jetzt vollstaendig in den Viewport, das vorherige
               Anschneiden der dritten Spalte am rechten Rand entfaellt.
               Politur 2026-05-11 (P1): von Viewport-vw auf Scroller-clientWidth
               umgestellt. 30vw rechnete gegen den Viewport, der Scroller-
               Container ist wegen Site-Section-Padding aber schmaler als
               100vw — auf einem Samsung S25 (~393 px Viewport, ~343 px
               Scroller-clientWidth) ueberhing die 3. Anbieter-Spalte um
               ~10-15 px nach rechts (Logos, Flaggen-Pille, "AG"-Suffix
               und Texte sichtbar abgeschnitten). `--scroller-w` wird per
               IIFE auf scroller.clientWidth gesetzt UND zusaetzlich am
               Wrapper gespiegelt (tabelleMiniLabelSetupIIFE/syncBreite),
               damit auch der .tabelle-sticky-mini-track als Wrapper-
               Descendant denselben Wert erbt.
               Politur 2026-05-11 (Sticky-Mini-Migration): Selektor vom
               .tabelle-scroller auf .tabelle-wrapper umgestellt (CSS-Custom-
               Properties vererben nur an Descendants, der Sticky-Mini-Track
               als Sibling des Scrollers brauchte den gemeinsamen Vorfahren). */
            --slot-breite: calc((var(--scroller-w, 100vw) - 1.5em) / 3);
            --vp-breite: 0px;
        }
        .tabelle-scroller {
            /* Politur 2026-05-08e (P1): --hdr-logo-h von 8rem auf 10rem
               erhoeht — bei der bisherigen 8rem-Reihe wurde das Logo
               (max-height 8em ≈ 5.76rem bei 0.72rem font-size) plus
               Anbietername (0.78rem * 1.2 line-height) plus Produktname
               (0.7rem * 1.15 line-height) am oberen Rand abgeschnitten,
               weil der Stack flex-flex-start arbeitet und keine Atemluft
               oben hatte. 10rem (~160 px) gibt dem Logo + 2 Textzeilen
               sauberen Platz, das Logo wird nicht mehr beschnitten.
               Politur 2026-05-11 (Folgepatch): von 10rem auf 11rem
               erhoeht. Logo-Wrap ist jetzt flex-shrink: 0 (siehe
               Z. ~1244), das Logo wird nicht mehr gestaucht — bei
               Mobile-30vw-Spalten gerendert mit ~100 px Hoehe. Stack:
               100 + 30 (Anbieter 2-Zeilen-Clamp) + 13 (Produkt
               1-Zeile-Clamp) + 16 (Padding/Gap) = 159 px, passt in
               11rem (176 px) ohne Ueberlauf in die Badge-Reihe. */
            --hdr-logo-h: 11rem;
            /* Politur 2026-05-13: Badge-Reihe (Inland/Auslandsanlage-Pille)
               auf Mobile entfaellt — die Information wandert kompakt in den
               .ab-meta-strip in der linken oberen Ecke der Logo-Zelle
               (Positionsnummer + Landesflagge). Track-Hoehe auf 0px setzt
               die Subgrid-Reihe inaktiv; .ab-zelle.ab-badge + .ab-badge-pill
               werden im Mobile-Block weiter unten zusaetzlich auf
               padding/min-height/display: none kollabiert. Sticky-Top der
               .ab-zelle.ab-zins (calc(--hdr-logo-h + --hdr-badge-h)) klebt
               damit direkt unterhalb der Logo-Reihe — auf Mobile ohnehin
               nur als Containing-Block-Anker relevant. */
            --hdr-badge-h: 0px;
            /* Folgepatch 14d: --row-data-h auf Mobile von 2.75rem auf 5rem
               erhoeht. Mehrzeilige Werte (Einlagensicherung, Hoechstanlage,
               PostIdent/VideoIdent, Zinsausschuettungs-Sonderfaelle) brauchen
               bei 122-px-Spaltenbreite und 12.8-px-Mini-Label-Reservierung
               (padding-top: 1.7em) mehr Platz, sonst ueberragen sie die
               44-px-Cell-Box visuell. 5rem = 80 px abzueglich padding ergibt
               etwa 54 px Inhaltsraum (~3-3.5 Textzeilen); `minmax(min, auto)`
               im Subgrid laesst die Reihe bei laengeren Inhalten weiter
               auto-wachsen. */
            --row-data-h: 5rem;
            /* Politur 2026-05-10e: Spacer-Reihen auf Mobile etwas hoeher als
               Desktop (Toggle-Overlay 30 px + Atemluft). Default 2.75rem
               (~44 px) wirkte noch knapp, 3rem (~48 px) gibt dem Banner sicht-
               bare Luft und bleibt deutlich kompakter als --row-data-h: 5rem
               (~80 px Daten-Reihen). VP-Spalte ist auf Mobile display:none —
               der VP-Gruppentitel-Button spielt keine Rolle. */
            --row-titel-h: 3rem;
            /* Politur 2026-05-13: CTA-Footer-Reihe auf Mobile von 5rem
               (--row-data-h) auf 4rem reduziert — 80% (Anwender-Wunsch).
               Inner-Link (font-size 0.7rem, padding 0.4375em) plus Zellen-
               padding 0.625em ergibt ca. 37-40 px Inhaltshoehe — passt
               bequem in 64 px (4rem). minmax laesst auch hier auto-Wachstum
               zu, wenn der CTA mehr Hoehe braucht. */
            --row-cta-h: 4rem;
            /* Politur 2026-05-11 (M2): Scroll-Snap auf Mobile abschalten. Der
               x-proximity-Snap des Default-Blocks cappte das echte
               scrollLeft-Maximum um ~14 px, sodass die letzte Anbieter-Spalte
               rechts angeschnitten blieb. Auf Touch ist Snap ohnehin eher
               hinderlich (schwungvolles Drag-Scrolling springt zurueck).
               scroll-padding-right schiebt zusaetzlich den Atemraum zum
               Schimmer-Gradient ein. */
            scroll-snap-type: none;
            scroll-padding-right: 0.5em;
        }
        /* Politur 2026-05-11 (P3): column-gap zwischen Anbieter-Spalten auf
           Mobile. Auf Smartphones (Samsung S25-Screenshots) stiessen die
           Spalten ohne sichtbaren Abstand aneinander, Texte (z.B.
           "Einlagensicherung AUSTRIA Ges.m.b.H.") wirkten ineinander
           uebergehend. Subgrid uebernimmt nur Tracks, nicht Gaps — die
           Reihen-Sync zwischen VP-Spalte und Anbieter-Spalten bleibt
           intakt. --slot-breite (P1) kompensiert die 2 × 0.5em-Gaps via
           1.5em-Reserve. */
        .tabelle-grid { column-gap: 0.5em; }
        /* Politur 2026-05-11 (P2): Schimmer-Gradient auf Mobile schmaler und
           halbtransparent. Default 2em × opacity 1 wirkte auf 30vw-Spalten
           als heller Streifen ueber fast einem Drittel der letzten Spalte —
           im Samsung-Screenshot als "Schattierung am rechten Rand der
           Bereiche-Banner" beanstandet. Auf Touch ist Drag-Scrolling auch
           ohne visuellen Scroll-Hinweis intuitiv, der Schimmer kann dezenter
           bleiben. ::before bleibt am Default-opacity-0 (linker Schimmer
           wird erst per .is-scroll-anfang aktiv), nur der dann sichtbare
           Wert wird angepasst. */
        .tabelle-wrapper::after,
        .tabelle-wrapper::before { width: 0.75em; }
        .tabelle-wrapper::after { opacity: 0.5; }
        .tabelle-wrapper.is-scroll-anfang::before { opacity: 0.5; }
        .vp-zelle, .ab-zelle { font-size: 0.72rem; padding: 0.375em 0.4375em; }
        .ab-zelle.ab-zins { font-size: 1.05rem; }
        /* Politur 2026-05-10f: 0.2em Abstand zwischen Zins-Header-Reihe und
           erstem Bereich (MERKMALE-Bezeichnung). Grid-Default `align-self:
           stretch` macht die Zinszelle 0.2em kuerzer als ihre fixe Track-Hoehe
           (--hdr-zins-h: 3rem), die Differenz wirkt als Luecke direkt unter
           dem Zinszahl-Block. Nur auf Mobile — auf Desktop kleben Header und
           Bereichstitel weiter direkt aneinander. */
        .ab-zelle.ab-zins,
        .vp-zelle.vp-header.vp-zins {
            margin-bottom: 0.2em;
        }
        /* Vergleichswert-Modus, Rechnung wie im Default-Block (Zellenschrift
           1.05rem = 16,8 px, Padding 0.25em = 4,2 px, Nominal absolut 13,2 px
           + bottom 2,75 px). Die 0.2em margin-bottom der Zinszelle (Block
           darueber) geht vom Track ab, die Zelle selbst ist also T - 3,36 px hoch:
             Fluss-Inhalt = 16,8 + Gap 6,3 + Balken 4 = 27,1 px
             bei T = 72 px (Zelle 68,64) -> Balken-Unterkante 47,87 /
             Nominal-Oberkante 52,69, Abstand 4,8 px, kollisionsfrei. */
        .vp-spalte.is-vergleichswert,
        .tabelle-grid.is-vergleichswert { --hdr-zins-h: 4.5rem; }
        /* Politur 2026-05-08e (P1): Logo-Stack vertikal zentrieren, damit
           das Logo nicht oben am Rand klebt. justify-content: center auf
           der flex-direction:column-Zelle zentriert vertikal Logo + Anbieter-
           name + Produktname als Block in der 10rem-Reihe — das Logo bekommt
           damit oben automatisch Atemluft. */
        .ab-zelle.ab-logo {
            justify-content: center;
        }
        /* Sticky-Header-Reaktivierung: Background der Sticky-Zellen muss
           solid sein, sonst scheint die scrollende Datenreihe darunter durch.
           Site-Site-Variable `--bg-card` wird hier wegen Subgrid-Hintergrund-
           Vererbung explizit als solid `#0a3a5c`-Aequivalent ausgedrueckt. */
        .ab-zelle.ab-badge,
        .ab-zelle.ab-zins,
        .vp-zelle.vp-header.vp-badge,
        .vp-zelle.vp-header.vp-zins {
            background: rgb(9, 87, 143);
        }
        /* Phase 14 (A3): Mini-Label als Banner ueber jeder Wert-Reihe.
           padding-top reserviert Platz, der Inhalt darunter bleibt zentriert.
           Mini-Label ist position: absolute — keine zusaetzliche Grid-Reihe,
           daher bleiben _vpRowSync und Track-Hoehen unangetastet
           (Stolperstein 12 vermieden). gruppe-zu wirkt durch das Eltern-
           opacity automatisch auch auf das Label.
           Folgepatch 14a-5: font-size von 0.6rem auf 0.7rem (~11.2 px),
           padding-top der Eltern-Zelle von 1.55em auf 1.7em entsprechend
           nachgezogen. Sekundaerer Text traegt damit ueber dem WCAG-
           Mindestwert von 11 px.
           Politur 2026-05-14 (Pass 1): padding-top um 0.2em auf 1.9em
           erhoeht — der Anwender hat gemeldet, dass die Vergleichskriterien
           direkt am unteren Rand des Mini-Labels aufsitzen. Pass 2 (2026-
           05-14, nach Live-Verifikation): noch zu eng — Anwender hat weitere
           0.1em nachverlangt. Endwert 2em. Der :has()-Override fuer
           truncated Labels (siehe naechster Block) wandert mit auf 2.8em. */
        .ab-zelle.ab-merkmal {
            padding-top: 2em;
            /* Politur 2026-05-13: Trennlinie nach jedem Paar aus Mini-Label
               und Datenzeile (Anwender-Wunsch). Default
               `.vp-zelle, .ab-zelle { border-bottom: 1px solid rgba(255,255,255,0.06) }`
               (Z. ~1082) ist zu zart — auf 0.14 verstaerkt (gleiche Staerke
               wie die Trennlinie zur Vorgruppe am .ab-gruppe-spacer border-top).
               Subgrid-Reihen-Subgrid: die Border fluchtet horizontal ueber
               alle drei Anbieter-Spalten zu einer durchgehenden Linie. */
            border-bottom: 1px solid rgba(255, 255, 255, 0.14);
        }
        /* Politur 2026-05-11 (Mini-Label-Truncate, Visual-Pass): bei Reihen
           mit sichtbarem Volltext-Button (`.is-truncated`) waechst das
           Mini-Label-Banner von ~20 auf ~30 px durch den groesseren Button.
           padding-top adaptiv per :has() von 1.7em auf 2.5em hochziehen,
           damit der Banner-Bereich kollisionsfrei zum Wert-Bereich endet.
           Reihen ohne Truncate-Button (kurze Labels, Banner bleibt schmal)
           behalten die schmale Reservierung — verhindert ueberfluessigen
           Leerraum bei kurzen Bezeichnungen. :has() ist seit Chrome 105 /
           Safari 15.4 / Firefox 121 stabil verfuegbar.
           Politur 2026-05-14 (Pass 1+2): +0.3em parallel zur Basis-Regel
           (oben). Pass 1 bewegte den Trennraum von 2.5em auf 2.7em, Pass 2
           nach Live-Verifikation auf 2.8em. */
        .ab-zelle.ab-merkmal:has(.ab-info-icon-mini-label.is-truncated) {
            padding-top: 2.8em;
        }
        /* Politur 2026-05-13: border-bottom der letzten merkmal-Zelle einer
           Gruppe unterdruecken, weil der direkt nachfolgende .ab-gruppe-spacer
           selbst eine border-top: 1px solid rgba(255,255,255,0.14) traegt
           (Default-Regel Z. ~1462). Ohne dieses Override summieren sich
           beide Linien zu einer optischen 2 px-Doppellinie am Gruppen-
           Uebergang. `:has(+ .ab-gruppe-spacer)` greift nur dann, wenn der
           DIREKTE DOM-Sibling ein gruppe-spacer ist — also genau bei der
           letzten merkmal-Zelle vor jedem Bereichstitel-Wechsel.
           Browser-Support analog zur Truncate-:has()-Regel oben. */
        .ab-zelle.ab-merkmal:has(+ .ab-gruppe-spacer) {
            border-bottom: none;
        }
        /* Folgepatch 14b: Pro Wert-Reihe nur EIN Mini-Label, mittig ueber die
           drei sichtbaren Anbieter-Spalten. Verankert in der ersten Anbieter-
           Spalte (DOM-first-child), absolut positioniert mit Viewport-Breite,
           horizontal per JS sticky am linken Viewport-Rand (siehe IIFE
           tabelleMiniLabelSetup, Variable --mini-label-x). Andere Anbieter-
           Spalten rendern weiter ein <span class="ab-mini-label"> im HTML
           (Render-Symmetrie, kein PHP-Index-Param noetig), das aber per
           Default-Mobile-Display:none unsichtbar bleibt. */
        .ab-mini-label { display: none; }
        .tabelle-grid > .anbieter-spalte:first-child .ab-mini-label {
            /* Politur 2026-05-11 (Mini-Label-Truncate): display: flex statt
               block. Damit Text-Span und Volltext-i-Button als Flex-Children
               nebeneinander zentriert sitzen koennen. Ellipsis-Eigenschaften
               (white-space/overflow/text-overflow) wandern auf .ab-mini-
               label-text — der Container kuemmert sich nur um Breite,
               Position und Sticky-Sync. */
            display: flex;
            align-items: center;
            justify-content: center;
            gap: 0.25em;
            position: absolute;
            /* Politur 2026-05-13: 0.2em Abstand zur Top-Kante der Datenzelle
               (Anwender-Wunsch) — Label klebt nicht mehr direkt an der
               Trennlinie der Vorgaenger-Zeile. em bezieht sich auf die
               eigene Schriftgroesse 0.8rem ≈ 12.8 px, ergibt ca. 2.6 px
               Abstand. */
            top: 0.2em;
            left: 0;
            /* Politur 2026-05-10e: Breite an die tatsaechlich sichtbare
               Tabellen-Breite gebunden (`--mini-label-w`, gesetzt von der
               IIFE `tabelleMiniLabelSetupIIFE` als `min(scroller.clientWidth,
               grid.offsetWidth)`). Der Scroller ist wegen Section-Padding
               schmaler als 100vw — vorher ragte das Mini-Label rechts ueber
               die letzte Anbieter-Spalte hinaus. Fallback auf den alten
               100vw-Wert, falls die IIFE nicht laeuft. */
            width: var(--mini-label-w, calc(100vw - 0.5em));
            box-sizing: border-box;
            padding: 0.15em 0.5em;
            font-size: 0.8rem;
            line-height: 1.25;
            /* Politur 2026-05-11 (Label-Hierarchie): Label-Schrift von
               0.78 → 0.55 Deckkraft gedimmt. Damit die Werte in den
               Daten-Zellen die dominantere Schriftfarbe sind und die
               Mini-Labels als sekundaere Tonstufe zuruecktreten
               (Anwender-Wunsch 2026-05-11). Gleiche Farb-Familie wie die
               Werte (reines Weiss), nur Helligkeit reduziert — keine
               eigene Toenung. */
            color: rgba(255, 255, 255, 0.55);
            /* Politur 2026-05-13: Hintergrund von var(--bg-card) auf transparent
               umgestellt (Anwender-Wunsch) — Mini-Label teilt jetzt den Page-
               Hintergrund mit der Datenzelle darunter, beide wirken als eine
               durchgehende Flaeche. Vorher (08e/P2) war hier var(--bg-card) als
               einheitliche Header-System-Optik gemeinsam mit .ab-gruppe-spacer
               und .ab-cta gesetzt. Trennung zur naechsten Reihe sitzt jetzt
               an .ab-zelle.ab-merkmal border-bottom (1 px, 0.14 Opacity). */
            background: transparent;
            text-transform: uppercase;
            letter-spacing: 0.04em;
            text-align: center;
            /* Politur 2026-05-11 (Mini-Label-Truncate): white-space, overflow
               und text-overflow wandern auf das innere .ab-mini-label-text.
               Der Container ist jetzt Flex — Ellipsis greift am Inner-Span,
               der per `min-width: 0` schrumpfen darf.
               Politur 2026-05-13: border-bottom entfernt — die Linie zwischen
               Label und Wert ist nicht mehr gewuenscht (Anwender-Wunsch:
               Label und Wert als zusammengehoeriges Paar). Trennung zur
               naechsten Reihe sitzt jetzt an .ab-zelle.ab-merkmal border-
               bottom. */
            border-bottom: none;
            /* Politur 2026-05-11 (Mini-Label-Truncate): `pointer-events: none`
               entfernt. Mini-Label traegt selbst keinen Click-Handler — die
               Eigenschaft war eine Vorbeugung ohne sichtbares Verhalten,
               blockierte aber Click-Events des Inline-i-Buttons in manchen
               Browser-/Touch-Konstellationen (Anwender-Meldung: Hand-Cursor
               sichtbar, Klick reagierte nicht). Ohne diese Eigenschaft
               faengt der Button seine eigenen Klicks regulaer ueber die
               Event-Delegation in `infoTooltipSetup()`. */
            /* Politur 2026-05-11 (A): translateX → translate3d zwingt den
               Browser, das Element auf eine eigene GPU-Compositor-Layer zu
               promoten. Touch-Scrolling laeuft auf dem Compositor-Thread; ohne
               eigenen Layer wird der translateX-Update vom Main-Thread
               (CSS-Custom-Property setzen → Style-Recalc → Paint) 1-2 Frames
               verzoegert sichtbar — der User berichtete ein "Springen" der
               Legende beim horizontalen Daumen-Scroll. backface-visibility
               ist ein zusaetzlicher Compositor-Hint, der das Layer-Promote
               noch deterministischer macht. */
            transform: translate3d(var(--mini-label-x, 0px), 0, 0);
            backface-visibility: hidden;
            will-change: transform;
            /* Politur 2026-05-11 (Mini-Label-Truncate, Hit-Test-Fix):
               z-index: 2 ueber den nachfolgenden Anbieter-Spalten-Geschwistern
               (die per DOM-Order ohne explizites z-index stapeln). Hintergrund:
               das Mini-Label-Banner sitzt absolute in der first-child-Anbieter-
               Spalte und wird per `transform: translate3d(--mini-label-x)`
               visuell ueber alle drei Spalten gezogen. Ohne z-index lag das
               Banner im Stacking-Kontext der first-child-Spalte und wurde von
               second/third-child `.ab-zelle.ab-merkmal` (jeweils
               position: relative, z-auto) im Hit-Test ueberlagert — Klicks
               auf den Inline-i-Button landeten in der jeweils darunter-
               liegenden Wert-Zelle statt am Button. Unter dem Sticky-Header
               (z-index: 3) und ueber den Geschwistern (z-auto) ist `2` die
               richtige Stufe. Analog am `.mb-gruppentitel`-Bereichstitel-
               Toggle (gleiche transformierte Banner-Mechanik). */
            z-index: 2;
        }
        /* Politur 2026-05-11 (Mini-Label-Truncate): Inner-Span traegt die
           Ellipsis-Eigenschaften, die zuvor am Container lagen. min-width: 0
           ist Pflicht, damit der Flex-Child wirklich schrumpfen kann
           (Default min-width: auto an Flex-Items = min-content) — sonst
           greift text-overflow: ellipsis nicht. flex: 0 1 auto erlaubt das
           Schrumpfen, behaelt aber die Content-Breite als Bevorzugung,
           damit kurze Labels nicht den Platz fuellen. */
        .ab-mini-label-text {
            min-width: 0;
            flex: 0 1 auto;
            overflow: hidden;
            text-overflow: ellipsis;
            white-space: nowrap;
        }
        /* Folgepatch 14c: VP-Spalte auf Mobile komplett unsichtbar.
           Reihen-Labels uebernehmen die Mini-Labels in der ersten Anbieter-
           Spalte (siehe oben), Bereichsbezeichnungen mit Toggle rendern
           als zweite Form/Button-Pair in der .ab-gruppe-spacer (siehe
           weiter unten). VP-Drawer-Mechanik ist komplett rueckgebaut. */
        .vp-spalte { display: none; }

        /* Phase 14 (A6): CTA Icon-only auf Mobile.
           SVG sichtbar, Text ausgeblendet, 48-px-Tap-Target via min-w/h.
           Spezifizitaet `.vergleich-dashboard .anbieter-link` schlaegt
           das Listen-globale Styling aus vergleich.min.css. */
        /* Politur 2026-05-10d: Rechteckiger Button mit zwei Fontello-Icons
           (Bank + Externer-Link-Pfeil) — exakt wie die alte Tabellenansicht.
           Override gegen `.icon`-Defaults aus style.css (font-size: 3em;
           position: absolute; margin-left: -1.6em) und `.icon-bank`-Mobile-
           Override (position: absolute) per hoeherer Spezifizitaet
           (`.vergleich-dashboard .anbieter-link i.icon-bank` = 3 Klassen + 1 Tag). */
        .vergleich-dashboard .anbieter-link {
            display: flex;
            align-items: center;
            justify-content: center;
            min-width: 4rem;
            min-height: 2rem;
            padding: 0.25rem 0.5rem;
            gap: 0.25rem;
        }
        .vergleich-dashboard .anbieter-link i.icon-bank,
        .vergleich-dashboard .anbieter-link i.icon-link-ext {
            display: inline-block;
            position: relative;
            top: 0;
            font-size: 1.25rem;
            margin: 0;
            line-height: 1;
            color: inherit;
            width: auto;
            height: auto;
        }
        .vergleich-dashboard .anbieter-link-text {
            display: none;
        }
        /* Phase 14 (A7): Mobile-Switch fuer Truncate. Volltext aus, Kurzform
           plus Info-Icon ein. Info-Icon mit ausreichend grosser Hitbox. */
        .ab-merkmal-voll { display: none; }
        .ab-merkmal-kurz { display: inline; }
        .ab-info-icon {
            position: relative;
            /* Politur 2026-05-11 (M1b): eigener Stacking-Kontext, damit der
               unsichtbare `::before`-Slop im Hit-Test ueber dem textfuehrenden
               Eltern-DIV `.ab-zelle.ab-merkmal` liegt. Ohne diesen Kontext
               lieferte `elementFromPoint(iconCx, iconY ± 5)` die Elternzelle
               statt des Icons — die vertikale Slop-Region war faktisch
               wirkungslos (VRT-Befund 2026-05-11 C2). */
            isolation: isolate;
            display: inline-flex;
            align-items: center;
            justify-content: center;
            width: 1.4em;
            height: 1.4em;
            margin-left: 0.35em;
            padding: 0;
            /* Politur 2026-05-11 (i-Buttons transparent): Anwender-Wunsch —
               i-Buttons generell ohne eigenen Hintergrund. Glyph + Hand-
               Cursor tragen das visuelle Feedback. */
            background: transparent;
            color: rgba(255, 255, 255, 0.85);
            border: none;
            border-radius: 50%;
            /* Politur 2026-05-11 (M1b): Icon visuell verdoppelt — 0.85em → 1.7em
               Schrift, width/height von 1.4em skaliert mit auf ~27 px Visual
               (vorher ~14 px). Anwender meldete, dass das Icon zu klein zum
               Erkennen sei. Doppelte Schrift macht das ⓘ-Glyph deutlich
               sichtbar; bei `--row-data-h: 5rem` (80 px) und 30vw-Spalten
               (~112 px) bleibt im Layout genug Platz fuer Text + Icon. */
            font-size: 1.7em;
            line-height: 1;
            cursor: pointer;
            vertical-align: baseline;
        }
        /* Folgepatch 14a-1: Pseudoelement-Slop fuer 44-px-Tap-Target.
           Politur 2026-05-11 (M1b): Slop in max(em, rem)-Mix umgestellt, damit
           er bei Icon-font-size-Aenderungen nicht ueberproportional waechst —
           der absolute rem-Boden deckelt die em-Berechnung. Hitbox bleibt
           damit unabhaengig vom Icon-Visual-Faktor in einem stabilen Korridor
           (Visual + 2 × Slop): bei 1.7em-Icon ≈ 27 + 28 = 55 px vertikal,
           27 + 25 = 52 px horizontal — sicher ueber dem 44-px-AAA-Wert.
           Plus z-index: 1 im neuen Stacking-Kontext des Icons (siehe
           `isolation: isolate` oben) — Slop liegt im Hit-Test ueber der
           Eltern-Zelle, der vertikale Tap-Lift wirkt jetzt auch praktisch. */
        .ab-info-icon::before {
            content: '';
            position: absolute;
            top: max(-1.4em, -0.875rem);
            bottom: max(-1.4em, -0.875rem);
            left: max(-1.25em, -0.78125rem);
            right: max(-1.25em, -0.78125rem);
            z-index: 1;
        }
        .ab-info-icon:hover,
        .ab-info-icon:focus-visible {
            color: #fff;
        }
        .ab-info-icon:focus-visible {
            outline: 2px solid var(--accent, #A9F6DB);
            outline-offset: 2px;
        }
        /* Politur 2026-05-11 (Mini-Label-Truncate): Volltext-Button im Mini-
           Label. Spezifizitaet `.ab-info-icon.ab-info-icon-mini-label` (0,2,0)
           schlaegt die generische `.ab-info-icon`-Regel (0,1,0) im Mobile-
           Block. Default unsichtbar — JS-IIFE `tabelleMiniLabelTruncateSetup`
           setzt `.is-truncated`, sobald die Text-Span-Ellipsis gegriffen hat
           (`text.scrollWidth > text.clientWidth`). font-size kleiner als der
           generische 1.7em-Icon, weil das Mini-Label nur 0.8rem traegt —
           ein 1.7em-Button waere zu hoch fuer das schmale Banner. Hitbox-
           Slop entsprechend reduziert. background: transparent — Anwender-
           Wunsch 2026-05-11 (i-Buttons generell ohne eigenen Hintergrund);
           der ⓘ-Glyph plus Hand-Cursor traegt das visuelle Feedback. */
        .ab-info-icon.ab-info-icon-mini-label {
            display: none;
            /* Politur 2026-05-11 (Mini-Label-Truncate, Visual-Pass):
               font-size: 2em statt 1em — Anwender-Wunsch: i-Button im
               Mini-Label-Banner doppelt so gross, damit er in etwa die
               Groesse der Merkmal-Truncate-Buttons (`.ab-info-icon` Mobile,
               font-size: 1.7em an 0.85rem-Schrift) erreicht. width/height
               von 1.2em auf 1em reduziert — kompakterer Quad (25.6 × 25.6 px
               statt 30.7 × 30.7 px), Glyph fuellt die Box. Damit die Banner-
               Hoehe nur moderat (~30 px statt ~35 px) waechst und nicht zu
               sehr in den Werte-Bereich der Zelle ragt. */
            font-size: 2em;
            width: 1em;
            height: 1em;
            margin-left: 0;
            background: transparent;
            /* Politur 2026-05-11 (Mini-Label-Truncate, Visual-Pass): Idle-Color
               an die Mini-Label-Schriftfarbe `rgba(255,255,255,0.55)`
               (Mini-Label-Regel oben, 0.78 -> 0.55 gedimmt)
               angeglichen — Anwender-Wunsch: der Button soll farblich nicht
               heller als der Label-Text wirken. Hover/Focus boostet weiter
               auf #fff als Feedback. */
            color: rgba(255, 255, 255, 0.55);
        }
        .ab-info-icon.ab-info-icon-mini-label.is-truncated {
            display: inline-flex;
        }
        .ab-info-icon.ab-info-icon-mini-label::before {
            /* Slop kleiner als generisches Icon — ~28 px Hitbox bei
               ~12 px Visual. Wert in max(em, rem) analog zu generischem
               Icon, aber etwa halbiert. Slop reicht weiter als die
               Banner-Hoehe, was OK ist: nur der Klick zaehlt, nicht das
               visuelle Pseudoelement. */
            top: max(-0.8em, -0.5rem);
            bottom: max(-0.8em, -0.5rem);
            left: max(-0.6em, -0.4rem);
            right: max(-0.6em, -0.4rem);
        }
        .ab-info-icon.ab-info-icon-mini-label:hover,
        .ab-info-icon.ab-info-icon-mini-label:focus-visible {
            color: #fff;
        }
        /* Politur 2026-05-13: Anlageland-Pille und runder Pos-Badge auf Mobile
           durch den kompakten Meta-Strip in der linken oberen Logo-Ecke ersetzt.
           Die Subgrid-Badge-Reihe (--hdr-badge-h: 0px, weiter oben) ist track-
           hoehenfrei; die Zelle selbst kollabiert hier zusaetzlich (padding 0,
           min-height 0, overflow hidden). Inhalt der Pille per display: none
           inaktiv — die Inland/Ausland-Information wandert in den Strip.
           .ab-pos-badge (Desktop-Kreis am Logo) wird auf Mobile ebenfalls
           unsichtbar geschaltet, weil die Positionsnummer ueber den Strip
           gezeigt wird. */
        .ab-zelle.ab-badge {
            padding: 0;
            min-height: 0;
            overflow: hidden;
        }
        .ab-badge-pill { display: none; }
        .ab-pos-badge { display: none; }

        /* Politur 2026-05-13: kompakter Meta-Strip — Positionsnummer (#NN) plus
           Landesflagge in der oberen linken Ecke der Logo-Zelle. `.ab-zelle.ab-
           logo` ist `position: sticky` (Z. ~1131) und dient damit als
           Containing-Block fuer das `position: absolute`-Element. `pointer-
           events: none` verhindert, dass der Strip Klicks auf das darunter-
           liegende `.ab-logo-link` blockiert. */
        .ab-meta-strip {
            display: flex;
            align-items: center;
            gap: 0.35em;
            position: absolute;
            top: 0.4em;
            left: 0.4em;
            z-index: 2;
            pointer-events: none;
            line-height: 1;
        }
        .ab-meta-pos {
            font-size: 0.7rem;
            font-weight: 600;
            color: rgba(255, 255, 255, 0.92); /* Kontrast-Politur 2026-06-12: 0.6 -> 0.92 */
            letter-spacing: 0.02em;
            line-height: 1;
            font-variant-numeric: tabular-nums;
            /* Platz fuer "#NN" reservieren — bleibt auch bei Sport-Ranking-Tie
               (gleicher Zins wie Vorgaenger, $rang leer) erhalten, damit die
               Flagge in allen Spalten an derselben x-Position startet. Wert
               so gewaehlt, dass zweistellige Nummern sicher passen
               (`#99` ≈ 1.55em); dreistellige (>= 100, theoretisch) wachsen
               den Slot intrinsisch ueber das min-width hinaus. */
            min-width: 1.7em;
            display: inline-block;
        }
        /* Flagge im klassischen Flaggen-Seitenverhaeltnis (ca. 20:14 nativ).
           Keine Abrundung — Anwender-Wunsch 2026-05-13: Flagge soll nicht
           zusaetzlich rund maskiert werden. Das Image bekommt fixe Hoehe
           und proportional auto-Breite, sodass das natuerliche Seitenverhaeltnis
           erhalten bleibt. Die Emoji-Variante wird per Site-globalem JS je nach
           Browser-Support gegen das `<img>` getauscht (Default: Emoji sichtbar,
           Image `display: none`; bei .no-emoji-flags umgekehrt).
           Folgepatch 2026-05-13: `display`-Override am Image entfernt — die
           Site-globale Verstecken-Logik (.no-emoji-flags: Regel in tabelle.min.css,
           Klasse wird von einem Inline-Script in dieser Datei gesetzt)
           wurde sonst aufgehoben
           und beide Varianten erschienen parallel. Groesse beider Varianten
           auf Mittelwert harmonisiert (zwischen vorherigem Emoji 0.95rem
           und Image 0.9rem). */
        .ab-meta-flag {
            display: inline-flex;
            align-items: center;
            line-height: 1;
        }
        .ab-meta-flag .flag-img-variant {
            width: auto;
            height: 0.85rem;
        }
        .ab-meta-flag .flag-emoji-variant {
            font-size: 1.05rem;
            line-height: 1;
        }
        /* Folgepatch 14c: Bereichsbezeichnung mit Toggle in der ersten
           Anbieter-Spalte. Der Button wird in der .ab-gruppe-spacer-Reihe
           absolut positioniert (volle Viewport-Breite), nutzt dieselbe
           --mini-label-x-Variable wie das Mini-Label fuer den Sticky-Sync
           am linken Viewport-Rand. min-height an allen .ab-gruppe-spacer,
           damit die Subgrid-Reihe Platz fuer den Button reserviert (Subgrid
           uebernimmt die maximale Reihen-Hoehe ueber alle Anbieter-Spalten,
           also wirkt das min-height ueber alle drei sichtbaren Spalten). */
        .tabelle-grid .ab-gruppe-spacer {
            min-height: 2.5em;
        }
        .tabelle-grid > .anbieter-spalte:first-child .ab-gruppe-spacer {
            position: relative;
        }
        .tabelle-grid > .anbieter-spalte:first-child .mb-gruppentitel {
            display: flex;
            position: absolute;
            top: 0;
            left: 0;
            /* Politur 2026-05-10e: Breite an die tatsaechlich sichtbare
               Tabellen-Breite gebunden (siehe Kommentar an .ab-mini-label
               weiter oben). */
            width: var(--mini-label-w, calc(100vw - 0.5em));
            box-sizing: border-box;
            /* Politur 2026-05-11 (A): translate3d + backface-visibility analog
               zum .ab-mini-label-Block (siehe oben). Erzwingt eigene
               GPU-Compositor-Layer, sodass der Bereichsbezeichnungs-Toggle
               nicht hinter dem Touch-Scroll herhinkt. */
            transform: translate3d(var(--mini-label-x, 0px), 0, 0);
            backface-visibility: hidden;
            will-change: transform;
            /* Politur 2026-05-11 (Mini-Label-Truncate, Hit-Test-Fix):
               analog zum .ab-mini-label-Block — z-index 2 ueber den
               nachfolgenden Spalten-Geschwistern, damit der Bereichstitel-
               Toggle-Button im transformierten Banner-Bereich klickbar
               bleibt (Vorbeugung gegen den Stacking-Konflikt, den Anwender
               am Mini-Label-Volltext-Button gemeldet hat). */
            z-index: 2;
            /* z-index/translate3d sind die Sticky-Sync-Mechanik (Banner folgt
               dem Horizontal-Scroll). Padding und Chevron-Position liegen in
               der .mb-gruppentitel-Regel unten (space-between, padding-right
               5% — Chevron am rechten Banner-Rand). */
        }
        /* Redesign 2026-06-19 (Expander-Band "Feine Koernung"): Auf Mobile
           traegt die .ab-gruppe-spacer-Reihe die Koernungs-Flaeche (siehe
           Default-Block oben; der fruehere Mobile-Transparent-Reset ist
           entfallen). Dieser Overlay-Button bleibt transparent und liefert nur
           Label + Chevron, am Viewport-Rand gepinnt (Sticky-Sync via
           --mini-label-x). Padding 2.5% links / 5% rechts: space-between setzt
           den Chevron damit an den rechten Banner-Rand. Prozentwerte beziehen
           sich auf die Banner-Breite (--mini-label-w = min(scroller.clientWidth,
           grid.offsetWidth)). */
        .tabelle-grid > .anbieter-spalte:first-child .mb-gruppentitel {
            background: transparent;
            border-top: none;
            border-bottom: none;
            padding-left: 2.5%;
            padding-right: 5%;
        }
        /* Hover (2026-06-19b): Koernung bleibt unveraendert; der Chevron hellt
           auf (wie Desktop). Touch-Geraete zeigen kaum Hover. */
        .tabelle-grid > .anbieter-spalte:first-child .mb-gruppentitel:hover .vp-gruppentitel-caret {
            color: #BFDBEF;
        }
        /* Redesign 2026-06-19: Der fruehere Mobile-Transparent-Reset der
           .ab-gruppe-spacer ist entfallen — auf Mobile gilt jetzt dieselbe
           Koernungs-Flaeche wie auf Desktop (Default-Block oben), sodass das
           Band auch auf kleinen Displays als eigene Struktur liest. */
        /* Mobile-Chevron etwas groesser fuer bessere Tap-Sichtbarkeit auf
           kleinen Displays (Desktop nutzt die 15px aus dem SVG-Attribut). */
        .tabelle-grid > .anbieter-spalte:first-child .mb-gruppentitel .vp-gruppentitel-caret svg {
            width: 18px;
            height: 18px;
        }
    }
    /* === Phase 4: Drag-to-Scroll, Snap, Mini-Scrollbar === */
    /* Mini-Scrollbar-Indikator (Frage 20 = C) */
    .tabelle-scrollbar-indikator {
        position: relative;
        height: 0.25em;
        margin: 0.5em 0.25em 0;
        background: rgba(255,255,255,0.08);
        border-radius: 999px;
        overflow: hidden;
        transition: opacity 150ms ease;
    }
    .tabelle-scrollbar-thumb {
        position: absolute;
        top: 0;
        left: var(--thumb-x, 0%);
        height: 100%;
        width: var(--thumb-width, 100%);
        background: rgba(255,255,255,0.6); /* Kontrast-Politur 2026-06-12: 0.4 -> 0.6 (~3:1 UI-Ziel) */
        border-radius: inherit;
    }
    @media (hover: hover) and (pointer: fine) {
        .tabelle-scrollbar-thumb { transition: background 125ms ease; }
        .tabelle-scroller:hover ~ .tabelle-scrollbar-indikator .tabelle-scrollbar-thumb,
        .tabelle-scroller.is-dragging ~ .tabelle-scrollbar-indikator .tabelle-scrollbar-thumb {
            background: rgba(255,255,255,0.75); /* Kontrast-Politur 2026-06-12: Hover mit angehoben */
        }
    }
    .ab-mini-logo {
        display: flex;
        align-items: center;
        justify-content: flex-end;
        max-height: 6em;
        max-width: 100%;
    }
    @media (max-width: 38em) {
        /* Mobile: VP-Spalte ist display:none — kein Container-Offset noetig
           (JS liefert vpBreitePx = 0). Mini-Spalten in der Breite des
           3-Anbieter-Mobile-Grids; column-gap muss zum Original-Grid passen
           (Z. ~1770) — sonst driften Mini-Spalten gegen Original-Spalten
           beim Horizontal-Scroll (~5.75 px/Spalte Versatz, vom visuellen
           Regressionstest 2026-05-11 detektiert).
           Politur 2026-05-12: font-size der Mini-Spalte auf 0.72rem (analog
           Mobile-`.ab-zelle`, Z. ~1785). Damit ergibt `max-height: 6em` am
           .ab-mini-logo die gleiche relativen Skalierung wie auf Desktop —
           ~69 px Logo-Hoehe (6 * 0.72rem). */
        .tabelle-sticky-mini-track {
            column-gap: 0.5em;
        }
        .anbieter-spalte-mini {
            font-size: 0.72rem;
            padding: 0.375em 0.4375em;
        }
    }




    @media (prefers-reduced-motion: reduce) {
        .vergleich-dashboard .balken-toggle-bar,
        .vergleich-dashboard .balken-toggle-arrow { transition: none; }
    }

    /* Touch-Geraete (kein Hover): Umschalt-Hinweis nicht einblenden, damit die
       Sprechblase nach dem Antippen nicht stehen bleibt. Maus-Hover und Tastatur-
       Fokus (:focus-visible) zeigen den Hinweis weiterhin. */
    @media (hover: none) {
        .vergleich-dashboard .balken-toggle[data-tooltip]:hover::after { opacity: 0; }
    }

    /* Mobile (gestapelt): groesserer Abstand ueber dem Toggle-Band, optisch wie
       der Abstand zwischen Laufzeit- und Anlagebetrag-Band (ca. --space-lg). */
    @media (max-width: 48em) {
        .vergleich-dashboard .filter-section .ausland-filter-group { margin-top: var(--space-sm); }
    }

    /* ── Europakarte (Länderfilter) ── */
    /* display:flex (column) ueberschreibt das auto-fit-Grid der zentralen .filter-row —
       sonst laegen .ek-kopf und .ek-spalten als Grid-Items nebeneinander. */
    /* Buehne: eine Nuance dunkler als das mittelblaue Panel (rgba(9,87,143)),
       aber bewusst NICHT navy-dunkel — der urspruengliche Handelsblatt-Ton war
       fuer die Seite zu dunkel (Anwender-Feedback 2026-07-07). min-height
       gibt der Karte (meet, zentriert) Raum, damit sie als Buehne wirkt.
       Full-Bleed (2026-07-09): margin -1.5em = horizontales Padding der
       .anpassen-panel — die Buehne laeuft bis an die Panelkante, nur der
       hell→dunkel→hell-Uebergang bleibt sichtbar; deshalb keine Rundung und
       keine Seiten-Border. Das horizontale Padding gleicht die 1.5em wieder
       aus, damit die Inhalte buendig zu den uebrigen Panel-Zeilen stehen. */
    .vergleich-dashboard .ek-sektion { position: relative; overflow: hidden; margin-left: -1.5em; margin-right: -1.5em; padding: var(--space-md) calc(var(--space-md) + 1.5em); background: linear-gradient(160deg, rgba(10,48,82,0.88), rgba(14,64,108,0.78)); border: 1px solid var(--border); border-left: 0; border-right: 0; min-height: 27em; display: flex; flex-direction: column; justify-content: center; }
    .ek-kopf { flex: 0 0 auto; }
    /* Karte liegt hinter den Spalten; Wrapper faengt keine Klicks, aber die
       Landflaechen/Marker schon — so klicken freie Flaechen zu den Checkboxen durch. */
    .ek-karte-wrap { position: absolute; inset: 0; z-index: 0; pointer-events: none; display: flex; align-items: center; justify-content: center; }
    .ek-karte-wrap svg { width: 100%; height: 100%; }
    .ek-karte-wrap .ek-land, .ek-karte-wrap .ek-marker { pointer-events: auto; }
    .ek-kopf, .ek-spalten { position: relative; z-index: 1; }
    /* Die vollbreiten Container fangen sonst Klicks im freien Korridor ab —
       durchlassen, nur ihre Inhalte (Pills, Zaehler, Glas-Spalten) fangen. */
    .ek-kopf, .ek-spalten { pointer-events: none; }
    .ek-kopf > *, .ek-spalten .filter-column { pointer-events: auto; }
    /* Karten-Zustaende (Land-Stroke = Buehnenfarbe, wirkt als Laendergrenze) */
    .ek-silhouette path { fill: rgba(255,255,255,0.07); }
    .ek-land { fill: rgba(255,255,255,0.2); stroke: rgba(10,48,82,0.9); stroke-width: .5; cursor: pointer; transition: fill var(--transition-base), filter var(--transition-base); }
    .ek-land.ek-gewaehlt { fill: var(--primary); filter: drop-shadow(0 0 6px rgba(0,112,192,0.55)); }
    .ek-land:not(.ek-gesperrt):not(.ek-gewaehlt):hover { fill: rgba(0,112,192,0.45); }
    .ek-land.ek-gesperrt { fill: rgba(255,255,255,0.02); cursor: default; }
    .ek-marker { fill: rgba(255,255,255,0.14); stroke: rgba(255,255,255,0.55); stroke-width: .5; cursor: pointer; transition: fill var(--transition-base), filter var(--transition-base); }
    .ek-marker.ek-gewaehlt { fill: var(--primary); filter: drop-shadow(0 0 6px rgba(0,112,192,0.55)); }
    .ek-marker:not(.ek-gesperrt):not(.ek-gewaehlt):hover { fill: rgba(0,112,192,0.45); }
    .ek-marker.ek-gesperrt { fill: rgba(255,255,255,0.05); cursor: default; }
    /* Sanfter Puls beim Zustandswechsel (JS setzt .ek-pop kurz) */
    @keyframes ekPop { 0% { opacity: .55; } 100% { opacity: 1; } }
    .ek-pop { animation: ekPop 250ms ease; }
    /* Kopf: nur noch Schnellauswahl + Zaehler. Der Gruppen-Umschalter liegt
       nicht mehr hier, sondern auf den Glas-Karten selbst (.ek-spalte) —
       die aktive Karte zeigt, was die Karte gerade darstellt. */
    .ek-kopf { display: flex; flex-wrap: wrap; gap: .5em .75em; margin-bottom: .75em; align-items: center; }
    .ek-presets { display: inline-flex; flex-wrap: wrap; gap: .3em; align-items: center; }
    .ek-presets-label { color: var(--text-secondary); font-size: .75em; margin-right: .2em; }
    .ek-preset-btn {
        cursor: pointer; font: inherit; font-size: .75em;
        padding: .3em .85em; border-radius: 1.4em; color: rgba(255,255,255,0.85);
        background: transparent; border: 1px solid rgba(255,255,255,0.3);
        transition: background var(--transition-base), border-color var(--transition-base);
    }
    .ek-preset-btn:hover { background: rgba(169,246,219,0.25); border-color: rgba(169,246,219,0.5); }
    .ek-zaehler { color: var(--text-secondary); font-size: .85em; margin-left: auto; align-self: center; }
    /* Checkbox-Spalten als Glas ueber der Karte */
    /* Mittel-Korridor zwischen den Spalten: die Karte (meet, zentriert) wird
       dort sichtbar — Kerneuropa liegt genau in der Mitte. */
    .ek-spalten { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: clamp(2em, 24%, 16em); row-gap: var(--space-md); }
    /* Bewusst duenn (Alpha .26, blur 1px), damit die Karte durchscheint. */
    .ek-spalten .filter-column { background: rgba(10,48,82,0.26); backdrop-filter: blur(1px); border: 1px solid var(--border); border-radius: var(--radius-sm); padding: .75em; }
    /* Die Karten SIND der Gruppen-Umschalter: ganze Flaeche aktivierbar.
       Aktiv-Markierung = dasselbe Muster wie die gewaehlte Laufzeit im
       "alle Zinsen"-Tab (.zins-item.highlight): Akzent-Border + transparente
       Akzentflaeche. */
    .ek-spalte { cursor: pointer; transition: background var(--transition-base), border-color var(--transition-base); }
    .ek-spalte:not(.ek-spalte-aktiv):hover { border-color: rgba(255,255,255,0.35); }
    .ek-spalten .ek-spalte-aktiv { background: rgba(169,246,219,0.15); border-color: var(--accent); }
    /* Karten-Titel als echter Button (Tastatur-Zugang zum Umschalten),
       optisch unveraendert — der .filter-label-Span behaelt den zentralen Stil. */
    .ek-spalte-kopf { display: block; width: 100%; background: none; border: none; padding: 0; margin: 0; font: inherit; color: inherit; text-align: left; cursor: pointer; }
    /* Glas-Karten trennen sich selbst — zentrale Separator-Raender/-Margins neutralisieren
       (die frueheren .filter-row-separator[style]-Regeln greifen ohne Inline-style nicht mehr). */
    .ek-spalten .filter-column-separator { margin: 0; }
    /* Tooltip */
    .ek-tooltip { position: absolute; z-index: 2; pointer-events: none; background: rgba(10,37,64,0.92); color: #fff; padding: .3em .6em; border-radius: .4em; font-size: .8em; white-space: nowrap; opacity: 0; transition: opacity 150ms ease; }
    .ek-tooltip.ek-sichtbar { opacity: 1; }
    /* Tablet-Zwischenbereich (50–64em): die Tafeln sind wegen des Karten-
       Korridors schmal — Checkboxen einspaltig, sonst laeuft das zentrale
       repeat(2,1fr)-Grid ueber den Tafelrand (VRT-Befund 2026-07-07). */
    /* Untere Kante 50.01em statt 50em (Audit A-7.2 / Phase K6 2026-08-12): exakt 800px
       traf sonst diesen Block UND den `max-width: 50em`-Block darunter gleichzeitig. */
    @media (min-width: 50.01em) and (max-width: 64em) {
        .ek-spalten .checkbox-group-wrap { grid-template-columns: 1fr; }
    }
    /* Schmale Viewports: Spalten untereinander, Karte wird dezente Textur.
       Gleicher Breakpoint wie die alte Sitz-Zeile (50em). */
    @media (max-width: 50em) {
        .ek-spalten { grid-template-columns: 1fr; }
        .ek-karte-wrap { opacity: .18; }
    }

    /* === Hochkontrast-Hell-Modus ============================================
       Port aus den Girokonto-AT-Cores (2026-07-09). Aktiv nur bei
       #vergleich.kontrast-hell. ALLE Overrides per Spezifitaet ueber den
       ID+Klasse-Praefix (schlaegt vergleich.min.css/tabelle.min.css) — NIE !important,
       em-Konvention (px nur border-width/box-shadow/outline/gradient/@media), keine
       Geometrie-/transform-Aenderung (FLIP unberuehrt), Heatmap-Logik unveraendert.
       Akzente konsequent Blau statt Tuerkis/Gruen. Bewusste Ausnahmen: das
       Sticky-Header-Band (Logo/Badge/Zins) bleibt Marken-Blau (weisse Logos/Zinszahlen
       bleiben lesbar); die Europakarte (.ek-*, dunkle Glas-Buehne) bleibt als
       Marken-Element unangetastet (Anwender-Entscheidung 2026-07-09).

       --- Toggle-Button (in BEIDEN Modi sichtbar; Basis = Dunkel-Standardansicht).
       Die .kontrast-zeile ist ein hoehenloser Anker (Markup AUSSERHALB der
       Partial-Swap-Zone zwischen #hilfe und Dashboard); der Button wird absolut auf die
       "Laufzeit"-Label-Zeile des Dashboards gelegt (rechtsbuendig). Positionswerte 1:1
       aus Girokonto uebernommen — Live-Nachmessung per VRT nach Upload.
       z-index 1: ueber dem statischen Label, unter den Sticky-Baendern (z 3). --- */
    #vergleich .kontrast-zeile {
        display: block; position: relative;
        height: 0; margin: 0; overflow: visible;
        z-index: 1;
    }
    #vergleich .kontrast-toggle {
        /* Mobile-Gestalt (0.85rem, Icon-only) in ALLEN Groessen. */
        position: absolute; right: 0.85em; top: 2.17em; /* VRT 2026-07-09: +3px, Button-Mitte exakt auf Label-Mitte */
        display: inline-flex; align-items: center;
        padding: 0.5em 0.95em; font-size: 0.85rem; font-weight: 600; line-height: 1.2;
        letter-spacing: 0.02em; color: #e8f2fb;
        background: rgba(9,87,143,0.62); box-shadow: 0 0.125em 0.5em rgba(0,0,0,0.25);
        border: 0; border-radius: 0; cursor: pointer;
        white-space: nowrap; -webkit-tap-highlight-color: transparent;
        transition: background 150ms ease, color 150ms ease;
    }
    #vergleich .kontrast-toggle:hover { background: rgba(13,110,175,0.96); color: #fff; }
    #vergleich .kontrast-toggle:focus-visible { outline: 2px solid #DFFDF2; outline-offset: 0.125em; }
    #vergleich .kontrast-toggle[aria-pressed="true"] { background: rgba(9,87,143,0.62); color: #fff; }
    #vergleich .kontrast-toggle .kontrast-icon { font-size: 1.05em; line-height: 1; }
    /* Eck-Winkel (Markup-Spans) — Form/Hintergrund/Schatten/Ecken wie der
       anpassen-filter-btn, in ALLEN Displaygroessen (einheitliche Button-Gestalt). */
    #vergleich .kontrast-toggle .ecke { position: absolute; width: 0.7em; height: 0.7em; pointer-events: none; }
    #vergleich .kontrast-toggle .ecke-tl { top: 0.22em; left: 0.22em; border-top: 2px solid #A9F6DB; border-left: 2px solid #A9F6DB; }
    #vergleich .kontrast-toggle .ecke-tr { top: 0.22em; right: 0.22em; border-top: 2px solid #A9F6DB; border-right: 2px solid #A9F6DB; }
    #vergleich .kontrast-toggle .ecke-bl { bottom: 0.22em; left: 0.22em; border-bottom: 2px solid #A9F6DB; border-left: 2px solid #A9F6DB; }
    #vergleich .kontrast-toggle .ecke-br { bottom: 0.22em; right: 0.22em; border-bottom: 2px solid #A9F6DB; border-right: 2px solid #A9F6DB; }
    /* Mobil (h1 via style.css ab 38em ausgeblendet -> Anker rutscht an den Kartenanfang):
       nur die Position justieren; Groesse/Gestalt kommen aus der Basis-Regel. */
    @media (max-width: 38em) {
        #vergleich .kontrast-toggle { top: 0.4em; right: 0.6em; }
    }

    /* ===== Ebene A — Flaeche (weisse Karte auf blauem Seitenhintergrund) =====
       Geometrie exakt wie die Dunkel-Basis (style.css #vergleich padding:0 0 .9em,
       kein umlaufender Rahmen) — der Hell-Modus darf beim Umschalten NICHTS verschieben.
       Karten-Rahmen als layout-neutraler box-shadow-Ring statt border. KEINE transition. */
    #vergleich.kontrast-hell {
        background: #fff; color: #111;
        padding: 0 0 0.9em; border-radius: 0.5em;
        box-shadow: 0 0 0 1px rgba(0,82,141,0.20), 0 0.5em 2em rgba(0,0,0,0.12);
    }
    /* Oberer H1-Strich: nur die Farbe kippen (transparent), NICHT border-top:none
       (die 1px Rahmenhoehe muss erhalten bleiben, sonst springt der Inhalt). */
    #vergleich.kontrast-hell h1 { color: #00385f; border-top-color: transparent; }
    /* Ansichts-Icons der Kopfleiste (Listen-/Tabellen-/Diagramm-/Hilfe-Icon):
       dunkles Blau NUR oberhalb 72em, wo #hilfe auf der weissen Karte liegt.
       Ab 72em abwaerts zieht style.css #hilfe ins blaue Kopf-Band (#09578f) —
       dort waren die Icons blau auf blau und praktisch unsichtbar (live gemessen
       11.08.2026: #00528D auf #09578f). Unterhalb 72em gelten deshalb wieder die
       Basis-Mintfarben; der 38em-Block weiter unten haelt sie zusaetzlich fest. */
    @media only screen and (min-width: 72.01em) {
        #vergleich.kontrast-hell #hilfe .icon { color: #00528D; }
        #vergleich.kontrast-hell #hilfe .current .icon { color: #003A66; }
        #vergleich.kontrast-hell #hilfe a:hover .icon, #vergleich.kontrast-hell #hilfe a:focus .icon { color: #0070C0; }
    }

    /* Toggle-Button im Hell-Modus — wie anpassen-filter-btn hell */
    #vergleich.kontrast-hell .kontrast-toggle { color: #00528D; background: #fff; box-shadow: 0 0.125em 0.5em rgba(0,82,141,0.25); }
    #vergleich.kontrast-hell .kontrast-toggle:hover { background: rgba(0,112,192,0.14); color: #003A66; }
    #vergleich.kontrast-hell .kontrast-toggle:focus-visible { outline-color: #0070C0; }
    #vergleich.kontrast-hell .kontrast-toggle[aria-pressed="true"] { background: #fff; color: #003A66; }
    #vergleich.kontrast-hell .kontrast-toggle .ecke-tl { border-top-color: #0070C0; border-left-color: #0070C0; }
    #vergleich.kontrast-hell .kontrast-toggle .ecke-tr { border-top-color: #0070C0; border-right-color: #0070C0; }
    #vergleich.kontrast-hell .kontrast-toggle .ecke-bl { border-bottom-color: #0070C0; border-left-color: #0070C0; }
    #vergleich.kontrast-hell .kontrast-toggle .ecke-br { border-bottom-color: #0070C0; border-right-color: #0070C0; }

    /* ===== Ebene B — Variablen-Override (.vergleich-dashboard); AAA >= 7:1 Fliesstext. ===== */
    #vergleich.kontrast-hell .vergleich-dashboard {
        --primary: #0070C0; --primary-dark: #00528D; --primary-darker: #003A66;
        --accent: #00528D; --accent-light: #0070C0;
        --bg-card: rgba(0,82,141,0.05); --bg-card-hover: rgba(0,82,141,0.10);
        --border: rgba(0,82,141,0.30); --border-light: rgba(0,82,141,0.14);
        --text-primary: #111; --text-secondary: rgba(0,0,0,0.88); --text-muted: #404040;
        background: #fff;
    }

    /* ===== Ebene C — hartkodierte Farben uebersteuern ===== */

    /* -- Sticky-VP-Spalte (Merkmal-Labels) hell, blaue Trennkante -- */
    #vergleich.kontrast-hell .vp-spalte { background: #fff; border-right-color: rgba(0,82,141,0.28); box-shadow: 0.25em 0 0.5em rgba(0,82,141,0.10); }
    #vergleich.kontrast-hell .vp-zelle { color: #003A66; background: #fff; }
    #vergleich.kontrast-hell .vp-zelle, #vergleich.kontrast-hell .ab-zelle { border-bottom-color: rgba(0,82,141,0.12); }
    #vergleich.kontrast-hell .ab-merkmal.ab-leer { color: rgba(0,0,0,0.65); }

    /* -- Sticky-Header-Stapel (Logo/Badge/Zins) bleibt als Marken-Blau-Band (bewusste
          Ausnahme, wie in Girokonto das Logo-Band). Badge/Zins sind im Dunkelmodus
          rgba(9,87,143,0.92) (auf Blau opak wirkend) -> auf der weissen Karte auf die
          opake Variante setzen. Logo-Reihe traegt bereits ihr eigenes #0070C0.
          Schrift/Zinszahl weiss (var(--text-primary) wuerde sie auf #111 kippen). -- */
    #vergleich.kontrast-hell .vp-zelle.vp-header,
    #vergleich.kontrast-hell .ab-zelle.ab-badge,
    #vergleich.kontrast-hell .ab-zelle.ab-zins { background: rgb(9,87,143); }
    #vergleich.kontrast-hell .vp-zelle.vp-header { color: rgba(255,255,255,0.9); }
    #vergleich.kontrast-hell .ab-anbieter { color: #fff; }
    #vergleich.kontrast-hell .ab-zelle.ab-zins { color: #fff; }

    /* -- Geschenk-Icon (cardgift-g.svg, Neukundenaktion): Tuerkis -> Marken-Hellblau.
          SVG kommt als <img> -> Filterkette faerbt exakt auf #0070C0; eigener Hover,
          weil der Basis-Hover (brightness/saturate) den Filter ersetzen wuerde. -- */
    #vergleich.kontrast-hell .ab-merkmal-html a.cardgifta img {
        filter: brightness(0) saturate(100%) invert(26%) sepia(98%) saturate(946%) hue-rotate(176deg) brightness(110%) contrast(113%);
    }
    #vergleich.kontrast-hell .ab-merkmal-html a.cardgifta:hover img {
        filter: brightness(0) saturate(100%) invert(26%) sepia(98%) saturate(946%) hue-rotate(176deg) brightness(110%) contrast(113%) brightness(125%);
    }

    /* -- Footer-CTA-Reihe / Empty-State -- */
    #vergleich.kontrast-hell .vp-zelle.vp-footer { background: #fff; border-top-color: rgba(0,82,141,0.15); color: #003A66; }
    #vergleich.kontrast-hell .ab-zelle.ab-cta { border-top-color: rgba(0,82,141,0.15); }
    #vergleich.kontrast-hell .tabelle-keine-ergebnisse { color: #111; }
    #vergleich.kontrast-hell .vergleich-dashboard .footer-line a { color: #00528D; }
    #vergleich.kontrast-hell .vergleich-dashboard .footer-line a:hover { color: #0070C0; }

    /* -- CTA-Button "Zum Anbieter" (var(--accent)+var(--primary-darker) -> Dunkel-auf-Dunkel):
          explizit blaue Fuellung, weisse Schrift; Pfeil-Chip invertiert. -- */
    #vergleich.kontrast-hell .vergleich-dashboard .anbieter-link { background: #0070C0; color: #fff; }
    #vergleich.kontrast-hell .vergleich-dashboard .anbieter-link:hover { background: #00528D; }
    #vergleich.kontrast-hell .vergleich-dashboard .anbieter-link .link-arrow { background: #fff; color: #00528D; }

    /* -- Fussnoten (tabelle.min.css: hartkodiert Weiss-Text + Tuerkis-Links) -- */
    #vergleich.kontrast-hell .tabelle-fussnoten { background: rgba(0,82,141,0.05); color: #333; }
    #vergleich.kontrast-hell .tabelle-fussnoten li strong { color: #111; }
    #vergleich.kontrast-hell .tabelle-fussnoten a { color: #00528D; }
    #vergleich.kontrast-hell .tabelle-fussnoten a:hover { color: #0070C0; }

    /* -- Gruppen-Titel-Baender (Koernung) hell-blau statt Schwarz/Weiss-Transparenz -- */
    #vergleich.kontrast-hell .ab-gruppe-spacer, #vergleich.kontrast-hell button.vp-gruppentitel {
        background-color: rgba(0,82,141,0.06);
        background-image: radial-gradient(circle, rgba(0,82,141,0.10) 0 0.7px, transparent 1.1px);
        border-top-color: rgba(0,82,141,0.16); border-bottom-color: rgba(0,82,141,0.10);
        box-shadow: inset 0 1px 0 rgba(255,255,255,0.6);
    }
    #vergleich.kontrast-hell button.vp-gruppentitel { color: #00528D; }
    #vergleich.kontrast-hell .vp-gruppentitel-caret { color: #00528D; }
    #vergleich.kontrast-hell button.vp-gruppentitel:hover .vp-gruppentitel-caret,
    #vergleich.kontrast-hell button.vp-gruppentitel.is-row-hover .vp-gruppentitel-caret { color: #0070C0; }

    /* -- Laufzeit-Pills / Anlagebetrag-Toggle: helle Leisten, blaue Akzente. Aktive
          Pill/Toggle nutzen background:var(--text-primary) -> im Hell-Modus #111 (dunkel);
          darum explizit blau fuellen, Schrift weiss. -- */
    #vergleich.kontrast-hell .vergleich-dashboard .laufzeit-nav { background: rgba(0,82,141,0.06); }
    #vergleich.kontrast-hell .vergleich-dashboard .laufzeit-pill { color: #003A66; }
    #vergleich.kontrast-hell .vergleich-dashboard .laufzeit-pill:hover { background: rgba(0,82,141,0.08); color: #003A66; }
    #vergleich.kontrast-hell .vergleich-dashboard .laufzeit-pill.active { background: #0070C0; color: #fff; }
    /* Fokus-Ring im Hell-Modus: der Akzent #A9F6DB faellt auf den hellen
       Leisten auf 1,13:1 (Pill) bzw. 1,15:1 (CTA-Zelle) -- ebenso der
       globale #4DE5B4 mit 1,45:1. Blau #0070C0 erreicht dort 4,67:1 bzw.
       4,75:1 (live gemessen 2026-08-06) und ist die im Hell-Modus bereits
       fuer Toggle, Filter-Button und Pager gesetzte Fokusfarbe. */
    #vergleich.kontrast-hell .vergleich-dashboard .laufzeit-pill:focus-visible,
    #vergleich.kontrast-hell .vergleich-dashboard .anbieter-link:focus-visible { outline-color: #0070C0; }
    #vergleich.kontrast-hell .vergleich-dashboard .toggle-group,
    #vergleich.kontrast-hell .vergleich-dashboard .filter-group .toggle-group { background: rgba(0,82,141,0.06); }
    #vergleich.kontrast-hell .vergleich-dashboard .toggle-btn { color: #003A66; }
    #vergleich.kontrast-hell .vergleich-dashboard .toggle-btn:hover { background: rgba(0,82,141,0.08); color: #003A66; }
    #vergleich.kontrast-hell .vergleich-dashboard .toggle-btn.active { background: #0070C0; color: #fff; }
    #vergleich.kontrast-hell .vergleich-dashboard .filter-label,
    #vergleich.kontrast-hell .vergleich-dashboard .filter-column-title { color: #003A66; }

    /* -- Europakarte-Buehne (Laenderfilter): bewusste dunkle Marken-Ausnahme
          (Anwender-Entscheidung 2026-07-09 — nicht umfaerben). Die Buehne ist ein
          dunkles Navy-Gradient-Panel; Ebene-B-Variablen + die generische filter-label-
          Hell-Regel oben wuerden die hellen Karten-Beschriftungen darauf ins Dunkle
          kippen. Darum die Schrift-Variablen lokal auf Dunkelmodus-Werte zuruecksetzen
          und die Beschriftungen wieder aufhellen (Quellreihenfolge schlaegt die
          gleichspezifische Regel oben). Land-/Marker-/Preset-/Spalten-Farben bleiben
          komplett unveraendert. -- */
    #vergleich.kontrast-hell .ek-sektion {
        --text-primary: #fff; --text-secondary: rgba(255,255,255,0.85); --text-muted: rgba(255,255,255,0.6);
        --accent: #00d4aa; --accent-light: #00d4aa;
    }
    #vergleich.kontrast-hell .ek-sektion .filter-label,
    #vergleich.kontrast-hell .ek-sektion .filter-column-title { color: rgba(255,255,255,0.85); }

    /* -- Auslandsanlagen-Balken-Toggle (Festgeld-spezifisch, tabelle.min.css: helle Bars
          #DCEBF8, State/Arrow #F1F6FC/#DCEBF8 -> auf Weiss unsichtbar). Blaue Umfaerbung;
          aktive Bars = Marken-Blau (wie laufzeit-pill.active). -- */
    #vergleich.kontrast-hell .vergleich-dashboard .balken-toggle-state { color: #00528D; }
    #vergleich.kontrast-hell .vergleich-dashboard .balken-toggle-arrow { color: #00528D; }
    #vergleich.kontrast-hell .vergleich-dashboard .balken-toggle-bars-dash .balken-toggle-bar { background: rgba(0,82,141,0.30); }
    #vergleich.kontrast-hell .vergleich-dashboard .balken-toggle[data-state="off"] .balken-toggle-bars-dash .balken-toggle-bar:nth-child(-n+8),
    #vergleich.kontrast-hell .vergleich-dashboard .balken-toggle[data-state="on"] .balken-toggle-bars-dash .balken-toggle-bar { background: #0070C0; }

    /* -- "weitere Filter"-Button (Floating): dunkelblau/tuerkis -> Weiss + Blau -- */
    #vergleich.kontrast-hell .anpassen-filter-btn { background: #fff; box-shadow: 0 0.125em 0.5em rgba(0,82,141,0.25); }
    #vergleich.kontrast-hell .anpassen-filter-btn:hover, #vergleich.kontrast-hell .anpassen-filter-btn.open { background: rgba(0,112,192,0.14); }
    #vergleich.kontrast-hell .anpassen-filter-btn:focus-visible { outline-color: #0070C0; }
    #vergleich.kontrast-hell .anpassen-filter-btn .ecke-tl { border-top-color: #0070C0; border-left-color: #0070C0; }
    #vergleich.kontrast-hell .anpassen-filter-btn .ecke-tr { border-top-color: #0070C0; border-right-color: #0070C0; }
    #vergleich.kontrast-hell .anpassen-filter-btn .ecke-bl { border-bottom-color: #0070C0; border-left-color: #0070C0; }
    #vergleich.kontrast-hell .anpassen-filter-btn .ecke-br { border-bottom-color: #0070C0; border-right-color: #0070C0; }
    #vergleich.kontrast-hell .anpassen-filter-btn .icon-wrap .icon-sliders { color: #00528D; }
    #vergleich.kontrast-hell .anpassen-badge { background: #0070C0; color: #fff; }

    /* -- Filter-Overlay-Panel: dunkelblau -> weisses Panel, blauer Rahmen; Backdrop bleibt.
          Titel/Hints nutzen var(--text-secondary) (Ebene B deckt sie ab) — nur die
          hartkodierten Weiss-Werte (.filter-panel-titel Farbe+Border, panel-close-btn)
          brauchen Overrides. Checkboxen sind native accent-color:#00528d (bereits blau). -- */
    #vergleich.kontrast-hell .anpassen-panel {
        background: #fff; border-color: rgba(0,82,141,0.35);
        box-shadow: 0 0.5em 2.5em rgba(0,0,0,0.25), 0 0.125em 0.5em rgba(0,0,0,0.15);
    }
    #vergleich.kontrast-hell .filter-panel-titel { color: #00528D; border-bottom-color: rgba(0,82,141,0.18); }
    #vergleich.kontrast-hell .filter-checkbox { color: #333; }
    #vergleich.kontrast-hell .filter-checkbox:hover { color: #003A66; }
    #vergleich.kontrast-hell .panel-close-btn { background: rgba(0,82,141,0.10); color: #00528D; }
    #vergleich.kontrast-hell .panel-close-btn:hover, #vergleich.kontrast-hell .panel-close-btn:focus { background: rgba(0,82,141,0.18); color: #003A66; }

    /* -- Pager (Skala/Chevrons): Tuerkis -> Blau -- */
    #vergleich.kontrast-hell .tabelle-pager-range { color: #111; }
    #vergleich.kontrast-hell .tabelle-pager-bereich, #vergleich.kontrast-hell .tabelle-pager-bereich strong { color: #00528D; }
    #vergleich.kontrast-hell .tabelle-pager-rest { color: #333; }
    #vergleich.kontrast-hell .tabelle-pager-btn { color: #0070C0; }
    #vergleich.kontrast-hell .tabelle-pager-btn:not(:disabled):hover, #vergleich.kontrast-hell .tabelle-pager-btn:focus-visible { color: #00528D; }
    #vergleich.kontrast-hell .tabelle-pager-btn:focus-visible { outline-color: #0070C0; }
    #vergleich.kontrast-hell .tabelle-pager-track::before { background: repeating-linear-gradient(90deg, rgba(0,82,141,0.20) 0 1px, transparent 1px 11px), rgba(0,82,141,0.08); }
    #vergleich.kontrast-hell .tabelle-pager-track:focus-visible { outline-color: #0070C0; }
    #vergleich.kontrast-hell .tabelle-pager-fenster { background: rgba(0,112,192,0.18); border-color: #0070C0; }

    /* -- Scroll-Indikator / native Scrollbar / Scroll-Fade-Kanten: Weiss -> Blau -- */
    #vergleich.kontrast-hell .tabelle-scrollbar-indikator { background: rgba(0,82,141,0.10); }
    #vergleich.kontrast-hell .tabelle-scrollbar-thumb { background: rgba(0,82,141,0.55); }
    #vergleich.kontrast-hell .tabelle-scroller:hover ~ .tabelle-scrollbar-indikator .tabelle-scrollbar-thumb,
    #vergleich.kontrast-hell .tabelle-scroller.is-dragging ~ .tabelle-scrollbar-indikator .tabelle-scrollbar-thumb { background: rgba(0,82,141,0.75); }
    #vergleich.kontrast-hell .tabelle-scroller { scrollbar-color: rgba(0,82,141,0.55) rgba(0,82,141,0.10); }
    #vergleich.kontrast-hell .tabelle-scroller::-webkit-scrollbar-track { background: rgba(0,82,141,0.10); }
    #vergleich.kontrast-hell .tabelle-scroller::-webkit-scrollbar-thumb { background: rgba(0,82,141,0.55); }
    #vergleich.kontrast-hell .tabelle-scroller::-webkit-scrollbar-thumb:hover { background: rgba(0,82,141,0.75); }
    #vergleich.kontrast-hell .tabelle-wrapper::after { background: linear-gradient(to right, transparent 0%, rgba(0,82,141,0.14) 100%); }
    #vergleich.kontrast-hell .tabelle-wrapper::before { background: linear-gradient(to left, transparent 0%, rgba(0,82,141,0.14) 100%); }

    /* -- Mobile (<=38em): weisses Vollband, #hilfe-Basisfarben, Merkmal-Trennlinien -- */
    @media (max-width: 38em) {
        /* Vollbreites weisses Band statt abgegrenzter Karte: die seitlichen Blau-Streifen
           neben #vergleich werden per box-shadow-Kopien (50vw versetzt, layout-neutral)
           weiss uebermalt. */
        #vergleich.kontrast-hell { border-radius: 0; box-shadow: 50vw 0 0 #fff, -50vw 0 0 #fff; }
        /* #hilfe zieht style.css mobil in das blaue Kopf-Band (#09578f) -> Basis-Akzentfarben
           erhalten, sonst Blau-auf-Blau. */
        #vergleich.kontrast-hell #hilfe .icon { color: var(--mint-500); }
        #vergleich.kontrast-hell #hilfe .current .icon { color: #104266; }
        #vergleich.kontrast-hell #hilfe a:hover .icon, #vergleich.kontrast-hell #hilfe a:focus .icon { color: var(--mint-100); }
        #vergleich.kontrast-hell .ab-zelle.ab-merkmal { border-bottom-color: rgba(0,82,141,0.14); }
        #vergleich.kontrast-hell .ab-mini-label { color: #00528D; }
        #vergleich.kontrast-hell .ab-info-icon { color: #00528D; }
        #vergleich.kontrast-hell .ab-info-icon:hover, #vergleich.kontrast-hell .ab-info-icon:focus-visible { color: #003A66; }
        #vergleich.kontrast-hell .ab-info-icon.ab-info-icon-mini-label { color: #00528D; }
        #vergleich.kontrast-hell .ab-info-icon.ab-info-icon-mini-label:hover, #vergleich.kontrast-hell .ab-info-icon.ab-info-icon-mini-label:focus-visible { color: #003A66; }
        #vergleich.kontrast-hell .tabelle-grid > .anbieter-spalte:first-child .mb-gruppentitel:hover .vp-gruppentitel-caret { color: #0070C0; }
    }
    /* === Ende Hochkontrast-Hell-Modus === */

    /* Diagramm-Ansichts-Icon (Zinsgebirge-Verdrahtung, AT-Portierung 2026-07-18): eigenes
       Inline-SVG "Gebirgskette" statt Fontello-icon-chart-area (das traegt unscoped Fremd-Styles
       der zentralen .icon-Regel: font-size:3em, position:absolute, margin-left:-1.6em). Das SVG
       erbt currentColor, die #hilfe-Farbregeln (Ruhe/Hover) greifen unveraendert. Gemessene
       DE-Verdrahtungsloesung: inline-block + vertical-align:middle richtet die Glyphe an der
       line-box-Mitte der Fontello-Nachbarn aus (Mittellinien-Abweichung <=2px; die
       Diagrammseiten-Referenz mit display:block/margin:0 auto versagte im Link-Kontext mit 5.2px
       bei 1280). Das gescopte a[href^='diagramm']{display:block;text-align:center} zentriert die
       in-flow-Glyphe im li, OHNE die absolut positionierten Fontello-Nachbarn zu verschieben. */
    #hilfe li a[href^='diagramm'] { display: block; text-align: center; }
    /* position MUSS relative sein, nicht static: style.css vergroessert die Tap-Targets mit
       "a > .icon::after { position:absolute; inset:-5px }". Bei static faellt der Bezugsrahmen
       auf einen Vorfahren zurueck — die unsichtbare Trefferflaeche wird ~330px breit und verdeckt
       die Nachbar-Icons (live gemessen 18.07.2026 auf AT und DE). relative behaelt die Position
       im Fluss und stellt den Bezugsrahmen wieder her. Beim Portieren nicht auf static setzen. */
    /* Vertikal-Politur 2026-07-18: symmetrischer top-Nudge zieht den Link-Zustand (hier)
       UND den current-Zustand auf der Diagrammseite auf die Mitte ihrer frueheren Positionen
       (der Link lag ~1.5px @Basis / ~3.8px @<=88em zu tief, die Diagrammseite spiegelbildlich
       hoch — Gegenwert im Diagramm-Core +1.5px / +3.8px). <=72em: beide nativ schon
       deckungsgleich, top:0 ueberschreibt dort nur die geerbten 88em-Werte. Live gemessen
       |A-B| <= 1px ueber 1920/1280/1024/375 (max 0.92px; <=72em nativ), beide nahe der Alt-Mitte. */
    #hilfe .zg-view-icon { display: inline-block; position: relative; margin: 0; width: 1.3em; line-height: inherit; vertical-align: middle; font-size: 1.85em; top: -1.5px; }
    #hilfe .zg-view-icon svg { width: 1.3em; height: 0.91em; vertical-align: middle; display: inline-block; }
    @media (max-width: 88em) { #hilfe .zg-view-icon { font-size: 1.3em; top: -3.8px; } }
    @media (max-width: 72em) { #hilfe .zg-view-icon { font-size: 1.2em; top: 0; } }

    /* Absatzwechsel innerhalb einer Fussnote (siehe .fn-absatz im PHP-Block):
       definierter Abstandshalter statt der vollen Leerzeile aus <br><br>.
       0.35em ergibt gemessene 7,4 px — unterhalb der 8,8 px zwischen zwei
       Anbieter-Eintraegen, damit die Absaetze sichtbar zum selben Eintrag
       gehoeren. Herkunft der Messreihe: fussnoten-absatzabstand-rollout-
       uebergabe.md (Tabellendarstellung/Festgeld DE). */
    .tabelle-fussnoten .fn-absatz { display: block; height: 0.35em; }
    