/* =========================================================================
   DRB PORTAL — Anwendungsschicht ueber drb-it.de v3 („Papier")
   ------------------------------------------------------------------------
   Diese Datei ERGAENZT `drb-site.css`. `drb-site.css` ist eine byte-identische
   Kopie des Website-Stylesheets und wird im Portal NIEMALS bearbeitet —
   Aenderungen passieren auf der Website und werden herueberkopiert.

   Was von dort kommt und hier nicht wiederholt wird:
     · die Schriften als @font-face (Instrument Sans, Martian Mono)
     · saemtliche Token hell UND dunkel samt Umschaltlogik
       (`prefers-color-scheme` + `:root[data-theme]`)
     · Grundstile, `.sr-only`, `.btn`, `.btn--quiet`, `.tlink`, `.theme-btn`
       (mit `.sonne` / `.mond`), `.tab-wrap` / `table.tab`, `.eyebrow`,
       `.mono`, `.muted`, `.lead`, `.plist`, `.mt-*`
     · der Fokusring, die Bewegungsabschaltung und der Druckstil

   ------------------------------------------------------------------------
   FORMENSPRUNG 05.08.2026 (Entscheidung des Inhabers)
   Der erste Stand war die Website, vertikal gestellt: Flaechen statt Karten,
   Punktlinien statt Wertepaaren, laufende Nummern statt Symbolen. Als
   Anwendungsoberflaeche wirkte das karg. Geaendert hat sich die FORM, nicht
   die Marke:

     · eine eigene Radienleiter fuer die Anwendung (10 / 12 / 14 / 18 px)
     · Panels sind echte Karten: heller als der Grund, mit Schatten in zwei
       Stufen — im Dunkelmodus traegt der Schatten kaum, dort traegt Helligkeit
     · Symbolfelder (getoentes Quadrat + Strichsymbol) in Navigation,
       Kennzahlen und Listen
     · die Kennzahl-Kachel als neues Bauteil
     · Navigation mit gefuellter Aktivflaeche statt Kante, ohne Nummern
     · Kopfzeile mit sichtbarem Suchfeld, Glocke und Konto-Baustein
     · Punktlinien-Wertelisten abgeschafft: Beschriftung/Wert in normaler
       Schrift. Martian Mono bleibt fuer echte Technikwerte.

   Keine neuen Grundfarben. Farbe kommt aus `--akz` und den vier Signalfarben,
   sonst nirgendwoher. Kein Farbverlauf, kein Blur, keine dritte Schrift.

   Einbindung: zwei `<link>` im Wurzel-Layout, drb-site.css zuerst, beide mit
   Cache-Buster `?v=N`. Bewusst KEIN `@import` — der erzeugte auf der Website
   eine dreistufige Ladekette und CLS bis 0,24.

   Alle Kontrastwerte in den Kommentaren sind nachgerechnet
   (sRGB -> relative Leuchtdichte -> WCAG 2.2). Toenungen sind in OKLab
   gemischt und der Mischwert ist als Hex mit angeschrieben, damit die Zahl
   nachpruefbar bleibt.
   ========================================================================= */


/* ============================================================== 1 · TOKEN */
:root {
    /* ----------------------------------------------------------- Signale
       Vier Bedeutungen, hell und dunkel getrennt festgelegt. Farbe traegt
       nie allein: jede Meldung hat zusaetzlich ein Symbol und ein Wort
       (WCAG 1.4.1). Die Werte sind trotzdem so gewaehlt, dass sie auch als
       TEXT zulaessig sind (>= 4.5:1), nicht nur als Kante (>= 3:1).

       Fehler und Hinweis nehmen die Token, die es schon gibt:
         Fehler  = --signal   Hinweis = --akz
       Neu sind nur Erfolg und Warnung — genau die beiden, die fehlten.

       Reihenfolge der Zahlen: Karte / Grund / Flaeche, also gegen
         hell    #FFFFFF / #FAFBFC / #F2F4F6 */

    --sig-ok:   oklch(0.480 0.120 152);  /* #10703A   6.18 / 5.96 / 5.60 */
    --sig-warn: oklch(0.510 0.105 85);   /* #816000   5.82 / 5.62 / 5.28 */
    --sig-bad:  var(--signal);           /* #A63C0C   6.42 / 6.19 / 5.82 */
    --sig-info: var(--akz);              /* #1A57AD   6.98 / 6.73 / 6.33 */

    /* ------------------------------------------------------------ Karten
       Die Karte ist HELLER als ihr Grund. Hell ist das reines Weiss auf dem
       Papiergrund — ein Flaechenunterschied von nur 1.04:1, den das Auge fuer
       sich genommen nicht sieht. Deshalb traegt hier der SCHATTEN die Hebung
       und die Haarlinie hilft nur an den Stellen, an denen der Schatten
       ausserhalb des Sichtfeldes liegt (Kartenoberkante am Bildschirmrand).

       `--card-2` ist die Stufe darunter: verschachtelte Karten, Tabellen,
       Fussleisten. `--card-line` ist die dekorative Kante der Karte
       (`--line`, 1.33:1) — sie markiert kein Bedienelement und faellt
       deshalb nicht unter WCAG 1.4.11. Kanten VON Bedienelementen nehmen
       durchgehend `--line-ui` (3.62:1 auf der Karte). */
    --card:      oklch(1 0 0);          /* #FFFFFF  ink 17.10 · ink-2 7.76 · ink-3 5.52 · akz 6.98 */
    --card-2:    var(--paper-2);        /* #F2F4F6  ink 15.51 · ink-2 7.04 */
    --card-line: var(--line);

    /* ------------------------------------------------------------ Schatten
       Zwei Stufen: Ruhe und Hover. Hell arbeitet der Schatten mit der Tinte
       bei sehr kleiner Deckung — ein schwarzer Schatten wirkt auf kuehlem
       Papier schmutzig. Rein dekorativ, traegt nie Information. */
    --shadow-1:
        0 1px 2px oklch(0.225 0.014 258 / 0.05),
        0 4px 12px -4px oklch(0.225 0.014 258 / 0.10);
    --shadow-2:
        0 2px 4px oklch(0.225 0.014 258 / 0.06),
        0 14px 30px -10px oklch(0.225 0.014 258 / 0.18);
    /* Alter Name, damit bestehende Regeln (Kontomenue, Schublade) tragen. */
    --lift: var(--shadow-2);

    /* ------------------------------------------------------------ Toenung
       Die getoenten Flaechen der Symbolfelder, der aktiven Navigationszeile
       und der Meldungen. Gemischt wird in OKLab gegen die Karte; die
       Prozentzahl ist ein Token, weil sie im Dunkelmodus hoeher liegen muss
       (dort verschluckt der dunkle Grund die Farbe schneller).

       Nachgerechnet, hell (gemischt in #FFFFFF):
         akz  12%  #E3EBF6   akz darauf 5.81 · ink 14.23 · ink-2 6.46
         ok   12%  #E4EDE6   ok  darauf 5.16 · ink 14.30
         warn 12%  #EFEBE2   warn darauf 4.89 · ink 14.37
         bad  12%  #F7E8E2   bad darauf 5.38 · ink 14.33
       Symbole brauchen 3:1, Text 4.5:1 — beides ist mit Abstand erfuellt. */
    --tint:      12%;
    --tint-soft:  8%;
    --tint-akz:     color-mix(in oklab, var(--akz)      var(--tint), var(--card));
    --tint-ok:      color-mix(in oklab, var(--sig-ok)   var(--tint), var(--card));
    --tint-warn:    color-mix(in oklab, var(--sig-warn) var(--tint), var(--card));
    --tint-bad:     color-mix(in oklab, var(--sig-bad)  var(--tint), var(--card));
    --tint-info:    color-mix(in oklab, var(--sig-info) var(--tint), var(--card));
    --tint-neutral: var(--card-2);
    /* Ruhigere Fassung fuer grosse Flaechen (Meldungen). */
    --wash-ok:   color-mix(in oklab, var(--sig-ok)   var(--tint-soft), var(--card));
    --wash-warn: color-mix(in oklab, var(--sig-warn) var(--tint-soft), var(--card));
    --wash-bad:  color-mix(in oklab, var(--sig-bad)  var(--tint-soft), var(--card));
    --wash-info: color-mix(in oklab, var(--sig-info) var(--tint-soft), var(--card));

    /* ------------------------------------------------------------- Radien
       `--radius: 8px` ist die Vorgabe der WEBSITE und bleibt dort unberuehrt.
       Die Anwendung hat eine eigene Leiter; sie wird weiter unten auf `.app`
       gesetzt, nicht hier — so bleibt drb-site.css ausserhalb der Anwendung
       exakt wie gehabt. */
    --r-sm:   10px;   /* Felder, Knoepfe, Statuszeichen  */
    --r-ico:  12px;   /* Symbolfelder                    */
    --r-md:   14px;   /* Karten                          */
    --r-lg:   18px;   /* Kacheln, Dialoge, leerer Zustand */

    /* -------------------------------------------------- Z-Index-Leiter
       Eine Leiter statt ad-hoc-Zahlen. Die Werte der Website (Kopf 60,
       Sprungmarke 200) stehen nie auf derselben Seite wie diese hier. */
    --z-base:   0;    /* Inhalt                 */
    --z-sticky: 30;   /* mitlaufende Leisten    */
    --z-bar:    40;   /* Kopfzeile              */
    --z-pop:    60;   /* Kontomenue, Tooltip    */
    --z-drawer: 70;   /* Schublade              */
    --z-modal:  80;   /* Dialog                 */
    --z-toast:  90;   /* Meldung                */
    --z-skip:  100;   /* Sprungmarke            */

    /* Nebel hinter der Schublade: dunkel, und in BEIDEN Modi derselbe Wert
       — er soll den Hintergrund abdunkeln, nicht mitkippen. Dekorativ. */
    --scrim: oklch(0.145 0.010 250 / 0.58);

    /* ----------------------------------------------------------- Dichte
       Luftig ist die Vorgabe. Wer einmal im Quartal hereinschaut, braucht
       grosse Ziele — Touch-Ziel mindestens 44 px (Bauplan § 4). Die Felder
       sind mit dem Formensprung eine Stufe groesser geworden. */
    --row-h:       46px;
    --row-h-dense: 40px;
    --field-h:     50px;
    --cell-pad-y:  14px;
    --tap-gap:     var(--s-2);   /* Mindestabstand harmlos <-> gefaehrlich */

    /* ------------------------------------------------------ App-Grundriss */
    --nav-w:    276px;
    --bar-h:     68px;
    /* KEINE Deckelung der Anwendungsbreite (Entscheidung des Inhabers, 05.08.2026).
       Vorher standen hier 1400 px mit `margin-inline: auto`; auf einem breiten
       Bildschirm schwamm die Anwendung damit in der Mitte, die Navigation klebte
       nicht am linken Rand, und rechts blieb Platz ungenutzt, obwohl Tabellen und
       Kachelreihen ihn brauchen koennen. Eine Anwendung ist kein Fliesstext:
       gedeckelt wird nur, was gelesen wird (`--read-w`), nicht das Raster. */
    --app-wrap: none;
    --read-w:    78ch;
    --auth-w:  27rem;   /* 432 px — die Anmeldekarte */
    --setup-w: 39rem;   /* 624 px — die Einrichtung des zweiten Faktors:
                           QR-Code aus der Hand scannbar und ein 39-stelliger
                           Schluessel zum Abtippen brauchen mehr als 432 px */

    /* Schriftgroessen-Schalter (spaetere Einstellung, drei Stufen). */
    --scale: 1;
}

:root[data-textsize="gross"]      { --scale: 1.125; }
:root[data-textsize="sehr-gross"] { --scale: 1.25;  }

/* ---------------------------------------------------------- Dunkle Variante
   Reihenfolge exakt wie in drb-site.css: erst die Systemvorgabe, dann die
   ausdrueckliche Wahl. Weil der zweite Block spaeter steht, gewinnt er bei
   gleicher Spezifitaet. Beide Bloecke sind inhaltsgleich — das ist Absicht
   und kein Versehen: CSS kennt keine Selektor-Variablen.

   Reihenfolge der Kontrastzahlen hier: Karte / Grund / Flaeche, also gegen
     dunkel  #15191C / #0E1114 / #202429

   WARUM DER SCHATTEN HIER ANDERS IST
   Auf dunklem Grund ist ein Schlagschatten fast wirkungslos: Schwarz auf
   Fast-Schwarz gibt keine Kante. Die Hebung traegt deshalb die HELLIGKEIT —
   die Karte ist `--paper-2` auf `--paper` (1.07:1) — verstaerkt durch eine
   eingesetzte Lichtkante an der Oberseite und eine sichtbare Aussenlinie.
   Der Schlagschatten bleibt, aber nur noch als Tiefenandeutung unter der
   Karte, dafuer weiter und dunkler als hell. */
@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) {
    --sig-ok:   oklch(0.800 0.120 155);  /* #7AD59C   9.96 / 10.66 /  8.79 */
    --sig-warn: oklch(0.860 0.120 98);   /* #E6D271  11.64 / 12.46 / 10.27 */
    --sig-bad:  var(--signal);           /* #FB9D59   8.48 /  9.08 /  7.48 */
    /* Hinweis kann hier NICHT --akz sein: im Dunkelmodus ist --akz Mint und
       damit optisch die Erfolgsfarbe — ein Hinweis saehe aus wie „erledigt".
       Deshalb ein eigenes, kuehles Blau. */
    --sig-info: oklch(0.800 0.090 240);  /* #87C6F2   9.59 / 10.27 /  8.46 */

    --card:      var(--paper-2);         /* #15191C  ink 15.31 · ink-2 9.47 · ink-3 6.64 · akz 10.58 */
    --card-2:    var(--paper-3);         /* #202429  ink 13.51 · ink-2 8.36 */
    --card-line: var(--line);            /* #2F3338  1.39 gegen die Karte — dekorativ */

    --shadow-1:
        inset 0 1px 0 oklch(1 0 0 / 0.045),
        0 6px 18px -8px oklch(0 0 0 / 0.62);
    --shadow-2:
        inset 0 1px 0 oklch(1 0 0 / 0.065),
        0 18px 38px -12px oklch(0 0 0 / 0.78);

    /* Nachgerechnet, dunkel (gemischt in #15191C):
         akz  14%  #22302D   akz darauf 8.22 · ink 11.89 · ink-2 7.36
         ok   14%  #222F2C   ok  darauf 7.83 · ink 12.03
         warn 14%  #2D2F29   warn darauf 8.92 · ink 11.73
         bad  14%  #312A26   bad darauf 6.76 · ink 12.21
         info 14%  #232E35   info darauf 7.52 · ink 12.01 */
    --tint:      14%;
    --tint-soft: 12%;
    }
}
:root[data-theme="dark"] {
    --sig-ok:   oklch(0.800 0.120 155);  /* #7AD59C   9.96 / 10.66 /  8.79 */
    --sig-warn: oklch(0.860 0.120 98);   /* #E6D271  11.64 / 12.46 / 10.27 */
    --sig-bad:  var(--signal);           /* #FB9D59   8.48 /  9.08 /  7.48 */
    --sig-info: oklch(0.800 0.090 240);  /* #87C6F2   9.59 / 10.27 /  8.46 */

    --card:      var(--paper-2);         /* #15191C  ink 15.31 · ink-2 9.47 · ink-3 6.64 · akz 10.58 */
    --card-2:    var(--paper-3);         /* #202429  ink 13.51 · ink-2 8.36 */
    --card-line: var(--line);

    --shadow-1:
        inset 0 1px 0 oklch(1 0 0 / 0.045),
        0 6px 18px -8px oklch(0 0 0 / 0.62);
    --shadow-2:
        inset 0 1px 0 oklch(1 0 0 / 0.065),
        0 18px 38px -12px oklch(0 0 0 / 0.78);

    --tint:      14%;
    --tint-soft: 12%;
}

/* Hoher Kontrast: die dekorative Linie rueckt auf die UI-Linie vor — dann
   traegt die Kante die Karte und nicht mehr der Schatten. */
@media (prefers-contrast: more) {
    .app {
        --line: var(--line-ui);
        --card-line: var(--line-ui);
    }
}

/* Dichtestufe. Ausschliesslich fuer das Adminportal gedacht — im Kundenportal
   bleibt es beim Touch-Raster. Die Breite der Navigation bleibt bewusst
   gleich: Zusammenklappen auf Symbolbreite ist moeglich, aber nicht die
   Vorgabe (Bauplan § 4). */
.app[data-density="kompakt"] {
    --row-h:      var(--row-h-dense);
    --field-h:    44px;
    --cell-pad-y: 10px;
}


/* ========================================================= 2 · KLEINTEILE */

/* --------------------------------------------------- Beschriftung und Wert
   Frueher stand hier die Briefzeile „Betrieb ......... Wert" mit Punktlinie
   und Mono-Schrift. Beides war Papier-Denken: die Punktlinie fuehrt das Auge
   ueber eine leere Strecke, die es in einer Anwendung nicht zu ueberbruecken
   gibt, und Mono macht aus jedem Firmennamen einen Maschinenwert.

   Jetzt: zwei Spalten, Beschriftung leise links, Wert kraeftig rechts, beides
   in der Textschrift. `.dots` bleibt als Klasse bestehen und ist unsichtbar —
   das Markup mehrerer Seiten setzt sie noch, und ein fehlender Selektor waere
   genau der Fehler, den `pnpm run klassen` verhindern soll. Wer eine Zeile
   neu schreibt, laesst sie weg. */
.row {
    display: grid;
    /* Die Beschriftungsspalte hat als Untergrenze ihr EIGENES Wort, nicht null.
       Mit `minmax(0, 1fr)` durfte sie auf 0 zusammenfallen, sobald der Wert
       breit war — und weil `.label` sichtbar ueberlaeuft, schob sich der Wert
       ueber sie: in der Kontextspalte von `/a/support/<nr>` stand bei 1440 px
       „Zusa10.08.2026, 17:00 Uhr · beantwortet" (Beschriftung 7 px sichtbar,
       47 px noetig). Dieselbe Wurzel wie F1-M2, nur ausgeloest von einer engen
       SPALTE statt einem engen BILDSCHIRM — der Umbruch unten haengt am
       Sichtfenster und greift dort deshalb nie. Der Wert darf stattdessen
       umbrechen; `auto` schrumpft bis zu seinem laengsten Wort. */
    grid-template-columns: minmax(min-content, 1fr) auto;
    align-items: baseline;
    gap: var(--s-2);
    padding-block: 11px;
}
.row + .row { border-top: 1px solid var(--line); }
.row .label {
    color: var(--ink-2);
    font-size: var(--t-small);
    /* `drb-site.css` setzt am Koerper global `overflow-wrap: break-word`.
       Wird die Beschriftungsspalte zu schmal, bricht das Wort MITTENDRIN —
       aus „Vertragsende" wurde eine Buchstabensaeule (Abnahme F1, M-2).
       Eine Beschriftung ist ein Wort und kein Datenstrom: sie bricht zwischen
       Woertern oder gar nicht. */
    overflow-wrap: normal;
}
.row .dots { display: none; }
.row .val {
    justify-self: end;
    text-align: right;
    font: 500 var(--t-small)/1.5 var(--f-sans);
    font-variant-numeric: tabular-nums;
    color: var(--ink);
}
/* Echte Technikwerte — Kennung, Zeitstempel, Slug, Wiederherstellungscode —
   bekommen zusaetzlich `.num` und damit Martian Mono zurueck. */
.row .val.num,
.row .val .num,
.row .val.pruefsumme,
.row .val .pruefsumme { font-family: var(--f-mono); }
/* Eine umgebrochene Pruefsumme rechtsbuendig zu setzen ergaebe eine Treppe.
   Der Kasten bleibt rechts (`justify-self: end`), die Zeilen darin stehen
   links. Bei einer einzigen Zeile ist beides nicht zu unterscheiden — der
   Kasten ist dann so breit wie sein Inhalt. */
.row .val.pruefsumme { text-align: left; }
/* Auf dem Telefon reicht eine Zeile nicht fuer beides. Nebeneinander gewinnt
   der Wert (Spalte `auto` = Mindestbreite seines Inhalts) und draengt die
   Beschriftung auf null — „Vertragsende · 30.11.2026 · ordentliche Kuendigung"
   war der Fall, der es zeigte. Untereinander steht beides vollstaendig da und
   der Wert rueckt nach links, weil rechtsbuendig ohne Gegenstueck nur wie ein
   Versehen aussieht. */
@media (max-width: 560px) {
    .row {
        grid-template-columns: minmax(0, 1fr);
        align-items: start;
        gap: 3px;
    }
    .row .val { justify-self: start; text-align: left; }
}
/* Telefonzeile: der Wert ist ein Verweis und braucht ein Tap-Ziel. */
.tel-row .val a {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
    color: var(--akz);
    text-decoration: none;
}
.tel-row .val a:hover { text-decoration: underline; text-underline-offset: 4px; }

/* Leise Mono-Auszeichnung ueber oder unter einer Angabe. */
.meta {
    font: 500 var(--t-meta)/1.6 var(--f-mono);
    letter-spacing: 0.09em;
    text-transform: uppercase;
    color: var(--ink-3);
}
/* Derselbe Satz, wenn er eine NUMMER traegt (Vorgangsnummer, Kennung) und
   deshalb Zeichen fuer Zeichen ablesbar sein muss. */
.meta--strong {
    text-transform: none;
    letter-spacing: 0.02em;
    font-weight: 600;
    color: var(--ink);
    font-variant-numeric: tabular-nums slashed-zero;
}
/* Versalien-Ueberschrift eines kleinen Blocks. */
.caps {
    display: block;
    font: 500 var(--t-meta)/1.6 var(--f-mono);
    letter-spacing: 0.09em;
    text-transform: uppercase;
    color: var(--ink-3);
}
/* Zahl, Datum, Betrag, Kennung — auch ausserhalb von Tabellen. */
.num {
    font-family: var(--f-mono);
    font-variant-numeric: tabular-nums slashed-zero;
    white-space: nowrap;
}

/* Ein Pfad oder eine Adresse — dieselbe Mono-Schrift wie `.num`, aber
   umbrechbar.

   `.num` haelt eine Zahl, ein Datum, einen Betrag in EINER Zeile zusammen, und
   das ist dort richtig. Ein Pfad ist dafuer zu lang: in `.num` bleibt er EIN
   Stueck und schiebt die Seite waagerecht auf — dieselbe Familie wie V-6/V-7
   bei der Pruefsumme. Deshalb eine EIGENE Klasse und keine Aenderung an
   `.num`: die Betraege und Datumsangaben in den Tabellen brauchen den
   Umbruchschutz weiterhin.

   Nachgemessen an der Auszeichnung der Notiz auf `/a/neuerungen`, mit den
   echten Stylesheets in Chromium, 26.08.2026: mit `.num` war das Schriftstueck
   408 px breit und rollte bei 320 px Fenster waagerecht; mit `.dateipfad` bleibt es
   bei 320 px. Die Notiz richtet sich nach ihrem Inhalt, deshalb hilft hier nur
   `anywhere` — `overflow-wrap: break-word` aendert die Mindestbreite nicht und
   liess die 408 px stehen.

   ⚠️ DER PREIS STEHT DAZU: `anywhere` erlaubt eine Bruchstelle an JEDEM
   Zeichen. Der Pfad bricht damit auch mitten im Wort, und ein Satzzeichen
   unmittelbar dahinter kann allein in die naechste Zeile rutschen. Das ist
   haesslicher als ein Umbruch am Schraegstrich und immer noch besser als eine
   Seite, die zur Seite rollt.

   `white-space: normal` steht ausdruecklich da, damit ein geerbtes `nowrap`
   den Pfad nicht wieder festnagelt. */
.dateipfad {
    font-family: var(--f-mono);
    white-space: normal;
    overflow-wrap: anywhere;
}

/* ⚠️ NICHT `.pfad` nennen: `drb-site.css:605` vergibt diesen Namen bereits an die
   Brotkrumen-Achse (`display: grid` samt `::before`-Linie). Beide Stylesheets
   liegen auf JEDER Portalseite (`layout.tsx:172-173`), ein Span mit `.pfad`
   bekaeme dort eine Rasterdarstellung und eine gezeichnete Linie dazu. */

/* Ein Befehl, den der Kunde ABTIPPT ODER KOPIERT — die dritte Sorte Mono nach
   `.num` und `.dateipfad`.

   Sie ist noetig, weil die beiden vorhandenen Klassen hier beide falsch sind:
   `.num` haelt mit `nowrap` alles in EINER Zeile und schob damit die Seite auf
   (Befund BETA-6, Familie M-1/M-3/K23-18); `.dateipfad` erlaubt mit `anywhere`
   eine Bruchstelle an JEDEM Zeichen und traefe damit auch die Mitte von
   `Get-FileHash` — an welchem Zeichen genau, entscheidet die Spaltenbreite.
   Bei einem Pfad ist das nur haesslich, bei einem Befehl ist es eine Falle: wer ihn
   abtippt, tippt den Umbruch mit ab und bekommt einen Befehl, den keine Schale
   ausfuehrt.

   Der Umbruch faellt deshalb NUR auf die Leerzeichen, die ohnehin im Befehl
   stehen. Jedes Stueck bleibt ganz; markiert und kopiert wird trotzdem der
   ganze Befehl, denn ein Zeilenumbruch aus dem Satz steht nicht im Text und
   landet nicht in der Zwischenablage.

   ⚠️ DAFUER REICHT KEINE EIGENSCHAFT AN DIESEM ABSATZ — DAS STUECK BRAUCHT DAS
   `nowrap`, NICHT DER ABSATZ. Ein Bindestrich ist eine Umbruchstelle, und der
   Browser nimmt sie auch dann, wenn das ganze Stueck noch in die Zeile passt.
   Nachgemessen im Nachbau der Seite mit beiden echten Stylesheets, Chromium,
   27.08.2026, 390 px: `Get-FileHash <Datei> -Algorithm SHA256` endete die erste
   Zeile auf `-` und begann die zweite mit `Algorithm`. `word-break: keep-all`,
   `line-break: strict` und `white-space: pre-wrap` aendern daran nichts —
   dreimal derselbe Umbruch. Deshalb traegt jedes durch Leerzeichen getrennte
   Stueck ein eigenes `span`, und das `span` haelt sich zusammen. Mit dieser
   Regel bricht dieselbe Zeile nach `<Datei>`.

   ⚠️ WER HIER MARKUP SCHREIBT, MUSS DIE STUECKE MITLIEFERN. `.befehl` allein,
   mit dem Befehl als blossem Text, laesst den Bindestrich-Umbruch wieder zu.
   `dokumente/<id>/page.tsx` hat dafuer das Bauteil `Befehl`, das die Stuecke
   aus einer Zeichenkette selbst schneidet — der Befehl steht dort als EIN
   lesbares Stueck Text und nicht als Kette von Spans.

   ⚠️ ZWEI ANDERE WEGE WURDEN VERWORFEN. Ein eigener Rollbereich verlangt auf
   dem Telefon, beim Markieren zu rollen — dasselbe Argument steht unten bei
   `.pruefsumme`. Kleinere Schrift ueber die volle Breite haelt nur bis zur
   naechsten Schriftvergroesserung des Lesers und macht ausgerechnet das
   kleiner, was Zeichen fuer Zeichen abgelesen wird.

   ⚠️ DAS TRAEGT NUR, WENN KEIN EINZELNES STUECK BREITER IST ALS DIE SPALTE.
   Das laengste hier ist `Get-FileHash`, rund 143 px. Bei 320 px bleiben der
   Spalte 254 px, es passt also. Fiele einmal ein laengeres Stueck an — ein
   Pfad ohne Leerzeichen etwa —, dann rollt die Seite wieder, und dann ist
   `.dateipfad` die richtige Klasse und nicht diese.

   `white-space: normal` und `overflow-wrap: normal` stehen ausdruecklich da:
   beide Eigenschaften werden vererbt, ein `nowrap` oder ein `anywhere` weiter
   oben duerfen den Befehl weder festnageln noch aufbrechen. */
.befehl {
    font-family: var(--f-mono);
    white-space: normal;
    overflow-wrap: normal;
    word-break: normal;
}
.befehl > span { white-space: nowrap; }

/* Die SHA-256-Pruefsumme: 64 Hexzeichen, die abgelesen, verglichen und kopiert
   werden. Das GEGENTEIL von `.num` — und sie stand bis zur Haertung genau dort
   (Befund V-6/V-7).

   Gemessen an der laufenden Anwendung, Vertrag KV-2026-5003, 06.08.2026:
   in `.num` war der Wert 627 px breit und lag bei 390 px Bildschirmbreite mit
   seiner rechten Kante bei 660 px — der Kunde sah rund 40 % und konnte den Rest
   weder lesen noch markieren. Auf `/a/vertraege/<id>/druck` zog derselbe Wert
   die ganze Seite auf: 1535 px bei 1440 px, 968 px bei 390 px.

   Umbruch und nicht Vierergruppen: Gruppen wuerden Zeichen in die Zwischenablage
   legen, die nicht zur Pruefsumme gehoeren — wer den Wert gegen `sha256sum`
   haelt, verglich dann zwei verschiedene Zeichenketten. Umbruch und nicht ein
   eigener Rollbereich: der verlangt auf dem Telefon, beim Markieren zu rollen.
   So bleibt es EIN Zug, und die Zwischenablage enthaelt genau 64 Zeichen.

   `overflow-wrap: anywhere` ist noetig, weil eine Pruefsumme EIN Wort ohne
   Trennstelle ist; `white-space: normal` steht ausdruecklich da, damit ein
   geerbtes `nowrap` sie nicht wieder festnagelt. */
.pruefsumme {
    font-family: var(--f-mono);
    font-variant-numeric: tabular-nums slashed-zero;
    white-space: normal;
    overflow-wrap: anywhere;
    max-width: 100%;
}

/* Sprungmarke. Gleiche Mechanik wie `.skip` auf der Website; eigener Name,
   weil das Wurzel-Layout ihn so setzt. */
.skip-link {
    position: absolute;
    left: var(--s-2);
    top: -100px;
    z-index: var(--z-skip);
    background: var(--akz);
    color: var(--paper);            /* hell 6.73 · dunkel 11.34 */
    padding: 10px var(--s-2);
    border-radius: var(--r-sm);
    font: 600 var(--t-meta)/1 var(--f-mono);
    text-decoration: none;
}
.skip-link:focus { top: var(--s-1); }


/* ==================================================== 3 · APP-GRUNDRISS */
.app {
    min-height: 100svh;
    display: grid;
    grid-template-rows: auto 1fr;
    background: var(--paper);
    /* Die Anwendungsschrift liegt eine Stufe ueber der Website (17–19 px):
       hier wird laenger und aus groesserer Entfernung gelesen. */
    font-size: calc(clamp(1.0625rem, 1rem + 0.25vw, 1.1875rem) * var(--scale));
    /* Die Radienleiter der Anwendung. Bewusst als Neubelegung von `--radius`
       auf DIESEM Element und nicht als Ueberschreiben von `.btn`: alles, was
       aus drb-site.css `var(--radius)` liest (Knopf, Tabellenhuelle), folgt
       damit von selbst der Anwendungsleiter, ohne dass eine einzige Regel der
       Website angefasst wird. Ausserhalb von `.app` gilt weiterhin 8 px. */
    --radius: var(--r-sm);
}

/* -------------------------------------------------------------- Kopfzeile
   Vier Felder: Wortmarke und Firmenkontext links · Suchfeld in der Mitte ·
   Glocke, Modus-Umschalter und Konto rechts.
   Kein `backdrop-filter` mehr: Blur ist im Formensprung ausdruecklich
   ausgeschlossen, und die durchscheinende Leiste war beim Rollen ueber einer
   Karte unruhig. Volle Papierfarbe, eine Linie darunter — fertig. */
.a-bar {
    position: sticky;
    top: 0;
    z-index: var(--z-bar);
    min-height: var(--bar-h);
    background: var(--paper);
    border-bottom: 1px solid var(--line);
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) auto;
    align-items: center;
    gap: var(--s-3);
    padding-inline: var(--pad);
}

/* Wortmarke und Betriebsname bilden EINE Spalte. `min-width: 0` an beiden
   Stellen ist Pflicht, sonst kann der Betriebsname nicht kuerzen und reisst
   das Raster auf. */
.a-bar__lead {
    display: flex;
    align-items: center;
    gap: 10px;
    min-width: 0;
}
/* `flex: 0 1 auto` statt `0 0 auto`, und `min-width: 0` dazu: Die Wortmarke
   stand vorher als unschrumpfbare Spalte im Raster, wurde aber von
   `min-width: 0` an `.a-bar__lead` trotzdem schmaler gerechnet — der Text lief
   dann einfach aus seinem Kasten heraus UNTER die Knoepfe rechts. Bei 390 px
   endete „DRB ADMINPORTAL" bei 216 px, die Glocke begann bei 208 px: die
   letzten Zeichen lagen hinter dem Knopf, auf dem Bild stand „DRB ADMINPORTA".
   Jetzt kuerzt die Marke sichtbar mit Auslassungspunkten, statt lautlos zu
   verschwinden. */
.a-bar__brand {
    display: inline-flex;
    align-items: center;
    gap: 0.7ch;
    min-height: 44px;
    min-width: 0;
    flex: 0 1 auto;
    text-decoration: none;
    color: var(--ink);
}
.a-bar__brand b {
    font: 600 0.9375rem/1 var(--f-sans);
    letter-spacing: -0.015em;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
/* Damit es zum Kuerzen gar nicht erst kommt: unter 480 px ist der Abstand
   zwischen Marke und Bedienknoepfen kleiner als der Zeilenabstand des
   Grundrasters. Die gewonnenen ~20 px genuegen — gemessen bei 390 px. */
@media (max-width: 479.98px) {
    .a-bar { gap: var(--s-1); }
}
/* Der Firmenname darf kuerzen. Als unteilbare Spalte riss er bei 375 px die
   Kopfzeile auf 559 px Mindestbreite auf und schob die ganze Seite seitlich
   weg — welcher Betrieb gemeint ist, sagt dort ohnehin die Navigation. */
.a-bar__context {
    min-width: 0;
    padding-left: 10px;
    border-left: 1px solid var(--line);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font: 400 var(--t-meta)/1.4 var(--f-mono);
    color: var(--ink-3);                    /* 5.33 hell · 7.11 dunkel auf --paper */
}
/* Auf dem Handy sagt der Kopf der Navigation, welcher Betrieb gemeint ist —
   in der Kopfzeile waere es dort nur noch ein Ellipsen-Stummel. */
@media (max-width: 719.98px) {
    .a-bar__context { display: none; }
}
.a-bar__mid { min-width: 0; justify-self: stretch; }
.a-bar__side {
    display: flex;
    align-items: center;
    gap: var(--s-1);
    justify-self: end;
}

/* ------------------------------------------------------------- Suchfeld
   Ein sichtbares Feld, kein Knopf: der Platz, an dem die Suche spaeter steht,
   ist damit schon jetzt zu sehen. Sie ist DEAKTIVIERT und sagt das auch —
   der Hinweis steht als Text neben dem Feld und nicht nur in einem Tooltip,
   denn ein Tooltip erreicht Tastatur und Touch nicht. */
.a-search {
    display: flex;
    align-items: center;
    gap: 0.5ch;
    width: min(100%, 30rem);
    min-height: 42px;
    padding-inline: 12px;
    background: var(--card-2);
    border: 1px solid var(--line);
    border-radius: var(--r-sm);
    color: var(--ink-3);
}
.a-search__ico { display: inline-flex; flex: 0 0 auto; }
.a-search__ico svg { width: 18px; height: 18px; }
.a-search input {
    flex: 1 1 auto;
    min-width: 0;
    border: 0;
    background: transparent;
    color: var(--ink);
    font: 400 var(--t-small)/1.5 var(--f-sans);
}
.a-search input::placeholder { color: var(--ink-3); }   /* 5.52 auf der Karte */
.a-search__hint {
    flex: 0 0 auto;
    font: 500 var(--t-meta)/1 var(--f-mono);
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--ink-3);
}
/* Unter 900 px hat die Suche keinen Platz mehr. Sie verschwindet ganz statt
   auf ein Symbol zu schrumpfen — ein Symbol ohne Wirkung erklaert nichts. */
@media (max-width: 899.98px) {
    .a-bar__mid { display: none; }
    .a-bar { grid-template-columns: auto minmax(0, 1fr) auto; }
}

/* ------------------------------------------------------- Neuerungen (E-26)
   Der Verweis auf den Aenderungsverlauf, links neben der Glocke. Ein VERWEIS,
   kein Knopf: fokussierbar, mit der Eingabetaste zu oeffnen und in einem
   neuen Reiter zu oeffnen, alles ohne eigenes Skript.

   Daneben steht ein DATUM und keine Marke. Eine Marke, die immer leuchtet, ist
   nach zwei Wochen Tapete (E-18); damit sie ausgehen koennte, braeuchte es
   einen gespeicherten Lesestand je Zugang — und der waere geloescht, bevor
   jemand gelesen hat, weil verlinkte Seiten im Voraus geladen werden. Das
   Datum ist eine Tatsache und kein Zustand.

   Die Hoehe ist DIESELBE wie an der Glocke daneben (40 px), damit die drei
   Bedienelemente rechts eine Linie bilden und nicht drei. */
.a-neu {
    display: inline-flex;
    align-items: center;
    gap: 0.5ch;
    min-height: 40px;
    flex: 0 0 auto;
    padding-inline: 10px;
    border: 1px solid var(--line-ui);   /* 3.62 hell · 3.63 dunkel */
    border-radius: var(--r-sm);
    color: var(--ink-2);
    text-decoration: none;
    transition: color var(--out) var(--ease), border-color var(--out) var(--ease);
}
.a-neu:hover { color: var(--akz); border-color: var(--akz); transition-duration: var(--in); }
.a-neu svg { width: 18px; height: 18px; flex: 0 0 auto; }
.a-neu__wort {
    font: 600 var(--t-meta)/1 var(--f-mono);
    letter-spacing: 0.06em;
    text-transform: uppercase;
    white-space: nowrap;
}
/* Der Tag bricht nicht um: „21.08." in einer Zeile und „2026" in der naechsten
   waere keine Datumsangabe mehr, sondern zwei Zahlen. Er ist kurz und steht in
   einem Kasten, der sich nach ihm richtet — der Fall, gegen den `.a-sum--urbild`
   gebaut wurde (K23-18), liegt hier nicht vor. */
.a-neu__stand {
    font: 500 var(--t-meta)/1 var(--f-mono);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
    color: var(--ink-3);                /* 5.33 hell · 7.11 dunkel auf --paper */
}

/* ═════════════════════════════════════════════════════════════════════════
   ⚠️ DIE KOPFZEILE WAR BEI 390 px RANDVOLL — GEMESSEN, NICHT VERMUTET
   ═════════════════════════════════════════════════════════════════════════
   Ohne den Verweis: Wortmarke 140.4 px + 16 px Zwischenraum + 138 px
   Bedienelemente = 294.4 px bei 294 px Platz. Der Verweis (153.4 px) hat die
   Bedienspalte deshalb nach LINKS ueber die Wortmarke geschoben — sie begann
   bei 62.6 px, waehrend die Marke erst bei 216.4 px endete. Kein Rollbalken,
   keine Auslassungspunkte, nichts: nur Text unter Knoepfen. Genau der Fall,
   der schon einmal „DRB ADMINPORTA" auf den Bildschirm gebracht hat.

   ⚠️ UND DIE MARKE KUERZT NICHT VON SELBST. Der Kommentar bei `.a-bar__brand b`
   sagt, sie kuerze „sichtbar mit Auslassungspunkten, statt lautlos zu
   verschwinden". Gemessen tut sie das nicht: `white-space: nowrap` macht die
   ganze Zeichenkette zur Mindestbreite, und `.a-bar__side` traegt kein
   `min-width: 0` — die Bedienspalte laeuft deshalb aus ihrer Rasterzelle nach
   links heraus, statt die Marke zu kuerzen. Ein `min-width: 0` an der Marke
   aendert daran nichts; das ist gemessen und nicht gelesen. HIER WIRD DAS
   NICHT REPARIERT — es ist ein eigener Befund an einem fremden Bauteil, und
   er besteht ohne den Verweis genauso (bei 320 px ueberlappt die Kopfzeile
   schon heute um 13 px). Der Verweis muss ihn nur nicht VERGROESSERN, und
   `tests/fachlich/neuerungen-am-bildschirm` misst genau das gegen einen
   Vergleichslauf ohne ihn.

   Der Verweis gibt deshalb nach Mass ab, in DIESER Reihenfolge:
     bis 1023 px   Wort und Jahreszahl
     bis  479 px   der Tag
   Der TAG bleibt am laengsten stehen — er ist das Einzige, woran der Kunde
   merkt, dass etwas Neues da ist (E-26 § 3), und „21.08." beantwortet die
   Frage genauso wie „21.08.2026". Vorgelesen wird immer der ganze Satz mit
   vollem Datum: er steht als `.sr-only` im Verweis und wird von keiner dieser
   Regeln beruehrt.

   ⚠️ UND DIE GLOCKE TRITT UNTER 1024 px ZURUECK. Das ist die unbequeme
   Haelfte der Rechnung: der Platz reichte auch nach dem Abgeben nicht. Die
   Glocke ist `disabled`, liegt nicht im Tabulator-Weg und sagt „kommt bald" —
   sie haelt einen Platz fuer etwas frei, das es noch nicht gibt. Auf einem
   schmalen Bildschirm ist Platz eine Nullsumme, und dort geht ein Weg, der
   FUNKTIONIERT, einem Platzhalter vor. Ueber 1024 px steht sie unveraendert,
   also genau dort, wo der Kopf spaeter nicht springen soll. Dasselbe hat das
   Suchfeld schon immer getan, nur frueher (unter 900 px). */
@media (max-width: 1023.98px) {
    .a-neu__wort, .a-neu__jahr { display: none; }
}
@media (max-width: 719.98px) {
    .a-neu { padding-inline: 8px; }
}
/* ⚠️ DER TAG WEICHT ALS LETZTES — UND IM ADMINPORTAL FRUEHER, weil dort die
   laengere Wortmarke steht („DRB ADMINPORTAL" ist 140.4 px breit, „DRB PORTAL"
   95 px). Gemessen bei 390 px: mit Tag beginnt der Verweis bei 176.1 px. Im
   Kundenportal endet die Marke bei 167 px — es passt. Im Adminportal endet sie
   erst bei 216.4 px — es passt nicht, und die Bedienspalte laege wieder ueber
   der Marke.

   Zwei Schwellen statt einer, und die Unterscheidung ist KEINE Vermutung: die
   Huelle des Adminportals setzt ohnehin `data-density="kompakt"` an `.app`.
   Der Grund, dass 390 px die wichtigere der beiden Schwellen ist: das ist die
   Breite, auf der der KUNDE steht, und der Tag ist das Einzige, woran er
   merkt, dass etwas Neues da ist. Deniz auf dem Telefon sieht dort das Symbol
   und hoert beim Vorlesen weiterhin das volle Datum. */
@media (max-width: 479.98px) {
    .app[data-density="kompakt"] .a-neu__stand { display: none; }
}
@media (max-width: 389.98px) {
    .a-neu__stand { display: none; }
}

/* ---------------------------------------------------------------- Glocke
   Quadratischer Knopf mit Platz fuer einen Zaehler. Der Zaehler steht als
   Zahl im Feld, nicht als farbiger Punkt — ein Punkt allein waere Information
   durch Farbe (WCAG 1.4.1). */
.a-bell {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    flex: 0 0 auto;
    background: none;
    border: 1px solid var(--line-ui);   /* 3.62 hell · 3.63 dunkel */
    border-radius: var(--r-sm);
    color: var(--ink-2);
    cursor: pointer;
    transition: color var(--out) var(--ease), border-color var(--out) var(--ease);
}
.a-bell:hover { color: var(--akz); border-color: var(--akz); transition-duration: var(--in); }
.a-bell svg { width: 20px; height: 20px; }
/* ⚠️ UNTER 1024 px TRITT DIE GLOCKE ZURUECK — Begruendung bei `.a-neu` oben.
   Kurz: sie ist `disabled`, liegt nicht im Tabulator-Weg und sagt „kommt
   bald"; auf einem schmalen Bildschirm ist Platz eine Nullsumme, und dort
   geht ein Weg, der FUNKTIONIERT, einem Platzhalter vor.

   ⚠️ DIESE REGEL MUSS HINTER DER GRUNDREGEL STEHEN. Sie stand zuerst weiter
   oben bei `.a-neu` — bei gleicher Spezifitaet gewinnt die SPAETERE Regel,
   und `display: inline-flex` aus `.a-bell` hat sie stumm ausgehebelt. Gemessen
   hat es die Messung, nicht der Blick: bei 390 px begann der Verweis dann bei
   180 px statt bei 226 px. */
@media (max-width: 1023.98px) {
    .a-bell { display: none; }
}
.a-bell__count {
    position: absolute;
    top: -6px;
    inset-inline-end: -6px;
    min-width: 20px;
    height: 20px;
    padding-inline: 5px;
    display: grid;
    place-items: center;
    border-radius: 999px;
    background: var(--akz);
    color: var(--paper);                /* hell 6.73 · dunkel 11.34 */
    font: 600 0.625rem/1 var(--f-mono);
    font-variant-numeric: tabular-nums;
}

/* -------------------------------------------------- Die Liste der Neuerungen
   Eine Zeitleiste ohne Schmuck: Tag, Ueberschrift, Absaetze. Neueste zuerst —
   die Reihenfolge stellt die Ableitung her, nicht das Stylesheet. */
.a-neuliste { list-style: none; margin: 0; padding: 0; }
.a-neuliste__eintrag + .a-neuliste__eintrag {
    margin-top: var(--s-4);
    padding-top: var(--s-4);
    border-top: 1px solid var(--line);
}
.a-neuliste__kopf {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.5ch var(--s-2);
    margin: 0 0 6px;
}
.a-neuliste__eintrag h2 { margin: 0 0 var(--s-2); }
/* Die Absaetze bekommen Lesebreite. Ein Fliesstext ueber die volle Breite
   eines 1440er Bildschirms ist nicht lesbar, und das hier ist Fliesstext. */
.a-neuliste__eintrag p:not(.a-neuliste__kopf) {
    max-width: var(--read-w);
    margin: 0 0 var(--s-2);
}
.a-neuliste__eintrag p:last-child { margin-bottom: 0; }
/* Die Kennzeichnung steht als WORT und nicht als Farbe allein (WCAG 1.4.1).
   Sie erscheint nur in der Innenansicht. */
.a-neuliste__intern {
    padding: 2px 8px;
    border: 1px solid var(--line-ui);
    border-radius: 999px;
    font: 600 var(--t-meta)/1.4 var(--f-mono);
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--ink-2);
}

/* ----------------------------------------------------------------- Raster
   Navigation LINKS, Inhalt rechts daneben (Bauplan § 4).
   Die DOM-Reihenfolge bleibt header -> main -> nav; platziert wird
   ausschliesslich ueber `grid-template-areas`. Wer mit der Tastatur
   arbeitet, landet damit sofort im Inhalt und muss sich nicht erst durch
   acht Navigationspunkte tabben — WCAG 2.4.3 verlangt eine SINNVOLLE
   Reihenfolge, keine, die dem Auge nachlaeuft. */
.a-shell {
    display: grid;
    grid-template-columns: var(--nav-w) minmax(0, 1fr);
    grid-template-areas: "nav main";
    align-items: start;
    width: 100%;
    /* `--app-wrap` ist `none` — die Navigation sitzt damit buendig am linken
       Bildschirmrand und der Inhalt nimmt den ganzen Rest ein. Die Zeile bleibt
       stehen, damit eine Deckelung spaeter an EINER Stelle wieder eingeschaltet
       werden kann, ohne die Regel zu suchen. */
    max-width: var(--app-wrap);
}
.a-shell > * { min-width: 0; }

.a-main { grid-area: main; padding: var(--s-4) var(--pad) var(--s-7); }
.a-nav  { grid-area: nav; }

/* Die Spalte fuer den Abschnitts-Index dieser Seite. Slice 1 hat noch keine
   Abschnitte; das Element steht leer im Markup, damit das Raster beim
   spaeteren Einbau nicht springt. */
.a-index { display: none; }
.a-index[data-empty] { pointer-events: none; }

/* Lesebreite im Inhalt. */
.a-read { max-width: var(--read-w); }

/* Die offenen Seiten (Anmeldung, Einladung) und die Fehlerseiten haben kein
   `.a-shell` — dort steht `<main class="a-main">` unmittelbar in `.app`.
   `.a-main` traegt aber `grid-area: main`, und diese Flaeche gibt es nur im
   `.a-shell`. Ohne die folgende Zeile spricht der Verweis eine Rasterlinie
   an, die niemand definiert hat: der Inhalt landet dann in einer
   stillschweigend erzeugten Spalte und schiebt die Seite seitlich auf
   (bei 390 px gemessen: 3 px Ueberhang, Inhalt um 193 px versetzt). */
.app > .a-main {
    grid-area: auto;
    width: 100%;
    max-width: var(--app-wrap);
    /* Bleibt: die offenen Seiten (Anmeldung, Einladung, Fehler) sind KEINE
       Anwendungsflaeche, sondern eine einzelne zentrierte Karte — die soll
       mittig stehen, auch wenn das Raster daneben die volle Breite nutzt. */
    margin-inline: auto;
}


/* ================================================ 4 · NAVIGATION (LINKS) */
.a-nav {
    position: sticky;
    top: var(--bar-h);
    height: calc(100svh - var(--bar-h));
    overflow-y: auto;
    overscroll-behavior: contain;
    border-right: 1px solid var(--line);
    padding: var(--s-3) var(--s-2) var(--s-4);
    display: flex;
    flex-direction: column;
    gap: var(--s-3);
}

.a-nav__head { padding-inline: 12px; }
.a-nav__firm {
    display: block;
    font: 600 var(--t-small)/1.4 var(--f-sans);
    letter-spacing: -0.01em;
    color: var(--ink);
}
.a-nav__since {
    display: block;
    margin-top: 2px;
    font: 400 var(--t-meta)/1.6 var(--f-mono);
    color: var(--ink-3);
}

.a-nav__list { display: grid; gap: 3px; list-style: none; }

/* Eine Zeile: Symbolfeld · Klartext · optionaler Zaehler.
   Die laufenden Nummern (01, 02, …) sind mit dem Formensprung entfallen —
   sie stammten aus dem Papier-Denken und nummerierten Bereiche, die in einer
   Anwendung keine Reihenfolge haben. An ihrer Stelle steht das Symbol.

   Die aktive Zeile ist eine GEFUELLTE Flaeche mit Radius, nicht mehr nur eine
   Kante. Farbe traegt dabei nicht allein: die Zeile ist zusaetzlich fetter
   gesetzt, das Symbolfeld wechselt seine Toenung, und `aria-current="page"`
   sagt es der Hilfstechnik ausdruecklich. */
.a-nav__item {
    display: flex;
    flex-wrap: nowrap;
    align-items: center;
    gap: 10px;
    min-height: var(--row-h);
    padding: 6px 12px;
    border-radius: var(--r-sm);
    text-decoration: none;
    color: var(--ink-2);
    font-size: var(--t-small);
    scroll-margin-block: calc(var(--bar-h) + var(--s-2));
    transition: background var(--out) var(--ease), color var(--out) var(--ease);
}
.a-nav__label {
    flex: 1 1 auto;
    min-width: 0;
    color: inherit;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
/* Zaehler rechts (Anzahl offener Support-Anfragen o. Ae.). */
.a-nav__count {
    flex: 0 0 auto;
    min-width: 22px;
    padding: 2px 7px;
    border-radius: 999px;
    background: var(--card-2);
    font: 600 var(--t-meta)/1.5 var(--f-mono);
    font-variant-numeric: tabular-nums;
    text-align: center;
    color: var(--ink-2);
}

.a-nav__item:hover {
    background: var(--card-2);
    color: var(--ink);
    transition-duration: var(--in);
}
.a-nav__item[aria-current="page"] {
    background: var(--tint-akz);   /* hell #E3EBF6 · dunkel #22302D */
    color: var(--ink);             /* 14.23 hell · 11.89 dunkel */
    font-weight: 600;
}
.a-nav__item[aria-current="page"] .a-ico {
    background: color-mix(in oklab, var(--akz) 22%, var(--card));
    color: var(--akz);             /* 5.81 hell · 8.22 dunkel — Symbole brauchen 3:1 */
}
.a-nav__item[aria-current="page"] .a-nav__count {
    background: var(--card);
    color: var(--akz);
}

/* Gesperrte Zeile („kommt bald"). Sie soll LEISER sein als eine normale
   Zeile, nicht lauter — deshalb keine Flaeche, kein Hover und seit dem
   Formensprung auch kein umrandeter Kasten mehr. Es bleibt eine dezente
   Markierung: das Wort in Mono-Versalien, dazu ein leiseres Symbolfeld.

   WARUM `.app` IM SELEKTOR STEHT: der allgemeine Zustand
   `.app [aria-disabled="true"]` weiter unten legt eine Flaeche unter jedes
   gesperrte Element. Er hat dieselbe Spezifitaet wie
   `.a-nav__item[aria-disabled="true"]` und steht spaeter in der Datei — ohne
   die dritte Klasse gewinnt er, und die gesperrten Zeilen sehen aus wie sechs
   gleichzeitig geoeffnete Seiten. Genau so ist es beim Bauen passiert. */
.app .a-nav__item[aria-disabled="true"],
.app .a-nav__item[aria-disabled="true"]:hover {
    background: transparent;
    border-color: transparent;
    color: var(--ink-3);           /* 5.33 hell · 7.11 dunkel auf --paper */
    cursor: default;
}
.app .a-nav__item[aria-disabled="true"] .a-ico {
    background: transparent;
    color: var(--ink-3);
    box-shadow: inset 0 0 0 1px var(--line);
}
.a-nav__soon {
    flex: 0 0 auto;
    font: 500 var(--t-meta)/1.5 var(--f-mono);
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--ink-3);
}

/* Fuss der Navigation: ein Mensch mit Telefonnummer, keine Linkliste. */
.a-nav__foot {
    margin-top: auto;
    padding: var(--s-2) 12px 0;
    border-top: 1px solid var(--line);
    font-size: var(--t-small);
    line-height: 1.6;
    color: var(--ink-2);
}
.a-nav__foot a {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
    font: 600 var(--t-small)/1 var(--f-mono);
    color: var(--akz);
    text-decoration: none;
}
.a-nav__foot a:hover { text-decoration: underline; text-underline-offset: 4px; }
/* Der Notfallhinweis am Fuss der Navigation — im KUNDENportal: „Website nicht
   erreichbar? Rufen Sie an." Dort ist die Signalfarbe richtig, der Satz meint
   eine Stoerung. */
.a-nav__alarm {
    display: block;
    margin-top: var(--s-1);
    padding: 10px 12px;
    border-radius: var(--r-sm);
    background: var(--wash-bad);       /* hell #F9EFEC · dunkel #2D2724 */
    color: var(--ink-2);               /* 6.87 hell · 7.88 dunkel */
    font-size: var(--t-meta);
    line-height: 1.6;
}
/* Dieselbe Zeile als BESTAETIGUNG statt als Alarm — im Adminportal steht dort
   „Zweiter Faktor aktiv…", also eine gute Nachricht. In der Abnahme trug sie
   die rote Waschung des Kundenportals: die Farbe sagte das Gegenteil des
   Satzes. Farbe traegt hier ohnehin nichts allein, der Satz sagt es aus. */
.a-nav__alarm--ok {
    background: var(--wash-ok);        /* hell #EDF4EF · dunkel #1B2620 */
    color: var(--ink-2);               /* 7.11 hell · 8.43 dunkel — gemessen */
}

/* Ueber 1024 px gibt es keine Schublade — also auch keinen Knopf und keinen
   Nebel. Beide stehen trotzdem im Markup, damit beim Wechsel der Fenster-
   breite nichts nachgeladen werden muss. */
.a-nav-toggle { display: none; }
.a-scrim { display: none; }

/* ------------------------------------------- Schublade unter 1024 px
   Die Navigation faehrt von LINKS ein — von der Seite, auf der sie auch am
   Schreibtisch steht. Der Bedienknopf sitzt in der Kopfzeile, der
   Hintergrund wird abgedunkelt, Escape schliesst, und der Fokus bleibt in
   der Schublade: Kopfzeile und Inhalt bekommen `inert` (das setzt die
   Komponente), damit der Tabulator nicht dahinter wandert. */
@media (max-width: 1023.98px) {
    .a-shell {
        grid-template-columns: minmax(0, 1fr);
        grid-template-areas: "main";
    }
    .a-main { padding-inline: var(--s-2); }

    /* Platz fuer den Bedienknopf links in der Kopfzeile. */
    .a-bar { padding-left: calc(var(--pad) + 56px); }

    .a-nav-toggle svg { width: 20px; height: 20px; }
    .a-nav-toggle {
        display: inline-flex;
        position: fixed;
        left: max(var(--s-2), calc(var(--pad) - var(--s-2)));
        top: calc((var(--bar-h) - 44px) / 2);
        z-index: calc(var(--z-bar) + 1);
        width: 44px;
        min-height: 44px;
        padding-inline: 0;
        justify-content: center;
    }

    .a-nav {
        position: fixed;
        inset-block: 0;
        left: 0;
        z-index: var(--z-drawer);
        width: min(86vw, 320px);
        height: 100svh;
        background: var(--paper);
        border-right: 1px solid var(--line-ui);
        box-shadow: var(--shadow-2);
        padding: var(--s-4) var(--s-2);
        transform: translateX(-100%);
        visibility: hidden;
        transition:
            transform var(--out) var(--ease),
            visibility 0s linear var(--out);
    }
    .a-nav[data-open] {
        transform: none;
        visibility: visible;
        transition: transform var(--in) var(--ease), visibility 0s;
    }

    /* Der Nebel ist ein <button>, damit er auch mit der Tastatur zu
       schliessen ist und nicht nur mit der Maus. */
    .a-scrim {
        display: block;
        position: fixed;
        inset: 0;
        z-index: calc(var(--z-drawer) - 1);
        background: var(--scrim);
        border: 0;
        cursor: pointer;
    }
    .a-scrim[hidden] { display: none; }
}


/* ========================================================= 5 · ANMELDUNG
   Zentriert auf ruhigem Grund: Bildmarke, darunter EINE Karte, maximal
   ~432 px breit, grosszuegige Felder, genau ein klarer Knopf, darunter leise
   die Nebenwege. Kein Bild, kein Verlauf, keine zweite Spalte.

   Der Selektor greift genau bei der offenen Huelle: sie hat eine `.a-bar`
   als direktes Kind und `.a-main` unmittelbar darunter. Die angemeldete
   Huelle hat ihr `.a-main` in `.a-shell` (greift also nicht), die
   Fehlerseiten haben gar keine Kopfzeile (greifen also auch nicht). */
.app:has(> .a-bar) > .a-main {
    display: grid;
    justify-items: center;
    align-content: start;
    padding-block: clamp(var(--s-4), 7vh, var(--s-6));
}
/* Die Bildmarke ueber der Karte. Rein dekorativ und deshalb aus dem
   Stylesheet: die Wortmarke steht als echter Text in der Kopfzeile, ein
   zweites Mal vorgelesen wuerde sie nur stoeren. */
.app:has(> .a-bar) > .a-main::before {
    content: "";
    width: 34px;
    height: 37px;
    margin-bottom: var(--s-3);
    background: url(logo-mark.svg) center / contain no-repeat;
}
.app:has(> .a-bar) > .a-main > .a-read {
    width: 100%;
    max-width: var(--auth-w);
}
/* Die Karte selbst wird eine Stufe angehoben — sie ist das einzige Element
   auf der Seite und darf das zeigen. */
.app:has(> .a-bar) > .a-main .a-panel {
    box-shadow: var(--shadow-2);
    border-radius: var(--r-lg);
    padding: var(--s-4);
}
/* Grosszuegige Felder, und der eine Knopf ueber die volle Breite. */
.app:has(> .a-bar) > .a-main .a-field input { min-height: 52px; }
.app:has(> .a-bar) > .a-main .a-actions > .btn { flex: 1 1 100%; justify-content: center; }
@media (max-width: 560px) {
    .app:has(> .a-bar) > .a-main .a-panel { padding: var(--s-3); }
}


/* ========================================================= 6 · ZUSTAENDE */

/* ----------------------------------------------------------------- Fokus
   `drb-site.css` setzt den Ring global auf `--akz`. Hell ist das Blau
   (6.98 auf der Karte), dunkel Mint (10.58 auf der Karte) — in beiden Modi
   weit ueber den 3:1, die ein Bedienelement braucht (WCAG 1.4.11).

   Was hier dazukommt: ein Trennring in Kartenfarbe zwischen Element und
   Akzentring. Ohne ihn steht der Ring auf einer getoenten Flaeche
   unmittelbar auf einer aehnlich hellen Flaeche und verliert seine Kante.
   `.btn` ist ausgenommen — dort liegt der Ring in drb-site.css bereits
   innen, weil die Flaeche selbst Akzentfarbe traegt. */
.app :focus-visible:not(.btn) {
    outline: 2px solid var(--akz);
    outline-offset: 2px;
    box-shadow: 0 0 0 4px var(--card);
}
/* Die klebende Kopfzeile darf den Fokus nicht verdecken (WCAG 2.2, SC 2.4.11). */
.app :is(a, button, input, select, textarea, summary, [tabindex]) {
    scroll-margin-block: calc(var(--bar-h) + var(--s-2));
}

/* ----------------------------------------------------------- Deaktiviert
   Ruhige Flaeche und leise Kante, bewusst OHNE `opacity`: der Text bleibt
   lesbar, und nichts scheint durch. */
.app :is(button, input, select, textarea):disabled,
.app [aria-disabled="true"] {
    color: var(--ink-3);           /* 5.52 hell · 6.64 dunkel auf der Karte */
    border-color: var(--line);
    background: var(--card-2);
    cursor: not-allowed;
}
.app .btn:disabled,
.app .btn[aria-disabled="true"] {
    background: var(--card-2);
    color: var(--ink-3);
    border: 1px solid var(--line);
    transform: none;
}
/* Das Suchfeld ist als GANZES deaktiviert; die obige Regel wuerde nur das
   `<input>` treffen und die Huelle unberuehrt lassen. */
.a-search:has(input:disabled) { background: var(--card-2); cursor: not-allowed; }
.a-search input:disabled { background: transparent; border: 0; cursor: not-allowed; }

/* -------------------------------------------------------------- Ausgewaehlt
   Flaeche UND Fettung — nie Farbe allein. */
.app [aria-selected="true"],
.app [data-selected] {
    background: var(--tint-akz);
    font-weight: 600;
}

/* -------------------------------------------------------------- Fehlerfeld
   Der Fehler steht nicht nur in Rot da: er hat ein Symbol, das Feld bekommt
   eine doppelt starke Kante, und `aria-invalid` sagt es der Hilfstechnik. */
.a-field--error input,
.a-field--error select,
.a-field--error textarea {
    border-color: var(--sig-bad);   /* 6.42 hell · 8.48 dunkel */
    border-width: 2px;
    background: var(--wash-bad);
}
.a-field__error {
    display: flex;
    align-items: flex-start;
    gap: 0.6ch;
    font-size: var(--t-small);
    color: var(--sig-bad);
}
/* Das Symbol steht im Markup (aria-hidden), nicht als ::before — sonst
   liest es der Screenreader je nach Engine als Zeichen mit vor. */
.a-field__mark {
    flex: 0 0 auto;
    display: inline-flex;
    margin-top: 1px;
}
.a-field__mark svg { width: 17px; height: 17px; }


/* ======================================================== 7 · PRIMITIVES */

/* ------------------------------------------------------------------ Knopf
   Vier Stufen, klar getrennt:
     primaer    `.btn`          volle Akzentflaeche (aus drb-site.css)
     sekundaer  `.btn--quiet`   nur Kante           (aus drb-site.css)
     ruhig      `.btn--ghost`   ohne Kante, Flaeche erst beim Hover
     Gefahr     `.a-btn--danger`
   Den Radius liefert `--radius`, das `.app` auf 10 px setzt. Die Hover- und
   Aktivzustaende sind deutlich und benutzen `--in` / `--out` / `--ease`
   asymmetrisch: hinein schneller als heraus. */
.app .btn { font-weight: 600; }
.app .btn:active { transform: translateY(0); transition-duration: var(--in); }
.app .btn svg { width: 18px; height: 18px; flex: 0 0 auto; }
.app .btn.a-btn--sm svg { width: 16px; height: 16px; }

.btn--ghost {
    background: transparent;
    color: var(--ink);
    border: 1px solid transparent;
}
.btn--ghost:hover {
    background: var(--card-2);
    border-color: var(--line);
    color: var(--ink);
    transform: none;
}
.app .btn--quiet:hover { background: var(--tint-akz); border-color: var(--akz); }

.btn.a-btn--danger {
    background: transparent;
    color: var(--sig-bad);            /* 6.42 hell · 8.48 dunkel */
    border: 1px solid var(--sig-bad);
}
.btn.a-btn--danger:hover { background: var(--wash-bad); color: var(--sig-bad); }

.btn.a-btn--sm { min-height: 40px; padding-inline: var(--s-2); font-size: var(--t-meta); }
.btn.a-btn--pending { cursor: progress; }

.a-actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--tap-gap); }

/* Steht eine Aktionsreihe NEBEN einem Eingabefeld (Suchformulare), muessen
   ihre Knoepfe genauso hoch sein wie das Feld — sonst schwebt ein 40-px-Knopf
   neben einem 50-px-Feld und die Reihe wirkt schief. Die Regel greift nur in
   Formularzeilen, damit freistehende Aktionsreihen ihre eigene Hoehe behalten. */
.a-form .a-actions .btn,
.a-form .a-actions > a.btn {
    min-height: var(--field-h);
}


/* ------------------------------------------------------------- Symbolfeld
   Quadrat mit Radius, getoenter Flaeche und einem Strichsymbol darin. Es
   traegt NIE allein die Bedeutung — daneben steht immer ein Wort. Die
   Symbole sind selbst gezeichnete Inline-SVGs (24×24, `stroke`,
   `currentColor`) aus `src/components/ui/Icon.tsx`; eine Symbol-Bibliothek
   wird bewusst nicht eingebunden.

   Kontraste der Symbolfarbe auf ihrer eigenen Toenung (Grenze 3:1):
     akz  5.81 hell · 8.22 dunkel
     ok   5.16 hell · 7.83 dunkel
     warn 4.89 hell · 8.92 dunkel
     bad  5.38 hell · 6.76 dunkel
     info 5.97 hell · 7.52 dunkel */
.a-ico {
    display: inline-grid;
    place-items: center;
    flex: 0 0 auto;
    width: var(--ico-size, 36px);
    height: var(--ico-size, 36px);
    border-radius: var(--r-ico);
    background: var(--tint-neutral);
    color: var(--ink-2);              /* 7.76 hell · 9.47 dunkel */
}
.a-ico svg { width: var(--ico-glyph, 20px); height: var(--ico-glyph, 20px); }
.a-ico--sm { --ico-size: 30px; --ico-glyph: 17px; border-radius: 9px; }
.a-ico--lg { --ico-size: 44px; --ico-glyph: 24px; }
.a-ico--akz  { background: var(--tint-akz);  color: var(--akz); }
.a-ico--ok   { background: var(--tint-ok);   color: var(--sig-ok); }
.a-ico--warn { background: var(--tint-warn); color: var(--sig-warn); }
.a-ico--bad  { background: var(--tint-bad);  color: var(--sig-bad); }
.a-ico--info { background: var(--tint-info); color: var(--sig-info); }

/* ------------------------------------------------------------- Umschalter
   Der Knopf kommt vollstaendig aus drb-site.css (`.theme-btn` mit `.sonne`
   und `.mond`) — samt der Logik, welches Zeichen in welchem Modus sichtbar
   ist. Hier nur die Einpassung in die App-Kopfzeile. */
.a-bar .theme-btn { width: 40px; height: 40px; border-radius: var(--r-sm); }

/* ------------------------------------------------------------ Ladeanzeige */
.a-spinner { display: inline-flex; align-items: center; gap: 0.75ch; }
.a-spinner__box {
    width: 14px;
    height: 14px;
    border: 2px solid currentColor;
    border-right-color: transparent;
    border-radius: 50%;
    opacity: 0.85;
    animation: a-dreh 700ms linear infinite;
}
@keyframes a-dreh { to { transform: rotate(360deg); } }
.a-spinner__text { font: 400 var(--t-meta)/1 var(--f-mono); color: var(--ink-2); }
/* Ein Bereich, der gerade laedt, wird leiser — aber nie unsichtbar. */
.app [aria-busy="true"]:not(.btn) { opacity: 0.72; }

/* ------------------------------------------------------------------ Karte
   Frueher eine Flaeche in `--paper-2` mit 8 px Radius. Jetzt eine echte
   Karte: heller als der Grund, 14 px Radius, weicher Schatten in der
   Ruhestufe, Haarlinie als Rueckversicherung.

   Warum die Linie trotz Schatten bleibt: hell steht Weiss auf #FAFBFC, das
   sind 1.04:1 — ohne Schatten waere die Karte unsichtbar, und der Schatten
   allein traegt nicht an der Oberkante, wo er hinter der klebenden Kopfzeile
   liegt. Dunkel ist es umgekehrt: dort traegt der Schatten fast nichts
   (Schwarz auf Fast-Schwarz), die Hebung kommt aus der Helligkeit
   (`--paper-2` auf `--paper`, 1.07:1) plus der eingesetzten Lichtkante aus
   `--shadow-1`. */
.a-panel {
    background: var(--card);
    border: 1px solid var(--card-line);
    border-radius: var(--r-md);
    box-shadow: var(--shadow-1);
    padding: var(--s-3);
}
.a-panel + .a-panel { margin-top: var(--s-3); }
.a-panel__head {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: start;
    gap: var(--s-2);
}
.a-panel__title { font-size: var(--t-h3); font-weight: 600; letter-spacing: -0.01em; }
.a-panel__desc { margin-top: 4px; max-width: 62ch; font-size: var(--t-small); color: var(--ink-2); }
.a-panel__body { margin-top: var(--s-3); }
/* Nebeninformation: eine Stufe tiefer und ohne Schatten, damit sie sich
   INNERHALB einer Karte als Einschub liest und nicht als zweite Karte. */
.a-panel--quiet {
    background: var(--card-2);
    box-shadow: none;
}
/* Verschachtelte Karte: dieselbe Absenkung, ohne dass die Seite es sagen muss. */
.a-panel .a-panel {
    background: var(--card-2);
    box-shadow: none;
}
@media (max-width: 560px) {
    .a-panel { padding: var(--s-2); }
    .a-panel__head { grid-template-columns: minmax(0, 1fr); }
}

/* -------------------------------------------------------- Kennzahl-Kachel
   Grosse Zahl, Beschriftung, Symbolfeld, optional eine farbige Fussleiste.
   Die Zahl steht in der Textschrift mit Tabellenziffern — Mono waere hier
   ein Rueckfall ins Papier-Denken; sie ist eine Aussage, kein Maschinenwert.
   Die Fussleiste traegt IMMER Text: eine farbige Leiste allein waere
   Information durch Farbe (WCAG 1.4.1). */
.a-kpi-grid {
    display: grid;
    gap: var(--s-2);
    grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
}
/* Die Kachelreihe ist KEIN `.a-panel`, also greift der Abstand aus
   `.a-panel + .a-panel` an ihr nicht: die Reihe stiess bei 1440 px ohne einen
   einzigen Pixel Zwischenraum an die folgende Karte, Kante auf Kante
   (gemessen: Kacheln bis 456 px, Karte ab 456 px — Abnahme F1, M-3). */
.a-kpi-grid + .a-panel,
.a-panel + .a-kpi-grid { margin-top: var(--s-3); }
.a-kpi {
    position: relative;
    /* Flex-Spalte statt Raster: nur so laesst sich die Fussleiste mit
       `margin-block-start: auto` an den UNTEREN Rand druecken. Mit
       `display: grid; align-content: start` klebte sie direkt unter dem Wert,
       und in einer Reihe unterschiedlich hoher Kacheln sassen die Leisten
       dann auf verschiedenen Hoehen. */
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 6px;
    background: var(--card);
    border: 1px solid var(--card-line);
    border-radius: var(--r-lg);
    box-shadow: var(--shadow-1);
    padding: var(--s-3);
    overflow: hidden;
}
a.a-kpi {
    text-decoration: none;
    color: inherit;
    transition:
        box-shadow var(--out) var(--ease),
        border-color var(--out) var(--ease),
        transform var(--out) var(--ease);
}
a.a-kpi:hover {
    box-shadow: var(--shadow-2);
    border-color: var(--line-ui);
    transform: translateY(-2px);
    transition-duration: var(--in);
}
a.a-kpi:active { transform: translateY(0); }
.a-kpi__top {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--s-2);
    margin-bottom: 6px;
}
.a-kpi__label {
    font-size: var(--t-small);
    color: var(--ink-2);              /* 7.76 hell · 9.47 dunkel */
}
/* Der Wert. `drb-site.css` setzt am Koerper global `overflow-wrap: break-word`
   — bei einer Zahl faellt das nie auf, bei einem WORT schon: „Erreichbar"
   stand in der Kachel als „Erreichba / r". Ein mitten im Wort umbrochener Wert
   ist keine Information mehr, sondern ein Fehler.

   Deshalb hier ausdruecklich `overflow-wrap: normal` und statt des Umbruchs
   eine Verkleinerung ueber `--kpi-value`: Zahlen behalten ihre Groesse, ein
   Wort bekommt sie ueber `.a-kpi--wort` eine Stufe kleiner und passt damit
   auch in die schmalste Kachel (bei 1440 px rund 290 px Innenbreite). */
.a-kpi__value {
    font: 600 var(--kpi-value, clamp(2rem, 1.5rem + 1.4vw, 2.625rem))/1.05 var(--f-sans);
    letter-spacing: -0.025em;
    font-variant-numeric: tabular-nums slashed-zero;
    overflow-wrap: normal;
    color: var(--ink);
}
/* Wortwert statt Zahl — etwa „Erreichbar" / „Gestört". */
.a-kpi--wort { --kpi-value: clamp(1.375rem, 1.05rem + 0.9vw, 1.75rem); }
.a-kpi__note { font-size: var(--t-meta); line-height: 1.6; color: var(--ink-3); }
/* Fussleiste: zieht sich ueber die volle Kachelbreite, deshalb die negativen
   Aussenabstaende — die Kachel hat `overflow: hidden`, der Radius schneidet. */
.a-kpi__foot {
    display: flex;
    align-items: center;
    gap: 0.6ch;
    /* `auto` oben schiebt die Leiste an den unteren Rand der Kachel — damit
       stehen die Leisten einer Reihe alle auf derselben Hoehe, unabhaengig
       davon, wie lang der Text darueber ist. Die seitlichen und unteren
       Negativwerte ziehen sie ueber die Polsterung der Karte hinaus, damit
       sie bis an die Kante laeuft. */
    margin: auto calc(var(--s-3) * -1) calc(var(--s-3) * -1);
    padding: 11px var(--s-3);
    border-top: 2px solid var(--line-ui);
    background: var(--card-2);
    font-size: var(--t-meta);
    line-height: 1.5;
    color: var(--ink-2);
}
.a-kpi__foot svg { width: 15px; height: 15px; flex: 0 0 auto; }
.a-kpi--akz  .a-kpi__foot { border-top-color: var(--akz);      background: var(--wash-info); }
.a-kpi--ok   .a-kpi__foot { border-top-color: var(--sig-ok);   background: var(--wash-ok); }
.a-kpi--warn .a-kpi__foot { border-top-color: var(--sig-warn); background: var(--wash-warn); }
.a-kpi--bad  .a-kpi__foot { border-top-color: var(--sig-bad);  background: var(--wash-bad); }

/* ------------------------------------------------------------- Seitenkopf */
.a-pagehead {
    padding-bottom: var(--s-3);
    margin-bottom: var(--s-3);
    border-bottom: 1px solid var(--line);
}
.a-pagehead__kicker { display: block; margin-bottom: var(--s-1); }
.a-pagehead__bar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--s-2) var(--s-3);
}
.a-pagehead h1 { font-size: var(--t-h2); }
.a-pagehead__lead {
    margin-top: var(--s-2);
    max-width: 68ch;
    font-size: var(--t-small);
    color: var(--ink-2);
}
/* SUP-6 — unter 560 px bricht `.a-pagehead__bar` um, und die Handlung rutscht
   unter die Ueberschrift. Der Kasten des Knopfes stand dort zwar buendig, seine
   BESCHRIFTUNG aber 24 px eingerueckt (`--s-3`, das Innenmass von `.btn`) — und
   sichtbar ist beim ruhigen Knopf vor allem die Beschriftung. Auf dem Bildschirm
   las sich das als schiefe Kante gegen das `h1`.
   Statt das Innenmass zu nehmen (das den Knopf zu einem Link schrumpfen liesse)
   bekommt die Handlung die volle Spaltenbreite: dann ist sie ein eigener Block
   und keine eingerueckte Zeile — und der Tap-Bereich waechst auf dem Telefon
   gleich mit. */
@media (max-width: 560px) {
    .a-pagehead__bar > .a-actions { inline-size: 100%; }
    .a-pagehead__bar > .a-actions > .btn { flex: 1 1 100%; justify-content: center; }
}

/* ---------------------------------------------------------------- Meldung
   Seit dem Formensprung darf die Signalfarbe sichtbarer werden: die Meldung
   hat eine leicht getoente Flaeche, ein Symbolfeld und eine Kante links.
   Die Toenung ist mit 8 % (hell) bzw. 12 % (dunkel) so schwach gewaehlt,
   dass Text darauf weit ueber 4.5:1 bleibt:
     hell   ink auf #F9EFEC (bad) 15.13 · auf #EDF3EE (ok) 15.19
     dunkel ink auf #2D2724 (bad) 12.74 · auf #202C2A (ok) 12.50
   Farbe traegt trotzdem nie allein: es gibt immer ein Symbol und ein Wort. */
.a-notice {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    gap: var(--s-2);
    align-items: start;
    padding: var(--s-2);
    border: 1px solid var(--card-line);
    border-left: 3px solid var(--sig-info);
    border-radius: var(--r-md);
    background: var(--wash-info);
}
.a-notice + .a-notice { margin-top: var(--s-2); }
/* Das Symbolfeld traegt seine Toenung ueber `.a-ico--ok|--warn|--bad|--info`,
   die die Komponente passend zum Ton setzt. Hier steht nur die Ausrichtung. */
.a-notice__mark { align-self: start; }
.a-notice__title { display: block; font-weight: 600; font-size: var(--t-small); }
.a-notice__body { font-size: var(--t-small); color: var(--ink-2); }
.a-notice > div > * + * { margin-top: 3px; }

.a-notice--success { border-left-color: var(--sig-ok); background: var(--wash-ok); }
.a-notice--warning { border-left-color: var(--sig-warn); background: var(--wash-warn); }
.a-notice--error { border-left-color: var(--sig-bad); background: var(--wash-bad); }
.a-notice--error .a-notice__title { color: var(--sig-bad); }

/* ---------------------------------------------------------------- Tabelle
   Huelle und Tabelle kommen als `.tab-wrap` + `table.tab` aus drb-site.css.
   Hier Dichte, Zeilen-Hover, Beschriftung — und die Flaeche: `.tab-wrap`
   bringt `--paper-2` mit, was im Dunkelmodus genau die Kartenfarbe ist und
   die Tabelle in der Karte verschwinden liesse. `--card-2` ist in beiden
   Modi die Stufe darunter. */
/* `position: relative` ist hier KEINE Kosmetik, sondern der Deckel auf dem
   seitlichen Rollen. `.tab-wrap` bringt aus drb-site.css `overflow-x: auto`
   mit und `table.tab` eine Mindestbreite von 34 rem — auf dem Handy ragt die
   Tabelle also weit ueber ihre Huelle hinaus und soll dort IN der Huelle
   gerollt werden. Das gilt aber nicht fuer die `.sr-only`-Marken in den
   Zellen: `.sr-only` ist `position: absolute`, und ohne einen positionierten
   Vorfahren ist ihr Bezugsrahmen der des ganzen Dokuments. Sie entkommen dem
   Abschneiden und ziehen die SEITE auf ihre Breite — bei 390 px gemessen:
   625 px Dokument, 235 px seitliches Rollen auf `/p/einstellungen`.
   Mit dieser Zeile ist die Huelle selbst der Bezugsrahmen, und die Marken
   werden mit abgeschnitten. Sichtbar bleiben sie ohnehin nicht; sie sind fuer
   die Vorlesefunktion da. */
.a-table {
    width: 100%;
    position: relative;
    background: var(--card-2);
    border-color: var(--line);
    border-radius: var(--r-md);
}
/* SUP-7 — der sichtbare Hinweis, dass rechts noch etwas steht.
   Die Tabelle rollt bei 390 px regelkonform IN ihrer Huelle (die Seite bleibt
   bei 0 px seitlichem Rollen), aber ohne Zeichen: die Spalte „Kunde" endete
   mitten im Wort, und nichts sagte, dass es weitergeht.
   Vier Hintergrundebenen, kein JavaScript und kein zusaetzliches Element:
     1+2 zwei Deckel in der Flaechenfarbe, `local` — sie liegen AM INHALT und
         wandern mit ihm weg, sobald gerollt wird
     3+4 zwei Schatten an den Huellenkanten, `scroll` — sie bleiben stehen
   Ergebnis: der Schatten steht genau dann, wenn dort wirklich noch Inhalt ist,
   und verschwindet am Ende von selbst. Ein Satz „seitlich verschiebbar" waere
   auch dann zu lesen, wenn die Tabelle vollstaendig passt — also gelogen.
   Der Schatten kommt aus `--ink` und dreht damit im Dunkelmodus mit. */
.a-table {
    background-image:
        linear-gradient(to right,  var(--card-2), transparent),
        linear-gradient(to left,   var(--card-2), transparent),
        radial-gradient(farthest-side at 0   50%, color-mix(in oklab, var(--ink) 24%, transparent), transparent),
        radial-gradient(farthest-side at 100% 50%, color-mix(in oklab, var(--ink) 24%, transparent), transparent);
    background-position: 0 0, 100% 0, 0 0, 100% 0;
    background-repeat: no-repeat;
    background-size: 28px 100%, 28px 100%, 14px 100%, 14px 100%;
    background-attachment: local, local, scroll, scroll;
}
.a-table table.tab :is(th, td) { padding-block: var(--cell-pad-y); }
.a-table--dense table.tab :is(th, td) { padding-block: 9px; }
.a-table table.tab tbody tr:hover { background: var(--card); }
.a-table table.tab caption {
    text-align: left;
    padding: var(--s-2);
    border-bottom: 1px solid var(--line);
}

/* --------------------------------------------------------- Leerer Zustand */
.a-empty {
    display: grid;
    justify-items: start;
    gap: var(--s-1);
    border: 1px dashed var(--line-ui);
    border-radius: var(--r-lg);
    background: var(--card-2);
    padding: var(--s-4);
}
.a-empty__ico { margin-bottom: var(--s-1); }
.a-empty__title { font-size: var(--t-h3); font-weight: 600; }
.a-empty__reason {
    max-width: 56ch;
    font-size: var(--t-small);
    color: var(--ink-2);
}
.a-empty__action { margin-top: var(--s-2); }

/* --------------------------------------------------------------- Formular */
.a-form { display: grid; gap: var(--s-3); max-width: 46rem; }
.a-formrow { display: grid; gap: var(--s-3); }
.a-formrow--2 { grid-template-columns: 1fr 1fr; }
.a-formrow--3 { grid-template-columns: repeat(3, 1fr); }
.a-formrow__error { grid-column: 1 / -1; }
@media (max-width: 700px) {
    .a-formrow--2, .a-formrow--3 { grid-template-columns: 1fr; }
}

/* Eine Rasterspalte ist von sich aus mindestens so breit wie ihr breitester
   Inhalt (`min-width: auto`). Ein `<select>` ist so breit wie seine LAENGSTE
   Option — „Bearbeitung — darf Support-Anfragen stellen und Daten pflegen"
   misst 522 px. Damit wurde die Spalte 522 px breit, obwohl das Blatt nur
   324 px hat, und /a/kunden/[id] liess sich bei 390 px um 165 px zur Seite
   schieben (Abnahme F1, M-1 — gemessen ueber `scrollWidth - clientWidth`).
   `min-width: 0` gibt der Spalte die Erlaubnis zu schrumpfen; das Auswahlfeld
   kuerzt dann seine Beschriftung, statt die ganze Seite zu verschieben.
   Gilt fuer die Formularraster und deren unmittelbare Kinder — nicht global,
   damit Tabellen weiterhin INNERHALB ihrer Karte rollen duerfen. */
.a-form,
.a-formrow,
.a-field,
.field { min-width: 0; }
.a-form > *,
.a-formrow > *,
.a-field > *,
.field > * { min-width: 0; }

/* ------------------------------------------------------------------- Feld
   `drb-site.css` kennt keine Formularelemente — die Website hat keine.
   Deshalb steht hier das vollstaendige Feld und nicht nur eine Ergaenzung.

   Die Beschriftung stand frueher in Mono-Versalien. Das war dieselbe
   Papier-Idee wie die Punktlinie: ein Formular ist kein Formblatt. Jetzt
   Textschrift, halbfett, in `--ink` — lesbar auf einen Blick.

   Ohne `align-content: start` dehnt sich das Feld auf die Hoehe der
   hoechsten Spalte der Zeile; die Eingaben stuenden dann nicht mehr auf
   einer Linie, sobald ein Nachbarfeld einen Fehlertext traegt. */
/* `.field` ist der alte Name aus Whitebuild v2. `src/app/a/_lib/SelectField.tsx`
   setzt ihn noch neben `.a-field`; solange er im Markup steht, ist er hier ein
   echter Zweitname und keine leere Regel. */
.a-field, .field { display: grid; gap: 7px; align-content: start; }
.a-field label {
    font: 600 var(--t-small)/1.4 var(--f-sans);
    color: var(--ink);               /* 17.10 hell · 15.31 dunkel */
}
.a-field input,
.a-field select,
.a-field textarea {
    min-height: var(--field-h);
    padding: 0 14px;
    background: var(--card);
    color: var(--ink);
    border: 1px solid var(--line-ui);   /* 3.62 hell · 3.63 dunkel — WCAG 1.4.11 */
    border-radius: var(--r-sm);
    font: 400 1rem/1.5 var(--f-sans);
    transition:
        border-color var(--out) var(--ease),
        box-shadow var(--out) var(--ease);
}
.a-field textarea { padding-block: 12px; line-height: var(--lh-text); }
.a-field input:hover,
.a-field select:hover,
.a-field textarea:hover { border-color: var(--ink-3); }
.a-field input:focus,
.a-field select:focus,
.a-field textarea:focus {
    border-color: var(--akz);
    box-shadow: 0 0 0 3px var(--tint-akz);
    transition-duration: var(--in);
}
.a-field input::placeholder,
.a-field textarea::placeholder { color: var(--ink-3); }
/* ─────────────────────────────────────────── Der Erklaersatz — Befund K2-1
   HIER STAND NUR GROESSE UND FARBE, und das hat eine ganze Bauetappe
   unsichtbar gemacht. Ueberall in `_lib`/`components` haengt die Klasse an
   einem `<p>` und ist dadurch OHNEHIN ein Block; die Regel musste den Block
   nie selbst herstellen. Im Abschnitt „Wie dieser Vertrag zustande kam"
   (Befund K14-2) haengt sie dagegen an einem `<span>` innerhalb von
   `<span class="val">` — und dort blieb sie `inline`. Gemessen am Bildschirm:
   neun Zeilen mit `display: inline` und Versatz 0 px zwischen Wert und
   Erklaerung, also derselbe Textfluss ohne Trenner („…zugesagtDer Kunde
   hat…"). Dieselbe Ursache in der Ruecklauf- und der Versandmaske, wo der
   Satz aus `sichtpruefungAusgeschlossen()` mit der allgemeinen Erklaerung
   verschmolz.

   Die Regel stellt den Block deshalb SELBST her. An einem `<p>` aendert das
   nichts (`display: block` ist dort schon die Vorgabe); an einem `<span>`
   behebt es den Befund — und beim naechsten Abschnitt, der die Klasse an ein
   Inline-Element haengt, wiederholt er sich nicht.

   `text-align: start` ist noetig, nicht Geschmack: `.row .val` richtet RECHTS
   aus (`drb-site.css`). Ein Erklaersatz von 700 px rechtsbuendig liest sich
   an der linken Kante ausgefranst; der Wert daneben bleibt rechtsbuendig. */
.a-field__desc {
    display: block;
    margin-block-start: 4px;
    font-size: var(--t-small);
    line-height: var(--lh-text);
    color: var(--ink-2);
    text-align: start;
}
.a-field__req { font: 600 var(--t-small)/1 var(--f-sans); color: var(--sig-bad); }

/* ---------------------------------------------------------- Statuszeichen
   Getoente Flaeche statt reiner Kontur — mit dem Formensprung darf das
   Signal sichtbarer werden. Das Wort bleibt Pflicht; die Farbe kommt dazu. */
.a-stat {
    display: inline-flex;
    align-items: center;
    gap: 0.5ch;
    border: 1px solid var(--line-ui);
    border-radius: 999px;
    padding: 4px 11px;
    background: var(--card-2);
    font: 600 var(--t-meta)/1.5 var(--f-mono);
    letter-spacing: 0.05em;
    text-transform: uppercase;
    color: var(--ink-2);
    white-space: nowrap;
}
.a-stat svg { width: 14px; height: 14px; }
.a-stat--ok   { border-color: var(--sig-ok);   background: var(--tint-ok);   color: var(--sig-ok); }
.a-stat--warn { border-color: var(--sig-warn); background: var(--tint-warn); color: var(--sig-warn); }
.a-stat--bad  { border-color: var(--sig-bad);  background: var(--tint-bad);  color: var(--sig-bad); }

/* --------------------------------------------------------- Konto-Baustein
   Rechts oben steht kein nackter Knopf mehr, sondern eine Person: Zeichen
   mit den Initialen, Name, Rolle. Natives `<details>` — aufklappen, mit
   Escape schliessen und die Tastaturbedienung liefert der Browser. Selbst
   gebaute Menues sind die haeufigste Quelle kaputter Fokusreihenfolgen. */
.a-menu { position: relative; }
.a-menu > summary { list-style: none; cursor: pointer; }
.a-menu > summary::-webkit-details-marker { display: none; }
.a-menu__panel {
    position: absolute;
    top: calc(100% + 8px);
    right: 0;
    z-index: var(--z-pop);
    min-width: 17rem;
    box-shadow: var(--shadow-2);
}
.a-menu__panel > * + * { margin-top: var(--s-2); }

.a-account {
    display: inline-flex;
    align-items: center;
    gap: 0.7ch;
    min-height: 44px;
    padding: 4px 10px 4px 4px;
    border: 1px solid var(--line-ui);
    border-radius: 999px;
    background: none;
    color: var(--ink);
    transition: background var(--out) var(--ease), border-color var(--out) var(--ease);
}
.a-account:hover { background: var(--card-2); border-color: var(--akz); transition-duration: var(--in); }
.a-account__mark {
    display: grid;
    place-items: center;
    width: 34px;
    height: 34px;
    flex: 0 0 auto;
    border-radius: 999px;
    background: var(--tint-akz);
    color: var(--akz);               /* 5.81 hell · 8.22 dunkel */
    font: 600 var(--t-meta)/1 var(--f-mono);
    letter-spacing: 0.04em;
}
.a-account__text {
    display: grid;
    gap: 1px;
    min-width: 0;
    text-align: left;
}
.a-account__name {
    font: 600 var(--t-small)/1.25 var(--f-sans);
    color: var(--ink);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 12rem;
}
.a-account__role {
    font: 400 var(--t-meta)/1.3 var(--f-mono);
    color: var(--ink-3);             /* 5.33 hell · 7.11 dunkel auf --paper */
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 12rem;
}
.a-account svg { width: 16px; height: 16px; color: var(--ink-3); }
/* Unter 720 px bleibt nur das Zeichen — Name und Rolle stehen dann im
   aufgeklappten Blatt, das sie ohnehin wiederholt. */
@media (max-width: 719.98px) {
    .a-account { padding: 3px; border-color: transparent; }
    .a-account__text, .a-account > svg { display: none; }
}

/* Der Kopf im aufgeklappten Blatt. */
.a-menu__who { display: flex; align-items: center; gap: var(--s-2); }
.a-menu__mail {
    font: 400 var(--t-meta)/1.5 var(--f-mono);
    color: var(--ink-2);
    overflow-wrap: anywhere;
}


/* ------------------------------------------------------- Protokollliste
   Die fuenf juengsten Eintraege auf der Adminportal-Uebersicht. Kein
   Tabellenkopf: es sind drei Angaben je Zeile, und eine Tabelle mit drei
   Spalten und fuenf Zeilen liest sich schlechter als eine Liste.

   Der Zeitstempel bricht unter 560 px in die zweite Zeile, statt die
   Beschreibung auf zwei Zeichen zusammenzuquetschen. */
.a-log { display: grid; gap: 2px; }
.a-log > li {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) auto;
    align-items: center;
    gap: var(--s-2);
    padding: 10px var(--s-2);
    border-radius: var(--r-sm);
}
.a-log > li:nth-child(odd) { background: var(--card-2); }
.a-log > li > span { display: grid; gap: 1px; min-width: 0; }
.a-log b { font-weight: 600; }
.a-log small {
    font-size: var(--t-small);
    color: var(--ink-2);            /* 7.76 hell · 9.47 dunkel */
    overflow-wrap: anywhere;
}
.a-log time {
    font-size: var(--t-meta);
    color: var(--ink-3);            /* 5.52 hell · 6.64 dunkel */
}
@media (max-width: 560px) {
    .a-log > li { grid-template-columns: auto minmax(0, 1fr); }
    .a-log time { grid-column: 2; }
}


/* ------------------------------------------ Einrichtung zweiter Faktor
   Diese Seite liegt in der Anmeldehuelle, braucht aber MEHR Platz als die
   432 px der Anmeldekarte: ein QR-Code, den eine Kamera aus der Hand lesen
   soll, und ein 39-stelliger Schluessel zum Abtippen passen dort nicht.
   Deshalb weitet `.a-setup` seine Huelle — ueber `:has`, damit die uebrigen
   Anmeldeseiten schmal bleiben und hier keine zweite Huelle noetig ist. */
.app:has(> .a-bar) > .a-main > .a-read:has(> .a-setup) { max-width: var(--setup-w); }

/* Der QR-Code auf eigenem hellem Feld, mittig.
   Die weisse Flaeche ist in BEIDEN Modi weiss und das ist kein Versehen:
   der Code wird von einer Kamera gelesen. Er ist mit `#111111` auf `#ffffff`
   erzeugt (Seite `zwei-faktor/einrichten/page.tsx`) — ein heller Code auf
   dunklem Grund wird von vielen Scannern verweigert. Die Kante steht
   deshalb in `--line-ui`: gegen den dunklen Grund muss das Feld als Flaeche
   erkennbar bleiben (3:1 fuer Kanten, WCAG 1.4.11). */
.a-setup__qr {
    display: block;
    width: fit-content;
    margin-inline: auto;
    padding: var(--s-2);
    background: #fff;
    border: 1px solid var(--line-ui);
    border-radius: var(--r-md);
}
.a-setup__qr img {
    display: block;
    width: clamp(208px, 46vw, 264px);
    height: auto;
}

/* Der Schluessel zum Abtippen: Martian Mono, mittig, gross genug zum
   Ablesen. Bewusst NICHT `.num` — die Klasse verbietet den Umbruch, und
   39 Zeichen schoeben die Karte damit seitlich auf. */
.a-setup__key {
    margin-inline: auto;
    max-width: 30ch;
    text-align: center;
    font: 500 var(--t-body)/1.7 var(--f-mono);
    font-variant-numeric: tabular-nums slashed-zero;
    letter-spacing: 0.04em;
    overflow-wrap: anywhere;
    color: var(--ink);              /* 17.10 hell · 15.31 dunkel */
}

/* Die zehn Wiederherstellungscodes: eigenstaendige Felder statt Fliesstext
   oder Punktlinien-Zeilen. Zwei Spalten, ab 560 px drei — mehr wuerde die
   Codes so schmal machen, dass sie umbrechen. */
.a-codes {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--s-1);
    list-style: none;
    padding: 0;
}
.a-codes > li {
    padding: 10px var(--s-2);
    background: var(--card-2);
    border: 1px solid var(--card-line);
    border-radius: var(--r-sm);
    text-align: center;
    font: 500 var(--t-body)/1.4 var(--f-mono);
    font-variant-numeric: tabular-nums slashed-zero;
    letter-spacing: 0.04em;
    color: var(--ink);              /* 15.51 hell · 13.51 dunkel (auf --card-2) */
}
@media (min-width: 560px) {
    .a-codes { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
/* Auf Papier zaehlt nur die Liste — sie ist der Grund, warum gedruckt wird. */
@media print {
    .a-codes > li { border-color: #000; color: #000; background: #fff; }
}


/* ========================================================== 8 · DOKUMENTE
   Der Dokumentenbereich beider Oberflaechen (Bauanleitung 20 § 5.5).

   Keine neuen Farben, keine neuen Radien: alles kommt aus den Kennwerten in
   Abschnitt 1. Beide Themen werden gleichwertig bedient — was hier steht,
   nutzt ausschliesslich Variablen, die in hell UND dunkel gesetzt sind.        */

/* -------------------------------------------------------- Filterleiste
   Die Chips sind VERWEISE, keine Knoepfe: die Ansicht bleibt damit teilbar
   und die Zurueck-Taste tut, was sie soll. Sie sehen trotzdem wie Bedienung
   aus — deshalb dieselbe Kante und derselbe Radius wie ein kleiner Knopf. */
.a-filterbar {
    display: flex;
    flex-wrap: wrap;
    gap: var(--s-1);
    margin-block-end: var(--s-2);
}
.a-chip {
    display: inline-flex;
    align-items: center;
    min-height: 36px;
    padding-inline: 12px;
    border: 1px solid var(--card-line);
    border-radius: var(--r-sm);
    background: var(--card-2);
    color: var(--ink-2);
    font: 500 var(--t-small)/1 var(--f-sans);
    text-decoration: none;
}
.a-chip:hover { color: var(--ink); border-color: var(--ink-3); }
/* Der aktive Chip traegt Farbe, nicht nur Fettung: „welcher Filter laeuft"
   muss auch ohne Schriftvergleich zu sehen sein. */
.a-chip--on {
    border-color: var(--akz);
    background: var(--tint-akz);
    color: var(--akz);
}

/* ------------------------------------------------------------ Suchfeld
   Ein `GET`-Formular in einer Zeile. Auf dem Telefon untereinander — ein
   Suchfeld, das auf 120 px zusammengedrueckt wird, ist keines. */
.a-docsearch {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--s-1);
    margin-block-end: var(--s-3);
}
.a-docsearch label {
    color: var(--ink-2);
    font-size: var(--t-small);
}
.a-docsearch input {
    flex: 1 1 16rem;
    min-height: var(--field-h);
    padding-inline: 12px;
    border: 1px solid var(--card-line);
    border-radius: var(--r-sm);
    background: var(--card);
    color: var(--ink);
    font: 400 var(--t-small)/1.4 var(--f-sans);
}

/* --------------------------------------------------------- Die Liste
   Bewusst eine Liste und keine Tabelle: ein Dokument hat einen Titel und
   eine Handvoll Angaben, keine Spalten, die man vergleicht. `table.tab`
   braechte ausserdem aus der Website eine Mindestbreite von 34 rem mit. */
.a-doclist {
    list-style: none;
    margin: 0;
    padding: 0;
}
/* AUSDRUECKLICH `li.` — den blanken Namen `.a-doc` gibt es in dieser Datei
   ZWEIMAL: hier als Zeile der Dokumentenliste (`<li>`), weiter unten als
   A4-Blatt (`<article>`). Ohne die Einengung galten beide Regeln fuer beide
   Bauteile: das Blatt erbte dieses Raster (und legte Kopf, Hinweis, Text und
   Fuss in drei Spalten), die Listenzeile erbte Rand, Schatten und die
   190-mm-Grenze des Blattes. Das war die Wurzel von V-7. */
li.a-doc {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) auto;
    align-items: center;
    gap: var(--s-2);
    padding-block: 14px;
}
li.a-doc + li.a-doc { border-top: 1px solid var(--line); }
li.a-doc strong {
    display: block;
    color: var(--ink);
    font-weight: 600;
}
/* Interne Unterlagen im Adminportal: gedaempft, nicht versteckt. Im
   Kundenportal kommen sie gar nicht erst vor. */
.a-doc--intern { opacity: 0.72; }

/* Das Kennzeichen der Art. Kammer A ruhig, Kammer B dezent grau — der
   Unterschied sagt „bleibt" gegen „laeuft ab" und ist kein Schmuck. */
.a-doc-art {
    display: inline-flex;
    align-items: center;
    min-height: 26px;
    padding-inline: 9px;
    border: 1px solid var(--akz);
    border-radius: var(--r-sm);
    background: var(--tint-akz);
    color: var(--akz);
    font: 500 var(--t-meta)/1 var(--f-mono);
    white-space: nowrap;
}
.a-doc-art--kammer-b {
    border-color: var(--card-line);
    background: var(--card-2);
    color: var(--ink-2);
}
.a-doc-meta {
    display: block;
    margin-block-start: 3px;
    color: var(--ink-2);
    font: 400 var(--t-meta)/1.5 var(--f-mono);
    font-variant-numeric: tabular-nums;
}
/* Die Jahreszahl ueber einer Gruppe. Eine Zwischenzeile, keine Ueberschrift
   mit Anspruch — sie ordnet, sie kuendigt nichts an. */
.a-doc-jahr {
    margin-block: var(--s-3) var(--s-1);
    color: var(--ink-2);
    font: 500 var(--t-meta)/1 var(--f-mono);
    letter-spacing: 0.08em;
}

/* Auf dem Telefon steht die Handlung unter dem Titel und nicht daneben:
   nebeneinander bliebe fuer den Titel nichts uebrig. */
@media (max-width: 640px) {
    li.a-doc {
        grid-template-columns: auto minmax(0, 1fr);
    }
    li.a-doc .a-actions {
        grid-column: 1 / -1;
    }
}

/* ------------------------------------------------------- Hochladen
   Ein schlichtes Formular in einer Spalte. Die Hinweiszeile darunter nennt
   die FOLGE der Auswahl (Kammer, Frist, Fundstelle) — wer eine
   Aufbewahrungsfrist ausloest, soll sie beim Ausloesen sehen. */
.a-upload {
    display: grid;
    gap: var(--s-1);
    max-width: var(--read-w);
}
.a-upload label {
    margin-block-start: var(--s-1);
    color: var(--ink-2);
    font-size: var(--t-small);
}
.a-upload :is(input, select) {
    min-height: var(--field-h);
    padding-inline: 12px;
    border: 1px solid var(--card-line);
    border-radius: var(--r-sm);
    background: var(--card);
    color: var(--ink);
    font: 400 var(--t-small)/1.4 var(--f-sans);
}
.a-upload button { justify-self: start; margin-block-start: var(--s-2); }

/* ------------------------------------------------- Dateiauswahl (AD-5)
   Der native Knopf von `<input type="file">` traegt die Sprache des
   BROWSERS — auf einem englischen Browser steht dort „Choose File". Er
   wird deshalb verborgen und ein deutsch beschriftetes `<label>` davor
   gelegt. Verborgen heisst hier `clip-path`, NICHT `display: none`:
   ein ausgeblendetes Feld waere nicht mehr fokussierbar, und die
   Pflichtpruefung des Browsers haette nichts, woran sie ihre Meldung
   haengen kann. */
.a-file {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--tap-gap);
}
/* Die doppelten Klassen sind Absicht und kein Schoenheitsfehler: `.a-upload
   :is(input, select)` und `.a-upload label` haben je (0,1,1) und schluegen
   einen einfachen Klassenselektor sonst. */
.a-file .a-file__input {
    position: absolute;
    width: 1px;
    min-height: 0;
    height: 1px;
    padding: 0;
    border: 0;
    clip-path: inset(50%);
    white-space: nowrap;
    overflow: hidden;
}
.a-file .a-file__btn {
    display: inline-flex;
    align-items: center;
    min-height: var(--field-h);
    margin-block-start: 0;
    padding-inline: 14px;
    border: 1px solid var(--card-line);
    border-radius: var(--r-sm);
    background: var(--card-2);
    color: var(--ink);
    font: 500 var(--t-small)/1.2 var(--f-sans);
    cursor: pointer;
}
.a-file .a-file__btn:hover { border-color: var(--ink-2); }
/* Der Fokus liegt auf dem verborgenen Feld — sichtbar werden muss er am
   Knopf, sonst wandert er beim Tastaturweg unsichtbar durch das Formular. */
.a-file .a-file__input:focus-visible + .a-file__btn {
    outline: 2px solid var(--akz);
    outline-offset: 3px;
}
.a-file .a-file__name {
    color: var(--ink-2);
    font: 400 var(--t-meta)/1.4 var(--f-mono);
    overflow-wrap: anywhere;
}
.a-upload-hint {
    margin: 0;
    color: var(--ink-2);
    font: 400 var(--t-meta)/1.6 var(--f-mono);
}
/* Der rote Hinweis bei `zugangsdaten`: keine gefuellte Warnflaeche, sondern
   eine Kante — die Meldung soll gelesen und nicht ueberblaettert werden. */
.a-upload-warn {
    margin: 0;
    padding: 10px 12px;
    border-inline-start: 3px solid var(--sig-bad);
    background: var(--wash-bad);
    color: var(--ink);
    font-size: var(--t-small);
}

/* -------------------------------------------------------- Vorschau
   Der Rahmen mit der eingebauten PDF-Anzeige des Browsers. Feste Hoehe in
   `vh`, damit eine Seite als Ganzes sichtbar ist, ohne dass zwei
   Bildlaufleisten ineinander laufen. */
.a-viewer {
    display: block;
    width: 100%;
    height: min(72vh, 900px);
    border: 1px solid var(--card-line);
    border-radius: var(--r-md);
    background: var(--card-2);
}
/* Derselbe Rahmen, aber mit Text statt Anzeige: waehrend das Ticket gezogen
   wird und wenn die Vorschau ausfaellt. */
.a-viewer--leer {
    display: grid;
    place-items: center;
    height: 12rem;
    padding: var(--s-3);
    color: var(--ink-2);
    font-size: var(--t-small);
    text-align: center;
}

/* --------------------------------------------------------- Verlauf
   Die Zeilen, die der Kunde an seinem Dokument sieht — einschliesslich der
   Abrufe von DRB. Bewusst schlicht: es ist eine Chronik, keine Auswertung. */
.a-verlauf {
    list-style: none;
    margin: 0;
    padding: 0;
    max-width: var(--read-w);
}
.a-verlauf > li {
    display: flex;
    flex-wrap: wrap;
    gap: var(--s-1);
    padding-block: 10px;
    color: var(--ink);
    font-size: var(--t-small);
}
.a-verlauf > li + li { border-top: 1px solid var(--line); }
.a-verlauf-zeit {
    color: var(--ink-2);
    font: 400 var(--t-meta)/1.6 var(--f-mono);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}


/* =============================================== 9 · BEWEGUNG REDUZIEREN
   drb-site.css schaltet `animation` und `transition` bereits global mit
   `!important` ab. Hier steht nur, was danach noch falsch aussaehe: das
   Ladezeichen waere ein stehender Dreiviertelkreis, und die angehobene
   Kachel bliebe ohne ihre Verschiebung. */
@media (prefers-reduced-motion: reduce) {
    .a-spinner__box {
        border-right-color: currentColor;
        opacity: 0.5;
    }
    a.a-kpi:hover,
    a.a-kpi:active,
    .app .btn:hover,
    .app .btn:active { transform: none; }
}


/* ================================================== 9 · ANGEBOTSMASCHINE
   Der Konfigurator, die Summenspalte und das Angebotsblatt fuer den Druck.

   DREI ENTSCHEIDUNGEN, die man am Aussehen ablesen koennen soll:

   1. Die RECHNUNG steht immer im Bild (`.a-quote__side`, klebend). Sie ist
      der Grund fuer das ganze Werkzeug: wer eine Zeile hinzufuegt, sieht im
      selben Moment Einmalsumme, Monatssumme und die Gesamtkosten ueber die
      Mindestlaufzeit. Letztere sind bei Dauerschuldverhaeltnissen eine
      Pflichtangabe nach PAngV und fehlten in DRB Nexus vollstaendig.

   2. Die INTERNE Sicht ist optisch abgesetzt (`.a-intern`, warngetoent) und
      traegt das Wort „intern" im Titel. Sie steht in keinem Kundendokument;
      im Druckstil weiter unten verschwindet sie ersatzlos.

   3. Das Angebotsblatt (`.a-doc`) ist DIESELBE Ausgabe fuer Bildschirm und
      Papier. In Nexus waren Vorschau und PDF zwei getrennte Vorlagen
      (`public/templates/*_formular.html` gegen `src/templates/*_template.html`)
      — WYSIWYG war dort eine Illusion, und die Drift fiel niemandem auf.
      Deshalb gibt es hier kein zweites Markup, sondern nur andere Regeln
      unter `@media print`.

   Keine neuen Farben. Alles kommt aus `--akz`, den vier Signalfarben und der
   Papierleiter. */

.a-quote {
    display: grid;
    gap: var(--s-3);
    align-items: start;
}
@media (min-width: 1100px) {
    .a-quote { grid-template-columns: minmax(0, 1fr) 21rem; }
}
.a-quote__main { display: grid; gap: var(--s-3); min-width: 0; }
.a-quote__side { display: grid; gap: var(--s-2); min-width: 0; }
@media (min-width: 1100px) {
    /* Klebend erst dort, wo die Spalte auch neben dem Inhalt steht. Auf
       schmalen Anzeigen stuende sie sonst mitten im Lesefluss fest. */
    .a-quote__side { position: sticky; top: var(--s-2); }
}

/* ------------------------------------------------------------- Bausteine */

.a-pick { display: grid; gap: var(--s-3); }
.a-pick__group { display: grid; gap: var(--s-1); }
.a-pick__title {
    font: 600 var(--t-meta)/1.5 var(--f-sans);
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--ink-3);            /* 5.52 hell · 5.11 dunkel auf --card */
}
.a-pick__item {
    display: flex;
    align-items: flex-start;
    gap: var(--s-2);
    min-height: 44px;               /* Touch-Ziel, Bauplan § 4 „Dichte" */
    padding: 10px var(--s-2);
    background: var(--card-2);
    border: 1px solid var(--line-ui);
    border-radius: var(--r-sm);
    cursor: pointer;
    transition: border-color var(--out) var(--ease), background var(--out) var(--ease);
}
.a-pick__item:hover { border-color: var(--akz); }
.a-pick__item input { margin-block-start: 3px; flex: none; }
.a-pick__item:has(input:checked) {
    /* Toenung 12 % Akzent auf der Karte: #E4EAF3 hell · #1B2733 dunkel.
       Text darauf: 15.31 hell · 12.44 dunkel — weit ueber 4.5:1. */
    background: color-mix(in oklab, var(--akz) var(--tint), var(--card));
    border-color: var(--akz);
}
.a-pick__body { display: grid; gap: 2px; min-width: 0; flex: 1 1 auto; }
.a-pick__name { font-weight: 600; }
.a-pick__price {
    font: 500 var(--t-small)/1.5 var(--f-mono);
    font-variant-numeric: tabular-nums;
    color: var(--ink-2);            /* 7.76 hell · 7.03 dunkel */
}
.a-pick__hint { font-size: var(--t-small); color: var(--ink-3); }
/* Ein Kaestchen, das gesperrt IST — und nicht nur nicht reagiert.
   Der Grund steht als Hinweistext daneben (Befund BETA-1, BEFUNDE § 2cw);
   ohne diese Regel saehe die Zeile aus wie jede andere, und der Bediener
   klickte dagegen. `cursor: not-allowed` steht auf dem ganzen Feld, weil das
   `<label>` den Klick sonst an den gesperrten Eingang weiterreicht. */
.a-pick__item[data-gesperrt='ja'] {
    cursor: not-allowed;
    border-style: dashed;
    background: var(--card);
}
.a-pick__item[data-gesperrt='ja']:hover { border-color: var(--line-ui); }
.a-pick__item[data-gesperrt='ja'] .a-pick__name { color: var(--ink-3); }

/* Menge, Nachlass und Begruendung einer gewaehlten Zeile. */
.a-pick__fields {
    display: grid;
    gap: var(--s-1);
    grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
    padding-block-start: var(--s-1);
}
.a-pick__fields label {
    display: grid;
    gap: 4px;
    font: 600 var(--t-small)/1.4 var(--f-sans);
}
.a-pick__fields :is(input, select) { width: 100%; }

/* ------------------------------------------------------- Die Rechenspalte */

.a-sum { display: grid; gap: 0; }
.a-sum__row {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--s-2);
    padding-block: 8px;
}
.a-sum__row + .a-sum__row { border-top: 1px solid var(--line); }
.a-sum__label { color: var(--ink-2); }
.a-sum__value {
    font-family: var(--f-mono);
    font-variant-numeric: tabular-nums slashed-zero;
    font-weight: 500;
    white-space: nowrap;
    text-align: right;
}
.a-sum__row--total {
    margin-block-start: var(--s-1);
    padding-block-start: var(--s-2);
    border-top: 2px solid var(--line-ui);
}
.a-sum__row--total .a-sum__label { color: var(--ink); font-weight: 600; }
.a-sum__row--total .a-sum__value { font-weight: 600; font-size: 1.0625rem; }
.a-sum__note {
    margin-block-start: var(--s-1);
    font-size: var(--t-small);
    line-height: 1.55;
    color: var(--ink-2);
}

/* ------------------------------- Eine Fassung in der Freigabevorschau

   Befund K23-17: Wer „Freigeben und einfrieren" drückt, gab bis hierher einen
   verbindlichen Antrag ab, ohne dessen Fassungen je gesehen zu haben. Die
   Vorschau zeigt sie — ruhig abgesetzt, damit sich die Fassungen voneinander
   und vom übrigen Vorgang unterscheiden lassen. Kein Kartenspiel: das ist eine
   Arbeitsansicht und keine Verkaufsseite. */
.a-fassung {
    padding-block: var(--s-2);
    border-top: 1px solid var(--line);
}
.a-fassung:first-of-type { border-top: 0; }
.a-fassung__kopf {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: var(--s-1);
    margin-block-end: var(--s-1);
}
.a-fassung__kennung {
    color: var(--ink-2);
    font: 400 var(--t-meta)/1.4 var(--f-mono);
}
.a-fassung__marke {
    padding-inline: 8px;
    border: 1px solid var(--akz);
    border-radius: var(--r-sm);
    background: var(--tint-akz);
    color: var(--akz);
    font: 500 var(--t-meta)/1.6 var(--f-sans);
}

/* ---------------------------------------- Der Block, der ABGETIPPT wird

   ⚠️ HIER IST DIE RECHENSPALTE KEINE RECHENSPALTE MEHR, sondern das URBILD
   einer Prüfsumme (Konzept 23 § 4). Unter dem Block steht der Satz
   `FASSUNGSBLOCK_KODIERUNG`, der den Leser ausdrücklich AUFFORDERT, genau
   diese Zeilen abzutippen und selbst zu hashen.

   Befund K23-18, am Bildschirm gemessen: `.a-sum__value` trägt
   `white-space: nowrap` — richtig für einen Betrag, der nicht über zwei
   Zeilen brechen soll. Im Block sind zwei der elf Werte aber TEXT
   („Bezeichnung", „Zahlungsweise"), und eine Fassung heißt schon einmal
   „Kauf Business mit Fotoshooting und Textarbeit". Die Seite rollte dadurch
   bei 390 px um 394 px zur Seite und bei 320 px um 464 px — abgeschnitten
   waren genau die Werte, über die gerechnet wird.

   Ein Wert, den man auf dem Telefon nicht ganz lesen kann, kann man auch
   nicht abtippen. Deshalb bricht er hier um; die Rechenspalte im Angebot
   bleibt davon unberührt. */
.a-sum--urbild .a-sum__row { flex-wrap: wrap; }
.a-sum--urbild .a-sum__label { min-width: 0; }
.a-sum--urbild .a-sum__value {
    min-width: 0;
    white-space: normal;
    overflow-wrap: anywhere;
}
/* Nebeneinander bleibt auf dem Telefon für beides kein Platz; untereinander
   steht jede Angabe vollständig da. Rechtsbündig wäre der umgebrochene Wert
   eine Treppe — dieselbe Überlegung wie bei `.row .val.pruefsumme`. */
@media (max-width: 560px) {
    .a-sum--urbild .a-sum__row {
        flex-direction: column;
        align-items: stretch;
        gap: 2px;
    }
    .a-sum--urbild .a-sum__value { text-align: left; }
}
.a-sum__words {
    font-size: var(--t-small);
    line-height: 1.55;
    color: var(--ink-2);
    font-style: italic;
}

/* Die interne Sicht: warngetoent, ausdruecklich beschriftet, im Druck weg.
   Toenung 8 % Warnfarbe auf der Karte: #F7F3E9 hell · #211D15 dunkel;
   Text darauf 15.72 hell · 12.71 dunkel. */
.a-intern {
    padding: var(--s-2);
    background: color-mix(in oklab, var(--sig-warn) var(--tint-soft), var(--card));
    border: 1px solid color-mix(in oklab, var(--sig-warn) 40%, var(--card-line));
    border-radius: var(--r-md);
}
.a-intern__title {
    margin-block-end: var(--s-1);
    font: 600 var(--t-meta)/1.5 var(--f-sans);
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--sig-warn);         /* 5.82 auf --card */
}

/* ----------------------------------------------------------- Befundliste */

.a-findings { display: grid; gap: var(--s-1); list-style: none; padding: 0; margin: 0; }
.a-findings li {
    display: flex;
    gap: var(--s-1);
    align-items: flex-start;
    font-size: var(--t-small);
    line-height: 1.55;
}
.a-findings__mark { flex: none; font-family: var(--f-mono); }
.a-findings--hart .a-findings__mark { color: var(--sig-bad); }
.a-findings--weich .a-findings__mark { color: var(--sig-warn); }

/* ================================================== Das Angebotsblatt (A4)
   Bildschirm und Papier aus EINEM Markup. Auf dem Bildschirm liegt es als
   Blatt auf dem Grund, auf Papier ist es schlicht die Seite. */

/* AUSDRUECKLICH `article.` — die Gegenprobe zu `li.a-doc` weiter oben.

   BIS ZUR HAERTUNG standen hier und dort blanke `.a-doc`-Regeln mit gleicher
   Spezifitaet. Das Blatt uebernahm damit `display: grid` aus der Listenzeile und
   legte seine Kinder in drei Rasterspalten ab. Gemessen am 06.08.2026 auf
   `/a/vertraege/<id>/druck` bei 1440 px: Kopf 360 px, der Hinweis mit der
   Pruefsumme **0 px**, Vertragstext 260 px — und die `auto`-Spalte zog die Seite
   auf 1535 px, weil sie mindestens so breit wird wie ihr breitester unteilbarer
   Inhalt (die Pruefsumme, damals in `.num`, 627 px). Auf dem Angebotsblatt
   standen Parteien, Zwischenueberschriften und Fuss auf 0 px. Das war die Wurzel
   von V-7 — die Pruefsumme war nur der Hebel.

   `display: block` bleibt trotzdem ausgeschrieben: es ist die Vorgabe fuer
   `<article>` und sagt deshalb nichts Neues, aber es sagt AUSDRUECKLICH, dass
   dieses Blatt kein Raster ist. */
article.a-doc {
    display: block;
    max-width: 190mm;
    margin-inline: auto;
    padding: var(--s-4);
    background: var(--card);
    border: 1px solid var(--card-line);
    border-radius: var(--r-md);
    box-shadow: var(--shadow-1);
    color: var(--ink);
}
.a-doc__head {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    gap: var(--s-3);
    padding-block-end: var(--s-2);
    border-bottom: 2px solid var(--ink);
}
.a-doc__brand { font: 600 1.125rem/1.3 var(--f-sans); letter-spacing: -0.01em; }
.a-doc__from { font-size: var(--t-small); line-height: 1.6; color: var(--ink-2); }
.a-doc__meta {
    display: grid;
    gap: 2px;
    font: 500 var(--t-small)/1.6 var(--f-mono);
    font-variant-numeric: tabular-nums;
    text-align: right;
}
.a-doc__parties {
    display: grid;
    gap: var(--s-3);
    margin-block: var(--s-3);
}
@media (min-width: 640px) {
    .a-doc__parties { grid-template-columns: 1fr 1fr; }
}
.a-doc__party { display: grid; gap: 2px; line-height: 1.6; }
.a-doc__title {
    margin-block: var(--s-3) var(--s-2);
    font: 600 1.375rem/1.25 var(--f-sans);
    letter-spacing: -0.015em;
}
.a-doc__lead { margin-block-end: var(--s-3); line-height: 1.65; max-width: var(--read-w); }
/* DIE ROLLBARE HUELLE DER BLATT-TABELLEN — Befund AB-7.
   ------------------------------------------------------------------------
   `article.a-doc table` hatte kein `overflow-x`, anders als `.a-doc__text`
   ein paar Regeln weiter unten. GEMESSEN auf `/p/angebote/<id>` bei 390 px
   Sichtfeld: `scrollWidth` 592. Verursacher sind `th/td.a-doc__right`
   (EINZELPREIS, BETRAG) mit `white-space: nowrap` — auf dem Telefon standen
   also genau die Preisspalten ausserhalb des Bildes.

   ⚠️ DAS IST DIE BESTELLSEITE. An ihr haengt § 312j Abs. 2 BGB. Ein Betrag,
   der ausserhalb des Bildes steht und den nichts als rollbar ausweist, ist
   nicht zur Kenntnis genommen worden.

   ⚠️ DIE HUELLE MUSS EIN EIGENES ELEMENT SEIN. `overflow-x` auf dem `<table>`
   selbst greift nicht zuverlaessig: eine Tabellenbox ist kein gewoehnlicher
   Blockcontainer. Deshalb liegt die Rolle auf `div.a-doc__tabelle` — dieselbe
   Bauart wie `.tab-wrap` in `drb-site.css`.

   ⚠️ `min-width` STEHT HIER BEWUSST NICHT. `.tab-wrap` gibt seiner Tabelle
   `min-width: 34rem` und ERZWINGT damit das Rollen. Dieses Blatt soll bei
   1440 px genau so aussehen wie bisher; es rollt nur dann, wenn der Inhalt es
   verlangt. */
.a-doc__tabelle {
    overflow-x: auto;
    margin-block-end: var(--s-3);
}
article.a-doc table {
    width: 100%;
    border-collapse: collapse;
    margin-block-end: var(--s-3);
}
/* In der Huelle traegt die Huelle den Abstand — sonst zaehlte er doppelt. */
.a-doc__tabelle > table { margin-block-end: 0; }
article.a-doc th,
article.a-doc td {
    padding: 10px 8px;
    text-align: left;
    vertical-align: top;
    border-bottom: 1px solid var(--line);
}
article.a-doc thead th {
    font: 600 var(--t-meta)/1.4 var(--f-sans);
    letter-spacing: 0.05em;
    text-transform: uppercase;
    color: var(--ink-2);
    border-bottom: 1px solid var(--ink-3);
}
.a-doc__right {
    text-align: right;
    font-family: var(--f-mono);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}
.a-doc__pos { color: var(--ink-3); font-family: var(--f-mono); width: 3ch; }
.a-doc__incl {
    margin: 6px 0 0;
    padding-left: 1.1em;
    font-size: var(--t-small);
    line-height: 1.55;
    color: var(--ink-2);
}
.a-doc__totals {
    margin-block-end: var(--s-3);
    padding: var(--s-2);
    background: var(--card-2);
    border-radius: var(--r-sm);
}
.a-doc__section {
    margin-block: var(--s-3) var(--s-1);
    font: 600 var(--t-meta)/1.4 var(--f-sans);
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--ink-2);
}
.a-doc__note { font-size: var(--t-small); line-height: 1.6; color: var(--ink-2); }
.a-doc__foot {
    margin-block-start: var(--s-4);
    padding-block-start: var(--s-2);
    border-top: 1px solid var(--line);
    font-size: var(--t-small);
    line-height: 1.6;
    color: var(--ink-2);
}
.a-doc__sign {
    display: grid;
    gap: var(--s-4);
    margin-block-start: var(--s-4);
}
@media (min-width: 640px) {
    .a-doc__sign { grid-template-columns: 1fr 1fr; }
}
.a-doc__signline {
    padding-block-start: 6px;
    border-top: 1px solid var(--ink-3);
    font-size: var(--t-small);
    color: var(--ink-2);
}

/* Der eingefrorene VERTRAGSTEXT und die § 312k-Bestaetigung.

   Sie kommen als EINE Zeichenkette aus der Datenbank — genau die Bytes, ueber
   die der Hash gebildet wurde. Deshalb `<pre>`: jede andere Auszeichnung
   wuerde Zeilenumbrueche und Leerzeilen neu setzen, und der Ausdruck saehe
   dann anders aus als die Datei, deren Pruefsumme darunter steht.

   ⚠️ BEFUND K8-1 — HIER STAND DIE LESE-SCHRIFT, UND DAS WAR EIN
   SICHERHEITSFEHLER, KEINE GESCHMACKSFRAGE.

   Bis zu diesem Schnitt lautete die Regel:

       font: var(--t-body)/1.7 var(--f-sans);   (Instrument Sans)
       white-space: pre-wrap;
       overflow-wrap: anywhere;

   mit der Begruendung „das hier ist ein Vertrag, kein Auszug aus einer
   Konsole". Das Dokument darin ist aber bei FESTER SPALTENBREITE gesetzt: der
   fremde Wortlaut wird gar nicht mehr umbrochen (seit Runde 7 steht ein Wert,
   der nicht in eine Zeile passt, in einer Anlage); das Blatt wird bei 96 Spalten
   umbrochen, und jede so entstandene Sichtzeile traegt ein Zitatpraefix. Genau
   das war die Abwehr gegen eine untergeschobene Feststellung.

   In einer Proportionalschrift gibt es keine Spalten, und mit
   `overflow-wrap: anywhere` gibt es eine Bruchstelle an JEDEM Zeichen. Am
   echten Browser gemessen (Chrome, 390 px, Kasten 358 px) genuegten deshalb
   55 gewoehnliche Zeichen — kein Sonderzeichen, kein Tabulator —, damit eine
   Sichtzeile des Zitats OHNE Praefix am linken Rand steht, also dort, wo die
   Feststellungen des Dokuments stehen. Die Bruchstelle war ueber die Zahl der
   Fuellzeichen stufenlos steuerbar.

   Der Betrachter, den die Anwendung selbst stellt, war damit derjenige, gegen
   den die Vorschrift nicht traegt — und zwar fuer das eine Dokument, dessen
   Leser § 312k Abs. 4 BGB als Verbraucher benennt.

   DESHALB:
     · `var(--f-mono)` — dieselbe Festbreitenschrift, in der `.pruefsumme`
       zweitausend Zeilen weiter oben aus demselben Grund gesetzt ist.
     · `white-space: pre` statt `pre-wrap` — der Betrachter bricht NICHT mehr
       selbst um. Was der Leser sieht, sind die Zeilen des Dokuments.
     · `overflow-x: auto` statt `overflow-wrap: anywhere` — der Preis dafuer:
       auf dem Telefon muss man waagerecht rollen. Das ist die richtige Seite
       des Handels; ein Umbruch, den niemand kontrolliert, kostet den Beweiswert.
     · `tab-size: 8` — ausdruecklich, weil `fremdtext.ts` mit acht rechnet. Der
       Vorgabewert ist zwar acht, aber eine Zahl, an der eine Abwehr haengt,
       steht hin.

   ⚠️ SEIT E-15 IST DAS KEINE HAELFTE EINER ABWEHR MEHR, SONDERN GUTE
   DARSTELLUNG — und der Unterschied gehoert hierher, damit niemand diese Regel
   fuer mehr haelt, als sie ist. Die zweite Haelfte war eine Zitatklammer mit
   abgeleiteter Kennung; das Zitat gibt es nicht mehr, denn fremder Blocktext
   steht nicht mehr im Blatt, sondern in einer eigenen Anlage
   (`beweis/anlage.ts`). Was diese Regel heute leistet: der Leser sieht die
   Zeilen des Dokuments und nicht die seines Fensters. Wer sie aufweicht,
   beschaedigt die Lesbarkeit des Beweispapiers — die Abwehr gegen einen
   untergeschobenen Satz haengt nicht mehr daran. */
.a-doc__text {
    margin-block-start: var(--s-3);
    font: var(--t-small)/1.55 var(--f-mono);
    white-space: pre;
    overflow-x: auto;
    tab-size: 8;
    color: var(--ink);
}

/* Die Druckleiste ueber dem Blatt — auf Papier verschwindet sie. */
.a-printbar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--s-2);
    max-width: 190mm;
    margin: 0 auto var(--s-3);
}
.a-printbar__hint { font-size: var(--t-small); color: var(--ink-2); max-width: 46ch; }

/* --------------------------------------------------------------- Papier
   BEWUSST KEIN CHROMIUM IM PORTAL. Die Ausgabe entsteht ueber die
   Druckansicht des Browsers: das spart ein 300-MB-Abbild und einen zweiten
   Angriffspfad im selben Prozess, und sie rendert genau das, was auf dem
   Bildschirm steht — die Vorlagendrift aus Nexus kann gar nicht entstehen. */
@media print {
    @page { size: A4; margin: 18mm 16mm; }

    article.a-doc {
        max-width: none;
        margin: 0;
        padding: 0;
        border: 0;
        border-radius: 0;
        box-shadow: none;
        background: #fff;
        color: #000;
    }
    /* AUF PAPIER ROLLT NICHTS — Befund AB-7. Bliebe die Huelle ein
       Rollbereich, schnitte der Ausdruck genau die Preisspalte ab, die sie am
       Bildschirm rettet, und ohne Rollbalken saehe es niemand. Dieselbe
       Ueberlegung wie bei `.a-doc__text` weiter unten, nur eine Ebene hoeher.
       Dass die Tabelle auf Papier auch wirklich hineinpasst, traegt nicht
       diese Regel, sondern die Satzbreite: A4 minus 2 x 16 mm. */
    .a-doc__tabelle { overflow: visible; }
    .a-doc__head { border-bottom-color: #000; }
    article.a-doc :is(th, td) { border-bottom-color: #999; }
    .a-doc__totals { background: #f2f2f2; }
    article.a-doc thead { display: table-header-group; }
    article.a-doc tr { break-inside: avoid; }
    .a-doc__sign,
    .a-doc__foot { break-inside: avoid; }

    /* DER DOKUMENTTEXT BRICHT AUCH AUF PAPIER NICHT SELBST UM (K8-1).
       `white-space: pre` gilt hier weiter — was der Leser auf dem Ausdruck
       sieht, sind die Zeilen des Dokuments und keine, die der Drucker gesetzt
       hat.

       Damit sie in die Satzbreite passen, ist die Schrift kleiner gesetzt.
       Gerechnet: A4 minus 2 × 16 mm Rand = 178 mm Satzbreite. Gemessen am
       echten Browser sind 96 Zeichen Martian Mono bei 14 px 941 px breit, also
       0,70 em je Zeichen; bei 7,2 pt (= 9,6 px) ergeben 96 Spalten 645 px
       = 171 mm und bleiben damit innerhalb der Satzbreite. Der Vertragstext
       ist bei 96 Spalten gesetzt (`absatz()`), und seit E-15 ist 96 auch die
       Breite, in der fremde Werte gehalten werden (`BLATT_SPALTEN`) — 96 ist
       also der Fall, der passen muss.

       ⚠️ BEFUND NR-5 — HIER STAND EIN SATZ, DER MEHR ZUSAGTE, ALS DIESE REGEL
       LEISTET: „`overflow: visible`, weil ein Rollbereich auf Papier den Rest
       abschneidet statt ihn zu zeigen."

       `visible` schneidet EBENFALLS ab — nur an der Blattkante statt an der
       Kastenkante, und ohne Rollbalken, der es verriete. Gemessen: sechs
       eigene Zeilen des Abschlussblattes waren 101 bis 124 Zeichen breit, vier
       davon liefen ueber die Kante des Papiers hinaus; verloren gingen zwei
       SHA-256-Summen und ein Fingerabdruck.

       `visible` bleibt trotzdem richtig, aber aus einem anderen Grund: es ist
       die einzige Einstellung, unter der eine zu breite Zeile ueberhaupt
       AUFFALLEN kann. Was diese Regel traegt, ist nicht sie selbst, sondern die
       Rechnung darueber PLUS die Zusage der Fachschicht, dass keine Zeile
       breiter als 96 Spalten wird — auch keine eigene (`eigenzeile()`,
       gemessen in `tests/fachlich/beweisblatt-passt-aufs-papier`). */
    .a-doc__text {
        font-size: 7.2pt;
        line-height: 1.45;
        overflow: visible;
    }

    /* Was auf Papier nichts zu suchen hat: die Bedienleiste und ALLES
       Interne. Der Deckungsbeitrag steht in keinem Kundendokument. */
    .a-printbar,
    .a-intern,
    .a-nav,
    .a-bar { display: none !important; }
}

/* ============================================================ 10 · SUPPORT
   Der Supportbereich des KUNDENPORTALS (Bauanleitung 21 § 7).

   Keine neuen Farben, keine neuen Radien, keine neue Schrift: alles kommt aus
   den Kennwerten in Abschnitt 1. Beide Themen werden damit gleichwertig
   bedient — jede Variable hier ist in hell UND dunkel gesetzt.

   Zwei Dinge, die diese Abteilung bewusst NICHT tut:
     · Sie kennt kein Rot fuer „ueberfaellig". Der Wartungsvertrag schliesst
       zugesicherte Service-Level aus (§ 5 Abs. 3); eine Farbe, die eine
       Fristverletzung behauptet, waere eine Aussage, die vertraglich nicht
       geschuldet ist.
     · Der Zustandspunkt traegt die Bedeutung nie allein. Neben ihm steht
       IMMER das Wort (WCAG 1.4.1); der Punkt ordnet nur schneller zu.        */

/* --------------------------------------------------------- Die Anfrageliste
   Eine Liste und keine Tabelle: eine Anfrage hat einen Betreff und eine
   Handvoll Angaben, keine Spalten, die man vergleicht.                       */
.a-anfrageliste {
    list-style: none;
    margin: 0;
    padding: 0;
}
.a-anfrage {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) auto;
    align-items: start;
    gap: var(--s-2);
    padding-block: 14px;
}
.a-anfrage + .a-anfrage { border-top: 1px solid var(--line); }
.a-anfrage a { text-decoration: none; }
.a-anfrage strong {
    display: block;
    color: var(--ink);
    font-weight: 600;
}
.a-anfrage a:hover strong { color: var(--akz); }

/* Die drei Zusatzzeilen unter dem Betreff, von leise nach laut. */
.a-anfrage-meta {
    display: block;
    margin-block-start: 3px;
    color: var(--ink-2);
    font: 400 var(--t-meta)/1.5 var(--f-mono);
    font-variant-numeric: tabular-nums;
}
.a-anfrage-nr { color: var(--ink-2); }
.a-anfrage-warte {
    display: block;
    margin-block-start: 4px;
    color: var(--sig-warn);
    font-size: var(--t-small);
}
.a-anfrage-zusage {
    display: block;
    margin-block-start: 4px;
    color: var(--ink-2);
    font-size: var(--t-small);
}
/* Das WORT. Es steht rechts und traegt die Bedeutung — nicht der Punkt. */
.a-anfrage-stand {
    align-self: center;
    color: var(--ink-2);
    font: 500 var(--t-meta)/1 var(--f-sans);
    white-space: nowrap;
}

.a-anfrage-punkt {
    flex: none;
    inline-size: 10px;
    block-size: 10px;
    margin-block-start: 7px;
    border-radius: 50%;
    background: var(--ink-3);
}
.a-anfrage-punkt--offen  { background: var(--sig-info); }
.a-anfrage-punkt--arbeit { background: var(--akz); }
.a-anfrage-punkt--wartet { background: var(--sig-warn); }
.a-anfrage-punkt--fertig { background: var(--sig-ok); }

/* Die abgeschlossene Gruppe ist gedaempft, nicht versteckt. */
.a-anfrage--offen .a-anfrage-stand { color: var(--ink-2); }
.a-anfrage--arbeit .a-anfrage-stand { color: var(--akz); }
.a-anfrage--wartet .a-anfrage-stand { color: var(--sig-warn); }
.a-anfrage--fertig .a-anfrage-stand { color: var(--sig-ok); }

@media (max-width: 640px) {
    .a-anfrage { grid-template-columns: auto minmax(0, 1fr); }
    .a-anfrage-stand { grid-column: 2; align-self: start; }
}

/* ------------------------------------------------------------- Aufklapper
   `<details>` statt eines Schalters: geht ohne eine Zeile JavaScript auf und
   zu, und die Zurueck-Taste laesst es in Ruhe.                              */
.a-aufklapp { margin-block-start: var(--s-2); }
.a-aufklapp > summary {
    cursor: pointer;
    padding-block: 8px;
    color: var(--akz);
    font: 500 var(--t-small)/1.4 var(--f-sans);
}
.a-aufklapp > summary:hover { text-decoration: underline; }

/* ------------------------------------------------------------- Artkarten
   Sieben grosse Klickflaechen mit einem Satz Erklaerung statt eines
   Auswahlfeldes. Auf der Stoerungskarte steht der Satz, der dieses Modul
   verkauft.                                                                  */
.a-artkarten {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    gap: var(--s-2);
}
@media (min-width: 700px) {
    .a-artkarten { grid-template-columns: 1fr 1fr; }
}
.a-artkarte {
    display: block;
    block-size: 100%;
    padding: var(--s-3);
    border: 1px solid var(--card-line);
    border-radius: var(--r-md);
    background: var(--card);
    box-shadow: var(--shadow-1);
    text-decoration: none;
    transition:
        border-color var(--in) var(--ease),
        box-shadow var(--in) var(--ease);
}
.a-artkarte:hover {
    border-color: var(--akz);
    box-shadow: var(--shadow-2);
}
.a-artkarte-titel {
    display: block;
    color: var(--ink);
    font: 600 var(--t-base)/1.35 var(--f-sans);
}
.a-artkarte-text {
    display: block;
    margin-block-start: 6px;
    color: var(--ink-2);
    font-size: var(--t-small);
    line-height: 1.6;
}

/* ----------------------------------------------------------- Formularteile */
.a-form { display: grid; gap: var(--s-3); }
.a-form textarea {
    inline-size: 100%;
    min-block-size: 8rem;
    padding: 10px 12px;
    border: 1px solid var(--card-line);
    border-radius: var(--r-sm);
    background: var(--card);
    color: var(--ink);
    font: 400 var(--t-small)/1.6 var(--f-sans);
    resize: vertical;
}
.a-feldtitel {
    display: block;
    margin-block-end: 6px;
    color: var(--ink);
    font: 600 var(--t-small)/1.4 var(--f-sans);
}
.a-fieldset {
    margin: 0;
    padding: var(--s-2);
    border: 1px solid var(--card-line);
    border-radius: var(--r-sm);
    background: var(--card-2);
}
.a-fieldset legend {
    padding-inline: 6px;
    color: var(--ink);
    font: 600 var(--t-small)/1.4 var(--f-sans);
}
.a-wahl {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    margin-inline-end: var(--s-3);
    min-block-size: 40px;
    color: var(--ink);
    font-size: var(--t-small);
    cursor: pointer;
}
/* Traegt die Wahl einen Erklaersatz, ist sie mehrzeilig — dann gehoert der
   Knopf an die ERSTE Zeile und nicht in die Mitte eines fuenfzeiligen Blocks
   (Befund K2-1, zweite Haelfte). Die einzeiligen Wahlen („Ja"/„Nein" im
   Kundenportal) behalten `center`; deshalb steht die Bedingung am Vorhanden-
   sein des Satzes und nicht als neue Vorgabe fuer alle. */
.a-wahl:has(.a-field__desc) {
    align-items: start;
    padding-block: 6px;
}
/* Der Rumpf der Wahl ist ein Flex-Kind und damit ohnehin ein Block; die
   Breitenangabe verhindert nur, dass zwei Wahlen mit langem Satz sich in
   EINE Zeile zwaengen und dort auf je 200 px zusammenschrumpfen. */
.a-wahl:has(.a-field__desc) > span:last-child { flex: 1 1 auto; min-inline-size: 0; }
.a-fieldset:has(.a-wahl .a-field__desc) .a-wahl { display: flex; margin-inline-end: 0; }
.a-anhangfelder { display: grid; gap: var(--s-1); }
/* Der Satz zu Gesundheits-, Bank- und Ausweisdaten. Ruhig, aber vorhanden. */
.a-datenhinweis {
    margin: 0;
    padding: var(--s-2);
    border-inline-start: 3px solid var(--line-ui);
    color: var(--ink-2);
    font-size: var(--t-small);
    line-height: 1.6;
}
.a-hinweiszeile {
    margin: 0;
    color: var(--ink-2);
    font-size: var(--t-small);
    line-height: 1.6;
}

/* ------------------------------------------------------------ Das Gespraech
   Aelteste Zeile oben, neueste unten. Kunde und DRB unterscheiden sich durch
   FLAECHE und AUSRICHTUNG, nicht nur durch Farbe.                            */
.a-anfrage-raster { display: grid; gap: var(--s-3); }
@media (min-width: 900px) {
    .a-anfrage-raster {
        grid-template-columns: minmax(0, 1fr) 20rem;
        align-items: start;
    }
}

.a-gespraech {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    gap: var(--s-2);
}
.a-beitrag {
    max-inline-size: 44rem;
    padding: var(--s-2) var(--s-3);
    border: 1px solid var(--card-line);
    border-radius: var(--r-md);
    background: var(--card-2);
}
.a-beitrag p { margin-block: 0 0.6em; line-height: 1.65; }
.a-beitrag p:last-child { margin-block-end: 0; }
.a-beitrag-kopf {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--s-1);
    margin-block-end: 8px;
    color: var(--ink-2);
    font: 400 var(--t-meta)/1.4 var(--f-mono);
}
.a-beitrag-kopf strong { color: var(--ink); font-weight: 600; }
/* Der Beitrag des Kunden steht rechts — wie in jedem Nachrichtenverlauf. */
.a-beitrag--kunde {
    margin-inline-start: auto;
    background: var(--tint-akz);
    border-color: var(--akz);
}
.a-beitrag--drb { background: var(--card); }
.a-beitrag--system { background: var(--card-2); border-style: dashed; }
/* Steht ausschliesslich im Adminportal. Im Kundenportal wird eine interne
   Zeile gar nicht erst geladen — nicht ausgeblendet, sondern nicht geladen. */
.a-beitrag--intern {
    border-inline-start: 3px solid var(--sig-warn);
    background: var(--wash-warn);
}

/* Die schmale graue Zwischenzeile aus `support_events`. */
.a-verlaufszeile {
    padding-inline: var(--s-2);
    color: var(--ink-2);
    font-size: var(--t-small);
    line-height: 1.6;
    text-align: center;
}
.a-verlaufszeile span {
    font-family: var(--f-mono);
    font-variant-numeric: tabular-nums;
}

/* -------------------------------------------------------------- Anhaenge */
.a-anhangliste { list-style: none; margin: var(--s-1) 0 0; padding: 0; }
.a-anhang {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: var(--s-1);
    padding-block: 6px;
}
.a-anhang + .a-anhang { border-top: 1px solid var(--line); }
.a-anhang-meta {
    color: var(--ink-2);
    font: 400 var(--t-meta)/1 var(--f-mono);
    font-variant-numeric: tabular-nums;
}

/* ------------------------------------------------------- Kostenvoranschlag
   Ein eigener, umrandeter Block — KEIN Gespraechsbeitrag. Er ist der Punkt,
   an dem sonst Streit entsteht, und sieht deshalb anders aus als alles
   andere auf der Seite.                                                      */
.a-kv {
    padding: var(--s-3);
    border: 2px solid var(--akz);
    border-radius: var(--r-md);
    background: var(--tint-akz);
    /* Der Kasten ist der MASSSTAB fuer seine Zeilen (Befund AB-1, Begruendung
       an `.a-kv-zeile`). Er steht im Kundenportal in einer breiten Flaeche und
       im Support in einer schmalen Spalte; welche Aufteilung lesbar ist, haengt
       an SEINER Breite und nicht an der des Fensters. */
    container-type: inline-size;
}
.a-kv h3 {
    margin: 0 0 6px;
    color: var(--ink);
    font: 600 var(--t-base)/1.35 var(--f-sans);
}
.a-kv-umfang { margin: 0 0 var(--s-2); color: var(--ink); line-height: 1.65; }
/* Beschriftung und Wert.

   ═══════════════════════════════════════════════════════════════════════════
   ⚠️ BEFUND AB-1 — DIESE REGEL HAT DIE PFLICHTANGABEN NACH § 312j ABS. 2 BGB
   UNLESBAR GEMACHT
   ═══════════════════════════════════════════════════════════════════════════
   Sie stand auf `grid-template-columns: minmax(0, 1fr) auto`. Die Wertspalte
   (`auto`) waechst bis zu ihrem `max-content`; besteht der Wert aus ganzen
   Saetzen, nimmt sie die volle Breite — und fuer die Beschriftung bleibt
   NICHTS uebrig. Am Bildschirm gemessen: `dt` 0 px breit, „Was Sie bestellen"
   in 15 Sichtzeilen, ein Buchstabe je Zeile. Bei 390, 768 UND 1440 px.

   Im Kostenvoranschlag ist es nie aufgefallen, weil dort Minuten und Betraege
   stehen. Es ist derselbe Fehler wie K2-1: eine Klasse, die an ihrer ersten
   Stelle harmlos ist und an der zweiten den Text zerlegt. Und wie dort wird er
   AN DER REGEL behoben und nicht an der Fundstelle — sonst wiederholt er sich
   beim naechsten Kasten.

   Die Regel entscheidet nach dem PLATZ, den der Kasten hat:
     · schmal → gestapelt, Beschriftung und Wert je ueber die volle Breite,
       linksbuendig. Bei 390 px sind das 272 statt 0 bzw. 112 px.
     · breit  → die Beschriftung so breit wie ihre Woerter (`auto`), der Rest
       fuer den Wert; `minmax(12ch, 1fr)` ist der Boden, damit nicht die
       Wertspalte zusammenfaellt, wenn die Beschriftung einmal lang ist.

   ⚠️ `@container` UND NICHT `@media`: derselbe Kasten steht im Kundenportal in
   einer breiten Flaeche und im Support in einer schmalen Spalte. Ein
   Fenstermass beantwortet die Frage nicht, die hier gestellt ist.

   Gemessen in `tests/fachlich/pflichtangaben-sind-lesbar` — mit Nullprobe
   gegen genau diese alte Aufteilung. */
.a-kv-zeile {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: 4px var(--s-2);
    margin: 0 0 var(--s-2);
    font-size: var(--t-small);
}
.a-kv-zeile dt { color: var(--ink-2); }
.a-kv-zeile dd {
    margin: 0;
    color: var(--ink);
    font-family: var(--f-mono);
    font-variant-numeric: tabular-nums;
    /* Gestapelt beginnt der Wert links unter seiner Beschriftung. Rechtsbuendig
       waere er dort ein Block, der an beiden Kanten ausfranst. */
    text-align: start;
}
/* Ein Wert, der nicht der letzte ist, braucht Abstand zur naechsten
   Beschriftung — sonst laufen im Kostenvoranschlag drei Paare ineinander. */
.a-kv-zeile dd + dt { margin-block-start: 6px; }
@container (min-width: 30rem) {
    .a-kv-zeile { grid-template-columns: auto minmax(12ch, 1fr); }
    /* Nebeneinander steht der Wert am rechten Rand seiner Spalte — so stehen
       die Betraege des Kostenvoranschlags untereinander bündig. */
    .a-kv-zeile dd { text-align: end; }
    .a-kv-zeile dd + dt { margin-block-start: 0; }
}
.a-kv-summe {
    margin: 0;
    color: var(--ink);
    font: 700 1.5rem/1.2 var(--f-sans);
    font-variant-numeric: tabular-nums;
}
.a-kv-hinweis {
    margin: 6px 0 0;
    color: var(--ink-2);
    font-size: var(--t-small);
    line-height: 1.6;
}
.a-kv-knopf { margin-block-start: var(--s-2); display: grid; gap: var(--s-2); }

/* ------------------------------------------------------------- Kontingent
   Ein Balken, eine Zahl in Klartext, ein Verfallshinweis. Kein Prozentsatz:
   ein Prozentsatz ist eine Bewertung, eine Minutenzahl eine Auskunft.        */
.a-kontingent { display: grid; gap: var(--s-2); }
.a-kontingent-balken {
    block-size: 12px;
    border-radius: 999px;
    background: var(--card-2);
    border: 1px solid var(--card-line);
    overflow: hidden;
}
.a-kontingent-balken__fuellung {
    display: block;
    block-size: 100%;
    background: var(--akz);
}
.a-kontingent-rest {
    margin: 8px 0 0;
    color: var(--ink);
    font-size: var(--t-small);
    line-height: 1.6;
}
.a-kontingent-zusage {
    margin: 4px 0 var(--s-2);
    color: var(--ink-2);
    font-size: var(--t-small);
    line-height: 1.6;
}

/* -------------------------------------------------------- Die Zeitbuchungen
   Jede Zeile mit ihrer PFLICHTNOTIZ. Wer die Notiz sieht, streitet nicht
   ueber die Minute.                                                          */
.a-zeitliste { list-style: none; margin: 0; padding: 0; }
.a-zeitzeile {
    display: grid;
    grid-template-columns: 5rem minmax(0, 1fr) auto;
    align-items: baseline;
    gap: 4px var(--s-2);
    padding-block: 12px;
}
.a-zeitzeile + .a-zeitzeile { border-top: 1px solid var(--line); }
.a-zeitzeile--strittig {
    border-inline-start: 3px solid var(--sig-warn);
    padding-inline-start: var(--s-2);
    background: var(--wash-warn);
}
.a-zeitzeile-min {
    color: var(--ink);
    font: 600 var(--t-small)/1.4 var(--f-mono);
    font-variant-numeric: tabular-nums;
}
.a-zeitzeile-notiz { color: var(--ink); font-size: var(--t-small); line-height: 1.6; }
.a-zeitzeile-nr {
    margin-inline-start: 8px;
    color: var(--ink-2);
    font: 400 var(--t-meta)/1 var(--f-mono);
}
.a-zeitzeile-art {
    color: var(--ink-2);
    font-size: var(--t-meta);
    white-space: nowrap;
}
.a-zeitzeile-tag {
    grid-column: 2;
    color: var(--ink-2);
    font: 400 var(--t-meta)/1 var(--f-mono);
    font-variant-numeric: tabular-nums;
}
.a-zeitzeile-strittig {
    grid-column: 1 / -1;
    color: var(--sig-warn);
    font-size: var(--t-small);
}
.a-zeitzeile .a-aufklapp { grid-column: 1 / -1; margin-block-start: 0; }

@media (max-width: 640px) {
    .a-zeitzeile { grid-template-columns: 4.5rem minmax(0, 1fr); }
    .a-zeitzeile-art { grid-column: 2; }
}

/* Was das Paket ausserdem enthaelt — die Antwort auf „18 von 30 Minuten". */
.a-leistungsliste {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    gap: 6px;
    color: var(--ink-2);
    font-size: var(--t-small);
    line-height: 1.6;
}
.a-leistungsliste li { padding-inline-start: 1.2em; position: relative; }
.a-leistungsliste li::before {
    content: "";
    position: absolute;
    inset-inline-start: 0;
    inset-block-start: 0.55em;
    inline-size: 6px;
    block-size: 6px;
    border-radius: 50%;
    background: var(--sig-ok);
}


/* ================================================ 11 · SUPPORT (ADMINPORTAL)
   Warteschlange und Anfrage-Akte von `/a/support` (Bauanleitung 21 §§ 7.5, 7.6).

   Abschnitt 10 gehoert dem KUNDENPORTAL. Was dort schon steht — `.a-gespraech`,
   `.a-beitrag*`, `.a-kontingent*`, `.a-kv*`, `.a-zeitzeile*` — wird hier
   WIEDERVERWENDET und ausdruecklich nicht noch einmal definiert: dieselbe
   Sache soll auf beiden Bildschirmen gleich aussehen, und zwei Definitionen
   derselben Klasse laufen beim naechsten Umbau auseinander. Neu ist nur, was
   es im Kundenportal nicht gibt.

   Zwei Leitgedanken:
     1. DICHT, NICHT SCHOEN. Deniz arbeitet das zwischen zwei Terminen ab, oft
        am Telefon. Es zaehlt, wie viele Zeilen ohne Scrollen lesbar sind.
     2. DIE INTERNE NOTIZ MUSS ANDERS AUSSEHEN, nicht anders beschriftet sein.
        Die Verwechslung „intern gegen an den Kunden" ist der teuerste Fehler
        des Moduls; sie wird durch Flaeche, Kante und Randlinie verhindert und
        nicht durch Aufmerksamkeit.

   Keine neuen Farben, keine neuen Radien: alles aus den Kennwerten in
   Abschnitt 1, damit beide Themen gleichwertig bedient sind.                 */

/* -------------------------------------------------------- Der Lage-Streifen
   Vier Zahlen ueber der Liste, kein Diagramm. Sie beantworten die einzige
   Frage, die zwischen zwei Terminen zaehlt: muss ich jetzt etwas tun?        */
.a-lage {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
    gap: var(--s-1);
    margin: 0 0 var(--s-3);
}
.a-lage__zelle {
    min-width: 0;
    padding: var(--s-2);
    background: var(--card);
    border: 1px solid var(--card-line);
    border-radius: var(--r-md);
    box-shadow: var(--shadow-1);
}
.a-lage dt {
    font: 500 var(--t-meta)/1.4 var(--f-sans);
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--ink-2);            /* 7.76 hell · 7.20 dunkel (auf --card) */
}
.a-lage dd {
    margin: 4px 0 0;
    font: 600 clamp(1.25rem, 1.05rem + 0.7vw, 1.625rem)/1.1 var(--f-sans);
    font-variant-numeric: tabular-nums slashed-zero;
    color: var(--ink);              /* 17.10 hell · 15.10 dunkel */
    overflow-wrap: normal;
}

/* ------------------------------------------------------------ Die Restzeit
   „in 40 min", „seit 2 h überfällig". Die Farbe kommt ZUSAETZLICH zum Wort;
   der Text sagt dasselbe wie der Farbton (WCAG 1.4.1). Der Kunde sieht diese
   Stufen nie — der Wartungsvertrag schliesst zugesicherte Service-Level aus
   (§ 5 Abs. 3), und eine Ampel behauptete genau das.                         */
.a-restzeit {
    font: 500 var(--t-small)/1.2 var(--f-mono);
    font-variant-numeric: tabular-nums slashed-zero;
    white-space: nowrap;
    color: var(--ink-2);
}
.a-restzeit--gelb   { color: var(--sig-warn); }   /* 5.82 hell · 5.31 dunkel */
.a-restzeit--orange { color: var(--sig-bad); }    /* 6.42 hell · 5.94 dunkel */
.a-restzeit--rot    { color: var(--sig-bad); font-weight: 700; }

/* ---------------------------------------------------------------- Die Akte
   Eine Spalte auf dem Telefon, zwei ab 1100 px. Die Kontextspalte steht im
   DOM HINTER dem Gespraech: wer mit der Tastatur arbeitet, liest erst, worum
   es geht, und handelt dann.                                                 */
.a-akte {
    display: grid;
    gap: var(--s-3);
    grid-template-columns: minmax(0, 1fr);
}
.a-akte__haupt { min-width: 0; }
@media (min-width: 1100px) {
    .a-akte { grid-template-columns: minmax(0, 1fr) 25rem; align-items: start; }
}

/* ----------------------------------------------------------- Kontextspalte */
.a-kontext {
    display: grid;
    gap: var(--s-2);
    min-width: 0;
}
.a-kontext-block {
    min-width: 0;
    padding: var(--s-2);
    background: var(--card);
    border: 1px solid var(--card-line);
    border-radius: var(--r-md);
    box-shadow: var(--shadow-1);
}
.a-kontext-block > h2 {
    margin: 0 0 var(--s-1);
    font: 600 var(--t-small)/1.3 var(--f-sans);
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--ink-2);
}
/* In der schmalen Spalte gibt es keine Lesebreite zu halten. */
.a-kontext-block .a-form { gap: var(--s-2); max-width: none; }

/* ------------------------------------------------------ Antwort oder Notiz */
.a-modus {
    display: flex;
    flex-wrap: wrap;
    gap: var(--s-1);
    margin: 0;
    padding: 0;
    border: 0;
}
.a-modus legend {
    flex-basis: 100%;
    float: none;
    margin-block-end: 6px;
    padding: 0;
}
.a-modus__wahl {
    display: flex;
    flex: 1 1 15rem;
    gap: 10px;
    align-items: flex-start;
    min-width: 0;
    padding: 10px 12px;
    background: var(--card);
    border: 1px solid var(--line-ui);   /* Bedienkante: 3.62:1 */
    border-radius: var(--r-sm);
    cursor: pointer;
}
.a-modus__wahl > span { display: grid; gap: 2px; min-width: 0; }
.a-modus__wahl:hover { border-color: var(--akz); }
.a-modus__wahl--an { border-color: var(--akz); background: var(--tint-akz); }

/* Das Schreibfeld. Im internen Zustand traegt es dieselbe Warntoenung wie der
   gespeicherte interne Beitrag (`.a-beitrag--intern`) — dieselbe Sache,
   dieselbe Farbe. Toenung 8 % Warnfarbe auf der Karte:
   #F7F3E9 hell · #211D15 dunkel; Text darauf 15.72 hell · 12.71 dunkel.      */
.a-schreibfeld { display: grid; gap: var(--s-1); }
.a-schreibfeld--intern {
    padding: var(--s-2);
    background: color-mix(in oklab, var(--sig-warn) var(--tint-soft), var(--card));
    border: 1px solid color-mix(in oklab, var(--sig-warn) 40%, var(--card-line));
    border-radius: var(--r-md);
}
.a-schreibfeld__marke {
    margin: 0;
    font: 600 var(--t-meta)/1.5 var(--f-sans);
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--sig-warn);         /* 5.82 auf --card */
}

/* Die Vorschau. Gestrichelte Kante, weil hier nichts gespeichert wird — sie
   zeigt, was beim Kunden ankommt.                                            */
.a-vorschau {
    display: grid;
    gap: var(--s-1);
    padding: var(--s-2);
    background: var(--card-2);
    border: 1px dashed var(--line-ui);
    border-radius: var(--r-md);
}

/* --------------------------------------------- Kostenvoranschlaege der Akte
   Die Liste der bisherigen Voranschlaege. Der EINZELNE Voranschlag benutzt
   `.a-kv` aus Abschnitt 10 — derselbe Kasten wie beim Kunden.                */
.a-kv-liste {
    display: grid;
    gap: var(--s-1);
    margin: 0 0 var(--s-2);
    padding: 0;
    list-style: none;
    font-size: var(--t-small);
    line-height: 1.5;
}
.a-kv-liste > li + li {
    padding-block-start: var(--s-1);
    border-block-start: 1px solid var(--line);
}

/* ================================================ Der Vergleich der Fassungen
   Konzept 23 § 6 — ein Angebot, das in mehreren Fassungen nebeneinander steht.

   ═══════════════════════════════════════════════════════════════════════════
   DREI ENTSCHEIDUNGEN, UND JEDE HAT EINEN GRUND
   ═══════════════════════════════════════════════════════════════════════════
   1. GLEICHE ZEILEN, GLEICHE REIHENFOLGE, GLEICHE HOEHE. Nur so lassen sich
      die Unterschiede LESEN. Die Karten liegen deshalb in einem Raster mit
      `align-items: stretch`, und die Zeilen einer Karte stehen in derselben
      Ordnung wie die der Nachbarkarte (das haelt `Fassungsspalte`, nicht dieses
      Stylesheet: dort ist festgelegt, dass jede Fassung ALLE vier Summenzeilen
      traegt, auch wenn ein Betrag 0,00 € ist).

   2. DER PREIS STEHT GANZ OBEN. § 6 PAngV verlangt die Gesamtkosten; auf einem
      Telefon liegen die Karten UNTEREINANDER, und dann ist das Erste, was der
      Daumen erreicht, der Preis — nicht eine Ueberschrift und nicht ein Absatz.

   3. `auto-fit` UND `minmax(17rem, 1fr)` STATT EINER SPALTENZAHL. Es gibt zwei,
      drei oder vier Fassungen; eine feste Zahl waere bei jeder anderen falsch.
      17rem ist die Breite, unter der die Betragszeile („Mindestlaufzeit" +
      „24 Monate") umbricht — darunter faellt das Raster von selbst auf eine
      Spalte, und dann ist es die Telefonansicht.

   ⚠️ KEINE FARBE ALS EINZIGES MERKMAL (WCAG 1.4.1). Die empfohlene Fassung
   traegt eine staerkere Kante UND einen Satz im Klartext. Wer die Kante nicht
   sieht, liest den Satz.                                                      */
.a-vergleich {
    display: grid;
    /* ⚠️ `min(17rem, 100%)` UND NICHT `17rem` — GEMESSEN BEI 320 px. Mit dem
       festen Mass behaelt die Spalte ihre 272 px auch dann, wenn die Huelle
       schmaler ist; zusammen mit dem Innenabstand der Karte rollte die ganze
       Seite waagerecht. `min()` laesst die Spalte unter das Mass fallen, sobald
       es nicht mehr passt. */
    grid-template-columns: repeat(auto-fit, minmax(min(17rem, 100%), 1fr));
    gap: var(--s-2);
    align-items: stretch;
    margin: var(--s-3) 0 0;
    padding: 0;
    list-style: none;
}
.a-vergleich__karte {
    display: flex;
    flex-direction: column;
    gap: var(--s-2);
    min-inline-size: 0;
    padding: var(--s-3);
    border: 1px solid var(--card-line);
    border-radius: var(--r-md);
    background: var(--card-2);
}
/* ═══════════════════════════════════════════════════════════════════════════
   ⚠️ SUBGRID — OHNE DAS IST ES EINE LISTE UND KEIN VERGLEICH
   ═══════════════════════════════════════════════════════════════════════════
   GEMESSEN: Die Bezeichnung „Hälftig — 50 % bei Auftrag, 50 % bei Abnahme"
   braucht zwei Zeilen, „Sofort bezahlen" eine; die empfohlene Karte traegt
   ausserdem eine Zeile mehr im Kopf. Damit begann die Betragsliste in jeder
   Karte auf einer anderen Hoehe — „Einmalig" der einen stand neben „Monatlich"
   der anderen. Wer so vergleicht, vergleicht Verschiedenes.

   Die vier Baugruppen jeder Karte (Kopf, Preisblock, Zeilen, Fuss) liegen
   deshalb in den REIHEN DES AEUSSEREN RASTERS: jede Reihe ist so hoch wie ihre
   hoechste Karte, und die Baugruppen stehen nebeneinander auf gleicher Hoehe.

   ⚠️ ES SIND IMMER VIER, auch wenn eine leer ist (`:empty` weiter unten) — bei
   wechselnder Anzahl waere `span 4` falsch, und die Karten verrutschten
   gegeneinander.

   Ohne `subgrid` (aeltere Browser) bleibt die Karte ein Flexstapel: sie sieht
   dann aus wie vorher und ist lesbar, nur nicht zeilenweise buendig. Das ist
   der Rueckfall und keine Zusage.                                             */
@supports (grid-template-rows: subgrid) {
    .a-vergleich { grid-template-rows: auto auto auto auto; }
    .a-vergleich__karte {
        display: grid;
        grid-row: span 4;
        grid-template-rows: subgrid;
    }
}
/* Die Empfehlung: zwei Millimeter Akzentkante und eine getoente Flaeche —
   dieselbe Sprache wie beim Pflichtangabenkasten, damit „hier schaut hin"
   ueberall gleich aussieht. */
.a-vergleich__karte--tipp {
    border: 2px solid var(--akz);
    background: var(--tint-akz);
}
.a-vergleich__kopf { min-inline-size: 0; }
.a-vergleich__name {
    margin: 0;
    color: var(--ink);
    font: 600 var(--t-base)/1.35 var(--f-sans);
}
.a-vergleich__marke {
    margin: 4px 0 0;
    color: var(--akz);
    font: 600 var(--t-meta)/1.4 var(--f-sans);
}
/* Der Gesamtbetrag. Er ist die groesste Zahl auf der Karte und steht ueber
   seiner Beschriftung, nicht darunter: gelesen wird zuerst die Zahl. */
.a-vergleich__preisblock { min-inline-size: 0; }
.a-vergleich__preis {
    margin: 0;
    color: var(--ink);
    font: 700 1.75rem/1.15 var(--f-sans);
    font-variant-numeric: tabular-nums;
    overflow-wrap: anywhere;
}
.a-vergleich__preislabel {
    margin: 2px 0 0;
    color: var(--ink-2);
    font-size: var(--t-small);
    line-height: 1.4;
}
.a-vergleich__zeilen {
    margin: 0;
    padding-block-start: var(--s-2);
    border-block-start: 1px solid var(--line);
    font-size: var(--t-small);
}
/* Beschriftung links, Wert rechts — und bei wenig Platz untereinander.
   DIESELBE ENTSCHEIDUNG WIE BEI `.a-kv-zeile` (Befund AB-1): eine
   `auto`-Wertspalte nimmt bei einem langen Wert die ganze Breite und laesst
   der Beschriftung nichts. Hier steht deshalb `minmax(0, auto)` fuer die
   Beschriftung und der Rest fuer den Wert. */
.a-vergleich__zeile {
    display: grid;
    grid-template-columns: minmax(0, auto) minmax(6ch, 1fr);
    gap: 2px var(--s-2);
    margin: 0 0 6px;
}
.a-vergleich__zeile dt { margin: 0; color: var(--ink-2); }
.a-vergleich__zeile dd {
    margin: 0;
    color: var(--ink);
    font-family: var(--f-mono);
    font-variant-numeric: tabular-nums;
    text-align: end;
    overflow-wrap: anywhere;
}
/* Ein ganzer Satz (die Zahlungsweise) gehoert nicht rechtsbuendig und nicht in
   die Festbreitenschrift — er ist Sprache und keine Zahl. Er steht als letzte
   Zeile, damit sein Umbruch die Zeilen darueber nicht gegeneinander
   verschiebt. */
.a-vergleich__satz {
    font-family: var(--f-sans);
    text-align: start;
    line-height: 1.5;
}
/* Der Fuss steht unten — im Flexrueckfall durch `margin-block-start: auto`,
   im Raster durch seine eigene Reihe. */
.a-vergleich__fuss { margin-block-start: auto; min-inline-size: 0; }
.a-vergleich__pruef {
    margin: 0;
    color: var(--ink-2);
    font-size: var(--t-meta);
    line-height: 1.5;
}
.a-vergleich__erloschen {
    margin: var(--s-2) 0 0;
    padding-block-start: var(--s-2);
    border-block-start: 1px solid var(--line);
    color: var(--ink-2);
    font-size: var(--t-small);
    line-height: 1.5;
}
/* Eine Karte ohne Erloeschenshinweis traegt ihn trotzdem als leeres Feld —
   sonst haetten die Karten verschieden viele Baugruppen, und `span 4` waere
   falsch. Ohne Inhalt nimmt es keinen Platz. */
.a-vergleich__erloschen:empty {
    margin: 0;
    padding: 0;
    border: 0;
}
