/* ============================================================================
 * wawi.css -- Das Erscheinungsbild. Farben stehen NICHT hier, sondern in
 * wawi-farben.css; hier faellt kein einziger Farbwert.
 *
 * Reihenfolge im Layout: bootstrap.min.css, wawi-farben.css, wawi.css.
 * Bootstrap liefert den Unterbau (Reboot, Raster, Utilities, das bisschen
 * JavaScript). Das Aussehen bestimmt diese Datei -- sie legt sich als
 * eigene Schicht darueber, damit Wawi nach Wawi aussieht und nicht nach
 * "irgendein Bootstrap-Projekt".
 *
 * ABER: _LayoutKunde LAEDT KEIN BOOTSTRAP -- absichtlich, seit es die
 * anmeldefreien Seiten gibt. Auf acht Seiten steht diese Datei also
 * ALLEIN. Was sie an Bootstrap-Unterbau voraussetzt, gibt es dort nicht,
 * und am 17.09.2026 hat genau das drei Seiten still beschaedigt
 * (gestaltung.md 3 und 9.17).
 *
 *   Wer einem Layout ein Stilblatt wegnimmt, nimmt ihm auch, was darin
 *   an Grundregeln mitkam.
 *
 * DESHALB GILT HIER: Diese Datei traegt sich selbst. Bootstrap ist
 * Zugabe, nicht Voraussetzung.
 *
 * Die Bootstrap-Klassennamen (.btn,
 * .form-control, .table, .alert) sind deshalb hier mitdefiniert: Die
 * Seiten benutzen sie bereits, und ein Zwang, 20 Dateien umzuschreiben,
 * waere der teuerste Teil der Uebung ohne jeden Gewinn.
 *
 * Aufbau:
 *   1. Masse       Raum, Rundung, Schrift, Dichte
 *   2. Grundlagen  Seite, Text, Verweise, Fokus
 *   3. Geruest     Kopf, Seitenleiste, Tableiste, Inhalt
 *   4. Bauteile    Knopf, Feld, Marke, Karte, Liste, Meldung
 *   5. Fachliches  Zustaende, Alterung, Summen, Belege
 *   6. Hilfsklassen
 *
 * Verbindliche Erlaeuterung: docs/gestaltung.md
 * ========================================================================= */

/* Die eine Regel, die diese Datei selbsttragend macht -- und die einzige
   hier, die nicht dem Aussehen dient, sondern dem Rechnen.

   SIE STAND NUR IN BOOTSTRAP, und deshalb galt sie auf den acht
   anmeldefreien Seiten nicht. Weiter unten setzt diese Datei an JEDEM
   Textfeld width: 100% -- ohne den Reset ragt dort jedes Feld um
   Polsterung und Rand ueber seinen Behaelter hinaus. Gemessen auf
   /Groesse: Blatt 327 breit, Feld 353, Kante bei 377 von 377, waehrend
   der Knopf darunter bei 351 endet.

   IM BETRIEB AENDERT SIE BEWEISBAR NICHTS: Dort steht dieselbe Regel
   schon in bootstrap.min.css. Nachgezaehlt auf der Personenakte -- 548
   Elemente, 0 davon nicht border-box. Eine identische Regel kann nichts
   verschieben. */
*, ::before, ::after { box-sizing: border-box; }

/* Die drei uebrigen Stellen, an denen diese Datei allein anders aussieht
   als mit Bootstrap darunter. Gemessen am Musterblatt, nicht geraten:
   Ohne Bootstrap bewegten sich dort 111 von 1310 Kanten, und ALLE gingen
   auf genau diese drei zurueck -- die Abschnitte, Karten und Figuren
   darunter wuchsen nur mit.

     h3    Zeilenhoehe 21,6 statt 27,9
     dt    Schriftstaerke 400 statt 700
     code  Schriftgroesse 17 statt 14,875, dazu eine andere Schriftart

   DIE WERTE SIND AUS bootstrap.min.css ABGELESEN UND NICHT GEWAEHLT.
   Was der Betrieb heute zeigt, soll er morgen zeigen -- eine schoenere
   Zahl waere eine Aenderung an jeder Maske, getarnt als Aufraeumen.

   UND SIE STEHEN GANZ OBEN, damit jede spaetere Regel dieser Datei sie
   schlaegt. Sie sind der Unterbau, nicht die Meinung. */
h1, h2, h3, h4, h5, h6 { line-height: 1.2; }
dt { font-weight: 700; }

/* Der Absatzabstand. Ohne ihn nimmt der Browser seine Vorgabe "1em oben
   und unten" -- und die WAECHST MIT DER SCHRIFTGROESSE: gemessen 18, 17,
   14 und 13 Punkte an ein und demselben Blatt, statt durchgehend 0 oben
   und 16 unten. Ein Raster, das mit der Schrift mitwandert, ist keines
   (gestaltung.md 3: alles sitzt auf 4px). */
p { margin-top: 0; margin-bottom: 1rem; }

/* Bedienelemente erben die Schrift NICHT von selbst -- das ist eine
   Eigenheit der Browser, kein Versehen der Seite. Ohne diese Zeile
   faellt jedes Feld und jeder Knopf, den keine eigene Regel trifft, auf
   Arial 13,33 zurueck: gemessen an 15 Feldern und 5 Knoepfen des
   Musterblatts.

   DIE GESTALTETEN FELDER WAREN NIE BETROFFEN -- die Feldregel weiter
   unten setzt die Schrift selbst. Genau deshalb faellt es nicht auf:
   Was man gebaut hat, sieht richtig aus; was man noch nicht gebaut hat,
   wird es nicht. */
button, input, optgroup, select, textarea {
    margin: 0; font-family: inherit; font-size: inherit; line-height: inherit;
}
code, kbd, samp, pre {
    font-family: SFMono-Regular, Menlo, Monaco, Consolas,
                 "Liberation Mono", "Courier New", monospace;
}
code, kbd, samp { font-size: 0.875em; }

/* Die Tastenmarke traegt ihr Polster selbst -- die Vorgabe des Browsers
   kennt keins, und ohne dieses hier schrumpfte "Enter" von 46 auf 34
   Punkte Breite. Farbe steht bewusst NICHT dabei: Die holt sich diese
   Datei aus den eigenen Rollen und nicht aus Bootstraps Variablen. */
kbd { padding: 0.1875rem 0.375rem; border-radius: 0.25rem; }
kbd kbd { padding: 0; font-size: 1em; }

/* Das Datumsfeld rechnet innen mit eigenem Polster, und das schob es um
   zwei Punkte hoeher als jedes andere Feld derselben Zeile. Zwei Punkte
   sind nichts -- eine Zeile, in der ein Feld anders hoch ist als seine
   Nachbarn, ist etwas. */
::-webkit-datetime-edit { display: block; padding: 0; }
::-webkit-datetime-edit-fields-wrapper,
::-webkit-datetime-edit-text,
::-webkit-datetime-edit-day-field,
::-webkit-datetime-edit-month-field,
::-webkit-datetime-edit-year-field,
::-webkit-datetime-edit-hour-field,
::-webkit-datetime-edit-minute { padding: 0; }

/* =========================================================== 1. MASSE ==== */

:root {
    /* Alles sitzt auf einem 4px-Raster. Zwischenwerte gibt es nicht --
       "mal eben 7px" ist der Anfang vom Ende jeder Ruhe im Bild. */
    --raum-1: 0.25rem;   /*  4px */
    --raum-2: 0.5rem;    /*  8px */
    --raum-3: 0.75rem;   /* 12px */
    --raum-4: 1rem;      /* 16px */
    --raum-5: 1.5rem;    /* 24px */
    --raum-6: 2rem;      /* 32px */
    --raum-7: 3rem;      /* 48px */
    --raum-8: 4rem;      /* 64px */

    --rund-1: 6px;       /* Marken, kleine Felder      */
    --rund-2: 10px;      /* Knoepfe, Felder, Karten    */
    --rund-3: 16px;      /* Grosse Flaechen, Blaetter  */
    --rund-voll: 999px;

    /* Systemschrift. Kein Webfont: Er kostet Ladezeit, blockiert das
       erste Bild und bringt bei einer Arbeitsanwendung nichts, was die
       Systemschrift nicht auch koennte. Auf jedem Geraet sieht Wawi damit
       aus wie ein Programm dieses Geraets. */
    --schrift: system-ui, -apple-system, "Segoe UI Variable Text", "Segoe UI",
               Roboto, "Helvetica Neue", Arial, sans-serif;

    /* Die Skala wurde am 28.08.2026 um eine Stufe angehoben: Am
       Schreibtisch war sie kaum lesbar. Was vorher der Normalfall war,
       ist jetzt die kompakte Stufe -- wer viele Zeilen aufs Bild bringen
       will, waehlt sie unter Darstellung. */
    --gr-xs:  0.8125rem;  /* 13px  Marken, Fusszeilen       */
    --gr-s:   0.875rem;   /* 14px  Beschriftungen, Hinweise */
    --gr-m:   1.0625rem;  /* 17px  Tabellen, Listen         */
    --gr-n:   1.125rem;   /* 18px  Fliesstext, Felder       */
    --gr-l:   1.25rem;    /* 20px  Abschnittstitel          */
    --gr-xl:  1.5625rem;  /* 25px  Seitentitel              */
    --gr-2xl: 2rem;       /* 32px  Kennzahlen               */

    /* Dichte. Standard ist "bequem" -- Finger, Baustelle, Handschuh.
       Wer im Buero mit Maus arbeitet, bekommt automatisch etwas mehr
       Zeilen aufs Bild (siehe Medienabfrage weiter unten). */
    --ziel: 2.75rem;           /* 44px, Mindestgroesse fuer Fingerziele */
    --feld-hoehe: 2.75rem;
    --zeile-y: 0.75rem;        /* senkrechtes Polster in Tabellenzeilen */

    --leiste: 15rem;           /* Breite der Seitenleiste */
    --inhalt-max: 76rem;       /* Lesebreite des Arbeitsbereichs */

    --wechsel: 140ms cubic-bezier(0.2, 0, 0.2, 1);
}

/* Zeigegeraet vorhanden: kleiner, dichter, mehr Zeilen sichtbar. */
@media (hover: hover) and (pointer: fine) {
    :root {
        --ziel: 2.25rem;
        --feld-hoehe: 2.25rem;
        --zeile-y: 0.5rem;
    }
}

/* Ausdrueckliche Wahl schlaegt beides. */
:root[data-dichte="bequem"]  { --ziel: 2.75rem; --feld-hoehe: 2.75rem; --zeile-y: 0.75rem; }
:root[data-dichte="kompakt"] { --ziel: 2rem;    --feld-hoehe: 2rem;    --zeile-y: 0.375rem; }

/* Schriftgroesse, drei Stufen, waehlbar unter Einstellungen. Die Stufen
   belegen nur die Groessen neu -- Abstaende, Rundungen und Zielgroessen
   bleiben, wo sie sind. Ein reiner Zoom ueber die Wurzelschrift waere
   kuerzer gewesen, haette aber auch die Seitenleiste und jeden Abstand
   mitgezogen; das ist keine Schriftgroesse mehr, sondern eine Lupe.
   Der Browser-Zoom kann das ohnehin besser.
                                        xs    s      m      n      l      xl     2xl
   kompakt                              12    13     15     16     18     22     28
   normal (die Werte oben)              13    14     17     18     20     25     32
   gross                                14    15     19     20     22     28     36

   --gr-n faellt auch in der kompakten Stufe NICHT unter 16 px:
   Eingabefelder haengen daran, und alles darunter loest in Safari auf dem
   iPhone beim Antippen einen Zoom aus, aus dem der Nutzer nicht mehr
   herausfindet. */
:root[data-schrift="kompakt"] {
    --gr-xs: 0.75rem;   --gr-s: 0.8125rem; --gr-m: 0.9375rem; --gr-n: 1rem;
    --gr-l: 1.125rem;   --gr-xl: 1.375rem; --gr-2xl: 1.75rem;
}
:root[data-schrift="gross"] {
    --gr-xs: 0.875rem;  --gr-s: 0.9375rem; --gr-m: 1.1875rem; --gr-n: 1.25rem;
    --gr-l: 1.375rem;   --gr-xl: 1.75rem;  --gr-2xl: 2.25rem;
}

@media (prefers-reduced-motion: reduce) {
    :root { --wechsel: 1ms; }
    *, *::before, *::after { animation-duration: 1ms !important; transition-duration: 1ms !important; }
}

/* ====================================================== 2. GRUNDLAGEN ==== */

html { -webkit-text-size-adjust: 100%; }

body {
    background: var(--grund);
    color: var(--tinte);
    font-family: var(--schrift);
    font-size: var(--gr-n);
    line-height: 1.55;
    margin: 0;
    min-height: 100dvh;
    -webkit-font-smoothing: antialiased;
}

h1, h2, h3 { font-weight: 600; letter-spacing: -0.011em; color: var(--tinte); }
h1 { font-size: var(--gr-xl); margin: 0 0 var(--raum-4); line-height: 1.25; }
h2 { font-size: var(--gr-l);  margin: 0 0 var(--raum-3); line-height: 1.3; }
h3 { font-size: var(--gr-n);  margin: 0 0 var(--raum-2); }

a { color: var(--akzent-tinte); text-decoration-color: color-mix(in oklab, currentColor 35%, transparent); text-underline-offset: 0.15em; }
a:hover { color: var(--akzent-stark); text-decoration-color: currentColor; }

/* Der Fokusring ist kein Schoenheitsfehler, sondern die einzige
   Orientierung fuer alle, die mit der Tastatur arbeiten -- im Buero sind
   das die schnellsten Nutzer. Er wird nirgends entfernt. */
:focus-visible {
    outline: 2px solid var(--fokus);
    outline-offset: 2px;
    border-radius: var(--rund-1);
}

::selection { background: var(--akzent-flaeche); color: var(--akzent-tinte); }

hr { border: none; border-top: 1px solid var(--linie); margin: var(--raum-5) 0; }

/* Sprungmarke: Wer mit der Tastatur kommt, will nicht durch die ganze
   Navigation tabben, um an den Inhalt zu gelangen. */
.sprung {
    position: absolute; left: var(--raum-3); top: -4rem; z-index: 100;
    background: var(--flaeche-hoch); color: var(--tinte);
    border: 1px solid var(--linie); border-radius: var(--rund-2);
    padding: var(--raum-2) var(--raum-4); box-shadow: var(--schatten-2);
    transition: top var(--wechsel);
}
.sprung:focus { top: var(--raum-3); }

/* ========================================================= 3. GERUEST ==== */

/* Der Aufbau ist auf jedem Geraet derselbe -- nur die Navigation wandert:
   am Telefon unten unter den Daumen, ab Tablet an die linke Kante. Zwei
   Anordnungen, ein Markup, keine zweite Wahrheit. */

.rahmen {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: auto 1fr;
    min-height: 100dvh;
}

/* Ohne diese zwei Zeilen laeuft die ganze Seite seitlich ueber, sobald
   irgendwo eine breite Tabelle steht: Ein Rasterfeld ist von sich aus
   mindestens so breit wie sein Inhalt (min-width: auto), und ein
   Scrollrahmen um eine Tabelle mit width:100% meldet als Mindestbreite
   die volle Tabellenbreite. Gemessen bei 375 px: 304 px Ueberlauf, und
   die Tabelle scrollte nicht in ihrem Rahmen, sondern schob die Seite.
   Beides verschwindet, sobald die Felder unter ihren Inhalt duerfen. */
.rahmen > * { min-width: 0; }

/* ---- Kopf ---- */

.kopf {
    position: sticky; top: 0; z-index: 30;
    display: flex; align-items: center; gap: var(--raum-3);
    padding: 0 var(--raum-4);
    height: 3.5rem;
    background: color-mix(in oklab, var(--flaeche) 88%, transparent);
    backdrop-filter: saturate(1.6) blur(10px);
    border-bottom: 1px solid var(--linie);
}

/* Die farbige Oberkante ist der Mandantenschluessel. Sie ist das erste,
   was ins Auge faellt, und sagt vor jedem Wort: in wessen Daten bin ich.
   Das ist bei einer mandantenfaehigen Anwendung keine Zierde -- eine
   Rechnung im falschen Mandanten ist ein echter Schaden. */
.kopf::before {
    content: ""; position: absolute; inset: 0 0 auto 0; height: 3px;
    background: var(--mandant);
}

.marke {
    display: inline-flex; align-items: center; gap: var(--raum-2);
    font-weight: 650; font-size: var(--gr-l); letter-spacing: -0.02em;
    color: var(--tinte); text-decoration: none;
}
.marke .zeichen {
    display: grid; place-items: center;
    width: 1.75rem; height: 1.75rem; border-radius: var(--rund-1);
    background: var(--mandant); color: var(--auf-farbe);
    font-size: var(--gr-s); font-weight: 700;
}

/* Mandantenmarke im Kopf: Name, in der Mandantenfarbe, anklickbar zum
   Wechseln. Ein reines Etikett wuerde die Frage "und wie komme ich hier
   raus" offen lassen. */
.mandantmarke {
    display: inline-flex; align-items: center; gap: var(--raum-2);
    max-width: 14rem; padding: var(--raum-1) var(--raum-3);
    border: 1px solid var(--mandant-rand); border-radius: var(--rund-voll);
    background: var(--mandant-flaeche); color: var(--mandant-tinte);
    font-size: var(--gr-s); font-weight: 600; text-decoration: none;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* Sie ist der Weg zum Mandantenwechsel und damit ein Fingerziel: ohne
   Zeigegeraet --ziel hoch. Gemessen am 27.09.2026 bei 375 px: 32 px,
   und die Lupe daneben schrumpfte im Kopf auf 30 px Breite (dafuer
   flex:none an .kopfsuche-verweis). */
.mandantmarke { min-height: var(--ziel); }
@media (hover: hover) and (pointer: fine) {
    .mandantmarke { min-height: 0; }
}
.mandantmarke:hover { color: var(--mandant-tinte); border-color: var(--mandant); }
.mandantmarke::before {
    content: ""; width: 0.5rem; height: 0.5rem; border-radius: 50%;
    background: var(--mandant); flex: none;
}

.kopf .schub { margin-left: auto; }
.kopf .benutzer { font-size: var(--gr-s); color: var(--tinte-leise); }
/* Der Name ist seit D2 ein Verweis auf /Passwort (ablaeufe.md 7.1):
   leise wie vorher, erst beim Zeigen als Verweis erkennbar -- der Kopf
   bekommt keinen zweiten farbigen Punkt neben der Mandantenmarke. Am
   Finger --ziel hoch, wie die Mandantenmarke. */
.kopf a.benutzer {
    display: inline-block; line-height: var(--ziel);
    text-decoration: none; white-space: nowrap;
    max-width: 12rem; overflow: hidden; text-overflow: ellipsis;
}
.kopf a.benutzer:hover, .kopf a.benutzer:focus-visible { color: var(--akzent-tinte); text-decoration: underline; }

/* ---- Navigation ---- */

.navi ul { list-style: none; margin: 0; padding: 0; }

.navi a {
    display: flex; align-items: center; gap: var(--raum-3);
    padding: var(--raum-2) var(--raum-3); min-height: var(--ziel);
    border-radius: var(--rund-2);
    color: var(--tinte-leise); text-decoration: none;
    font-size: var(--gr-m); font-weight: 500;
    transition: background var(--wechsel), color var(--wechsel);
}
.navi a:hover { background: var(--flaeche-satt); color: var(--tinte); }
.navi a[aria-current] { background: var(--akzent-flaeche); color: var(--akzent-tinte); font-weight: 600; }
.navi svg { width: 1.25rem; height: 1.25rem; flex: none; stroke-width: 1.75; }
.navi .gruppe {
    margin: var(--raum-4) 0 var(--raum-1); padding: 0 var(--raum-3);
    font-size: var(--gr-xs); text-transform: uppercase; letter-spacing: 0.06em;
    color: var(--tinte-schwach);
}

/* Am Telefon: fuenf Ziele unten, der Rest hinter "Mehr". Nicht weil es
   modern aussieht, sondern weil der Daumen oben nicht hinkommt. */
.tableiste {
    position: fixed; inset: auto 0 0 0; z-index: 40;
    display: grid; grid-auto-flow: column; grid-auto-columns: 1fr;
    background: var(--flaeche); border-top: 1px solid var(--linie);
    padding: var(--raum-1) var(--raum-1) calc(var(--raum-1) + env(safe-area-inset-bottom));
}
.tableiste a, .tableiste .mehrknopf {
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    gap: 2px; padding: var(--raum-1); min-height: var(--ziel);
    border: none; background: none; cursor: pointer;
    font-family: inherit; font-size: var(--gr-xs); font-weight: 500;
    color: var(--tinte-leise); text-decoration: none; border-radius: var(--rund-2);
}
.tableiste a[aria-current] { color: var(--akzent-tinte); }
.tableiste svg { width: 1.375rem; height: 1.375rem; stroke-width: 1.75; }

/* Das Mehr-Blatt faehrt von unten ein. Geschaltet wird es ueber ein
   verstecktes Kontrollkaestchen: kein JavaScript, kein Bibliotheksstand,
   der irgendwann nicht mehr passt. */
/* Nur visuell versteckt, nicht aus der Bedienung genommen: Wer mit der
   Tastatur arbeitet, muss das Menue erreichen und sehen koennen, dass er
   darauf steht. */
.mehrschalter { position: absolute; width: 1px; height: 1px; opacity: 0; margin: 0; }
.mehrschalter:focus-visible ~ .tableiste .mehrknopf {
    outline: 2px solid var(--fokus); outline-offset: -2px;
}
/* DAS BLATT IST UNTEN VERANKERT UND WAECHST NACH OBEN -- und bis zum
   17.09.2026 wuchs es dabei aus dem Bild heraus.

   inset: auto 0 0 0 setzt die Unterkante auf den Boden, die Hoehe kommt
   aus dem Inhalt. Damit gilt zwingend:

       Oberkante = Fensterhoehe - Blatthoehe

   Bei 16 Eintraegen ist das Blatt 753 Punkte hoch. Auf einem 812er
   Fenster steht die Oberkante bei +59 und alles ist im Bild; auf einem
   667er bei MINUS 86 -- die obersten Eintraege liegen ueber dem
   Bildschirmrand und sind unerreichbar, weil es weder max-height noch
   overflow gab. Der erste verlorene Eintrag war ausgerechnet der, den
   ich am selben Tag erst mobil erreichbar gemacht hatte: Preisrunden.

   667 ist kein Altgeraet: Safari zeigt auf aktuellen iPhones wegen
   seiner Leisten Viewports in genau diesem Bereich.

   DAS IST DIE BEHEBUNG, und sie ist eine Zeile: Das Blatt bekommt eine
   Hoechsthoehe und scrollt in sich, statt oben hinauszuwachsen.

   dvh UND NICHT vh: vh rechnet mit der Fensterhoehe OHNE eingeblendete
   Browserleisten -- also mit genau der Hoehe, die auf dem Geraet nicht
   zur Verfuegung steht. Das waere derselbe Fehler noch einmal, nur
   kleiner.

   DER ABZUG LAESST DIE OBERKANTE SICHTBAR: Ein Blatt, das exakt bis zum
   Rand reicht, sieht aus wie eine Seite und nicht wie ein Blatt darueber
   -- und wer nicht sieht, dass etwas darueber liegt, sucht den Weg
   zurueck nicht. */
.mehrblatt {
    position: fixed; inset: auto 0 0 0; z-index: 45;
    max-height: calc(100dvh - var(--raum-6));
    overflow-y: auto; overscroll-behavior: contain;
    background: var(--flaeche-hoch); border-top: 1px solid var(--linie);
    border-radius: var(--rund-3) var(--rund-3) 0 0;
    box-shadow: var(--schatten-2);
    padding: var(--raum-3) var(--raum-3) calc(var(--raum-6) + env(safe-area-inset-bottom));
    transform: translateY(101%); transition: transform var(--wechsel);
}
.mehrblatt::before {
    content: ""; display: block; width: 2.5rem; height: 4px; margin: 0 auto var(--raum-3);
    border-radius: var(--rund-voll); background: var(--linie-stark);
}
/* ZWEI SPALTEN, GEGLIEDERT WIE DIE SEITENLEISTE (CEO, Cockpit Nr. 30,
   27.09.2026; Muster /muster.html#b30 -- dort stehen dieselben drei
   Regeln fuer die Probe, und Mehrblattquelltexttests prueft, dass sie
   gleich bleiben).

   ZEILENWEISE gefuellt: Die Tabreihenfolge ist die Lesereihenfolge
   (links, rechts, naechste Zeile), und zwei Nachbarn stehen auf
   derselben Hoehe, auch wenn einer umbricht. columns: 2 liesse die
   Zeilen auseinanderlaufen. minmax(0, 1fr) und nicht 1fr -- sonst
   drueckt das laengste Wort die Spalte auf (gestaltung.md, Kap. 4).

   Die Gruppen trennt eine Linie (--linie, Trennung wie ein Kartenrand)
   statt einer Ueberschrift: 17 px statt 40. Sie traegt keine Aussage
   allein -- Reihenfolge und Abstand gliedern mit.

   In einer halben Spalte bleiben bei 375 px rund 127 px fuer den Text:
   weniger Polster, und ein langes Wort trennt, statt hinauszuragen
   ("Wiederkehrende Rechnungen", in Stufe gross "Einstellungen"). Die
   Zeilenhoehe gilt nur fuer den zweizeiligen Fall -- einzeilig haelt
   --ziel die Hoehe bei 44. Am Schreibtisch ist das Blatt ausgeblendet
   (ab 900 px), dort aendert sich nichts.

   POLSTER UND ABSTAND --raum-1 STATT --raum-2 (der Vorschlag hatte 2):
   Mit 2 lief "Wiederkehrende" in Stufe gross 6 px aus dem Verweis
   (gemessen 27.09.2026, 375 px) -- der Text ist ein anonymes
   Flex-Element, break-word senkt seine Mindestbreite nicht, und ohne
   Woerterbuch trennt der Browser nicht. overflow-wrap: anywhere haette
   es gehalten, brach aber "Referenzmapp|e" mitten im Wort. Mit --raum-1
   bleiben 8 px mehr fuer den Text, und alles steht im Kasten. */
.mehrblatt ul {
    display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 0 var(--raum-2);
}
.mehrblatt ul + ul {
    margin-top: var(--raum-2); padding-top: var(--raum-2);
    border-top: 1px solid var(--linie);
}
.mehrblatt a {
    gap: var(--raum-1); padding-inline: var(--raum-1);
    line-height: 1.25; hyphens: auto; overflow-wrap: break-word;
}
body:has(.mehrschalter:checked) .mehrblatt { transform: translateY(0); }
body:has(.mehrschalter:checked) .verdunkelung { opacity: 1; pointer-events: auto; }
.verdunkelung {
    position: fixed; inset: 0; z-index: 44; opacity: 0; pointer-events: none;
    background: oklch(16% 0.022 258 / 0.4); transition: opacity var(--wechsel);
}

/* ---- Inhalt ---- */

.inhalt {
    padding: var(--raum-5) var(--raum-4) calc(var(--ziel) + var(--raum-8));
    width: 100%; max-width: var(--inhalt-max); margin: 0 auto;
}

/* Ein Bereich, den wawi.js nach dem Speichern der Positionen frisch vom
   Server holt (bereicheNachladen). Der Rahmen ist nur ein Griff fuer das
   Skript und darf das Layout nicht beruehren -- ohne eigene Box, auch
   leer keine Luecke. */
[data-nachladen] { display: contents; }

/* Ab Tablet: Navigation an die linke Kante, Tableiste weg. 900px ist die
   Grenze, an der ein iPad quer und ein kleines Notebook anfangen -- ein
   iPad hoch (768px) bleibt bewusst beim Telefonaufbau, weil es dort in
   der Hand gehalten wird. */
@media (min-width: 900px) {
    /* minmax(0, 1fr) und nicht 1fr: Eine Rasterspalte schrumpft von sich
       aus nie unter die Mindestbreite ihres Inhalts. Eine breite Tabelle
       drueckt die Spalte sonst auseinander, und die ganze Seite laeuft
       seitlich ueber -- gemessen bei genau 900 px: 51 px Ueberlauf. */
    .rahmen { grid-template-columns: var(--leiste) minmax(0, 1fr); grid-template-rows: auto 1fr; }
    /* Ohne Anmeldung gibt es keine Seitenleiste -- dann darf der Inhalt
       nicht neben einer leeren Spalte kleben. */
    .rahmen:not(:has(.seitenleiste)) { grid-template-columns: 1fr; }
    .kopf { grid-column: 1 / -1; }
    .seitenleiste {
        position: sticky; top: 3.5rem; align-self: start;
        height: calc(100dvh - 3.5rem); overflow-y: auto;
        padding: var(--raum-4) var(--raum-3);
        border-right: 1px solid var(--linie);
        display: block;
    }
    .inhalt { padding: var(--raum-6) var(--raum-6) var(--raum-8); }
    .tableiste, .mehrblatt, .verdunkelung, .mehrschalter { display: none; }
}

@media (max-width: 899px) {
    .seitenleiste { display: none; }
}

/* ======================================================= 4. BAUTEILE ==== */

/* ---- Knopf ----
   Genau drei Staerken, und sie sind eine Rangfolge, keine Farbwahl:
   .haupt  = die eine Handlung, die diese Seite erwartet (hoechstens eine!)
   (leer)  = alles Weitere
   .leise  = Nebensache, tritt hinter den Text zurueck
   .gefahr = macht etwas kaputt oder kostet Geld
*/
.knopf, .btn {
    display: inline-flex; align-items: center; justify-content: center; gap: var(--raum-2);
    min-height: var(--ziel); padding: 0 var(--raum-4);
    border: 1px solid var(--linie-stark); border-radius: var(--rund-2);
    background: var(--flaeche); color: var(--tinte);
    font-family: inherit; font-size: var(--gr-m); font-weight: 550; line-height: 1;
    text-decoration: none; cursor: pointer; white-space: nowrap;
    box-shadow: var(--schatten-1);
    transition: background var(--wechsel), border-color var(--wechsel), color var(--wechsel);
}
.knopf:hover, .btn:hover { background: var(--flaeche-leise); border-color: var(--tinte-schwach); color: var(--tinte); }
.knopf:active, .btn:active { background: var(--flaeche-satt); }
.knopf:disabled, .btn:disabled, .knopf[aria-disabled="true"] {
    opacity: 0.5; cursor: not-allowed; box-shadow: none;
}
.knopf svg, .btn svg { width: 1.125rem; height: 1.125rem; stroke-width: 1.75; }

.knopf.haupt, .btn-primary {
    background: var(--akzent); border-color: var(--akzent); color: var(--auf-farbe);
}
.knopf.haupt:hover, .btn-primary:hover {
    background: var(--akzent-stark); border-color: var(--akzent-stark); color: var(--auf-farbe);
}

.knopf.leise, .btn-outline-secondary, .btn-secondary {
    background: transparent; border-color: var(--linie); color: var(--tinte-leise);
    box-shadow: none;
}
.knopf.leise:hover, .btn-outline-secondary:hover, .btn-secondary:hover {
    background: var(--flaeche-satt); border-color: var(--linie-stark); color: var(--tinte);
}

/* DER UMRISS TRAEGT --gefahr-satt UND NICHT --gefahr-rand, und das ist
   eine Kontrastfrage, keine Geschmacksfrage.

   Gemessen am 17.09.2026 gegen die tatsaechlich gerenderten Farben
   (Canvas, nicht gerechnet): --gefahr-rand erreicht 1,52 zu 1 auf
   weissem Grund und 1,42 auf dem Warngrund. WCAG verlangt 3 zu 1 fuer
   den Umriss eines Bedienelements -- der Knopf war als Knopf nicht
   erkennbar. Sein TEXT war es (7,86), sein Rahmen nicht: Er las sich
   als hervorgehobener Text.

   Aufgefallen ist es an der Vorabrechnungskarte, wo der Knopf die
   einzige Handlung ist und auf farbigem Grund steht. Es gilt aber
   ueberall -- auch auf Weiss war der Umriss unter der Schwelle.

   --gefahr-satt erfuellt sie in BEIDEN Modi und auf beiden Gruenden:
   hell 6,05 (weiss) und 5,63 (Warngrund), dunkel 5,19 und 3,91.
   --gefahr-rand bleibt, was es ist -- der Rahmen einer Karte, und der
   ist kein Bedienelement.

   KEINE NEUE FARBROLLE: --gefahr-satt gibt es, es heisst "satt" und ist
   fuer die kraeftige Anwendung gedacht. Ein eigener Wert nur fuer den
   Knopfumriss waere ein Wert mehr im System, den ausser ihm niemand
   braucht. */
.knopf.gefahr, .btn-danger {
    background: transparent; border-color: var(--gefahr-satt); color: var(--gefahr-tinte);
    box-shadow: none;
}
.knopf.gefahr:hover, .btn-danger:hover {
    background: var(--gefahr-flaeche); border-color: var(--gefahr-satt); color: var(--gefahr-tinte);
}

/* KLEIN HEISST AM SCHREIBTISCH KLEIN, NICHT AM FINGER. Gemessen am
   27.09.2026 bei 375 px: .knopf.klein war 32 px hoch (Module, Hosting,
   Sortiment) -- unter den 44 px, die ein Daumen trifft. Die Hoehe kommt
   deshalb aus --ziel, und nur mit Zeigegeraet bleibt es bei 2rem. */
.knopf.klein, .btn-sm { min-height: var(--ziel); padding: 0 var(--raum-3); font-size: var(--gr-s); }
@media (hover: hover) and (pointer: fine) {
    .knopf.klein, .btn-sm { min-height: 2rem; }
}

.btn-link, .knopf.alsverweis {
    background: none; border-color: transparent; box-shadow: none;
    color: var(--akzent-tinte); text-decoration: underline; padding: 0 var(--raum-1);
    min-height: auto;
}
.btn-link:hover, .knopf.alsverweis:hover { background: none; border-color: transparent; color: var(--akzent-stark); }

/* ---- Felder ---- */

label, .beschriftung {
    display: block; font-size: var(--gr-s); font-weight: 550;
    color: var(--tinte-leise); margin-bottom: var(--raum-1);
}

.feld, .form-control, .wahl, .form-select, textarea, select,
input[type="text"], input[type="search"], input[type="email"],
input[type="password"], input[type="number"], input[type="date"], input[type="tel"] {
    display: block; width: 100%; min-height: var(--feld-hoehe);
    padding: var(--raum-2) var(--raum-3);
    border: 1px solid var(--linie-stark); border-radius: var(--rund-2);
    background: var(--flaeche); color: var(--tinte);
    font-family: inherit; font-size: var(--gr-n); line-height: 1.4;
    transition: border-color var(--wechsel), box-shadow var(--wechsel);
    /* 16px Schriftgroesse ist Pflicht: Alles darunter loest in Safari auf
       dem iPhone beim Antippen einen Zoom aus, aus dem der Nutzer nicht
       mehr herausfindet. */
}
textarea { min-height: 6rem; resize: vertical; line-height: 1.55; }

.wahl, .form-select, select {
    appearance: none;
    /* Der Pfeil als eingebettete Grafik -- keine zweite Datei, kein
       Zeichensatz, und er faerbt sich mit der Tinte nicht mit, deshalb
       ein neutrales Grau, das in beiden Modi funktioniert. */
    background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%238892a6' stroke-width='1.75' stroke-linecap='round'%3E%3Cpath d='M4 6.5 8 10.5 12 6.5'/%3E%3C/svg%3E");
    background-repeat: no-repeat; background-position: right var(--raum-3) center;
    background-size: 1rem; padding-right: var(--raum-6);
}

.feld:focus, .form-control:focus, .wahl:focus, .form-select:focus,
textarea:focus, select:focus, input:focus, .form-check-input:focus {
    outline: none;
    border-color: var(--akzent);
    box-shadow: 0 0 0 3px color-mix(in oklab, var(--akzent) 22%, transparent);
}
.feld::placeholder, .form-control::placeholder { color: var(--tinte-schwach); }
.feld:disabled, .form-control:disabled, input:disabled, textarea:disabled, select:disabled {
    background: var(--flaeche-leise); color: var(--tinte-schwach); cursor: not-allowed;
}

/* KAESTCHEN UND OPTIONSFELD (#208, #221): das Aussehen des Browsers, nicht
   das von Bootstrap. Mit form-check-input setzt Bootstrap appearance: none
   und zeichnet selbst -- Rand #dee2e6 auf Weiss (1,3 : 1, gemessen auf
   /Personen/Bearbeiten und /Artikel/Bearbeiten), im dunklen Modus weiss,
   angehakt in Bootstrap-Blau. Der Browser folgt color-scheme und
   accent-color: Rand 4,5 : 1 hell und 4,9 : 1 dunkel, angehakt in --akzent.
   Bootstraps Fokusschein (blau) weicht unserem Fokusring. Muster:
   /muster.html#b4. */
input[type="checkbox"], input[type="radio"], .form-check-input {
    width: 1.125rem; height: 1.125rem; min-height: 0; display: inline-block;
    appearance: auto;
    accent-color: var(--akzent); margin: 0; padding: 0;
    vertical-align: middle;
}

/* Falsch ausgefuellt: Der Hinweis gehoert an das Feld, nicht in eine
   Meldung am Seitenkopf -- dort sucht ihn niemand. */
.feld[aria-invalid="true"], .form-control[aria-invalid="true"], .feld.falsch {
    border-color: var(--gefahr-satt);
}
.feldfehler {
    display: block; margin-top: var(--raum-1);
    font-size: var(--gr-s); font-weight: 550; color: var(--gefahr-tinte);
}
.feldhinweis { display: block; margin-top: var(--raum-1); font-size: var(--gr-s); color: var(--tinte-leise); }

/* ---- Karte ---- */

.karte {
    background: var(--flaeche);
    border: 1px solid var(--linie);
    border-radius: var(--rund-3);
    padding: var(--raum-5);
    margin: var(--raum-5) 0;
    box-shadow: var(--schatten-1);
}
.karte > h2:first-child, .karte > h1:first-child { margin-top: 0; }

/* KNOEPFE UNTEN ANGEBUNDEN (CEO 21.09.2026: "die Buttons sollten immer auf
   gleicher Hoehe sein"). Zwei Karten nebeneinander sind im Raster gleich
   hoch, ihr Inhalt nicht -- ohne diese Regel steht die Knopfleiste der
   kuerzeren hoeher. Ein Modifier und keine Grundregel: Als Flex-Spalte
   kollabieren die Abstaende der Kinder nicht mehr, und das soll nicht
   still jede Karte der Anwendung verschieben. */
.karte.leiste-unten { display: flex; flex-direction: column; }
.karte.leiste-unten > .knopfleiste { margin-top: auto; padding-top: var(--raum-5); }
/* Eine Karte, die etwas Dringendes traegt. Nur der Rand, keine
   Flaeche: Der Inhalt soll lesbar bleiben, nicht getoent werden.
   Dieselbe Bauart wie .karte.gefahr, eine Stufe leiser. */
.karte.warn { border-color: var(--warn-rand); }
.karte.gefahr { border-color: var(--gefahr-rand); }
.karte.leise { box-shadow: none; background: transparent; }
/* Eine breite Karte als Sprungziel (Akte#adressen) landet sonst UNTER dem
   klebenden Kopf (.kopf, 3.5rem): Gemessen am 18.09.2026 waren die
   Beschriftungen der Adressmaske verdeckt. Derselbe Wert wie im
   Musterbuch, dort mit der Rechnung: Die Ueberschrift steht mit 1rem Luft
   unter dem Kopf, weil die Karte ihr eigenes Polster mitbringt. */
.karte.breit { scroll-margin-top: calc(3.5rem + var(--raum-4) - var(--raum-5)); }
/* Ein Sprungziel IN einer Karte -- eine Ueberschrift, ein Abschnitt ohne
   eigenes Polster (#209: "Vorhandenes Konto zuordnen" in der Akte des
   Betreibers). Dieselbe Rechnung wie .abschnitt im Musterbuch: 1rem Luft
   unter dem Kopf. Die Karte zieht ihr Polster ab, das hier fehlt. */
.sprungziel { scroll-margin-top: calc(3.5rem + var(--raum-4)); }

/* ---- Marken (Zustand, Rolle, Kennzeichen) ----
   Eine Marke traegt immer Text. Der Punkt davor ist die zweite Spur fuer
   alle, die Farben nicht unterscheiden koennen -- neun Prozent der
   Maenner sehen Rot und Gruen nicht als verschiedene Farben. */
.marke, .zustand, .rolle, .kennzeichen, .etikett, .standard, .art, .badge {
    display: inline-flex; align-items: center; gap: var(--raum-1);
    padding: 0.125rem var(--raum-2);
    border: 1px solid var(--linie); border-radius: var(--rund-1);
    background: var(--flaeche-leise); color: var(--tinte-leise);
    font-size: var(--gr-xs); font-weight: 600; line-height: 1.5;
    white-space: nowrap;
}
.zustand::before {
    content: ""; width: 0.4rem; height: 0.4rem; border-radius: 50%;
    background: currentColor; flex: none;
}

.marke.gut, .zustand.z-bezahlt, .zustand.z-aktiv {
    background: var(--gut-flaeche); border-color: var(--gut-rand); color: var(--gut-tinte);
}
.marke.warn, .zustand.z-entwurf, .zustand.z-pausiert, .kennzeichen {
    background: var(--warn-flaeche); border-color: var(--warn-rand); color: var(--warn-tinte);
}
.marke.gefahr, .zustand.z-storniert {
    background: var(--gefahr-flaeche); border-color: var(--gefahr-rand); color: var(--gefahr-tinte);
}
.marke.akzent, .zustand.z-festgeschrieben, .zustand.z-versendet, .standard {
    background: var(--akzent-flaeche); border-color: var(--akzent-rand); color: var(--akzent-tinte);
}
.zustand.z-beendet, .marke.still { color: var(--tinte-schwach); }

/* ---- Meldungen ----
   Eine Meldung sagt, was passiert ist und was jetzt zu tun ist. Sie steht
   dort, wo die Handlung ausgeloest wurde, und verschwindet nicht von
   selbst -- eine Einblendung, die nach drei Sekunden weg ist, hat der
   Nutzer im Zweifel nie gesehen. */
.meldung, .alert {
    display: flex; gap: var(--raum-3); align-items: flex-start;
    padding: var(--raum-3) var(--raum-4); margin: 0 0 var(--raum-4);
    border: 1px solid var(--info-rand); border-left-width: 3px;
    border-radius: var(--rund-2);
    background: var(--info-flaeche); color: var(--info-tinte);
    font-size: var(--gr-m);
}
.meldung.gut, .alert-success {
    background: var(--gut-flaeche); border-color: var(--gut-rand); color: var(--gut-tinte);
}
.meldung.warn, .alert-warning {
    background: var(--warn-flaeche); border-color: var(--warn-rand); color: var(--warn-tinte);
}
.meldung.gefahr, .alert-danger {
    background: var(--gefahr-flaeche); border-color: var(--gefahr-rand); color: var(--gefahr-tinte);
}
.meldung.info, .alert-info {
    background: var(--info-flaeche); border-color: var(--info-rand); color: var(--info-tinte);
}

/* Der stille Hinweis: steht dauerhaft, erklaert und alarmiert nicht. */
.hinweis {
    max-width: 68ch; font-size: var(--gr-m); color: var(--tinte-leise);
    border-left: 2px solid var(--linie); padding-left: var(--raum-4);
    margin: 0 0 var(--raum-4);
}
.hinweis.warnhinweis {
    border-left-color: var(--warn-rand); background: var(--warn-flaeche);
    color: var(--warn-tinte); padding: var(--raum-3) var(--raum-4);
    border-radius: var(--rund-2);
}

/* Der Kundenhinweis: was ueber DIESEN KUNDEN dauerhaft gilt, an der
   Stelle, an der ueber seinen Auftrag entschieden wird.

   Warum akzentfarben und nicht warnfarben: "Rechnung immer an die
   Buchhaltung" ist eine Arbeitsanweisung, keine Gefahr. In derselben
   Farbe wie die ueberschrittene Umsatzgrenze verduennte er die, die es
   wirklich ist -- und wer drei rote Kaesten sieht, liest den dritten
   nicht mehr.

   Das Etikett traegt die Herkunft, weil weiter unten ein zweites
   Notizfeld steht, das dem VORGANG gehoert. Ohne Unterscheidung landet
   die Dauerangabe im Feld, das nur diesen einen Auftrag betrifft. */
.kundenhinweis {
    display: flex; gap: var(--raum-3); align-items: baseline;
    max-width: 68ch; margin: 0 0 var(--raum-4);
    padding: var(--raum-3) var(--raum-4);
    background: var(--flaeche-leise);
    border-left: 3px solid var(--akzent-rand);
    border-radius: var(--rund-2);
    font-size: var(--gr-m);
}
.kundenhinweis .etikett {
    flex: none; font-size: var(--gr-xs); font-weight: 600;
    letter-spacing: 0.02em; color: var(--akzent-tinte);
}
.kundenhinweis .text { color: var(--tinte); white-space: pre-wrap; }
/* Eine AUFZAEHLUNG ist kein Fliesstext: pre-wrap haelt die Umbrueche
   der Vorlage, und die stehen im Razor zwischen den Eintraegen. Auf dem
   Bildschirm brach die Liste dann an jeder Schleifengrenze um -- die
   DOM-Messung sah davon nichts, der Bildbeleg sofort. */
.kundenhinweis.liste .text {
    white-space: normal; display: flex; flex-wrap: wrap; gap: 0 var(--raum-2);
}
/* Der Weg zur Pflegestelle, rechts abgesetzt: Er ist kein Teil der
   Auskunft, sondern die Antwort auf "und wenn der Richtige fehlt".
   Leise, weil er im Normalfall nicht gebraucht wird -- aber immer da,
   weil eine Beschraenkung ohne Ausweg zum Umweg zwingt. */
.kundenhinweis .dazu {
    margin-left: auto; flex: none; align-self: center;
    font-size: var(--gr-xs); color: var(--tinte-leise);
}
.kundenhinweis .dazu:hover, .kundenhinweis .dazu:focus-visible { color: var(--akzent); }

/* Die Spalte "Zuletzt": Datum und Herkunft in einer Zelle.

   Die HERKUNFT steht leiser als das Datum. Sie ordnet ein, das Datum
   ist die Auskunft -- wer die Liste ueberfliegt, sucht "wie lange her"
   und nicht "woher gewusst". Gleich laut nebeneinander lesen sie sich
   als zweiteilige Angabe, und die Spalte wird doppelt so breit gelesen,
   wie sie es verdient.

   Sie steht trotzdem da, weil "zuletzt" drei Lesarten hat und eine Zahl
   ohne Herkunft sich jeder zurechtlegt.

   Ziffern tabellarisch: Untereinanderstehende Daten sind der ganze Sinn
   dieser Spalte -- man vergleicht sie mit dem Auge, nicht durch Lesen. */
td.zuletzt { white-space: nowrap; font-variant-numeric: tabular-nums; }
td.zuletzt .woher {
    display: block; font-size: var(--gr-xs); color: var(--tinte-schwach);
    text-transform: uppercase; letter-spacing: 0.04em;
}

/* ---- Tabellen und Listen ---- */

.tabellenrahmen, .table-responsive {
    overflow-x: auto;
    border: 1px solid var(--linie); border-radius: var(--rund-3);
    background: var(--flaeche);
    box-shadow: var(--schatten-1);
}

table, .tabelle, .table {
    width: 100%; border-collapse: collapse;
    font-size: var(--gr-m);
    background: var(--flaeche);
}
.tabellenrahmen > table, .table-responsive > table { border-radius: inherit; }

/* Bootstrap faerbt Tabellenzellen nicht ueber background, sondern ueber
   einen 9999px breiten inneren Schatten (--bs-table-bg). Der liegt ueber
   jeder eigenen Flaeche und ist auf Weiss festgenagelt: Im dunklen Modus
   blieb die ganze Tabelle weiss, waehrend der Rest der Seite dunkel war.
   Hier wird der Schatten abgeschaltet, damit wieder gilt, was in dieser
   Datei steht. */
.table > :not(caption) > * > * {
    box-shadow: none;
    background-color: transparent;
    color: inherit;
}

thead th {
    position: sticky; top: 0; z-index: 1;
    background: var(--flaeche-leise);
    text-align: left; font-size: var(--gr-xs); font-weight: 650;
    text-transform: uppercase; letter-spacing: 0.05em;
    color: var(--tinte-leise);
    padding: var(--raum-2) var(--raum-3);
    border-bottom: 1px solid var(--linie);
    white-space: nowrap;
}
tbody td { padding: var(--zeile-y) var(--raum-3); border-bottom: 1px solid var(--linie-leise); vertical-align: middle; }
tbody tr:last-child td { border-bottom: none; }
tbody tr:hover td { background: var(--flaeche-leise); }
th.rechts, td.rechts { text-align: right; }

/* Zeilen, die noch da sind, aber nicht mehr mitzaehlen: gesperrte
   Personen, bezahlte und stornierte Belege. Sie werden gedaempft, nicht
   entfernt -- wer sie sucht, findet sie; wer die Liste ueberfliegt,
   uebersieht sie. Die Farbe allein traegt die Aussage nicht: In der
   Zeile steht zusaetzlich ihr Zustand. */
tbody tr.gesperrt { color: var(--tinte-schwach); }
tbody tr.gesperrt a { color: var(--tinte-leise); }
tbody tr.gesperrt:hover td { background: var(--flaeche-leise); }

/* ---- Sortierbare Spaltenkoepfe ----
   Der ganze Kopf ist der Verweis, nicht nur das Wort darin. Der Pfeil
   steht nur an der Spalte, nach der sortiert wird -- ein Pfeil an jeder
   Spalte sagt nichts mehr. */
table.sortierbar thead th { padding: 0; }
table.sortierbar thead th > a {
    display: flex; align-items: center; gap: var(--raum-1);
    padding: var(--raum-2) var(--raum-3);
    color: inherit; text-decoration: none; white-space: nowrap;
}
table.sortierbar thead th > a:hover { background: var(--flaeche-satt); color: var(--tinte); }
table.sortierbar thead th .pfeil { color: var(--akzent); font-size: var(--gr-s); }
table.sortierbar thead th[aria-sort="ascending"] > a,
table.sortierbar thead th[aria-sort="descending"] > a { color: var(--tinte); }
table.sortierbar thead th.rechts > a { justify-content: flex-end; }

/* Die mit der Tastatur gewaehlte Zeile. Sie ist keine Auswahl im Sinne
   von "markiert", sondern nur die, die Enter oeffnen wuerde -- deshalb
   ein Balken an der Kante und kein Farbteppich. */
tbody tr.gewaehlt td { background: var(--akzent-flaeche); }
tbody tr.gewaehlt td:first-child { box-shadow: inset 3px 0 0 var(--akzent); }

.tastaturhilfe { margin-top: var(--raum-4); }

/* WER DEN GRUND UEBERSCHREIBT, MUSS DIE SCHRIFT MITNEHMEN.

   Bootstrap setzt fuer kbd BEIDE Farben, und zwar ueber Kreuz:
   "color: var(--bs-body-bg); background-color: var(--bs-body-color)" --
   helle Schrift auf dunklem Grund. Diese Regel hat den Grund auf
   --flaeche gestellt und die Schrift nicht angefasst. Uebrig blieb
   weiss auf weiss, Kontrast 1,00: Die Taste war ein leeres Kaestchen.

   Gemessen am 30.08.2026 auf /Auftraege, betroffen war jede Liste mit
   der Tastaturhilfe. Der Text stand die ganze Zeit im DOM und war dort
   auch abzulesen -- gesehen hat es erst der Bildbeleg. */
.tastaturhilfe kbd {
    font-family: inherit; font-size: var(--gr-xs);
    border: 1px solid var(--linie-stark); border-bottom-width: 2px;
    border-radius: var(--rund-1); padding: 0.05em 0.4em; margin: 0 0.1em;
    background: var(--flaeche);
    color: var(--tinte);
}

/* Eine Zeile ist anklickbar, wenn sie einen Verweis enthaelt -- dann ist
   die ganze Zeile das Ziel, nicht nur die vier Buchstaben der Nummer. */
tbody tr[data-ziel] { cursor: pointer; }
tbody tr:has(a.zeilenziel) { cursor: pointer; }
tbody tr:has(a.zeilenziel):hover td { background: var(--akzent-flaeche); }

/* Auf schmalen Bildschirmen wird aus jeder Zeile eine Karte. Eine
   Tabelle mit acht Spalten laesst sich am Telefon nicht lesen, und
   seitliches Schieben verliert die erste Spalte, also den Namen.
   Eingeschaltet wird das je Tabelle mit data-karten; jede Zelle traegt
   dann ihre Beschriftung in data-titel. */
@media (max-width: 700px) {
    table[data-karten] { display: block; background: none; }
    table[data-karten] thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }
    table[data-karten] tbody, table[data-karten] tr, table[data-karten] td { display: block; width: auto; }
    table[data-karten] tr {
        background: var(--flaeche); border: 1px solid var(--linie);
        border-radius: var(--rund-2); padding: var(--raum-3);
        margin-bottom: var(--raum-3); box-shadow: var(--schatten-1);
    }
    table[data-karten] td { border: none; padding: var(--raum-1) 0; display: flex; justify-content: space-between; gap: var(--raum-4); }
    table[data-karten] td::before {
        content: attr(data-titel); flex: none;
        font-size: var(--gr-s); font-weight: 550; color: var(--tinte-leise);
    }

    /* Eine Zelle kann zwei Zeilen tragen -- die Leistung und darunter,
       aus welchem Abo sie stammt; das Ende und darunter, wie lange das
       her ist. Das <br> dazwischen wirkt in einem Flexcontainer NICHT:
       Beide standen nebeneinander, und der Zusatz sah aus wie eine
       zweite Spalte mit eigenem Wert. Er bricht jetzt um und stellt
       sich unter seinen Wert. */
    table[data-karten] td { flex-wrap: wrap; }
    table[data-karten] td > br { display: none; }
    table[data-karten] td > .leise.klein { flex: 1 1 100%; text-align: right; }

    table[data-karten] td.hauptzeile { font-size: var(--gr-n); font-weight: 600; padding-bottom: var(--raum-2); }
    table[data-karten] td.hauptzeile::before { display: none; }
    .tabellenrahmen:has(table[data-karten]), .table-responsive:has(table[data-karten]) {
        border: none; background: none; box-shadow: none; overflow: visible;
    }
}

/* ---- Leisten und Anordnungen ---- */

.listenkopf {
    display: flex; align-items: center; gap: var(--raum-4); flex-wrap: wrap;
    margin-bottom: var(--raum-4);
}
.listenkopf h1 { margin: 0; flex: 1 1 auto; }

.knopfleiste, .ausgabeleiste, .lauf, .blaettern, .versandform {
    display: flex; gap: var(--raum-3); align-items: center; flex-wrap: wrap;
}
.knopfleiste { margin-top: var(--raum-5); }
/* Ausdruecklich, wie bei jedem Bauteil mit eigenem display: Sonst schlaegt
   display:flex das hidden-Attribut. Gebraucht fuer die Speicherleiste des
   Positionsrasters, die das Skript ausblendet, wo es selbst speichert. */
.knopfleiste[hidden] { display: none; }
/* "Abbrechen" als Verweis in der Knopfleiste (Personen, Artikel) ist
   genauso ein Ziel wie der Knopf daneben: .btn-link setzt min-height auf
   auto, in der klebenden Leiste der Personenmaske stand er deshalb als
   schmaler Streifen unter dem Speicherknopf (27.09.2026, 375 px). */
.knopfleiste .btn-link, .knopfleiste .knopf.alsverweis { min-height: var(--ziel); }

/* DER ZWEITE WEG AM FUSS EINES FORMULARS -- abgesetzt, nicht daneben.

   Er ist kein zweiter Hauptknopf und soll auch nicht so aussehen: Die
   Trennlinie und der grosse Abstand darueber sagen, dass hier etwas
   ANDERES anfaengt als das Absenden. Zwei Knoepfe in einer Leiste
   laden zum Danebengreifen ein; zwei Knoepfe mit einer Linie und einer
   Frage dazwischen tun es nicht.

   UND ER BLEIBT LINKSBUENDIG UND SCHMAL, auch am Telefon. Die
   .knopfleiste macht ihre Knoepfe unter 40rem volle Breite -- das ist
   dort richtig, weil sie der Abschluss der Maske sind. Hier waere es
   falsch: Ein Knopf ueber die ganze Breite liest sich als der
   wichtigste der Seite, und der wichtigste ist "Eintragen". */
.nichtsweg {
    margin-top: var(--raum-6);
    padding-top: var(--raum-4);
    border-top: 1px solid var(--linie);
}
.nichtsweg .feldhinweis { margin-bottom: var(--raum-2); }

/* ---- Die Bestellzeilen der Mitgliederseite ----

   SIE WAREN EINE AUFZAEHLUNG, und das war nicht gemeint: ohne eigene
   Regel blieb <ul> bei den Vorgaben des Browsers -- Punkte davor und
   40 Punkte Einzug. Ein Formular, das aussieht wie eine Stichwortliste,
   und der Einzug schob das Anzahlfeld bis auf 377 von 377 Punkten, also
   buendig an den rechten Rand. Kein Querscrollen, aber kein Rand mehr.

   UND SIE STANDEN UNTEREINANDER, obwohl an der Maske seit dem ersten
   Tag steht: "Groesse und Anzahl nebeneinander". Der Satz war die
   Absicht, die Regel dazu hat gefehlt -- bei vier Stuecken sind das
   vier Bildschirmhoehen statt zwei, auf einem Geraet, das man mit dem
   Daumen bedient.

   DIE ANZAHL BLEIBT SCHMAL: Dort stehen ein bis zwei Ziffern. Ein Feld
   in Laenge seines Inhalts sagt, was hineingehoert -- ein breites lud
   dazu ein, "2 Stueck" zu schreiben. */
.bestellzeilen {
    list-style: none; margin: 0; padding: 0;
    display: flex; flex-direction: column; gap: var(--raum-4);
}
.bestellzeile {
    display: flex; flex-wrap: wrap; align-items: flex-end;
    gap: var(--raum-2) var(--raum-3);
    padding-bottom: var(--raum-4);
    border-bottom: 1px solid var(--linie-leise);
}
.bestellzeile:last-child { border-bottom: none; padding-bottom: 0; }

/* Die Bezeichnung nimmt die ganze Zeile: Sie ist die Frage, die beiden
   Felder darunter sind die Antwort. */
.bestellzeile .stueck {
    flex: 1 0 100%;
    display: flex; align-items: center; gap: var(--raum-3);
    min-width: 0;
}
.bestellzeile .stuecktext { min-width: 0; overflow-wrap: anywhere; }

/* DAS STUECKBILD (CEO 21.09.2026): gross genug, um ein Shirt von einer
   Jacke zu unterscheiden, klein genug, dass am Telefon drei Stuecke auf
   den Schirm passen. "contain" statt "cover" -- ein abgeschnittener
   Aermel ist beim Wiedererkennen schlimmer als ein Rand. Antippen zeigt
   es gross. */
.stueckbild {
    flex: 0 0 var(--raum-8);
    width: var(--raum-8); height: var(--raum-8);
    border: 1px solid var(--linie);
    border-radius: var(--rund-1);
    overflow: hidden;
}
.stueckbild img {
    display: block; width: 100%; height: 100%;
    object-fit: contain;
}

/* Dasselbe Bild in der Sortimentsmaske: klein in der Liste, damit man die
   Zeile wiedererkennt, gross beim Aendern, damit man sieht, was die
   Mitglieder sehen werden. */
.sortimentsbild {
    width: var(--raum-7); height: var(--raum-7);
    object-fit: contain; vertical-align: middle;
    margin-right: var(--raum-2);
    border: 1px solid var(--linie);
    border-radius: var(--rund-1);
}
.sortimentsbild.gross {
    display: block;
    width: auto; height: auto;
    max-width: min(100%, 16rem); max-height: 16rem;
    margin: 0 0 var(--raum-3);
}
.bestellzeile .feldgroesse { flex: 1 1 7rem; min-width: 0; }
.bestellzeile .feldanzahl { flex: 0 0 5.5rem; }
/* Das box-sizing, das hier einen Tag lang danebenstand, ist weg: Es steht
   jetzt als Reset am Kopf dieser Datei und gilt ueberall. Ein Feld, das
   seine eigene Ausnahme braucht, war nie das Problem -- die fehlende
   Grundregel war es. */
.bestellzeile select, .bestellzeile input { width: 100%; }

/* DIE KLEBENDE KNOPFLEISTE IST DIE AUSNAHME, NICHT DIE REGEL -- und
   das war zuerst umgekehrt.

   Der Zweck stimmt: In einer langen Maske liegt "Speichern" nach
   fuenfundzwanzig Feldern ausserhalb des Bildes, und wer schnell
   sichern will, scrollt erst einmal suchen. Nur bezieht sich
   position: sticky auf den naechsten Scrollcontainer -- die SEITE --
   und nicht auf das Formular. Ein kurzes Formular in einer langen
   Seite bekam die Leiste damit auch, und dort schadet sie nur:

     /Belege, Suchformular         329 px   verdeckt Label + Suchfeld
     /Einstellungen/Bankkonten     463 px   verdeckt "IBAN - darf fehlen"
     /Einstellungen/Stilproben     484 px   verdeckt Label + Textfeld (3x)
     /Personen/Bearbeiten         2098 px   verdeckt nichts

   Gemessen bei 800x900 (Entwickler-Zwei fand den ersten Fall, der Rest
   kam beim Nachsehen dazu). Die Trennlinie liegt genau dort, wo der
   Zweck endet: Wo das Formular laenger ist als das Bild, tut die
   Leiste, wofuer sie gebaut wurde. Wo es kuerzer ist, verdeckt sie
   Inhalt und bringt nichts -- es gibt ja nichts zu erreichen.

   NACHGETRAGEN, UND DER ERSTE DAVON IST EINE EIGENE LEHRE:

     /Einstellungen/Bankkonten, Aendern-Zustand   438 px   nichts
     /Kontoauszug/Import                          365 px   nichts

   Die Bankkontenmaske steht zweimal in dieser Liste, weil sie zwei
   Zustaende hat: Beim Anlegen einen anderen Hinweistext und andere
   Knoepfe als beim Aendern. Gemessen hatte ich nur das Anlegen --
   ausgerechnet der Aendern-Zustand ist der, in dem jemand einen
   Tippfehler korrigiert, also der, in dem ein verdecktes Feld weh
   taete. EINE MASKE IST NICHT GEMESSEN, WENN EIN ZUSTAND VON IHR
   GEMESSEN IST. Beide Nachtraege von Entwickler-Zwei.

   UND EINE VIERTE ACHSE, die heute dreimal mitgespielt hat, ohne
   benannt zu werden: WIE VIEL DRIN IST.

   Der Ueberlauf hing an der Zeilenzahl -- mit einem Konto sah die
   Bankkontenmaske harmlos aus, mit zweien lief sie ueber. Die
   Mahnfreigabe-Gruppierung liess sich nur halb pruefen, weil der
   Bestand drei Kunden hatte und "38 Kunden ohne E-Mail-Adresse" damit
   nicht herstellbar war; der eingeklappte Zustand musste von Hand
   nachgestellt werden. Und den vierten Kontozustand gab es ueberhaupt
   erst, nachdem jemand ein leeres Konto angelegt hatte.

   EIN DUENN BEFUELLTES PROJEKT IST ALS PRUEFSTAND SYSTEMATISCH ZU
   FREUNDLICH, und zwar genau bei dem, was mit dem Bestand waechst:
   Tabellen, Listen, Auswahlfelder, jede Spalte, die vom laengsten Wort
   lebt. Beobachtung von Entwickler-Zwei.

   Die Frage lautet also nicht "habe ich die Maske gemessen", sondern:
   WELCHE SEITE, WELCHE BREITE, WELCHER ZUSTAND -- UND MIT WIE VIEL
   DRIN.

   AUF /Belege TRAF ES DAS FELD AUS ABNAHMESATZ 7, die Suche ueber
   Aussteller und Betrag: 43 px Ueberlappung, elementFromPoint auf die
   Feldmitte liefert die Leiste. Am Telefon war die Belegsuche damit
   nicht bedienbar -- und man sieht es dem Bild nicht an. Sichtbar sind
   die Beschriftung und darunter Knoepfe; EIN VERDECKTES FELD SIEHT AUS
   WIE EIN NICHT VORHANDENES.

   Ein Abstand loest das nicht: Die Klebeposition haengt am Fensterrand,
   nicht am Inhalt -- mehr Luft zwischen Feld und Leiste verschiebt
   beide mit. Nur das Kleben selbst laesst sich weglassen.

   .lang WIRD GESETZT, WO ES GEMESSEN IST -- und nur dort.

   Personen/Bearbeiten: 2098 px, laenger als das Bild, verdeckt nichts.
   Das ist die einzige Maske, die es heute traegt.

   Beim Auftrag haette ich es fast mitgesetzt, mit der Begruendung, das
   Positionsraster wachse ja mit. Es steht gar nicht in dem Formular:
   Der Auftragskopf endet in Zeile 146, das Raster beginnt bei 173.
   Leer sind es 504 px, und die Leiste klebt dort nicht.

   Artikel- und Abo-Maske sind Kandidaten und ungemessen. Wer sie
   nachmisst und ueber ein Bild hinaus kommt, setzt .lang dazu. */
.maske > .knopfleiste { margin-top: var(--raum-6); }

.maske.lang > .knopfleiste {
    position: sticky; bottom: 0; z-index: 5;
    padding: var(--raum-3) 0;
    background: color-mix(in oklab, var(--grund) 88%, transparent);
    backdrop-filter: blur(8px);
    border-top: 1px solid var(--linie);
}
@media (max-width: 899px) {
    /* 5.5rem statt einer Rechnung aus --ziel: Die Tableiste ist hoeher
       als ein Fingerziel -- sie traegt Symbol und Text untereinander und
       dazu den sicheren Bereich am unteren Rand. Gemessen bei 390 px:
       81 px. Mit der frueheren Rechnung ueberdeckte die Knopfleiste sie
       um 21 px. */
    .maske.lang > .knopfleiste { bottom: 5.5rem; }
}
.ausgabeleiste, .lauf { margin: var(--raum-4) 0; }
.blaettern { margin-top: var(--raum-4); }

/* Am Telefon gehoert die Hauptaktion ueber die volle Breite und als
   erstes -- ein Knopf, den man mit dem Daumen trifft, ohne zu zielen. */
@media (max-width: 560px) {
    .knopfleiste { flex-direction: column-reverse; align-items: stretch; }
    .knopfleiste .knopf, .knopfleiste .btn { width: 100%; }
}

.suche {
    display: flex; gap: var(--raum-2); align-items: center; flex-wrap: wrap;
    margin: var(--raum-4) 0 var(--raum-3);
}
.suche .feld, .suche .form-control { flex: 1 1 16rem; min-width: 0; }
.suche .wahl, .suche .form-select { flex: 0 1 12rem; }

/* Aktenkopf: Titelblock links, Handlung rechts aussen. Ohne das
   margin-left klebte der Knopf am Namen und sah aus wie ein Teil davon. */
.aktenkopf { display: flex; align-items: flex-start; gap: var(--raum-4); flex-wrap: wrap; margin-bottom: var(--raum-5); }
.aktenkopf > :first-child { flex: 1 1 20rem; min-width: 0; }
.aktenkopf > :last-child:not(:first-child) { margin-left: auto; }
.aktenkopf h1 { margin-bottom: 0; }
.untertitel { margin: var(--raum-1) 0 0; display: flex; gap: var(--raum-2); align-items: center; flex-wrap: wrap; color: var(--tinte-leise); font-size: var(--gr-m); }
.pfad { margin: 0 0 var(--raum-2); font-size: var(--gr-s); color: var(--tinte-leise); }
.trefferzahl { margin: 0 0 var(--raum-3); font-size: var(--gr-m); color: var(--tinte-leise); }

/* ---- Masken ---- */

.maske, .unterformular { display: flex; flex-direction: column; gap: var(--raum-1); max-width: 52rem; }
.maske label, .unterformular label { margin-top: var(--raum-4); }
.nebeneinander { display: flex; gap: var(--raum-4); flex-wrap: wrap; }
.nebeneinander > div { flex: 1 1 12rem; min-width: 0; }
.nebeneinander > div.breit { flex: 2 1 18rem; }

/* Und die Gegenrichtung: Ein Datum, eine Dauer, eine Menge brauchen
   keine Handbreite. Ohne sie bekommt jedes Feld denselben Anteil, und
   ein Feld fuer "3,5" ist so breit wie eines fuer einen Firmennamen --
   die Regel aus Abschnitt 5 gilt fuer die Spalte wie fuer das Feld. */
.nebeneinander > div.eng { flex: 0 1 9rem; }
.schmal { max-width: 10rem; }

/* Die Stufe zwischen .schmal und voller Breite: ein Feld fuer eine
   kurze Bezeichnung, einen Kurznamen, ein Kennzeichen.

   Sie fehlte, und das Ergebnis war sichtbar: Die Bezeichnung einer
   Stilprobe stand in .schmal (10rem) und zeigte von
   "Frohe Weihnachten (29.08.2026)" die Haelfte -- ein Vorschlag, den
   man nicht lesen kann, ist keiner. In voller Breite dagegen sah das
   Feld aus wie ein weiteres Feld der Nachricht. */
.mittel { max-width: 24rem; }
.feldgruppe { margin-top: var(--raum-2); }
.feldgruppe[hidden] { display: none; }
.schalter { display: flex; align-items: center; gap: var(--raum-2); font-size: var(--gr-m); color: var(--tinte); margin-top: var(--raum-3); }
.schalter input { width: auto; margin: 0; }
/* DAS KAESTCHEN IM SCHALTER (#202, #208). Die Zeile darueber ist fuer das
   Datumsfeld im Schalter (/Export) gedacht und traf das Kaestchen mit:
   Mit form-check-input setzt Bootstrap appearance: none, und "width: auto"
   liess davon 2 px uebrig -- ein Strich statt eines Kaestchens, gemessen
   auf /Abos, /Einstellungen, /Artikel, auch am Haken fuer einen Rueckstand.

   Deshalb hier die Breite aus der Feldregel (1.125rem) und das Aussehen
   des Browsers zurueck: Dessen Rand hat den noetigen Kontrast und folgt
   color-scheme und accent-color -- Bootstraps gezeichnetes Kaestchen hat
   einen Rand von 1,3 : 1 auf Weiss und bleibt im dunklen Modus weiss.
   flex: none, damit ein langer Text (ein Rueckstand bei 375 px) das
   Kaestchen nicht zusammendrueckt. Muster: /muster.html#b4. */
.schalter input[type="checkbox"], .schalter input[type="radio"] {
    width: 1.125rem; flex: none; appearance: auto;
}
.schalter label { margin: 0; }
.zahlungsform { display: flex; flex-wrap: wrap; gap: var(--raum-3); align-items: flex-end; margin-top: var(--raum-4); }
.zahlungsform label { display: flex; flex-direction: column; gap: var(--raum-1); margin: 0; }
.zahlungsform label.weit { flex: 1 1 14rem; }
.hinzu { margin-top: var(--raum-5); }
.hinzu summary { cursor: pointer; font-size: var(--gr-m); color: var(--akzent-tinte); min-height: var(--ziel); display: flex; align-items: center; }

/* Eintragen in einer Zeile: Ein Kontaktweg oder eine Adresse soll nicht
   mehr kosten als das Abtippen. Der Knopf sitzt am Ende der Reihe auf
   Feldhoehe -- nicht darunter, wo er wie ein Abschluss des ganzen
   Formulars aussaehe. */
.zeilenform { margin-top: var(--raum-4); }
.zeilenform .nebeneinander { align-items: flex-end; gap: var(--raum-3); }
.zeilenform .nebeneinander + .nebeneinander { margin-top: var(--raum-3); }
.zeilenform label { margin-top: 0; }

/* Schmalere Grundbreiten als in einer gewoehnlichen Maske: Hier sollen
   fuenf Felder in eine Zeile passen, nicht zwei. Ohne das brach die
   Adresse nach "PLZ" um und "Ort" stand allein in der zweiten Zeile --
   gemessen bei 911 px Formularbreite. */
.zeilenform .nebeneinander > div { flex: 1 1 8rem; }
.zeilenform .nebeneinander > div.breit { flex: 2 1 12rem; }
.zeilenform .eng { flex: 0 1 6rem; }
.zeilenform .mitknopf {
    flex: 0 0 auto; display: flex; align-items: center; gap: var(--raum-4);
}
.zeilenform .mitknopf .schalter { margin-top: 0; white-space: nowrap; }

/* ---- Register ---- */

.register {
    display: flex; flex-wrap: wrap; gap: var(--raum-1);
    margin: 0 0 var(--raum-5); border-bottom: 1px solid var(--linie);
}
.register a {
    padding: var(--raum-2) var(--raum-3); font-size: var(--gr-m); font-weight: 550;
    color: var(--tinte-leise); text-decoration: none;
    border-bottom: 2px solid transparent; margin-bottom: -1px;
}
.register a:hover { color: var(--tinte); }
.register a.aktiv, .register a[aria-current] { color: var(--akzent-tinte); border-bottom-color: var(--akzent); }

/* ---- Kacheln (Startseite, Einstellungen) ---- */

.kacheln, .unterpunkte {
    display: grid; gap: var(--raum-4);
    grid-template-columns: repeat(auto-fit, minmax(min(15rem, 100%), 1fr));
    margin: 0 0 var(--raum-5);
}
.kachel, .unterpunkte a {
    display: flex; flex-direction: column; gap: var(--raum-1);
    padding: var(--raum-4) var(--raum-5);
    border: 1px solid var(--linie); border-radius: var(--rund-3);
    background: var(--flaeche); color: var(--tinte); text-decoration: none;
    box-shadow: var(--schatten-1);
    transition: border-color var(--wechsel), transform var(--wechsel);
}
.kachel:hover, .unterpunkte a:hover { border-color: var(--akzent-rand); color: var(--tinte); }
/* In der Kachel steht die Zahl immer links an der Beschriftung -- auch
   ein Betrag. Rechtsbuendig gehoeren Betraege nur in Spalten, wo sie
   untereinander verglichen werden. */
.kachel .zahl { font-size: var(--gr-2xl); font-weight: 650; font-variant-numeric: tabular-nums; letter-spacing: -0.02em; text-align: left; }
.kachel .was, .unterpunkte .was { font-weight: 600; font-size: var(--gr-m); }
.kachel .wozu, .unterpunkte .wozu { font-size: var(--gr-s); color: var(--tinte-leise); }
.kachel.warnung .zahl { color: var(--gefahr-tinte); }

/* ---- Akte: Karten nebeneinander ----
   Eine Akte besteht aus mehreren kurzen Abschnitten. Untereinander
   gestapelt ergeben sie eine Seite, die man scrollen muss, um zu sehen,
   dass fast nichts drinsteht -- am breiten Bildschirm stehen sie deshalb
   nebeneinander. Was breit sein muss (Verbindungen, Gefahrenbereich),
   nimmt die ganze Zeile. */
.spalten {
    display: grid; gap: var(--raum-4);
    /* min(24rem, 100%) und nicht 24rem: Eine feste Mindestbreite ist auf
       einem 375-px-Bildschirm breiter als der Bildschirm, und das Raster
       schiebt die Seite hinaus -- gemessen: 40 px Ueberlauf. Mit min()
       gibt die Spalte auf, sobald der Platz fehlt. */
    grid-template-columns: repeat(auto-fit, minmax(min(24rem, 100%), 1fr));
    align-items: start;
}
.spalten > .karte { margin: 0; height: 100%; }
.spalten > .karte.breit { grid-column: 1 / -1; }

/* Ein Aufklapper im Kartenraster nimmt immer die ganze Zeile: Zugeklappt
   ist er eine Zeile hoch, und eine halbe Zeile neben einer hohen Karte
   sieht aus wie ein Rest. */
.spalten > .fachgruppe, .spalten > .kopfdaten { grid-column: 1 / -1; margin: 0; }

/* ---- Feld und Wert ----
   Die Beschriftung ist klein und grau, der Wert steht gross darunter:
   Gesucht wird der Wert, nicht sein Name. Umgekehrt -- fette
   Beschriftung, blasser Wert -- liest sich wie ein Formular, das noch
   auszufuellen ist.

   Markup: <dl class="daten"><div><dt>…</dt><dd>…</dd></div>…</dl>.
   Die Gruppierung in <div> ist in HTML5 erlaubt und noetig, damit ein
   Paar zusammen in einer Rasterzelle landet. */
.daten {
    display: grid; gap: var(--raum-4) var(--raum-5); margin: 0;
    grid-template-columns: repeat(auto-fit, minmax(min(11rem, 100%), 1fr));
}
.daten > div { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.daten dt { font-size: var(--gr-s); color: var(--tinte-leise); }
.daten dd { margin: 0; overflow-wrap: anywhere; }
.leerwert { color: var(--tinte-schwach); }

.notiz {
    margin: var(--raum-4) 0 0; padding: var(--raum-3) var(--raum-4);
    background: var(--flaeche-leise); border-radius: var(--rund-2);
    font-size: var(--gr-m); white-space: pre-wrap;
}

/* ---- Eintragsliste ----
   Adressen, Kommunikationswege, Verbindungen: Gegenstand links, seine
   Handlung rechts. Getrennt wird mit Linien und nicht mit Karten -- eine
   Karte in einer Karte ist eine Schachtel zu viel. */
.eintraege { list-style: none; margin: 0; padding: 0; }
.eintraege > li {
    display: flex; align-items: flex-start; justify-content: space-between;
    gap: var(--raum-4); padding: var(--raum-3) 0;
    border-bottom: 1px solid var(--linie-leise);
}
.eintraege > li:first-child { padding-top: 0; }
.eintraege > li:last-child { border-bottom: none; padding-bottom: 0; }
.eintraege > li > div:first-child { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--raum-1) var(--raum-2); min-width: 0; }
.eintraege > li > div:first-child > div { flex: 1 0 100%; }
.zeilenaktionen { display: flex; gap: var(--raum-3); align-items: center; flex: none; }

/* "Zuletzt angelegt" auf der Startseite: Sorte, Beleg, Zeitpunkt.

   Der Zeitpunkt steht rechts und leise -- die REIHENFOLGE ist die
   Auskunft, das Datum bestaetigt sie nur. Mit Uhrzeit, weil hier Dinge
   von heute stehen und fuenfmal dasselbe Datum untereinander nichts
   ordnet. */
.eintraege > li > a { display: flex; align-items: baseline; gap: var(--raum-2); min-width: 0; text-decoration: none; color: var(--tinte); }
.eintraege > li > a:hover span:not(.etikett), .eintraege > li > a:focus-visible span:not(.etikett) { color: var(--akzent); text-decoration: underline; }
.eintraege .wann {
    flex: none; white-space: nowrap; font-variant-numeric: tabular-nums;
    font-size: var(--gr-xs); color: var(--tinte-schwach);
}

/* In /Suche traegt dieselbe Spalte den Beisatz -- oft einen Kundennamen,
   und der ist kein Zeitpunkt, der in eine Zeile passt. Gemessen am
   27.09.2026 bei 375 px: Ein langer Name lief quer aus der Karte (385 px
   breit, Seite 443 statt 375), und der Verweis daneben schrumpfte auf
   0 px.

   DER ERSTE AUSWEG WAR FALSCH: Die Spalte auf die halbe Zeile zu kappen
   hielt die Seite in 375 px, aber der Verweis schrumpfte unter seine
   Mindestbreite -- "Auftrag A2026-0018" braucht 175 px und bekam 131, die
   Nummer lag 21 px ueber dem Beisatz (Pruefer, 27.09.2026). Ein
   Flex-Element mit min-width:0 laesst sich kleiner machen als sein
   laengstes Wort; es bricht dann nicht um, es ragt hinaus.

   Deshalb am Telefon UMBRECHEN STATT QUETSCHEN: Die Zeile darf zwei
   Zeilen haben. Passen Verweis und Beisatz in voller Breite
   nebeneinander, bleiben sie es ("K-0091" neben einem Namen); passen
   sie nicht, rueckt der Beisatz darunter, rechtsbuendig, und bricht in
   sich um. Der Verweis beansprucht dafuer 12rem und nie weniger als sein
   laengstes Wort (min-width: auto statt 0) -- so entscheidet der Umbruch,
   bevor gequetscht wird. Ein kurzer Zeitpunkt auf der Startseite ("Zuletzt
   angelegt") bleibt damit neben einem zweizeiligen Titel stehen. */
@media (max-width: 560px) {
    .eintraege > li:has(> a + .wann) { flex-wrap: wrap; row-gap: var(--raum-1); }
    .eintraege > li:has(> a + .wann) > a { flex: 1 1 12rem; min-width: auto; }
    .eintraege .wann {
        flex: 0 1 auto; min-width: 0; margin-left: auto;
        white-space: normal; overflow-wrap: anywhere; text-align: right;
    }

    /* DIE ZEILENAKTIONEN RUECKEN UNTER DEN TEXT, WENN SIE IHN ERDRUECKEN.
       Gemessen am 27.09.2026 bei 375 px in der Akte: Neben "Ändern",
       "als Standard" und "entfernen" blieben der Lieferanschrift 72 px,
       und ihr Etikett (102 px) lag ueber dem Knopf "Ändern". Mit
       Fingerzielhoehe wird das nicht besser.

       Der Text beansprucht deshalb 12rem; passen die Aktionen daneben
       nicht mehr, brechen sie in die naechste Zeile und stehen rechts.
       Eine einzelne Aktion ("entfernen") bleibt neben dem Text. */
    .eintraege > li:has(> .zeilenaktionen) { flex-wrap: wrap; row-gap: var(--raum-1); }
    .eintraege > li:has(> .zeilenaktionen) > div:first-child { flex: 1 1 12rem; }
    .eintraege > li > .zeilenaktionen { margin-left: auto; }
}

/* Ein Kontaktweg ist oft ein Wort ohne Trennstelle -- eine lange
   Mailadresse lief in der Akte am Telefon ueber den Rand (27.09.2026).
   anywhere statt break-word: Nur anywhere zaehlt auch bei der
   Mindestbreite mit, und die entscheidet hier, weil die Zeilenaktionen
   daneben nicht schrumpfen. */
.eintraege > li > div:first-child { overflow-wrap: anywhere; }

.beziehungen .beschriftung { color: var(--tinte-leise); font-size: var(--gr-s); }
.beziehungen > li.beendet { color: var(--tinte-schwach); }
.beziehungen > li.beendet a { color: var(--tinte-leise); }

/* ---- Auswahl aus wenigen Moeglichkeiten ----
   Art einer Person, Rollen: kein Rahmen um die Gruppe, sondern Felder,
   die man antippen kann. Fingergroesse, damit sie auch am Telefon
   treffbar sind. */
fieldset { border: none; margin: 0; padding: 0; min-width: 0; }
legend { padding: 0; font-size: var(--gr-s); font-weight: 550; color: var(--tinte-leise); }

.artwahl, .rollen {
    display: flex; flex-wrap: wrap; gap: var(--raum-2);
    margin-top: var(--raum-4);
}
.artwahl legend, .rollen legend { flex: 1 0 100%; margin-bottom: var(--raum-2); }
.artwahl label, .rollen label {
    display: inline-flex; align-items: center; gap: var(--raum-2);
    min-height: var(--ziel); padding: 0 var(--raum-4); margin: 0;
    border: 1px solid var(--linie-stark); border-radius: var(--rund-2);
    background: var(--flaeche); color: var(--tinte); font-size: var(--gr-m);
    font-weight: 500; cursor: pointer;
}
.artwahl label:hover, .rollen label:hover { border-color: var(--akzent-rand); background: var(--akzent-flaeche); }
.artwahl label:has(:checked), .rollen label:has(:checked) {
    border-color: var(--akzent); background: var(--akzent-flaeche); color: var(--akzent-tinte); font-weight: 600;
}
.artwahl label:has(:focus-visible), .rollen label:has(:focus-visible) {
    outline: 2px solid var(--fokus); outline-offset: 2px;
}

/* Knopfgruppe, aus der genau eine Sache gilt (Sprache, Schriftgroesse). */
.sprachwahl { display: flex; flex-wrap: wrap; gap: var(--raum-2); }

/* ---- Kleinigkeiten, die sonst nirgends hingehoeren ---- */
td.rollen, td.status { white-space: nowrap; }
td.rollen { display: table-cell; gap: 0; }
.fehlertext { color: var(--gefahr-tinte); font-size: var(--gr-s); }
.zuruecksetzen { font-size: var(--gr-s); }
table tr.summe td { border-top: 2px solid var(--tinte); font-weight: 650; }
table.positionen td { vertical-align: top; }

/* ---- Ergebnis einer Prüfung am Feld ----
   Steht direkt unter dem Feld, das geprüft wurde, und trägt seine Aussage
   in der Marke -- nicht nur in der Farbe. Wer Rot und Grün nicht
   unterscheidet, liest "gültig" und "nicht gültig". */
.uidergebnis {
    margin: var(--raum-3) 0 var(--raum-4);
    padding: var(--raum-3) var(--raum-4);
    border: 1px solid var(--linie); border-left-width: 3px;
    border-radius: var(--rund-2);
    background: var(--flaeche-leise);
    font-size: var(--gr-m);
}
.uidergebnis.gut    { border-color: var(--gut-rand);    background: var(--gut-flaeche);    color: var(--gut-tinte); }
.uidergebnis.gefahr { border-color: var(--gefahr-rand); background: var(--gefahr-flaeche); color: var(--gefahr-tinte); }
.uidergebnis.warn   { border-color: var(--warn-rand);   background: var(--warn-flaeche);   color: var(--warn-tinte); }
.uidergebnis .kopfzeile {
    display: flex; flex-wrap: wrap; align-items: center; gap: var(--raum-2);
    margin: 0;
}
.uidergebnis .uidregister { margin-top: var(--raum-3); }
.uidergebnis .uidregister dt { color: inherit; opacity: 0.75; }
.uidergebnis .leise { margin: var(--raum-2) 0 0; color: inherit; opacity: 0.8; font-size: var(--gr-s); }

/* Das Feld mit seinem Prüfknopf: Der Knopf gehört an das Feld, nicht in
   eine eigene Zeile darunter. */
.mitpruefung { display: flex; gap: var(--raum-2); align-items: stretch; }
.mitpruefung .form-control, .mitpruefung .feld { flex: 1 1 auto; min-width: 0; }
.mitpruefung .btn, .mitpruefung .knopf { flex: 0 0 auto; }
.mitpruefung .btn[aria-busy="true"] { opacity: 0.7; cursor: progress; }

/* ---- Zusammengeklappter Kopf ----
   Angaben, die einmal ausgefuellt und danach selten angefasst werden,
   stehen hinter einem Aufklapper. Zugeklappt zeigt er in einer Zeile,
   was drinsteht -- sonst muss man aufklappen, nur um nachzusehen, und
   hat nichts gewonnen. */
/* Zwei Anwendungen desselben Bauteils: der Auftragskopf und die
   Fachgruppen der Personenmaske. Sie sehen gleich aus, weil sie dasselbe
   tun -- zwei Aufklapper-Stile in einer Anwendung waeren einer zu viel. */
.kopfdaten, .fachgruppe {
    background: var(--flaeche); border: 1px solid var(--linie);
    border-radius: var(--rund-3); margin: 0 0 var(--raum-5);
    box-shadow: var(--schatten-1);
}
.kopfdaten > summary, .fachgruppe > summary {
    display: flex; align-items: baseline; gap: var(--raum-3); flex-wrap: wrap;
    padding: var(--raum-3) var(--raum-4); min-height: var(--ziel);
    cursor: pointer; border-radius: var(--rund-3);
    font-size: var(--gr-m); list-style-position: inside;
}
.kopfdaten > summary::marker, .fachgruppe > summary::marker { color: var(--tinte-schwach); }
.kopfdaten > summary:hover, .fachgruppe > summary:hover { background: var(--flaeche-leise); }
.kopfdaten > summary:focus-visible, .fachgruppe > summary:focus-visible {
    outline: 2px solid var(--fokus); outline-offset: -2px;
}
.kopfdaten > summary .was { font-weight: 600; color: var(--akzent-tinte); }
.kopfdaten > summary .lese { color: var(--tinte-leise); font-size: var(--gr-s); }
.kopfdaten[open] > summary, .fachgruppe[open] > summary {
    border-bottom: 1px solid var(--linie); border-radius: var(--rund-3) var(--rund-3) 0 0;
}
.kopfdaten > form { padding: var(--raum-4) var(--raum-5) var(--raum-5); }
.kopfdaten .maske { max-width: none; }

/* SEITENZWECK: dasselbe Aufklapper-Bauteil, mit Erklaertext statt Formular.
   Anlass: Der CEO stand am 18.09.2026 in "Beitraege" und wusste nicht,
   wofuer die Seite ist, was sie macht und wo ihr Ergebnis erscheint.

   Zugeklappt steht die Antwort schon da -- Regel 1 der Aufklapper in
   gestaltung.md: nicht "Hilfe", sondern was drinsteht. Zu ist der
   Normalzustand: Wer die Seite kennt, braucht die Erklaerung nicht, und
   was immer offen dasteht, wird zur Tapete. Die Zeile ist deshalb keine
   leise Nebenauskunft wie im Auftragskopf, sondern der Satz selbst. */
.seitenzweck > summary .lese { color: var(--tinte); font-size: var(--gr-m); }
.seitenzweck > .erklaerung {
    padding: var(--raum-3) var(--raum-5) var(--raum-4);
    display: grid; gap: var(--raum-2);
    max-width: 68ch;
}
.seitenzweck > .erklaerung p { margin: 0; }

/* Die Fachgruppe traegt ihren Inhalt ohne Formular-Wrapper: Die Felder
   sind direkte Kinder, also bekommen sie den Rand einzeln. */
.fachgruppe > summary { font-weight: 600; }
.fachgruppe > summary > .leise { font-weight: 400; }
.fachgruppe > :not(summary) { margin-left: var(--raum-5); margin-right: var(--raum-5); }
.fachgruppe > :not(summary):first-of-type { margin-top: var(--raum-4); }
.fachgruppe[open] { padding-bottom: var(--raum-5); }

/* Felder, deren Wirkung noch nicht gebaut ist. Der Hinweis gehoert VOR
   die Felder, nicht darunter: Wer erst nach dem Ausfuellen erfaehrt,
   dass nichts davon wirkt, hat umsonst getippt. */
.fachgruppe > summary .vorrat {
    background: var(--warn-flaeche); border-color: var(--warn-rand); color: var(--warn-tinte);
}
.fachgruppe.vorrat > :not(summary):not(.hinweis) { opacity: 0.85; }

/* Eine Gruppe mit einem Feldfehler faellt schon zugeklappt auf. Sie geht
   ausserdem von selbst auf (das entscheidet die Ansicht) -- aber wenn
   jemand sie wieder zuklappt, muss die Markierung bleiben. */
.fachgruppe.hatfehler { border-color: var(--gefahr-rand); }
.fachgruppe.hatfehler > summary { color: var(--gefahr-tinte); }

/* ---- Positionsraster ----
   Eine Tabelle, in der jede Zelle ein Eingabefeld ist. Damit sie wie ein
   Raster wirkt und nicht wie vierzig Formularfelder, tragen die Felder
   keinen eigenen Rahmen -- erst der Fokus zeichnet einen. Die Linien
   kommen von der Tabelle.

   Die Reihenfolge der Spalten ist die Reihenfolge des Denkens: was, wie
   viel, wovon, zu welchem Preis, mit welchem Nachlass, mit welcher
   Steuer, macht wie viel. */
table.raster { table-layout: fixed; }
table.raster th { padding: var(--raum-2) var(--raum-3); }
table.raster td { padding: 0; vertical-align: middle; }

table.raster .feld, table.raster .wahl {
    border: 1px solid transparent; border-radius: 0; background: none;
    min-height: 2.5rem; padding: var(--raum-2) var(--raum-3);
    font-size: var(--gr-m); box-shadow: none; width: 100%;
}
table.raster .feld:hover, table.raster .wahl:hover { background: var(--flaeche-leise); }
table.raster .feld:focus, table.raster .wahl:focus {
    background: var(--flaeche); border-color: var(--akzent);
    box-shadow: 0 0 0 3px color-mix(in oklab, var(--akzent) 22%, transparent);
    position: relative; z-index: 1;
}
table.raster .wahl { background-position: right var(--raum-2) center; padding-right: var(--raum-5); }
/* 1rem ohne Zeigegeraet, --gr-s mit: gestaltung.md Kap. 3, "16 px ist
   die Untergrenze fuer Eingabefelder". Gemessen am 27.09.2026 bei
   375 px: 14 px, und Safari zoomte beim Antippen hinein. */
table.raster .beschreibung { font-size: 1rem; color: var(--tinte-leise); min-height: 2rem; padding-top: 0; }
@media (hover: hover) and (pointer: fine) {
    table.raster .beschreibung { font-size: var(--gr-s); }
}
table.raster .beschreibung:placeholder-shown:not(:focus):not(:hover) { opacity: 0.55; }
table.raster .rechts { text-align: right; font-variant-numeric: tabular-nums; }
table.raster td.netto { padding: 0 var(--raum-3); }

/* Die Spaltenbreiten gelten NUR im Tabellenmodus.

   Sie standen frueher ohne Abfrage da und blieben in der Kartenform
   stehen -- dort ist jede Zelle eine eigene Flexzeile, und eine
   Breite von 6,5 rem heisst dann nicht "die Spalte ist so breit",
   sondern "die ganze Zeile ist so schmal". Die Zelle Menge war
   104 px breit, ihre Beschriftung allein braucht 112: Das Eingabefeld
   wurde auf 26 px gequetscht und lag ausserhalb seiner Zelle. Auf dem
   Telefon sah es aus, als fehlten die Felder ganz -- man las
   Beschriftungen ohne Eingaben. Gemessen am 29.08.2026 bei 375 px.

   Ein Zuruecknehmen in der Kartenform reichte nicht: Die Abo-Regeln
   traegen ein Attribut und schlagen jede Rueckname, die ohne eines
   auskommt. Deshalb gelten die Breiten gar nicht erst. */
@media (min-width: 761px) {
    table.raster .spalte-nr     { width: 3rem; }

    /* DIE NUMMER UNTER IHRE UEBERSCHRIFT (CEO 18.09.2026: "kann das +
       und die Positionsnummer etwas nach rechts"). Die Kopfzelle traegt
       das Polster aller th, die Datenzelle hatte keines: Gemessen stand
       die 1 bei x = 380, das P von POS bei 392. Nur im Tabellenmodus --
       in der Kartenform sitzt die Nummer oben rechts. */
    table.raster td.spalte-nr   { padding-left: var(--raum-3); }

    /* DIE ERSTE ZEILE EINER POSITION IST EINE LINIE (CEO 18.09.2026: "die
       Bezeichnung sollte auf der gleichen Hoehe wie die 1 und danach die
       Menge etc. sein, dass eine Linie ist"). Die Bezeichnungszelle traegt
       zwei Felder untereinander, alle anderen eines -- mittig ausgerichtet
       standen Nummer, Menge und Preis ZWISCHEN Bezeichnung und
       Beschreibung. Oben ausgerichtet stehen sie mit der Bezeichnung auf
       einer Linie, und die Beschreibung steht darunter allein.

       Nummer, Netto und Kreuz sind kein Feld, sondern Text und Knopf: Sie
       bekommen die Hoehe eines Feldes (2,5 rem) als Zeilenhoehe, damit
       ihre Mitte auf der Mitte der Felder liegt. */
    table.raster tr.zeile > td { vertical-align: top; }
    table.raster tr.zeile > td.spalte-nr,
    table.raster tr.zeile > td.netto,
    table.raster tr.zeile > td.spalte-weg { line-height: 2.5rem; }
    table.raster tr.zeile > td.spalte-weg .weg { vertical-align: middle; }

    /* Die Bezeichnung braucht eine Mindestbreite, sonst verschwindet
       sie. table-layout: fixed verteilt nach den angegebenen Breiten
       und gibt der einzigen Spalte ohne Angabe den Rest -- fordern die
       festen Spalten zusammen mehr als der Rahmen hergibt, ist der
       Rest NULL. Im Abo mit zwoelf Spalten war die Bezeichnungszelle
       dadurch bei jeder Fensterbreite 0 px breit, auch bei 1920. */
    table.raster .spalte-bezeichnung { width: 17rem; }   /* +1 rem von der Steuer, 18.09.2026 */
    table.raster .spalte-menge  { width: 6.5rem; }
    table.raster .spalte-einheit{ width: 6.5rem; }
    table.raster .spalte-preis  { width: 8rem; }
    table.raster .spalte-rabatt { width: 5.5rem; }

    /* STEUER: 6 statt 9 rem, und das ist gemessen.
       Die Auftragszeile brauchte 1056 px bei einem Rahmen von 997 --
       die letzte Spalte (der Loeschknopf) endete 3 px ausserhalb des
       Fensters und war nur ueber seitliches Rollen erreichbar. Ein
       Knopf, den man nicht sieht, ist fuer den, der ihn braucht, nicht
       da. Gemessen am 31.08.2026 auf 1366x768.

       Sechs rem tragen den Prozentwert samt Auswahlpfeil. Wofuer sie
       nicht reichen, ist die ausgeschriebene Kategorie ("20 %
       Normalsteuersatz") -- die steht im aufgeklappten Feld weiterhin
       ganz da, denn ein select zeigt seine Liste in voller Breite.

       5 STATT 6 REM SEIT 18.09.2026: Die Spalte zeigt nur noch den Satz
       ("20 %", "stfr.", "RC", "KU" -- Anzeigenamen.Kurz). Der CEO: "Die
       Steuer kann weiter nach rechts. Damit koennte Bezeichnung mehr Platz
       bekommen." Das gesparte rem geht AUSDRUECKLICH an die Bezeichnung
       (16 -> 17 rem): table-layout: fixed verteilt freien Platz anteilig
       auf alle Spalten, und die Bezeichnung bekaeme sonst nur ihren
       Viertelanteil davon.

       Und nicht 4,5 rem, das war der erste Versuch: "20 %" braucht bei
       17 px Schrift 37 px, das Auswahlfeld dazu 12 + 24 px Polster (der
       Pfeil) und 2 px Rahmen -- 75 px. Bei 1366 px Fensterbreite ergaben
       4,5 rem 72 px, und das Prozentzeichen waere abgeschnitten gewesen.
       Gemessen am 18.09.2026. */
    table.raster .spalte-steuer { width: 5rem; }
    table.raster .spalte-netto  { width: 7.5rem; }
    table.raster .spalte-weg    { width: 3rem; }

    /* Takt und Datum haben keine eigenen SPALTEN mehr -- sie stehen in
       der Taktzeile unter der Position. Die Breiten bleiben stehen, weil
       die Kartenform unter 761 px sie weiterhin benutzt. */
    table.raster .spalte-takt   { width: 7.5rem; }
    table.raster .spalte-datum  { width: 8.5rem; }

    /* Nur im Abo: Dort stehen zwoelf Spalten statt acht, und jede
       gesparte Breite ist eine, die der Bezeichnung zugutekommt. */
    table.raster[data-art="abo"] .spalte-einheit { width: 6rem; }
    table.raster[data-art="abo"] .spalte-preis   { width: 7.5rem; }

    /* KEINE EIGENE STEUERBREITE MEHR FUERS ABO. Sie stand hier auf 8 rem
       und war damit BREITER als die allgemeine (6 rem) -- ein Rest aus
       der Zeit, als das Abo zwoelf Spalten hatte und die Regel eine
       Ausnahme nach unten sein sollte. Nach dem Umbau auf neun Spalten
       waren es genau diese zwei rem, die das Raster mit 1008 px ueber
       den Rahmen von 997 hoben. Gemessen am 31.08.2026. */
}

/* Das Monatsende-Haekchen steht unter dem Datum, zu dem es gehoert, und
   nur wenn die Regel greift. Es traegt keine eigene Spalte: Ein Schalter,
   der fast immer verborgen ist, wuerde sonst eine Spalte lang leer
   bleiben. */
table.raster .monatsende {
    display: flex; align-items: center; gap: var(--raum-1);
    margin-top: var(--raum-1);
}
table.raster .monatsende[hidden] { display: none; }
table.raster .feld.datum { width: 100%; }
table.raster .spalte-weg    { text-align: center; }

/* Der Schliessknopf einer Zeile. Klein, grau, und erst beim Zeigen
   deutlich -- er soll erreichbar sein, aber nicht danach aussehen, als
   waere Loeschen die Hauptbeschaeftigung. */
table.raster .weg {
    border: none; background: none; cursor: pointer;
    font-size: 1.25rem; line-height: 1; color: var(--tinte-schwach);
    width: 2rem; height: 2rem; border-radius: var(--rund-1);
}
table.raster .weg:hover { background: var(--gefahr-flaeche); color: var(--gefahr-tinte); }

table.raster tr.fest { background: var(--flaeche-leise); }
table.raster tr.fest .feld, table.raster tr.fest .wahl { color: var(--tinte-leise); }

/* Zum Entfernen vorgemerkt: sichtbar durchgestrichen, aber noch da --
   ein Klick nimmt es zurueck. Weg ist sie erst beim Speichern. */
/* Die Blaesse liegt auf den FELDERN, nicht auf der Zeile: Sonst erbt
   die Marke "wird entfernt" sie mit -- und ausgerechnet der Hinweis,
   der die Zweideutigkeit aufloest, waere der blasseste Text der
   Zeile. */

/* ---- Die Taktzeile des Abos ----------------------------------------

   Zyklus, erste Periode und Ende stehen unter der Position statt neben
   ihr. Der Grund ist gemessen: Mit ihnen als Spalten brauchte das
   Raster 1400 px bei einem Rahmen von 997 -- vier Spalten lagen
   ausserhalb des Fensters, darunter Netto.

   SIE GEHOERT SICHTBAR ZUR ZEILE DARUEBER und nicht dazwischen: kein
   Trennstrich nach oben, eine leichte Einrueckung, und der Strich unter
   der Position wandert an ihr Ende. Sonst saehe eine Position aus wie
   zwei. */
table.raster tr.taktzeile > td { border-top: 0; padding-top: 0; }
table.raster tr.zeile:has(+ tr.taktzeile) > td { border-bottom: 0; }

/* KEIN display:flex AUF DER ZELLE -- und das ist kein Geschmack,
   sondern gemessen.

   Der erste Entwurf setzte "display: flex" auf das td, um Beschriftung
   und Feld nebeneinander zu stellen. Damit ist die Zelle KEINE
   Tabellenzelle mehr, und colspan verliert seine Wirkung: Die drei
   Felder standen alle bei x=347 untereinander statt nebeneinander,
   jedes 261 px breit. Am 31.08.2026 am Bild gesehen und nachgemessen.

   Die Zelle bleibt also table-cell. Seit 18.09.2026 gilt das weiter --
   die Flexbox sitzt in einem div IN der Zelle, nicht auf ihr.

   DIE TAKTZEILE IST EINE LEISTE, KEINE SPALTEN (CEO 18.09.2026: "sehr
   unuebersichtlich", dann: "die Spalte Erste Periode naeher an den
   Zyklus bringen und danach das nachziehen"). Vorher erbte jede Angabe
   die Breite der Spalten darueber: der Zyklus 514 px fuer ein
   Auswahlfeld, das Ende 298 px fuer Datum, Verlaengerung, Frist und
   Spur -- acht Zeilen untereinander, 255 px hoch.

   Jetzt EINE Zelle ueber die ganze Breite und darin vier Bloecke fester
   Breite: Sie fluchten in jeder Zeile gleich, gemessen bei 434, 610, 794
   und 978 px, und die Zeile ist 103 px hoch statt 255. */
table.raster tr.taktzeile > td.taktfelder { padding-bottom: var(--raum-2); }

table.raster tr.taktzeile .taktleiste {
    display: flex; flex-wrap: wrap; align-items: flex-start;
    gap: var(--raum-2) var(--raum-4);
}
table.raster tr.taktzeile .taktblock { display: flex; flex-direction: column; min-width: 0; }
table.raster tr.taktzeile .taktblock.zyklus { width: 10rem; }
table.raster tr.taktzeile .taktblock.periode,
table.raster tr.taktzeile .taktblock.ende { width: 10.5rem; }

/* Der vierte Block traegt Verlaengerung und Frist, OHNE Ueberschrift (CEO:
   "Am Ende die Ueberschrift kann weg"). Er hat trotzdem eine -- leer und
   fuer Vorleser verborgen --, damit sein erster Satz neben den FELDERN
   steht und nicht neben den Ueberschriften. Eine errechnete Einrueckung
   stand gemessen 5 px daneben und haette bei jeder anderen Schriftgroesse
   anders danebengestanden. */

table.raster tr.taktzeile .taktname {
    display: block; margin: 0 0 2px var(--raum-3);
    font-size: var(--gr-s); color: var(--tinte-leise);
    white-space: nowrap;
}

/* Die Felder darin duerfen schrumpfen -- sonst schiebt ein langes
   Auswahlfeld den Block ueber seine Breite hinaus. */
table.raster tr.taktzeile .feld,
table.raster tr.taktzeile .wahl { min-width: 0; }

table.raster tr.taktzeile .monatsende { margin-left: var(--raum-3); }

/* DIE SAETZE MIT EINER ZAHL DARIN -- "verlaengert sich um 12 Monate",
   "kuendbar bis 3 Monate vor Ende" -- bleiben eine Zeile. Vorher war das
   Zahlenfeld 160 px breit, und der Satz zerfiel in drei Zeilen, deren
   Anfang fett gesetzt war wie eine eigene Ueberschrift.

   Das Feld ist so breit wie sein Inhalt und leicht unterstrichen: ohne
   Loch im Satz, und man sieht, dass man dort tippen kann. Wo der Browser
   field-sizing nicht kennt, bleibt es schmal und fest. */
table.raster tr.taktzeile .satz {
    display: flex; align-items: center; gap: var(--raum-1);
    white-space: nowrap; margin-left: var(--raum-3);
    min-height: 2.5rem;   /* die Hoehe eines Feldes: Mitte auf Mitte */
    font-size: var(--gr-s); font-weight: 400; color: var(--tinte-leise);
}
table.raster tr.taktzeile .satz[hidden] { display: none; }
table.raster tr.taktzeile .satz .feld {
    width: 3.5rem; min-height: 1.6rem; padding: 1px var(--raum-1);
    text-align: center; font-size: 1rem;
    border-bottom: 1px solid var(--linie-stark);
}
/* Wie die Beschreibung: 16 px ohne Zeigegeraet, sonst die Satzgroesse. */
@media (hover: hover) and (pointer: fine) {
    table.raster tr.taktzeile .satz .feld { font-size: var(--gr-s); }
}
@supports (field-sizing: content) {
    table.raster tr.taktzeile .satz .feld { field-sizing: content; width: auto; min-width: 2.5ch; }
}

table.raster tr.taktzeile .ohneende {
    display: flex; align-items: center; min-height: 2.5rem;
    margin-left: var(--raum-3);
    font-size: var(--gr-s); color: var(--tinte-leise);
}
table.raster tr.taktzeile .ohneende[hidden] { display: none; }
table.raster tr.taktzeile .spur {
    display: block; margin: var(--raum-1) 0 0 var(--raum-3);
    font-size: var(--gr-s); color: var(--tinte-leise);
}

/* In der Kartenform (unter 761 px) wird die Zelle selbst zur Flexzeile;
   die Leiste nimmt darin die ganze Breite, und die Bloecke brechen um.
   Die Ueberschriften bleiben sichtbar -- die Zelle traegt kein data-titel
   mehr, das sie ersetzen koennte. */
@media (max-width: 760px) {
    table.raster tr.taktzeile .taktleiste { flex: 1 1 auto; }

    /* Untereinander steht der vierte Block nicht NEBEN den Feldern --
       seine leere Ueberschrift hielte nur eine Leerzeile frei. */
    table.raster tr.taktzeile .taktblock.amende > .taktname { display: none; }
}

table.raster tr.geht .feld, table.raster tr.geht .wahl,
table.raster tr.geht .netto, table.raster tr.geht td.spalte-nr { opacity: 0.5; }
table.raster tr.geht .feld, table.raster tr.geht .netto { text-decoration: line-through; }
table.raster tr.geht .weg { color: var(--gefahr-tinte); }

/* Und sie SAGT es. Durchgestrichen allein ist zweideutig: Der Nutzer
   klickt das Kreuz, sieht die Zeile durchgestrichen, haelt sie fuer
   geloescht und verlaesst die Maske -- beim naechsten Oeffnen steht sie
   wieder da. Der haeufigste Fall dafuer ist ein Tippfehler in den
   ersten zehn Minuten, und bei einer Rechnung, die danach entsteht,
   steht die Zeile drauf.

   Das Kreuz sieht aus wie Loeschen und ist Vormerken. Der zweistufige
   Weg ist richtig -- ein Versehen bleibt zuruecknehmbar --, er sagt es
   nur nicht. */
table.raster tr.geht td.spalte-bezeichnung::after {
    content: "wird entfernt";
    display: block; margin-top: 2px;
    font-size: var(--gr-s); color: var(--gefahr-tinte);
    text-decoration: none;
}

/* An der BEZEICHNUNG und nicht am Kreuz: Das Kreuz steht in der
   letzten Spalte, und bei acht Spalten liegt die ausserhalb des
   sichtbaren Bereichs -- der Rahmen scrollt waagrecht. Gemessen: Die
   Marke stand da, wo niemand hinsieht.

   Die Bezeichnung ist die Spalte, auf der der Blick ohnehin liegt,
   und in der Kartenform steht sie oben. */

/* ---- Die leere Zeile zum Anhaengen (CEO 18.09.2026) -----------------

   "Eine neue Aboleistung ist schwer erkennbar, weil alles gleich
   aussieht" -- und bei den Auftraegen ebenso. Die Zeile am Ende des
   Rasters trug Werte (1,00, Stueck, 20 %), einen Betrag 0,00 und ein
   Kreuz: Sie sah aus wie eine gespeicherte Position. Am 29.08.2026 hat
   eine Rolle sie genau dafuer gehalten.

   Die Kennung war laengst da -- die Vorlage setzt "neu" --, gestaltet
   hat sie niemand. Jetzt, SOLANGE DIE BEZEICHNUNG LEER IST:
   - ein "+" in der Positionsspalte statt einer Nummer,
   - die Frage im Bezeichnungsfeld in der Akzentfarbe: eine Einladung,
     keine Luecke,
   - die vorbelegten Werte blass, denn sie sind Vorschlaege und keine
     Daten -- im Abo auch die Taktzeile darunter,
   - eine gestrichelte Linie darueber: noch keine Position,
   - kein Kreuz: Es gibt nichts zu entfernen.

   :placeholder-shown und nicht die Klasse allein: "neu" bleibt an der
   Zeile, auch wenn schon getippt wurde -- dann ist sie eine Position wie
   jede andere und sieht so aus. Kein Skript, kein Zustand. */
table.raster tr.zeile.neu:has(input.bezeichnung:placeholder-shown) > td {
    border-top: 1px dashed var(--linie-stark);
}

/* Und die UNTERKANTE der Zeile davor ebenso. Die Tabelle legt ihre
   Rahmen zusammen (border-collapse), und bei gleicher Breite gewinnt
   durchgezogen gegen gestrichelt -- gemessen: oben "dashed", unten
   "solid", zu sehen war solid. */
table.raster tr:has(+ tr.zeile.neu input.bezeichnung:placeholder-shown) > td {
    border-bottom: 1px dashed var(--linie-stark);
}

table.raster tr.zeile.neu:has(input.bezeichnung:placeholder-shown) td.spalte-nr::before {
    content: "+"; color: var(--akzent); font-weight: 700; font-size: 1.15em;
}
table.raster tr.zeile.neu:has(input.bezeichnung:placeholder-shown) input.bezeichnung::placeholder {
    color: var(--akzent); opacity: 1;
}

/* Blass, AUSSER im Feld, in dem gerade getippt wird: Wer mit der Menge
   anfaengt statt mit der Bezeichnung, soll nicht in ein graues Feld
   schreiben. */
table.raster tr.zeile.neu:has(input.bezeichnung:placeholder-shown) .feld:not(.bezeichnung):not(.beschreibung):not(:focus),
table.raster tr.zeile.neu:has(input.bezeichnung:placeholder-shown) .wahl:not(:focus),
table.raster tr.zeile.neu:has(input.bezeichnung:placeholder-shown) td.netto,
table.raster tr.zeile.neu:has(input.bezeichnung:placeholder-shown) + tr.taktzeile .feld:not(:focus),
table.raster tr.zeile.neu:has(input.bezeichnung:placeholder-shown) + tr.taktzeile .wahl:not(:focus),
table.raster tr.zeile.neu:has(input.bezeichnung:placeholder-shown) + tr.taktzeile .taktname {
    opacity: 0.45;
}
table.raster tr.zeile.neu:has(input.bezeichnung:placeholder-shown) .weg { visibility: hidden; }

/* ---- Summenblock unter dem Positionsraster ---- */

/* Rechts und schmal: Eine Summe wird an der Kante gesucht, nicht in der
   Mitte. Unter 640 px nimmt er die volle Breite -- schmaler waere er
   dort nicht lesbar, nur gedraengt. */
.summenblock {
    margin: var(--raum-4) 0 0 auto;
    max-width: 22rem;
    display: flex;
    flex-direction: column;
    gap: var(--raum-2);
}
.summenblock .zeile {
    display: flex;
    justify-content: space-between;
    gap: var(--raum-4);
    font-size: var(--gr-m);
}
.summenblock .zeile.gesamt {
    border-top: 1px solid var(--linie);
    padding-top: var(--raum-2);
    font-weight: 600;
    font-size: var(--gr-n);
}
/* Der Nachlass steht ueber dem Summenblock, rechtsbuendig wie er --
   die Wirkung soll direkt darunter sichtbar sein. */
.nachlass { margin: var(--raum-4) 0 0 auto; max-width: 30rem; }
.nachlassfelder {
    display: flex; gap: var(--raum-3); align-items: flex-end;
    justify-content: flex-end; flex-wrap: wrap;
}
.nachlassfelder .eng { flex: 0 1 9rem; }
.nachlassfelder label { margin-top: 0; }
.nachlassfelder .mitknopf { display: flex; align-items: flex-end; }
.nachlass .hinweis { margin-top: var(--raum-3); }

.summenblock .betrag { font-variant-numeric: tabular-nums; }

/* Der Pflichttext ist Bestandteil der Rechnung, nicht ein Hinweis der
   Anwendung. Deshalb kursiv und in Tintenfarbe -- nicht als .feldhinweis
   in Grau, das liest sich wie ein Kommentar des Programms. */
.pflichttext {
    margin: var(--raum-4) 0 0;
    font-style: italic;
    font-size: var(--gr-m);
    color: var(--tinte);
    max-width: 60ch;
}

/* Der Hinweis auf Ungespeichertes steht unter dem Block und nicht daneben:
   Er gehoert zu allen drei Zahlen, nicht zu einer. */
.summenblock .ungespeichert {
    margin: var(--raum-2) 0 0;
    font-size: var(--gr-s);
    color: var(--warn-tinte);
}

@media (max-width: 640px) {
    .summenblock { max-width: none; }
}

table.raster tfoot td { border-top: 2px solid var(--linie-stark); padding: var(--raum-3); }

/* Am Telefon wird aus jeder Zeile ein Block: Beschriftung links, Feld
   rechts. Neun Spalten nebeneinander sind dort nicht bedienbar. */
@media (max-width: 760px) {
    table.raster, table.raster tbody, table.raster tfoot, table.raster tr, table.raster td { display: block; width: auto; }
    table.raster thead { display: none; }
    table.raster { table-layout: auto; }
    table.raster tbody tr {
        border: 1px solid var(--linie); border-radius: var(--rund-2);
        margin-bottom: var(--raum-3); padding: var(--raum-2);
        background: var(--flaeche); position: relative;
    }
    table.raster td { border: none; display: flex; align-items: center; gap: var(--raum-3); }

    /* Die Bezeichnungsspalte ist die einzige mit ZWEI Feldern und der
       einzige Freitext im Raster. Neben der 7-rem-Beschriftung blieben
       bei 375 px je 69 px fuer Bezeichnung und Beschreibung -- zum
       Tippen unbrauchbar --, und die Beschreibung lag unter der
       Zeilennummer (beide endeten bei 324 px). Gemessen am 29.08.2026
       an dem Bildschirm, an dem in der Werkstatt Positionen erfasst
       werden.

       Sie bekommt deshalb als einzige die Beschriftung UEBER die
       Felder. Menge, Einheit und Steuer tragen kurze Werte und bleiben
       einzeilig -- dort ist das Nebeneinander schneller zu lesen.
       Rechts bleibt Platz fuer die Zeilennummer, die in der Ecke
       der Karte haengt. */
    table.raster td.spalte-bezeichnung {
        flex-direction: column; align-items: stretch;
        gap: var(--raum-1); padding-right: 3rem;
    }

    /* Das Monatsende-Haekchen passt in der Kartenform nicht neben
       Beschriftung und Datumsfeld -- es ragte um 72 px hinaus. Es rutscht
       jetzt in die naechste Zeile, statt den Rahmen zu sprengen. */
    table.raster td.spalte-datum { flex-wrap: wrap; }
    table.raster td.spalte-datum .monatsende { margin-left: 7rem; }
    table.raster td[data-titel]::before {
        content: attr(data-titel); flex: 0 0 7rem;
        font-size: var(--gr-s); color: var(--tinte-leise);
    }

    /* Nach der allgemeinen Regel, nicht davor: flex-basis liegt auf der
       Hauptachse, und die ist in der Bezeichnungsspalte die SENKRECHTE.
       Aus "7 rem breit" wurde dort "112 px hoch" -- eine Beschriftung
       mit einer handbreiten Luecke darunter. Bei gleicher Spezifitaet
       gewinnt die spaetere Regel. */
    table.raster td.spalte-bezeichnung::before { flex: none; }
    table.raster td.spalte-nr { position: absolute; right: var(--raum-2); top: var(--raum-2); font-size: var(--gr-s); }
    table.raster td.spalte-weg { justify-content: flex-end; }

    /* Das Kreuz war in der Karte 32 px gross und sass knapp unter dem
       Steuerfeld (gemessen am 27.09.2026 bei 375 px) -- wer das Feld
       treffen wollte, traf die Zeile weg. Jetzt ein Fingerziel und mit
       Abstand zum Feld darueber. */
    table.raster td.spalte-weg { margin-top: var(--raum-2); }
    table.raster .weg { width: var(--ziel); height: var(--ziel); }
    /* In der Karte tragen die Felder einen sichtbaren Rahmen.

       Im Tabellenmodus reicht das Zellenraster als Struktur, und der
       Rahmen kommt beim Zeigen dazu. Auf dem Telefon gibt es weder das
       eine noch das andere -- kein Raster, kein Hover: "zrrzt" und
       "test" sahen aus wie hingeschriebener Text. Wer nicht sieht,
       dass ein Feld eines ist, tippt nicht hinein. */
    table.raster .feld, table.raster .wahl {
        min-height: var(--ziel);
        border-color: var(--linie); border-radius: var(--rund-1);
        background: var(--flaeche);
    }
    table.raster tfoot td { display: flex; justify-content: space-between; }
}


/* ---- Artikelauswahl im Positionsraster ----

   Die Vorschlaege SCHWEBEN ueber der Tabelle statt im Fluss zu stehen.
   Eine Liste, die Platz einnimmt, schoebe beim Tippen alle Zeilen darunter
   nach unten -- und der Nutzer tippt weiter, waehrend sich unter seinen
   Augen die Zeile verschiebt, die er gerade bearbeitet.

   Deshalb traegt die Zelle position: relative und die Liste absolute. */
table.raster td.spalte-bezeichnung { position: relative; }

.treffer.vorschlaege {
    /* FIXED, nicht absolute -- und das ist kein Feinschliff, sondern der
       Unterschied zwischen brauchbar und unbrauchbar.

       Der Tabellenrahmen traegt overflow-x: auto, damit zwoelf Spalten
       scrollen koennen. CSS macht daraus implizit auch overflow-y: auto:
       Ein absolut gesetztes Kind wird an der Rahmenkante ABGESCHNITTEN.
       Bei der letzten Zeile -- der leeren, in die am haeufigsten getippt
       wird -- waren von 208 px Liste noch 74 zu sehen. Von drei
       Vorschlaegen einer.

       Fixed haengt am Fenster statt am Rahmen. Die Koordinaten setzt das
       Skript beim Oeffnen; beim Scrollen schliesst es die Liste, statt sie
       nachzufuehren -- wer scrollt, tippt gerade nicht. */
    position: fixed; z-index: 40;
    max-width: none; max-height: 16rem; overflow-y: auto;

    /* Breiter als ihr Feld, wenn das Feld schmal ist. Bei 375 px ist die
       Bezeichnungsspalte 69 px breit -- eine Liste in Feldbreite bricht
       "Poloshirt, bestickt" auf fuenf Zeilen um, und zwei Vorschlaege
       fuellen bereits die volle Hoehe. Gemessen am 29.08.2026.

       Das Skript setzt die Breite des Feldes als width; min-width schlaegt
       das, ohne dass die Koordinatenrechnung davon wissen muss. Die linke
       Kante zieht das Skript zurueck, damit nichts hinausragt. */
    min-width: min(20rem, calc(100vw - 1.5rem));
}

/* Zweizeilig: oben, wonach gesucht wurde, darunter die Angaben, die man
   zum Unterscheiden braucht. Ein Artikel ohne Preis daneben zwingt zum
   Auswaehlen, um zu sehen, ob er der richtige war. */
.treffer .vorschlag { display: flex; flex-direction: column; gap: 2px; }
.treffer .vorschlag .zusatz {
    font-size: var(--gr-s); color: var(--tinte-leise);
    display: flex; gap: var(--raum-3);
}

/* Der markierte Vorschlag -- der, den Enter uebernehmen wuerde. Ein Balken
   an der Kante wie bei der gewaehlten Listenzeile, kein Farbteppich. */
.treffer button.gewaehlt {
    background: var(--akzent-flaeche); color: var(--akzent-tinte);
    box-shadow: inset 3px 0 0 var(--akzent);
}
.treffer button.gewaehlt .zusatz { color: var(--akzent-tinte); }

/* Kein Treffer ist kein Fehler: Wer etwas Einmaliges abrechnet, tippt es
   einfach. Der Satz sagt genau das, damit niemand nach einem Artikel
   sucht, den es nicht gibt. */
.treffer .ohnetreffer {
    padding: var(--raum-2) var(--raum-3);
    font-size: var(--gr-s); color: var(--tinte-leise);
}

/* Die Herkunft einer Zeile ist ein .etikett -- dasselbe Bauteil wie in der
   Akte, nur mit einem Kreuz daran. Ein zweites, das genauso aussieht,
   waere beim naechsten Feinschliff sofort auseinander.

   Bewusst KEINE Hervorhebung, wenn der Preis vom Artikel abweicht: Die
   Zeile ist dann nicht falsch, sondern angepasst. Wer den Bezug loesen
   will, tut es ueber das Kreuz -- ausdruecklich, nicht durch die
   Hintertuer. */
.herkunft { margin-top: 2px; }
.herkunft button {
    background: none; border: none; padding: 0; cursor: pointer;
    font: inherit; line-height: 1; color: var(--tinte-schwach);
}
.herkunft button:hover { color: var(--gefahr-tinte); }
.herkunft[hidden] { display: none; }

/* ---- Wahlgruppe: eine Zeile je Moeglichkeit, Zusatzfeld daneben ----

   Das Zusatzfeld steht IN der Zeile seiner Moeglichkeit, nicht darunter
   und nicht daneben in einer eigenen Spalte. Wer "eine Rolle" waehlt,
   sieht das Rollenfeld an derselben Stelle erscheinen, an der er gerade
   geklickt hat.

   Und es erscheint nur dann: Ein Feld, das zu einer nicht gewaehlten
   Moeglichkeit gehoert, kann leer bleiben und wird trotzdem gesendet --
   eine Rollenauswahl ohne Rolle traefe alle. */
.wahlgruppe { border: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--raum-1); }
.wahlzeile {
    display: flex; align-items: center; gap: var(--raum-3);
    padding: var(--raum-2) var(--raum-3);
    border: 1px solid transparent; border-radius: var(--rund-2);
    cursor: pointer;
}
.wahlzeile:hover { background: var(--flaeche-leise); }
.wahlzeile:has(input:checked) { border-color: var(--linie); background: var(--flaeche-leise); }
.wahlzeile input[type="radio"] { width: auto; margin: 0; flex: none; }
.wahlzeile .was { flex: none; }
.wahlzeile .dazu { display: flex; align-items: center; gap: var(--raum-2); margin-left: auto; }
.wahlzeile .dazu[hidden] { display: none; }
.wahlzeile .dazu .form-control, .wahlzeile .dazu .form-select { margin: 0; }
.wahlzeile .dazu .eng { width: 5rem; }

/* AM TELEFON BRICHT DIE WAHLZEILE UM -- das Feld zur Wahl steht UNTER
   ihrem Text, eingerueckt auf die Hoehe des Textes, nicht daneben.

   Gemessen am 27.09.2026 bei 375 px an /Nachrichten/Bearbeiten, Karte
   "Wann": Die Zeile war ein Flex ohne Umbruch, .dazu stand mit
   margin-left:auto rechts daneben und schob die Pflichtfelder hinaus --
   bis x = 573 (Sperrmonate), 445 (Jubilaeumsschritt), 430 (Monat).
   Wer ein Pflichtfeld nicht sieht, kann nicht speichern und weiss
   nicht, warum.

   Drei Dinge gehoeren zusammen: Die Zeile bricht um, .dazu nimmt die
   volle Breite und bricht in sich selbst um (Feld, Satz, Feld), und der
   Text der Wahl darf schrumpfen -- mit flex:none war er so breit wie
   sein laengster Satz. Die Einrueckung ist Kaestchen plus Abstand, also
   genau die Stelle, an der der Text der Wahl beginnt. */
@media (max-width: 640px) {
    .wahlzeile { flex-wrap: wrap; }
    .wahlzeile .was { flex: 1 1 0; min-width: 0; }
    .wahlzeile .dazu, .wahlzeile > .leise {
        flex: 1 1 100%; flex-wrap: wrap; min-width: 0;
        margin-left: 0; padding-left: calc(1.125rem + var(--raum-3));
    }
}

/* Das Zustandsband: was ist, und was daraus folgt -- in einer Zeile,
   ohne Kasten. Ein Rahmen drumherum machte aus einer Auskunft eine
   Warnung. */
.zustandsband {
    display: flex; align-items: baseline; gap: var(--raum-3);
    flex-wrap: wrap; margin: var(--raum-3) 0;
    font-size: var(--gr-m);
}
/* ---- Leere Zustaende ----
   Ein neuer Mandant sieht ueberall Leere. Sie muss sagen, was als
   naechstes zu tun ist -- "keine Eintraege" sagt das nicht. */
.leer {
    display: flex; flex-direction: column; align-items: center; gap: var(--raum-3);
    padding: var(--raum-7) var(--raum-4); text-align: center;
    color: var(--tinte-leise); font-size: var(--gr-m);
}
td.leer { display: table-cell; padding: var(--raum-6); }

/* ================================================== 5. FACHLICHES ======= */

/* Zahlen. Gleich breite Ziffern, sonst tanzen die Spalten. */
.betrag, .nummer, .zahl { font-variant-numeric: tabular-nums; }
.betrag { text-align: right; white-space: nowrap; }
.nummer { color: var(--tinte-leise); white-space: nowrap; }

/* ---- Kennzahlen ---- */
.opzahlen { display: flex; flex-wrap: wrap; gap: var(--raum-6); margin: var(--raum-4) 0 var(--raum-5); }
.opzahl { display: flex; flex-direction: column; gap: var(--raum-1); }
.opzahl .wert { font-size: var(--gr-2xl); font-weight: 650; font-variant-numeric: tabular-nums; letter-spacing: -0.02em; }
.opzahl.warnung .wert { color: var(--gefahr-tinte); }

/* ---- Alterungsstaffel ----
   Die Staffel ist die Nachricht der Seite: Wo das Geld haengt, soll man
   sehen, ohne eine Tabelle zu lesen. Die Farbe steht am linken Rand und
   ist eine Richtung, kein Ampelspiel. */
.staffel { display: flex; flex-wrap: wrap; gap: var(--raum-2); margin-bottom: var(--raum-5); }
.staffel a {
    display: flex; flex-direction: column; gap: 2px;
    padding: var(--raum-2) var(--raum-4); min-width: 9rem;
    border: 1px solid var(--linie); border-left: 3px solid var(--linie);
    border-radius: var(--rund-2); background: var(--flaeche);
    color: var(--tinte); text-decoration: none; font-size: var(--gr-s);
    box-shadow: var(--schatten-1);
}
.staffel a:hover { border-color: var(--linie-stark); }
.staffel a.aktiv { background: var(--flaeche-satt); border-color: var(--tinte-schwach); }

/* Ein Zusatz ist kein Filter: Er schaltet etwas hinzu, statt die Auswahl
   zu ersetzen. Deshalb steht er abgesetzt am Ende und traegt keinen
   eigenen Kasten, solange er aus ist. */
.staffel a.zusatz { margin-left: auto; border-style: dashed; }
.staffel a.zusatz.aktiv { border-style: solid; background: var(--akzent-flaeche); border-color: var(--akzent-rand); color: var(--akzent-tinte); }
.staffel a.leer { color: var(--tinte-schwach); background: var(--flaeche-leise); box-shadow: none; }
.staffel .betrag { font-size: var(--gr-l); font-weight: 650; text-align: left; }

.staffel a.a-nochnichtfaellig { border-left-color: var(--alter-0); }
.staffel a.a-faellig          { border-left-color: var(--alter-1); }
.staffel a.a-ueberdreissig    { border-left-color: var(--alter-2); }
.staffel a.a-uebersechzig     { border-left-color: var(--alter-3); }
.staffel a.a-ueberneunzig     { border-left-color: var(--alter-4); }

.zustand.a-nochnichtfaellig { color: var(--tinte-leise); }
.zustand.a-faellig       { border-color: var(--alter-1); color: var(--warn-tinte); background: var(--warn-flaeche); }
.zustand.a-ueberdreissig { border-color: var(--alter-2); color: var(--warn-tinte); background: var(--warn-flaeche); }
.zustand.a-uebersechzig  { border-color: var(--alter-3); color: var(--gefahr-tinte); background: var(--gefahr-flaeche); }
.zustand.a-ueberneunzig  { border-color: var(--alter-4); color: var(--gefahr-tinte); background: var(--gefahr-flaeche); }
.zustand[class*="a-"]::before { background: currentColor; }

/* ---- Beleg ---- */

table tr.nicht-verrechnet { color: var(--tinte-schwach); }
table tr.nicht-verrechnet .betrag { text-decoration: line-through; }

.summenblock { margin-top: var(--raum-5); display: flex; flex-direction: column; align-items: flex-end; gap: var(--raum-2); }
table.summen { border-collapse: collapse; font-size: var(--gr-m); min-width: 22rem; width: auto; background: none; }
table.summen td, table.summen th { padding: var(--raum-1) var(--raum-3); border: none; }
table.summen th { text-align: left; font-weight: 400; color: var(--tinte-leise); text-transform: none; letter-spacing: 0; font-size: var(--gr-m); position: static; background: none; }
table.summen tbody tr:hover td { background: none; }

/* Die Mindestbreite haelt Beschriftung und Betrag nebeneinander -- auf
   einem 375-px-Schirm ist sie aber breiter als die Karte (22rem = 352 px
   bei 326 px Inhalt) und schiebt die ganze Seite zur Seite. Dort nimmt
   die Tabelle stattdessen, was da ist. */
@media (max-width: 560px) {
    table.summen { min-width: 0; width: 100%; }

    /* DAS REICHTE NICHT, gemessen am 27.09.2026 bei 375 px an
       /Rechnungen/{id}: Die Summentabelle mit Steuerausweis hat vier
       Spalten und war auch ohne Mindestbreite 385 px breit in 343 px
       Platz -- ihr Inhalt ist breiter als 100 %. Und align-items:
       flex-end schob den Ueberhang nicht nach rechts, sondern nach LINKS
       aus dem Bild: "etto", "echnungsbetrag". Ein abgeschnittener
       Anfang faellt weniger auf als ein abgeschnittenes Ende -- man
       liest das Wort trotzdem, falsch.

       Deshalb am Telefon gestreckt statt rechtsbuendig (ein Ueberhang
       laege dann rechts, im Scrollbereich der Seite, und nicht vor dem
       Rand), und die Zellen bekommen das halbe Polster; die aeusseren
       gar keines, die Karte hat ihr eigenes. */
    .summenblock { align-items: stretch; }
    table.summen td, table.summen th { padding: var(--raum-1) var(--raum-2); }
    table.summen tr > :first-child { padding-left: 0; }
    table.summen tr > :last-child { padding-right: 0; }

    /* Die Mindestbreite setzt die letzte Zeile: "Rechnungsbetrag" ist ein
       Wort (214 px in --gr-l), der Betrag daneben mit Waehrung 143 px und
       nicht umbrechbar (.betrag) -- zusammen 357 in 343. Hier darf die
       Waehrung unter den Betrag rutschen; die Zahl selbst bricht nie. */
    table.summen tr.brutto td.betrag { white-space: normal; }
}
table.summen tr.gesamt td { border-top: 1px solid var(--linie); }
table.summen tr.brutto td { border-top: 2px solid var(--tinte); font-weight: 650; font-size: var(--gr-l); padding-top: var(--raum-2); }
.zahlungen table.summen tr.offen td { color: var(--gefahr-tinte); border-top: 2px solid var(--tinte); font-weight: 650; }
.zahlungen table.summen tr.ausgeglichen td { color: var(--gut-tinte); border-top: 2px solid var(--tinte); font-weight: 650; }

/* ---- Vorschlagsliste der Personen- und Artikelsuche ---- */
.treffer {
    list-style: none; margin: var(--raum-1) 0 0; padding: var(--raum-1);
    border: 1px solid var(--linie); border-radius: var(--rund-2);
    background: var(--flaeche-hoch); box-shadow: var(--schatten-2);
    max-width: 32rem;
}
.treffer li { border: none; }
.treffer button {
    width: 100%; text-align: left; background: none; border: none;
    border-radius: var(--rund-1); padding: var(--raum-2) var(--raum-3);
    font: inherit; font-size: var(--gr-m); color: var(--tinte); cursor: pointer;
}
.treffer button:hover { background: var(--akzent-flaeche); color: var(--akzent-tinte); }

/* Der leise Knopf sieht aus wie ein Verweis, am Finger ist er aber ein
   Ziel wie jedes andere: gemessen am 27.09.2026 bei 375 px 22 px hoch
   ("entfernen", "Ändern" in der Akte, "Zahlung entfernen"). Die Hoehe
   waechst ohne Zeigegeraet auf --ziel, das Bild bleibt ein Wort. */
.leise-knopf {
    background: none; border: none; padding: 0;
    font: inherit; font-size: var(--gr-s); color: var(--tinte-leise);
    text-decoration: underline; cursor: pointer;
    display: inline-flex; align-items: center; min-height: var(--ziel);
}
@media (hover: hover) and (pointer: fine) {
    .leise-knopf { min-height: 0; }
}
.leise-knopf:hover { color: var(--gefahr-tinte); }

/* ---- Anmeldung ----
   Ein Blatt in der Mitte, eine Aufgabe darauf. Am Telefon sitzt es oben
   statt mittig: Die Tastatur schiebt sonst genau das Feld aus dem Bild,
   in das getippt werden soll. */
.anmeldeblatt {
    min-height: 100dvh; display: flex; flex-direction: column;
    align-items: center; justify-content: flex-start;
    gap: var(--raum-5); padding: var(--raum-7) var(--raum-4);
}
.anmeldeblatt .karte { width: min(24rem, 100%); margin: 0; padding: var(--raum-6); }
/* Die Mandantenwahl steht auf demselben Blatt, braucht aber Platz fuer
   ganze Firmennamen. */
.anmeldeblatt .karte:has(.mandantenwahl) { width: min(34rem, 100%); }
.anmeldeblatt .karte h1 { font-size: var(--gr-l); }
@media (min-height: 44rem) { .anmeldeblatt { justify-content: center; padding-bottom: var(--raum-8); } }

.anmeldung { display: flex; flex-direction: column; gap: var(--raum-1); }
.anmeldung label { margin-top: var(--raum-4); }
.anmeldung .knopf, .anmeldung .btn { margin-top: var(--raum-5); width: 100%; }

/* ---- Mandantenwahl ---- */
.mandantenwahl { display: flex; flex-direction: column; gap: var(--raum-3); max-width: 34rem; }
.mandantenwahl .mandant {
    display: flex; flex-direction: column; align-items: flex-start; gap: var(--raum-1);
    text-align: left; background: var(--flaeche); color: var(--tinte);
    border: 1px solid var(--linie); border-radius: var(--rund-3);
    padding: var(--raum-4) var(--raum-5); min-height: var(--ziel);
    cursor: pointer; font: inherit; box-shadow: var(--schatten-1);
}
.mandantenwahl .mandant:hover { border-color: var(--akzent-rand); background: var(--akzent-flaeche); }
.mandantenwahl .bez { font-weight: 650; font-size: var(--gr-l); }
.mandantenwahl .detail { font-size: var(--gr-s); color: var(--tinte-leise); }

/* Die Mandantenmarke im Fliesstext. */
.mandant {
    font-weight: 600; color: var(--mandant-tinte); background: var(--mandant-flaeche);
    padding: 0.1em 0.5em; border: 1px solid var(--mandant-rand); border-radius: var(--rund-1);
}

.exportliste, .exportzeile { display: flex; flex-direction: column; gap: var(--raum-2); }
.exportzeile .feldhinweis { max-width: 60ch; }

/* Der Entwicklungsstreifen. Er muss sichtbar bleiben und darf trotzdem
   nicht wie ein Fehler aussehen. */
.entwicklungsstreifen {
    background: var(--warn-flaeche); color: var(--warn-tinte);
    border-bottom: 1px solid var(--warn-rand);
    text-align: center; font-size: var(--gr-s); padding: var(--raum-1) var(--raum-3);
}

/* ---- Das Betriebsband ----

   Ein Band ueber der Seite, das NUR dasteht, wenn etwas nicht der
   vorgesehene Zustand ist: eine Attrappe, die antwortet; ein
   Nachrichtenlauf, der aus ist. Im Normalbetrieb ist dort nichts --
   ein Hinweis, der immer dasteht, wird nach drei Tagen nicht mehr
   gelesen, und dann haette man die Anzeige gebaut und trotzdem den
   Dauerzustand.

   Es traegt eine Liste und keinen einzelnen Satz: Zwei Baender
   uebereinander heben sich gegenseitig auf. */
.betriebsband {
    padding: var(--raum-2) var(--raum-4);
    font-size: var(--gr-s);
}
.betriebsband .zustaende { list-style: none; margin: 0; padding: 0; }
.betriebsband .zustaende li { margin: 0; padding: 2px 0; }

/* Der Grund steht dabei, nicht nur der Zustand. Auf schmalen Geraeten
   rutscht er unter den Zustand, statt ihn zu verkuerzen: Wer den Grund
   nicht liest, sucht die Einstellung. */
.betriebsband .grund { opacity: 0.85; }
@media (min-width: 761px) { .betriebsband .grund { margin-left: var(--raum-2); } }
@media (max-width: 760px) { .betriebsband .grund { display: block; } }

/* RUHEND: nichts eingerichtet. Wer nichts eingerichtet hat, hat nichts
   kaputtgemacht -- das ist eine Auskunft und keine Warnung. Es faellt
   ohnehin auf, weil nichts kommt. */
.betriebsband.ruht {
    background: var(--flaeche-leise); color: var(--tinte-leise);
    border-bottom: 1px solid var(--linie);
}

/* BEHELF: eine Attrappe antwortet. Das Baustellenmuster ist Absicht
   und kein Schmuck.

   Der gefaehrliche Zustand ist nicht der leere, sondern dieser: Er
   ANTWORTET, und deshalb stoert er nie. Alles, was aussieht wie eine
   normale Meldung, gewoehnt man sich an. Ein Streifenband bleibt ein
   Fremdkoerper auf der Seite -- und ein Fremdkoerper wird entfernt.

   Die Streifen liegen unter dem Text und nicht dahinter: Der Verlauf
   mischt nur Randfarbe in die Flaeche, die Tinte bleibt die volle
   Warntinte. */
.betriebsband.behelf {
    color: var(--warn-tinte);
    border-bottom: 1px solid var(--warn-rand);
    background:
        repeating-linear-gradient(135deg,
            transparent 0 12px,
            color-mix(in oklab, var(--warn-rand) 45%, transparent) 12px 24px),
        var(--warn-flaeche);
}


/* ================================================ 6. HILFSKLASSEN ======= */

/* Zwei Achsen, zwei Klassen. Sie waren einmal eine: .leise hat gefaerbt
   UND verkleinert. Wer einen Namen nur farblich zuruecknehmen wollte, hat
   ihn dadurch ungewollt auch verkleinert -- in der Belegliste stand der
   Kundenname mit 14 px neben Betraegen mit 17 px, in derselben Zeile.
   Farbe und Groesse werden seitdem einzeln vergeben; wer beides will,
   schreibt "leise klein". */
.leise { color: var(--tinte-leise); }
.klein { font-size: var(--gr-s); }
.stark { font-weight: 650; }
.rechts { text-align: right; }
.mittig { text-align: center; }
.small, .text-secondary { font-size: var(--gr-s); color: var(--tinte-leise); }
.text-center { text-align: center; }
.d-flex { display: flex; }
.inline { display: inline; }
.px-3 { padding-left: var(--raum-3); padding-right: var(--raum-3); }
.py-1 { padding-top: var(--raum-1); padding-bottom: var(--raum-1); }
.py-4 { padding-top: var(--raum-4); padding-bottom: var(--raum-4); }
.py-5 { padding-top: var(--raum-6); padding-bottom: var(--raum-6); }

/* Nur fuer Hilfsmittel sichtbar -- Beschriftungen, die das Auge aus dem
   Zusammenhang liest, das Vorleseprogramm aber nicht. */
.nur-vorlesen {
    position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0;
}

@media print {
    .kopf, .seitenleiste, .tableiste, .mehrblatt, .sprung,
    .knopfleiste, .ausgabeleiste, .entwicklungsstreifen, .betriebsband { display: none !important; }
    .inhalt { padding: 0; max-width: none; }
    .karte, .tabellenrahmen { border: none; box-shadow: none; padding: 0; }
    body { font-size: 11pt; }
}

/* ---- Die Kundenseite ----

   Ein Blatt, eine Aussage. Sie traegt kein Menue, keine Marke und keinen
   Weg zurueck -- es gibt von hier aus nichts zu erreichen.

   Grosszuegiger gesetzt als die Anwendung: Wer sie oeffnet, liest drei
   Saetze und ist wieder weg. Enge, die beim taeglichen Arbeiten richtig
   ist, wirkt hier abweisend. */
.kundenseite { background: var(--flaeche-leise); }
.kundenblatt {
    min-height: 100dvh;
    display: flex; flex-direction: column; justify-content: center;
    gap: var(--raum-4);
    max-width: 34rem; margin: 0 auto;
    padding: var(--raum-7) var(--raum-5);
}
.kundenblatt h1 {
    font-size: var(--gr-l); font-weight: 600;
    color: var(--tinte-leise); margin: 0;
}
.kundenblatt .gross {
    font-size: var(--gr-xl); font-weight: 600;
    color: var(--tinte); margin: 0;
    text-wrap: balance;
}
.kundenblatt p { margin: 0; font-size: var(--gr-n); color: var(--tinte-leise); max-width: 60ch; }

/* ---- Die drei Zuschnitte des Werbebilds ----

   Ein Foto, drei Formate: hochkant, quadratisch, quer. Sie sind
   verschieden hoch, und ohne gemeinsame Bildflaeche rutschen Masse und
   Speichern-Knopf auf drei verschiedene Hoehen -- man liest dann dreimal
   dasselbe an drei Stellen, und keine der drei Spalten sieht aus wie die
   Wiederholung der anderen.

   Deshalb steht jedes Bild in einer Flaeche gleicher Hoehe und wird darin
   zentriert. Was darunter steht, beginnt bei allen dreien auf derselben
   Linie. */
.zuschnitte { display: flex; flex-wrap: wrap; gap: var(--raum-4); }

.zuschnitte .zuschnitt {
    flex: 1 1 14rem; margin: 0;
    display: flex; flex-direction: column; gap: var(--raum-2);
}
.zuschnitte .zuschnitt h3 { margin: 0; }

.zuschnitte .blatt {
    height: 20rem;
    display: flex; align-items: center; justify-content: center;
    background: var(--flaeche-leise);
    border: 1px solid var(--linie); border-radius: var(--rund-1);
    padding: var(--raum-2);
}
.zuschnitte .blatt img { max-width: 100%; max-height: 100%; display: block; }

/* Der Fuss haengt unten, nicht am Bild: Bei einem queren Zuschnitt bliebe
   sonst eine Handbreit Luft zwischen Bild und Knopf, bei einem hochkanten
   keine. */
.zuschnitte figcaption {
    margin-top: auto;
    display: flex; align-items: center; justify-content: space-between;
    gap: var(--raum-3);
}

@media (max-width: 560px) {
    /* Am Telefon untereinander, und dann darf die Flaeche niedriger sein:
       Drei Bilder zu 20 rem waeren zwei Bildschirmlaengen Scrollweg, bevor
       man den ersten Knopf sieht. */
    .zuschnitte .blatt { height: 14rem; }
}

/* ---- Hausfarbe und Logo ----

   Der Waehler ist der schnelle Weg, das Feld daneben der genaue: Eine
   Hausfarbe kommt meist als Hex-Wert aus einem Markenhandbuch, und den
   trifft niemand mit der Maus. Gesendet wird nur das Feld -- der Waehler
   traegt keinen Namen und fuellt es bloss.

   Die Probe rechts zeigt, wofuer der Wert da ist: So sitzt die Schlagzeile
   spaeter auf dem Werbebild. Ein Farbwert ohne Beispiel ist eine
   Zeichenfolge. */
.farbwahl { display: flex; align-items: center; gap: var(--raum-2); flex-wrap: wrap; }

/* Das Feld traegt sieben Zeichen und braucht keine Handbreite mehr:
   Sonst bricht die Probe in die zweite Zeile, und der Zusammenhang
   zwischen Wert und Wirkung zerfaellt in zwei Zeilen. */
.farbwahl .form-control { width: 7.5rem; flex: none; }

.farbwahl .farbfeld {
    width: 3rem; height: var(--ziel); padding: 2px; cursor: pointer;
    border: 1px solid var(--linie); border-radius: var(--rund-1);
    background: var(--flaeche);
}

.farbwahl .farbprobe {
    display: inline-flex; align-items: center;
    padding: var(--raum-1) var(--raum-3);
    border-radius: var(--rund-1);
    color: var(--auf-farbe); font-weight: 600; font-size: var(--gr-s);
    white-space: nowrap;
}

/* Das Logo auf kariertem Grund. Ein Logo ist oft ein PNG mit
   durchsichtigem Hintergrund und weisser Schrift -- auf einer weissen
   Karte waere es unsichtbar, und wer nichts sieht, laedt es noch dreimal
   hoch. Das Karo kommt aus zwei Flaechenrollen und traegt deshalb auch im
   dunklen Thema. */
/* Die Logoprobe: ein Logo auf einem Grund, der Durchsichtigkeit zeigt.

   DAS KARO WAR ZU HELL, UND DAMIT NUTZLOS FUER DEN FALL, FUER DEN ES
   GEBAUT WURDE.

   Es stand auf --flaeche gegen --flaeche-leise, also oklch(1.0) gegen
   oklch(0.974) -- zwei fast weisse Toene. Der Kommentar daneben sagte:
   "Ein Logo ist oft ein PNG mit durchsichtigem Hintergrund und weisser
   Schrift. Auf einer weissen Karte waere es unsichtbar." Auf diesem Karo
   war es das auch.

   Gemessen am 30.08.2026 mit genau so einem Logo: 1692 sichtbare Pixel,
   davon 100 Prozent sehr hell -- auf dem Bildschirm sah man nur das Karo.

   JETZT ZWEI FELDER MIT ABSTAND: ein helles und ein mittleres. Damit ist
   weisse Schrift auf dem dunklen Feld sichtbar und schwarze auf dem
   hellen. Kein Ton ist so dunkel, dass das Karo selbst ins Auge faellt --
   es soll den Grund zeigen, nicht mit dem Logo konkurrieren.

   Warum kein Umschalter "auf hell / auf dunkel": Das waere ein
   Bedienelement fuer eine Frage, die das Karo schon beantwortet. */
/* Die Logoprobe: ein Logo auf einem Grund, der Durchsichtigkeit zeigt.

   DAS KARO ALLEIN KANN DIE FRAGE NICHT BEANTWORTEN, und das ist keine
   Geschmacksfrage, sondern gemessen.

   Es stand auf --flaeche gegen --flaeche-leise, also oklch(1.0) gegen
   oklch(0.974). Der Kommentar daneben sagte: "Ein Logo ist oft ein PNG
   mit durchsichtigem Hintergrund und weisser Schrift. Auf einer weissen
   Karte waere es unsichtbar." Auf diesem Karo war es das auch --
   gemessen am 30.08.2026 mit genau so einem Logo: 1692 sichtbare Pixel,
   davon 100 Prozent sehr hell, Kontrast zum Karo 1,00.

   Ein dunkleres Karo loest es nicht: Bei --linie (oklch 91%) stieg der
   Kontrast auf 1,31 -- immer noch unsichtbar. Und ein Karo, das dunkel
   genug waere (etwa oklch 60%, Kontrast 4,5), faellt selbst so ins Auge,
   dass es mit dem Logo konkurriert statt es zu zeigen.

   ALSO ZWEI FELDER NEBENEINANDER: einmal auf hell, einmal auf dunkel.
   Das beantwortet die Frage, die ein Betrieb wirklich hat -- sein Logo
   landet auf einer weissen Rechnung UND auf einem farbigen Werbebild --
   und es beantwortet sie ohne Bedienelement. Wer nur eines von beiden
   sieht, weiss trotzdem, was los ist. */
.logoproben { display: flex; gap: var(--raum-3); flex-wrap: wrap; }

.logoprobe {
    display: inline-flex; align-items: center; justify-content: center;
    padding: var(--raum-3); border-radius: var(--rund-1);
    border: 1px solid var(--linie);
    background-color: var(--flaeche);
    background-image: repeating-conic-gradient(
        var(--flaeche-leise) 0% 25%, var(--flaeche) 0% 50%);
    background-size: 16px 16px;
}
/* Das dunkle Gegenstueck. Kein Karo: Auf dunklem Grund zeigt schon die
   Flaeche selbst, was durchsichtig ist -- ein zweites Muster waere
   Unruhe ohne Auskunft. */
.logoprobe.aufdunkel {
    background-color: var(--p-neutral-700);
    background-image: none;
    border-color: var(--p-neutral-700);
}
.logoprobe img { max-height: 96px; max-width: 100%; display: block; }

/* ---- Aufnahmen zum Auftrag (Idee 48) --------------------------------

   Ein Raster statt einer Tabelle: Bei einem Bild ist das Bild die
   Angabe. Dateiname, Groesse und Datum sind Beiwerk -- sie in Spalten zu
   setzen hiesse, den Blick auf das zu lenken, was am wenigsten zaehlt.

   auto-fill statt auto-fit, und das ist hier der Unterschied: auto-fit
   zieht eine EINZELNE Aufnahme auf die volle Breite, und ein 800 Pixel
   breites Feld fuer ein Vorschaubild sieht aus wie ein Fehler. auto-fill
   laesst die Spalten stehen, auch wenn sie leer bleiben. */
.aufnahmen {
    display: grid; gap: var(--raum-3);
    grid-template-columns: repeat(auto-fill, minmax(min(11rem, 100%), 1fr));
    margin-top: var(--raum-3);
}

.aufnahme {
    display: flex; flex-direction: column;
    border: 1px solid var(--linie); border-radius: var(--rund-1);
    background: var(--flaeche); overflow: hidden;
}

/* Fester Ausschnitt, object-fit: cover -- sonst bestimmt das hoechste
   Hochformat die Zeilenhoehe und daneben stehen drei Querformate in
   einem Feld voller Luft. Das ganze Bild sieht, wer es anklickt.

   ASPECT-RATIO ALLEIN REICHT NICHT, und das ist der Grund fuer die
   absolute Lage darunter: aspect-ratio nennt ein BEVORZUGTES
   Verhaeltnis, keine Obergrenze. Ein Bild im Fluss, das hoeher
   ausfaellt, sprengt es -- gemessen am 30.08.2026: Bei 1200x1600 stand
   das Blatt auf 221x295 statt 221x166, waehrend die Karte im Raster bei
   363 blieb. Der Fuss rutschte damit aus der Karte heraus, und die
   mittlere sah laenger aus als ihre Nachbarn.

   Aus dem Fluss genommen kann das Bild die Hoehe nicht mehr bestimmen;
   dann haelt das Verhaeltnis, und overflow schneidet den Ueberstand. */
.aufnahme a.blatt {
    display: block; position: relative; overflow: hidden; min-height: 0;
    aspect-ratio: 4 / 3; background: var(--flaeche-leise);
}
/* Auf das KIND, nicht auf img: In muster.html steht ein svg an der
   Stelle des Fotos. Eine Regel, die nur img trifft, laesst das Muster
   anders aussehen als die Anwendung -- und dann prueft man am Muster
   etwas, das es so nicht gibt. */
.aufnahme a.blatt > * {
    position: absolute; inset: 0;
    width: 100%; height: 100%; object-fit: cover; display: block;
}

.aufnahme .fuss {
    display: flex; align-items: center; gap: var(--raum-2);
    padding: var(--raum-2) var(--raum-3); border-top: 1px solid var(--linie);
}
.aufnahme .fuss .angabe { flex: 1; min-width: 0; }

/* Der Dateiname wird abgeschnitten statt umgebrochen: "IMG_20260830_
   141233_HDR.jpg" ueber drei Zeilen macht aus jedem Feld ein anderes
   hohes. Der ganze Name steht im title. */
.aufnahme .fuss .name {
    display: block; overflow: hidden; text-overflow: ellipsis;
    white-space: nowrap; font-size: var(--gr-s);
}
.aufnahme .fuss .klein { font-size: var(--gr-xs); }

/* WERBEBILD AUS DER AUFNAHME (CEO 18.09.2026: "Da sind ja vom Workflow die
   Aufnahmen gespeichert. Erst danach kommt das Werbebild.")

   Eine eigene Zeile unter dem Fuss und kein dritter Knopf darin: Die Karte
   ist ab 11 rem schmal, und neben "entfernen" bliebe dem Dateinamen
   nichts. Ueber die volle Breite ist der Weg auch am Telefon ein Ziel fuer
   den Daumen. */
.aufnahme .werbebildweg {
    display: block; padding: var(--raum-2) var(--raum-3);
    border-top: 1px solid var(--linie);
    font-size: var(--gr-s); text-decoration: none;
}
.aufnahme .werbebildweg:hover { background: var(--akzent-flaeche); color: var(--akzent-tinte); }

/* ---- Stapelauswahl: mehrere Zeilen, eine Aktion ----

   Die gewaehlte Zeile ist getoent, nicht umrandet: Ein Rahmen um jede
   dritte Zeile zerlegt die Liste, eine Toenung laesst sie eine Liste
   bleiben und beantwortet trotzdem "welche habe ich".

   Die Spalte mit den Haekchen ist schmal und traegt keine Ueberschrift --
   "Auswahl" ueber einer Haekchenspalte sagt niemandem etwas, was er nicht
   sieht. Fuer Vorleseprogramme steht der Text am Haekchen selbst. */
table .spalte-haken { width: 2.5rem; text-align: center; }
table tbody tr.gewaehlt > td { background: var(--akzent-flaeche); }

/* Die Leiste erscheint erst mit der Auswahl und bleibt am unteren Rand
   stehen: Wer in einer langen Liste unten angekommen ist, hat den Knopf
   sonst oben liegen lassen -- und scrollt zurueck, um etwas auszuloesen,
   das er gerade zusammengestellt hat. */
.stapelleiste {
    position: sticky; bottom: 0; z-index: 30;
    display: flex; align-items: center; gap: var(--raum-4); flex-wrap: wrap;
    margin-top: var(--raum-3);
    padding: var(--raum-3) var(--raum-4);
    background: var(--flaeche); border: 1px solid var(--linie);
    border-radius: var(--rund-2); box-shadow: var(--schatten-1);
}
.stapelleiste[hidden] { display: none; }
.stapelleiste .was { font-weight: 600; }
.stapelleiste .knopfleiste { margin: 0; margin-left: auto; }

/* SIE MUSS DER TABLEISTE AUSWEICHEN, und sie tat es nicht.

   Gemessen am 17.09.2026, fuenf Kunden gewaehlt: Die Leiste stand bei
   bottom 0 und reichte bis zur Fensterunterkante, die Tableiste liegt
   mit z-index 40 ueber ihrem z-index 30. Der Knopf "5 Mahnungen
   erzeugen" lag bei 610 bis 654, die Tableiste beginnt bei 586.

     elementFromPoint auf der Knopfmitte: a in .navi.tableiste

   NICHT NUR VERDECKT, SONDERN VERTAUSCHT: Wer dort tippt, wo sein Knopf
   steht, loest einen Verweis der Navigation aus und ist auf einer
   anderen Seite -- samt verlorener Auswahl. Ein verdeckter Knopf tut
   nichts; dieser tut etwas anderes.

   BEI 375 UND BEI 812 GLEICHERMASSEN. Das ist also kein Hoehenfall wie
   das Mehr-Blatt (Schritt 2b), sondern ein fehlendes Ausweichen: Wo
   ZWEI Dinge unten verankert sind, muss eines dem anderen Platz machen.

   899 UND NICHT 560: Die eigene Medienabfrage der Leiste steht bei 560
   und macht sie nur hochkant. Die Tableiste erscheint aber schon unter
   900 -- bei 700 px gemessen, 65 Punkte hoch ab 747, Knopf ab 755,
   ebenfalls verdeckt. Die Grenze gehoert dorthin, wo die Tableiste
   auftaucht, nicht dorthin, wo die Leiste umbricht.

   5.5rem WIE BEI .maske.lang > .knopfleiste, drei Bildschirmseiten
   weiter oben in dieser Datei -- mit derselben Begruendung und
   derselben Messung. Die Antwort stand schon da. */
@media (max-width: 899px) {
    .stapelleiste { bottom: 5.5rem; }
}

@media (max-width: 560px) {
    /* Am Telefon zuerst der Satz, darunter der Knopf ueber die volle
       Breite -- ein Knopf, der neben einem Satz klemmt, wird zum
       Streichholz. */
    .stapelleiste { flex-direction: column; align-items: stretch; }
    .stapelleiste .knopfleiste { margin-left: 0; }
    .stapelleiste .knopfleiste .knopf { flex: 1 1 auto; justify-content: center; }
}

/* Die Unterzeile einer Stapelauswahl: ein Beleg unter seinem Kunden.
   Eingerueckt und leiser, damit die Gliederung ohne Linien lesbar ist --
   ein Rahmen um jede Gruppe zerlegte die Liste in Kaesten. */
table tbody tr.unterzeile > td:nth-child(2) { padding-left: var(--raum-6); }
table tbody tr.unterzeile > td { border-top: none; font-size: var(--gr-s); }

/* ---- Kennzahlen: was erkannt wurde, in einer Reihe ----

   Eine Beschreibungsliste für kurze Schlüssel-Wert-Paare — „Trennzeichen ;",
   „Kodierung UTF-8", „Datensätze 3". Untereinander sind das drei Zeilen, in
   denen der Blick dreimal springt, und die Aussage ist EINE: so habe ich die
   Datei gelesen.

   Die Klasse wurde benutzt, bevor es sie gab: In beiden Importmasken stand
   sie am `dl`, und die drei Werte standen in voller Breite untereinander.
   Eine erfundene Klasse ist syntaktisch dasselbe wie eine richtige — kein
   Compiler, kein Test, kein roter Balken. */
.kennzahlen {
    display: flex; flex-wrap: wrap; gap: var(--raum-5);
    margin: var(--raum-3) 0;
}
.kennzahlen > div { display: flex; flex-direction: column; gap: 2px; }
.kennzahlen dt { font-size: var(--gr-s); color: var(--tinte-leise); }
.kennzahlen dd {
    margin: 0; font-weight: 600;
    font-variant-numeric: tabular-nums;
}

/* Angaben zu einem Beleg: Bezeichnung links, Wert rechts. Anders als die
   Kennzahlen stehen sie untereinander — es sind Einzelheiten zum Nachsehen
   („Gültig bis", „Beantwortet am"), keine Zahlen zum Überfliegen.

   Ohne Regel stellt eine Beschreibungsliste dt und dd untereinander mit
   Einrückung dar: lesbar, aber bei acht Paaren eine lange Treppe, in der
   Bezeichnung und Wert nicht auf einer Linie stehen. */
.angaben {
    display: grid; grid-template-columns: auto 1fr;
    gap: var(--raum-2) var(--raum-5);
    margin: var(--raum-3) 0;
}
.angaben dt { color: var(--tinte-leise); }
.angaben dd { margin: 0; font-variant-numeric: tabular-nums; }

@media (max-width: 480px) {
    /* Am Telefon untereinander: Zwei Spalten lassen für den Wert eine
       Handbreite übrig, und „noch nicht ausgestellt" bricht dann dreimal. */
    .angaben { grid-template-columns: 1fr; gap: 0 0; }
    .angaben dd { margin-bottom: var(--raum-3); }
}

/* ---- Der Textentwurf: eine Beschleunigung, kein Bedienschritt ----

   Der Knopf sitzt AM TEXTFELD, nicht auf einem eigenen Bildschirm. Das ist
   die Gestaltung, die der Festlegung entspricht: Paket C beschleunigt das
   Schreiben; fällt es aus, schreibt der Betrieb den Text selbst, wie er es
   immer getan hat. Ein eigener Bildschirm hätte daraus einen Schritt
   gemacht, den man geht oder nicht.

   Deshalb ist die Leiste auch ruhig und nicht bunt: Sie bietet etwas an,
   sie verlangt nichts. */
.entwurf {
    display: flex; align-items: center; gap: var(--raum-3);
    flex-wrap: wrap;
    margin-top: var(--raum-2);
}
.entwurf .woher { color: var(--tinte-leise); font-size: var(--gr-s); }

/* Während der Entwurf entsteht. Kein Sperren der Maske: Wer in der
   Zwischenzeit selbst zu tippen anfängt, hat sich entschieden — dann
   verfällt der Entwurf, statt das Getippte zu überschreiben. */
.entwurf[data-laeuft] .knopf { opacity: 0.6; pointer-events: none; }

/* Ein Ausfall ist eine Auskunft, keine Warnung: Die Maske bleibt
   vollständig benutzbar, das Textfeld war nie gesperrt. Rot wäre hier
   falsch — es ist nichts kaputt, es geht nur gerade nicht schneller. */
.entwurf-ausfall {
    margin-top: var(--raum-2);
    font-size: var(--gr-s); color: var(--tinte-leise);
    border-left: 3px solid var(--linie-stark);
    padding-left: var(--raum-3);
}
.entwurf-ausfall strong { color: var(--tinte); }

/* Die Längenanzeige eines Feldes mit harter Grenze. Sie steht immer da,
   nicht erst bei Überschreitung: Wer beim Tippen sieht, wie voll es wird,
   kürzt von selbst — wer erst am Ende eine Meldung bekommt, schreibt
   zweimal. */
.laenge { font-size: var(--gr-s); color: var(--tinte-leise); }
.laenge.zulang { color: var(--gefahr-tinte); font-weight: 600; }
textarea.zulang, input.zulang { border-color: var(--gefahr-rand); }

/* UEBER DER EMPFEHLUNG IST KEINE GEFAHR, und die Farbe sagt das.

   Zwischen "geht noch" und "geht nicht" liegt hier ein dritter Zustand:
   Der Text laesst sich speichern, ist aber laenger, als der Sache
   guttut. In Gefahrfarbe waere er eine Warnung vor etwas, das
   funktioniert -- und was zu oft warnt, wird nicht mehr gelesen.

   Deshalb volle Tinte statt leiser, ohne Rot und ohne Rand am Feld:
   sichtbar genug, um gelesen zu werden, und ruhig genug, um nicht zu
   erschrecken. */
.laenge.ueberempfehlung { color: var(--tinte); font-weight: 600; }

/* Eine Zwischenzeile, die eine Liste in zwei Antworten teilt — „125 werden
   nicht erreicht" und „66 werden erreicht". Sie trägt kein Warnzeichen: Es
   ist keine Störung, dass jemand keine E-Mail-Adresse hat, sondern eine
   Tatsache, die man vor dem Versand wissen muss. */
tr.gruppenzeile > td {
    background: var(--flaeche-leise);
    font-size: var(--gr-s);
    border-top: 1px solid var(--linie);
    padding-top: var(--raum-2); padding-bottom: var(--raum-2);
}

/* Weder gut noch schlecht: UNGEPRÜFT.

   Der dritte Zustand, den eine Prüfung haben kann und den man am ehesten
   vergisst — „geprüft und in Ordnung", „Fehler belegt" und „konnte nicht
   geprüft werden" sind drei Antworten, nicht zwei. Die dritte sieht als
   leiser Nebensatz aus wie die erste, und dann hält ein Betrieb einen
   ungeprüften Zeitraum für vollständig.

   Der gestrichelte Rand ist die zweite Spur neben dem Text: Er sagt „hier
   fehlt etwas", ohne eine Störung zu behaupten. Volle Tinte, damit er
   nicht in den Hintergrund rutscht — er ist kein Nebensatz. */
.marke.offen, .kennzeichen.offen {
    background: var(--flaeche);
    border-style: dashed; border-color: var(--linie-stark);
    color: var(--tinte);
}

/* Zwei Marken, die zusammen EINE Aussage sind.

   Der Fall, für den es das gibt: „eine Lücke belegt" **und** „an anderen
   Stellen war gar nicht prüfbar". Das sind zwei unabhängige Tatsachen —
   aber nebeneinander gesetzt sehen sie aus wie zwei Zustände desselben
   Dings, und man liest den zweiten als Wiederholung des ersten.

   Aneinandergeschoben werden sie zu einem Objekt mit zwei Teilen: erst
   was belegt ist, dann wie weit die Aussage trägt. Die Formen bleiben
   unterscheidbar — der zweite Teil behält seinen gestrichelten Rand. */
.markenpaar { display: inline-flex; align-items: stretch; }

.markenpaar > .marke:first-child {
    border-top-right-radius: 0; border-bottom-right-radius: 0;
}
.markenpaar > .marke + .marke {
    border-top-left-radius: 0; border-bottom-left-radius: 0;
    margin-left: -1px;
}

/* ---- Beleg links, Felder rechts, auf gleicher Höhe ----

   Das ist keine Anordnungsfrage, sondern **die letzte Probe**. Für den
   Fall, den keine maschinelle Prüfung erreichen kann: Ein Foto, auf dem
   zwei Belege nebeneinander liegen, sauber gelesen — der falsche. Jede
   Zahl stimmt, jeder Satz ist zulässig, und nur der Mensch sieht es.

   Er sieht es aber nur, wenn beides gleichzeitig dasteht: Wer zum Prüfen
   scrollen oder umschalten muss, prüft nicht. Deshalb gleiche Höhe und
   nebeneinander, solange der Platz reicht.

   Gebaut, bevor es die Auslesung gibt — später wäre es ein Umbau, jetzt
   kostet es nichts. */
.belegablage {
    display: grid; grid-template-columns: 1fr 1fr;
    gap: var(--raum-4); align-items: start;
}

/* Der Beleg bleibt stehen, während rechts getippt wird: Wer beim vierten
   Feld nachsehen will, was oben auf dem Beleg steht, soll nicht zurück
   scrollen müssen. */
.belegablage > .belegseite { position: sticky; top: var(--raum-4); }

/* Die Schau trägt eine Mindesthöhe, auch leer: Sonst springt die Maske,
   sobald ein Beleg gewählt wird, und der Blick verliert die Stelle. */
.belegschau {
    min-height: 24rem;
    display: flex; align-items: center; justify-content: center;
    padding: var(--raum-3);
    background: var(--flaeche-leise);
    border: 1px solid var(--linie); border-radius: var(--rund-1);
    overflow: auto;
}
.belegschau img, .belegschau embed, .belegschau iframe {
    max-width: 100%; display: block;
}

@media (max-width: 900px) {
    /* Untereinander, und der Beleg klebt oben: Auf einem schmalen
       Bildschirm ist "nebeneinander" nicht zu haben, aber "sichtbar,
       während ich tippe" schon. */
    .belegablage { grid-template-columns: 1fr; }
    .belegablage > .belegseite { position: static; }
    .belegschau { min-height: 14rem; }
}

/* ---------------------------------------------------------------
   SEITENRECHNUNG -- die Zeile, die mehrere Listen einer Seite
   zusammenrechnet.

   NICHT .feldhinweis: Der ist leise gesetzt, und eine Zeile, die
   traegt, was der Betrieb der ganzen Seite glaubt, darf nicht
   leiser sein als das, was sie zusammenrechnet. Derselbe Fehler
   stand am Kontoauszug -- dort war "nicht pruefbar" das Leiseste
   auf dem Bildschirm, obwohl es die Antwort auf die Frage war.

   Sie steht ueber den Karten und nicht in einer davon: In einer
   Karte waere sie die Aussage dieser einen Liste. Ihr Zweck ist
   gerade, dass sie ueber allen steht.

   KEINE TRENNLINIE. Sie stand zuerst da und wurde nach dem Messen
   entfernt: Der Textblock ist auf 62ch gesetzt (568 px), die Karte
   darunter war 961 px breit -- der Strich endete sichtbar auf halber
   Strecke und sah aus wie ein Rest, nicht wie eine Absicht. Der
   Abstand allein trennt, und die Karte bringt ihren eigenen Rahmen mit. */
.seitenrechnung {
    margin: 0 0 var(--raum-6);
    color: var(--tinte);
    font-size: var(--gr-m);
    max-width: 62ch;
}

.seitenrechnung .probe {
    display: block;
    margin-top: var(--raum-1);
    font-size: var(--gr-s);
    color: var(--tinte-leise);
}

/* ---------------------------------------------------------------
   EINE BREITE TABELLE SCROLLT IN SICH -- sie dehnt nicht die Seite.

   Gefunden von Entwickler-Zwei mit Zahlen: /Einstellungen/Bankkonten
   lief bei 375 px waagrecht ueber, die Seite wurde 751 px breit. Seine
   Einordnung war die genaue -- der Ueberlauf war nicht seiner, seine
   zwei neuen Spalten haben ihn nur von 532 auf 751 px mehr als
   verdoppelt.

   DIE URSACHE WAR NICHT DIE TABELLE, SONDERN DAS FEHLENDE GEFAESS:
   .karte.breit und .tabelle standen beide auf overflow-x: visible. Es
   gab gar keinen Rahmen, in dem etwas haette scrollen koennen -- also
   wuchs das Dokument. Und ein Dokument, das waagrecht mitwaechst,
   verschiebt JEDE Seite: Der Kopf steht dann breiter als der
   Bildschirm, und man scrollt eine Zeile lang nach rechts, um zu
   sehen, was ohnehin nicht dort hingehoert.

   Betroffen sind zwoelf Tabellen mit class="tabelle" ohne
   data-karten. Die mit data-karten brauchen es nicht: Sie werden
   unter 700 px zu Karten und haben dann gar keine Spalten mehr.

   display: block ist noetig, weil overflow auf display: table nicht
   greift -- die Zeilengruppen darin bleiben Tabellenzeilen, die
   Spalten richten sich weiter aneinander aus.

   NUR AUF SCHMALEN FENSTERN. Oberhalb ist Platz (bei 1100 px lief
   nichts ueber), und display: block aendert die Breitenberechnung --
   das gehoert nicht in einen Fall, den es nicht gibt. Waechst eine
   Tabelle spaeter ueber 760 px hinaus, ist das hier die Stelle. */
@media (max-width: 760px) {
    table.tabelle:not([data-karten]) {
        display: block;
        overflow-x: auto;
        max-width: 100%;
    }
}

/* Der gewaehlte Ansprechpartner unter den uebrigen. Kraeftiger, nicht
   andersfarbig: Die Zeile ist eine Aufzaehlung, kein Zustand -- eine
   zweite Farbe daneben liesse die anderen wie abgewertet aussehen,
   dabei sind sie nur nicht gewaehlt. */
.kundenhinweis .text a.gewaehlt { font-weight: 650; color: var(--akzent-tinte); }

/* Ein Fehler an EINER ZEILE einer Tabelle.

   Warum nicht .kennzeichen: Das ist eine Marke fuer kurze Zustaende --
   "offen", "gesperrt", "Entwurf" -- und traegt dafuer white-space:
   nowrap. Ein Fehlersatz darin macht die Zelle so breit wie den Satz;
   gemessen am 30.08.2026 in der Mahnliste: 599 px fuer eine Marke, 1316
   px Tabelle in einem 1031 px breiten Rahmen und 228 px seitlicher
   Ueberlauf der ganzen Seite.

   Der Fehler war vorher schon da und blieb verdeckt, weil die Tabelle am
   Telefon zu Karten wurde. Erst als sie fuer die Sammelauswahl Tabelle
   bleiben musste, stand er im Bild. */
.zeilenfehler {
    display: block; max-width: 32ch; margin-top: var(--raum-1);
    font-size: var(--gr-xs); line-height: 1.4; color: var(--warn-tinte);
    white-space: normal;
}

/* Die Fehlerzeile unter ihrer Datenzeile: kein eigener Eintrag, sondern
   ein Nachsatz. Ohne Trennlinie nach oben, damit sie mit der Zeile
   darueber zusammengehoert und nicht neben ihr steht. */
tr.fehlerzeile > td { border-top: none; padding-top: 0; }
/* Die Breitengrenze BLEIBT: In der eigenen Zeile beginnt der Text
   links, und 32ch lassen ihn umbrechen statt am Rand einer breiten
   Tabelle abgeschnitten zu werden. Gemessen bei 375 px -- ohne die
   Grenze lief er in die 809 px breite Zelle und war ab der Haelfte
   nur noch durch seitliches Scrollen lesbar. */
tr.fehlerzeile .zeilenfehler { margin-top: 0; }

/* ---- Die Suche in der Kopfleiste (Idee 59) ---- */

/* Sie waechst mit dem Platz, gibt ihn aber wieder her: Der Betriebsname
   links und der Benutzer rechts sind wichtiger als ein breites Feld. */
.kopfsuche {
    position: relative; flex: 1 1 14rem; max-width: 28rem;
    margin-inline: var(--raum-3);
}
/* 1rem ohne Zeigegeraet: Unter 16 px zoomt Safari beim Antippen, und
   ein iPad hochkant (768 px) zeigt das Feld noch. */
.kopfsuche input {
    width: 100%; min-height: 2.25rem; font-size: 1rem;
}
@media (hover: hover) and (pointer: fine) {
    .kopfsuche input { font-size: var(--gr-s); }
}

/* Die Liste SCHWEBT ueber dem Inhalt und schiebt die Kopfleiste nicht
   auseinander. Ohne das springt bei jedem Tastendruck die ganze Seite --
   und der Kopf klebt, also spraenge sie dauerhaft. */
.kopfsuche .treffer {
    position: absolute; top: calc(100% + var(--raum-1)); left: 0; right: 0;
    z-index: 30; max-width: none; max-height: 70vh; overflow-y: auto;
    margin: 0;
}
.treffer a {
    display: flex; align-items: baseline; gap: var(--raum-2);
    padding: var(--raum-2) var(--raum-3); border-radius: var(--rund-1);
    text-decoration: none; color: var(--tinte); font-size: var(--gr-m);
}
.treffer a:hover, .treffer a.gewaehlt {
    background: var(--akzent-flaeche); color: var(--akzent-tinte);
}
/* Der Beisatz weicht zuerst: Bei "Achensee Handels GmbH" und einem
   langen Ort ist der Name das, was gesucht wurde. */
.treffer a > span:nth-child(2) { flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.treffer a > .leise { margin-left: auto; flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Am Telefon TAUSCHT das Feld gegen einen Verweis -- es bricht nicht um.

   Gemessen bei 375 px: Ein umbrechendes Feld ragte 28 px aus dem Kopf
   und verdeckte den Inhalt. Der Kopf hat feste 3.5rem, und das ist kein
   Versehen: Er klebt, und die scroll-margin-Werte der Sprungmarken
   rechnen mit dieser Hoehe. Ein Kopf von zwei Zeilen naehme am Telefon
   ein Sechstel des Bildschirms und schoebe jede Sprungmarke darunter.

   Die Lupe fuehrt auf /Suche -- dort ist das Feld gross und die
   Trefferliste hat die ganze Seite. Am Telefon ist das der bessere Weg,
   nicht der kleinere. */
.kopfsuche-verweis { display: none; }

@media (max-width: 700px) {
    .kopfsuche { display: none; }
    .kopfsuche-verweis {
        display: inline-flex; align-items: center; justify-content: center;
        width: var(--ziel); height: var(--ziel); margin-left: auto; flex: none;
        border-radius: var(--rund-2); color: var(--tinte-leise);
    }
    .kopfsuche-verweis svg { width: 1.25rem; height: 1.25rem; }
    .kopfsuche-verweis:hover, .kopfsuche-verweis:focus-visible {
        background: var(--flaeche-leise); color: var(--akzent);
    }
}

/* ---- Der Speicherzustand an der Zeile (Idee 8) ----------------------

   ABNAHMESATZ 2: Ich sehe, DASS gespeichert ist -- nicht als Meldung,
   die wegblinkt, sondern als Zustand an der Zeile. Eine Meldung am
   Seitenkopf beantwortet "es ist etwas passiert"; die Frage ist aber
   "ist MEINE Zeile drin".

   Ein schmaler Streifen am Zeilenanfang und kein Haekchen: Ein Haekchen
   je Zeile bei zwanzig Zeilen ist ein Feld voller Haekchen, und die
   Zeile, die gerade NICHT gespeichert ist, faellt darin nicht auf. Der
   Streifen ist da oder nicht da. */
table.raster tr.zeile.gespeichert td:first-child {
    box-shadow: inset 3px 0 0 var(--gut-tinte);
}

/* Der Fehlschlag traegt dieselbe Form in der Gefahrenfarbe -- damit die
   beiden Zustaende an derselben Stelle stehen und nicht an zweien. */
table.raster tr.zeile.nichtgespeichert td:first-child {
    box-shadow: inset 3px 0 0 var(--gefahr-tinte);
}
table.raster tr.zeile.nichtgespeichert .feld,
table.raster tr.zeile.nichtgespeichert .wahl { border-color: var(--gefahr-tinte); }

/* ABNAHMESATZ 3, DER TEUERSTE: Ein gescheitertes Speichern ist sichtbar
   UND BLEIBT SICHTBAR, bis es geklappt hat oder jemand es weggeklickt
   hat.

   Beim Knopf sieht man den Fehlschlag, weil man hinsieht. Ohne Knopf
   sieht niemand hin -- und wer zehn Zeilen tippt und beim elften merkt,
   dass seit der zweiten nichts ankam, hat mehr verloren als die
   Automatik je einspart.

   Deshalb kein Aufblenden und kein Ausblenden: Der Hinweis steht, bis
   er behoben oder weggeklickt ist. Eine Einblendung mit Zeitablauf
   waere genau die Meldung, die der Abnahmesatz ausschliesst. */
.speicherhinweis {
    display: block; margin-top: var(--raum-1);
    font-size: var(--gr-xs); line-height: 1.35;
    color: var(--gefahr-tinte);
    background: var(--gefahr-flaeche); border-radius: var(--rund-1);
    padding: var(--raum-1) var(--raum-2);
    /* Aus der schmalen Nummernspalte heraus: Der Satz ist laenger als
       drei Zeichen, und in 3rem gebrochen waere er unlesbar.

       UNTER DIE ZEILE UND NICHT IN SIE HINEIN. Ohne top verdeckte der
       Hinweis das Beschreibungsfeld DERSELBEN Zeile -- also genau die
       Zeile, die der Nutzer gerade korrigieren soll. Ein Hinweis, der
       den Weg zu seiner eigenen Behebung verstellt, ist schlimmer als
       einer, der die Nachbarzeile ueberlappt. Gemessen am 31.08.2026 am
       Bildbeleg; die DOM-Messung sah nur ein Element mehr. */
    position: absolute; top: 100%; left: 0;
    z-index: 3; width: max-content; max-width: 22rem;
    /* UMBRECHEN STATT ABSCHNEIDEN (Pruefer, 27.09.2026). Der Rahmen des
       Rasters scrollt quer (overflow-x: auto) und schneidet ab, was ueber
       ihn hinausragt. Bei Telefonbreite ist er schmaler als 22rem -- der
       Satz lief ueber den Rand und war nur halb zu lesen, samt "Neu
       laden". Hoechstens so breit wie der Schirm abzueglich Rand und
       Kartenabstand, darin bricht er um; lange Woerter notfalls mitten
       im Wort. Gemessen bei 375 px: Der Rahmen steht 41 px vom linken
       und vom rechten Rand, also 293 px breit; mit 100vw - 4rem (311 px)
       ragte der Hinweis noch 20 px hinaus, mit 6rem (279 px) nicht. */
    max-width: min(22rem, calc(100vw - 6rem));
    white-space: normal; overflow-wrap: anywhere;
}
table.raster td.spalte-nr { position: relative; }

.speicherhinweis .wegklicken {
    border: 0; background: transparent; cursor: pointer;
    color: inherit; font-size: var(--gr-s); line-height: 1;
    padding: 0 0 0 var(--raum-2); margin-left: var(--raum-1);
}
.speicherhinweis .wegklicken:hover { opacity: 0.7; }

/* Der Ausweg im Hinweis ("Nochmal versuchen", "Neu laden"): unterstrichen
   in der Farbe des Hinweises, kein eigener Kasten -- er gehoert zum Satz. */
.speicherhinweis .nochmal {
    border: 0; background: transparent; cursor: pointer; padding: 0;
    color: inherit; font: inherit; font-weight: 600;
    text-decoration: underline; text-underline-offset: 2px;
}
.speicherhinweis .nochmal:hover { opacity: 0.7; }

/* "Jetzt speichern" im Hinweis auf Ungespeichertes: dieselbe Zeile, etwas
   abgesetzt. Er erscheint und verschwindet mit dem Hinweis. */
.summenblock .ungespeichert .knopf { margin-left: var(--raum-2); vertical-align: middle; }

/* DER VERLAUF UNTER DEM RASTER (0280, CEO 18.09.2026): zugeklappt und
   leise -- er ist zum Nachsehen da, nicht zum Arbeiten. Wann und wer in
   fester Breite, damit die Saetze untereinander beginnen. Ausdruecklich
   [hidden], wie bei jedem Bauteil, das das Skript ein- und ausblendet. */
.verlauf { margin-top: var(--raum-5); }
.verlauf[hidden] { display: none; }
.verlauf > summary {
    cursor: pointer; min-height: var(--ziel);
    display: flex; align-items: center; gap: var(--raum-1);
    font-size: var(--gr-s); color: var(--tinte-leise);
}
.verlaufsliste {
    list-style: none; margin: var(--raum-2) 0 0; padding: 0;
    display: grid; gap: var(--raum-2);
}
.verlaufsliste li {
    display: grid; grid-template-columns: 8.5rem 10rem 1fr; gap: var(--raum-3);
    align-items: baseline; font-size: var(--gr-s);
}
.verlaufsliste .wann { font-variant-numeric: tabular-nums; color: var(--tinte-leise); }
.verlaufsliste .wer {
    color: var(--tinte-leise);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
@media (max-width: 640px) {
    .verlaufsliste li { grid-template-columns: 1fr; gap: 0; }
}

/* ================= ZUGAENGE: BETREIBER, BENUTZER, PROTOKOLL ==========

   D1 der Betreiberverwaltung (28.09.2026, docs/konzept-betreiber.md).
   Muster: /muster.html#b31 bis #b37. Begruendung: gestaltung.md,
   Abschnitt "Zugaenge". Keine neue Farbrolle -- alles hier ist aus den
   vorhandenen Rollen zusammengesetzt. */

/* ---- Das Betreiberband ----

   Der Kopf im Betreiberbereich ist UMGEKEHRT: Grund aus --tinte, Schrift
   aus --grund. Das ist keine neue Farbe, sondern die staerkste
   Unterscheidung, die das System ohne eine hergibt: Kein Betrieb kann so
   aussehen, weil jeder Betrieb seinen Kopf aus --flaeche und seine
   Kante aus --mandant bekommt. Eine siebte Kantenfarbe hielte man fuer
   einen siebten Mandanten; ein umgekehrtes Band nicht.

   DIE MANDANTENKANTE FEHLT ABSICHTLICH. Sie sagt "in wessen Daten bin
   ich" -- und die Antwort ist hier: in keinen. Stuende sie da, truege
   sie die Farbe des Betriebs, aus dem man gekommen ist, und genau diese
   Verwechslung soll das Band verhindern.

   Im dunklen Modus kehrt es sich mit um und wird hell: Auch dort ist es
   der einzige Kopf seiner Art. Schrift gegen Grund ist in beiden Modi
   der hoechste Kontrast, den die Rollen hergeben (--tinte / --grund).

   Nur die obere Zeile klebt. Die Leiste darunter (Ziele und Ausgaenge)
   rollt mit weg -- ein zweizeiliger klebender Kopf naehme am Telefon ein
   Sechstel des Bildes, und jede Sprungmarke rechnet mit 3.5rem. */
.kopf.betreiber {
    background: var(--tinte); color: var(--grund);
    -webkit-backdrop-filter: none; backdrop-filter: none;
    border-bottom-color: var(--tinte);
}
.kopf.betreiber::before { content: none; }
/* Die Wortmarke teilt sich den Klassennamen mit den Marken (.marke,
   .zustand ...) und bekommt von dort Rahmen und --flaeche-leise. Auf dem
   umgekehrten Band waere das ein heller Kasten mit heller Schrift --
   gemessen am 28.09.2026: "Wawi" unsichtbar. */
.kopf.betreiber .marke { color: var(--grund); background: transparent; border-color: transparent; }
.kopf.betreiber .marke .zeichen { background: var(--grund); color: var(--tinte); }
.kopf.betreiber .bereich { font-size: var(--gr-m); font-weight: 650; white-space: nowrap; }
.kopf.betreiber .wozu { font-size: var(--gr-s); white-space: nowrap; }
.kopf.betreiber .benutzer {
    color: var(--grund);
    max-width: 12rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* Am Telefon traegt die Zeile nur Zeichen und Bereich. "Sie arbeiten in
   keinem Betrieb" und der Name passen bei 375 px nicht daneben; die
   Aussage tragen das umgekehrte Band und das Wort "Betreiberbereich". */
@media (max-width: 560px) {
    .kopf.betreiber .wozu, .kopf.betreiber .benutzer { display: none; }
}
/* Der Fokusring bleibt -- nur in der Farbe, die auf dem Band traegt. */
.kopf.betreiber a:focus-visible, .kopf.betreiber button:focus-visible {
    outline: 2px solid var(--grund); outline-offset: 2px;
}

/* Die Leiste unter dem Band: wohin (Mandanten, Protokoll) links, wie
   hinaus (zurueck in den Betrieb, abmelden) rechts. Am Telefon bricht sie
   in zwei Zeilen: Ziele oben, Ausgaenge darunter. */
.betreiberleiste {
    grid-column: 1 / -1;
    display: flex; flex-wrap: wrap; align-items: center;
    gap: var(--raum-2) var(--raum-3);
    padding: var(--raum-2) var(--raum-4);
    background: var(--flaeche); border-bottom: 1px solid var(--linie);
}
.betreiberleiste .ziele { display: flex; flex-wrap: wrap; gap: var(--raum-1); }
.betreiberleiste .ziele a {
    display: inline-flex; align-items: center; min-height: var(--ziel);
    padding: 0 var(--raum-3); border-radius: var(--rund-2);
    color: var(--tinte-leise); text-decoration: none;
    font-size: var(--gr-m); font-weight: 550;
}
.betreiberleiste .ziele a:hover { background: var(--flaeche-satt); color: var(--tinte); }
.betreiberleiste .ziele a[aria-current] { background: var(--akzent-flaeche); color: var(--akzent-tinte); font-weight: 600; }
.betreiberleiste .ausgaenge { display: flex; flex-wrap: wrap; gap: var(--raum-2); margin-left: auto; }
.betreiberleiste .ausgaenge form { margin: 0; }
/* Drei Kinder statt zwei: Kopf, Leiste, Inhalt. Ohne diese Zeile nimmt
   die LEISTE die 1fr-Zeile des Rahmens und wird so hoch wie die Seite. */
.rahmen:has(> .betreiberleiste) { grid-template-rows: auto auto 1fr; }

/* Die Marke "Betreiber" -- im Protokoll und ueberall, wo gesagt werden
   muss, dass etwas aus dem Betreiberbereich kam. Dieselbe Umkehrung wie
   das Band: Wer das Band einmal gesehen hat, erkennt sie ohne Lesen. */
.marke.betreiber { background: var(--tinte); border-color: var(--tinte); color: var(--grund); }

/* ---- Die Linkkarte ----

   Zeigt einen Einrichtungslink GENAU EINMAL, als Antwort auf das
   Anlegen. Drei Dinge stehen zusammen, ohne Scrollen: dass er nur jetzt
   da ist, der Link selbst, und bis wann er gilt.

   Der Link steht in voller Laenge und bricht an jeder Stelle um: Ein
   abgeschnittener Link mit "..." sieht kopierbar aus und ist es nicht.
   Ein Tipp waehlt ihn ganz aus (user-select: all) -- am Telefon der Weg,
   wenn der Kopierknopf fehlt, weil kein Skript laeuft. */
.karte.linkkarte { max-width: 44rem; }
.linkkarte .link {
    display: block; margin: var(--raum-2) 0 var(--raum-3);
    padding: var(--raum-3) var(--raum-4);
    background: var(--flaeche-leise); color: var(--tinte);
    border: 1px solid var(--linie-stark); border-radius: var(--rund-2);
    font-size: var(--gr-m); line-height: 1.5;
    overflow-wrap: anywhere; word-break: break-all;
    -webkit-user-select: all; user-select: all;
}
.linkkarte .frist { margin: 0; font-size: var(--gr-m); }
.linkkarte .frist strong { font-variant-numeric: tabular-nums; }
.linkkarte .kopierstand { margin: var(--raum-2) 0 0; font-size: var(--gr-s); font-weight: 600; color: var(--gut-tinte); }
.linkkarte .kopierstand.nicht { color: var(--warn-tinte); }
.linkkarte .kopierstand:empty { display: none; }
.linkkarte .knopfleiste [hidden] { display: none; }

/* ---- Zustand als Satz ----

   Zugaenge haben Zustaende, die ohne Datum nichts sagen: "eingeladen"
   verschweigt, bis wann; "gesperrt" verschweigt, seit wann. Die Marke
   darf deshalb einen Satz tragen und umbrechen (.satz) -- die kurze
   Marke (.zustand ohne .satz) bleibt einzeilig wie bisher.

   Die Farben sind vorhandene Rollen, und jede hat ihren Grund:
     aktiv        gut    -- so soll es sein
     eingeladen   info   -- wartet auf den Benutzer, niemand muss etwas tun
     abgelaufen   warn   -- die Verwaltung muss handeln: neuer Link
     gesperrt     neutral mit voller Tinte -- bewusst gesetzt, kein Fehler;
                  nicht rot (Rot ist teuer), nicht warn (nichts zu tun)
     abgeschaltet leise Tinte, HOHLER Punkt -- an der Konsole abgeschaltet,
                  in der Maske nicht aenderbar
   Keine Aussage haengt an der Farbe allein: Der Satz sagt es, und der
   hohle Punkt unterscheidet "abgeschaltet" auch ohne Farbe. */
.zustand.satz { white-space: normal; align-items: flex-start; text-align: left; }
.zustand.satz::before { margin-top: calc(0.75em - 0.2rem); }
/* In der Kartenform ist die Zelle ein Flex-Behaelter, der umbricht: Die
   Marke rutschte mit ihrer vollen Satzbreite in eine eigene Zeile an den
   linken Rand, weg von ihrer Beschriftung (gemessen D2, Benutzerliste bei
   375 px, 28.09.2026). Sie darf schrumpfen, bleibt rechts und bricht in
   sich um. */
@media (max-width: 700px) {
    table[data-karten] td > .zustand.satz {
        flex: 1 1 0; min-width: 0; max-width: max-content; margin-left: auto;
    }
}
.zustand.z-eingeladen {
    background: var(--info-flaeche); border-color: var(--info-rand); color: var(--info-tinte);
}
.zustand.z-abgelaufen {
    background: var(--warn-flaeche); border-color: var(--warn-rand); color: var(--warn-tinte);
}
.zustand.z-gesperrt {
    background: var(--flaeche-satt); border-color: var(--linie-stark); color: var(--tinte);
}
/* --tinte-leise und nicht --tinte-schwach wie .z-beendet: Auf dem Grund
   der Marke (--flaeche-leise) erreicht --tinte-schwach nur 3,67 zu 1,
   gemessen am 28.09.2026 im Hellen -- unter den 4,5 fuer Text. */
.zustand.z-abgeschaltet { color: var(--tinte-leise); }
.zustand.z-abgeschaltet::before { background: transparent; box-shadow: inset 0 0 0 1.5px currentColor; }

/* ---- Die Rollenwahl in einer Maske ----
   Die Wahlgruppe (.wahlzeile) stand bisher nur ausserhalb von .maske.
   Darin erbte jede Zeile den Abstand ueber einem Etikett (.maske label)
   und stand 16 px unter der vorigen -- zwei Zeilen einer Wahl sahen aus
   wie zwei Felder. Die Legende setzt ihre Masse selbst: Bootstrap macht
   sie schwebend und gross, ohne Bootstrap (Anmeldeblatt der Kundenseiten)
   waere sie eingerueckt; beides verschiebt die Zeilen darunter. */
.maske .wahlgruppe { margin-top: var(--raum-4); min-width: 0; }
.maske .wahlgruppe .wahlzeile { margin-top: 0; }
/* Das versteckte Namensfeld neben einem Passwortfeld (fuer den
   Passwortspeicher des Geraets) traegt hidden. Die Feldregel oben setzt
   display: block und schlaegt damit die Vorgabe des Browsers -- nur
   Bootstrap haelt es mit [hidden] { display: none !important } versteckt.
   Ohne Bootstrap stand es als leeres Feld da (Kundenbefund der
   Musterseite, 28.09.2026). */
input[type="text"][hidden] { display: none; }
.wahlgruppe > legend.beschriftung {
    float: none; width: 100%; padding: 0; margin: 0 0 var(--raum-1);
    font-size: var(--gr-s); line-height: inherit;
}

/* ---- Die Protokollliste ----

   Zeitpunkt, Ereignis als Satz, wer und als was. Wer und als was stehen
   in EINER Zelle: Die Marke ist eine Eigenschaft des Namens, keine
   eigene Spalte -- und eine Spalte weniger laesst dem Satz den Platz.
   Am Telefon Kartenform (data-karten); der Satz ist dort die
   Hauptzeile, weil er die Auskunft ist. */
table.protokoll td.wann { white-space: nowrap; font-variant-numeric: tabular-nums; color: var(--tinte-leise); }
/* Name und Marke in EINEM Element (.werals): In der Kartenform ist die
   Zelle ein Flex-Behaelter, und zwei Kinder wurden auseinandergezogen --
   die Marke rutschte in eine eigene Zeile an den linken Rand (gemessen
   im Muster bei 375, 28.09.2026). */
table.protokoll td.wer .werals { white-space: nowrap; }
table.protokoll td.wer .marke { margin-left: var(--raum-1); }
table.protokoll td.ereignis { min-width: 16rem; }
@media (max-width: 700px) {
    table.protokoll td.ereignis { min-width: 0; }
    table[data-karten].protokoll td.wer .werals { white-space: normal; text-align: right; flex: 1 1 0; min-width: 0; }
    table[data-karten].protokoll td.hauptzeile { font-size: var(--gr-m); }
}

/* ---- Die Mandantenuebersicht ----
   Ein Wert mit seinem Beisatz in derselben Zelle ("5", darunter "davon
   1 gesperrt"): Der Beisatz steht nur, wenn er etwas sagt. Beide stecken
   in EINEM Element (.wert) -- in der Kartenform zieht der Flex-Behaelter
   der Zelle sonst Wert und Beisatz auseinander, und der Beisatz schwebt
   zwischen zwei Zeilen, als gehoere er zur naechsten. */
table.mandanten td .beisatz { display: block; font-size: var(--gr-s); color: var(--tinte-leise); }
/* Die Koepfe duerfen hier umbrechen: "ZULETZT ANGEMELDET" war in beiden
   Listen die breiteste Zelle der Spalte und schob die Tabelle knapp
   ueber der Kartengrenze (701 px) aus ihrem Rahmen -- gemessen am
   28.09.2026 im Muster bei 768: Mandanten 716 in 660, Benutzer 672 in 660.
   Danach: Benutzer 660 in 660, Mandanten 690 in 660 -- die Mandanten
   rollen knapp ueber 700 px noch in ihrem Rahmen (in der Anwendung bis
   etwa 730 px Fensterbreite, der Rahmen dort ist 32 px breiter). */
table.mandanten thead th, table.benutzerliste thead th { white-space: normal; }
@media (max-width: 700px) {
    table[data-karten].mandanten td > .wert { text-align: right; }
}

/* ---- Betreiberbereich (D3) ----
   Anmelden und Abmelden: eine Karte mit einer Aufgabe, so breit wie die
   Karte des Anmeldeblatts (.anmeldeblatt .karte) -- das Blatt selbst ist
   100dvh hoch und passt nicht unter das Band. */
.karte.betreiberanmeldung { width: min(24rem, 100%); margin: var(--raum-6) auto; padding: var(--raum-6); }
.karte.betreiberanmeldung h1 { font-size: var(--gr-l); }
/* Die Handlungen je Benutzerzeile in der Akte: Link und Sperren stehen
   nebeneinander und brechen am Telefon um, statt die Zelle zu sprengen. */
td.aktionen .zeilenaktionen { flex-wrap: wrap; justify-content: flex-end; gap: var(--raum-2); }
td.aktionen .zeilenaktionen form { margin: 0; }
td.aktionen .feldhinweis { margin: 0; max-width: 18rem; }
/* Am Telefon (Kartenform) nimmt die Handlungszeile die ganze Breite, und
   ein Knopf mit Namen ("zz-Vera Verwaltung sperren") bricht in sich um statt
   ueber den Kartenrand zu ragen -- gemessen D3 bei 375 px, 28.09.2026. */
@media (max-width: 700px) {
    table[data-karten] td.aktionen .zeilenaktionen { flex: 1 1 100%; justify-content: flex-start; }
    table[data-karten] td.aktionen .knopf { white-space: normal; max-width: 100%; text-align: center; line-height: 1.25; }
}

/* ============================================= ENDE ZUGAENGE ========= */
