/* ==========================================================================
   SVM Verwaltung — Gestaltung

   Übernommen aus prototyp/styles.css, dem umgesetzten Design-Entwurf.
   Zwei Änderungen gegenüber dem Prototyp:

     1. Zwei Themes statt drei: "Weiß" und "Dunkel". Die Variante "Papier"
        ist entfernt — sie unterscheidet sich vom Standard in vier Werten
        und wäre ein dritter Satz, der bei jeder Änderung mitzuprüfen
        wäre. "Dunkel" steht weiter unten; ab Werk folgt die Anwendung dem
        Betriebssystem, der Schalter oben rechts übersteuert das.
     2. Die Abschnitte für die Telefon-Attrappen, die Rechnungsvorschau und
        das Auswertungs-Diagramm sind entfernt — die zugehörigen Bildschirme
        gibt es in dieser Ausbaustufe noch nicht. Die echte responsive
        Darstellung ist unverändert enthalten.

   Am Ende der Datei stehen die Klassen, die in der Webanwendung dazukommen
   und im Prototyp keine Entsprechung hatten.

   Keine externen Schriften, keine externen Skripte (Datenschutz, QA-10).
   ========================================================================== */

/* --- Tokens: Theme "Weiß" (Standard) ------------------------------------ */

:root {
  /* Sagt dem Browser, in welcher Richtung er zeichnet, was er selbst
     zeichnet: Rollbalken, die eingebaute Datumsauswahl, die Aufklappliste
     eines <select>. Ohne die Angabe blieben sie hell und stünden im
     dunklen Bild als weiße Kästchen. */
  color-scheme: light;

  --color-bg: #ffffff;
  --color-surface: #f4f5f7;
  --color-text: #1d1f20;
  --color-accent: #3f6b96;
  --color-divider: color-mix(in srgb, #1d1f20 14%, transparent);
  --sidebar-bg: #f4f5f7;

  --color-neutral-100: #f5f5f8;
  --color-neutral-200: #e7e7ea;
  --color-neutral-300: #d4d4d7;
  --color-neutral-600: #7a7a7d;
  --color-neutral-800: #424244;
  --color-neutral-900: #2b2b2d;

  --color-accent-100: #eef3f9;
  --color-accent-300: #b5d9fd;
  --color-accent-600: #597ea3;
  --color-accent-700: #416180;
  --color-accent-800: #2c455d;
  --color-accent-900: #1d2d3d;

  /* Semantische Farben: Status, nie Dekoration. */
  --color-ok: #2f6b4f;
  --color-warn: #8a5a12;
  --color-err: #a3392f;

  --font-heading: "Segoe UI Semibold", "Segoe UI", "Helvetica Neue", system-ui, sans-serif;
  --font-heading-weight: 600;
  --font-body: "Segoe UI", "Helvetica Neue", system-ui, Tahoma, sans-serif;
  --font-mono: ui-monospace, Menlo, Consolas, monospace;

  --space-1: 3.4px;
  --space-2: 6.8px;
  --space-3: 10.2px;
  --space-4: 13.6px;
  --space-6: 20.4px;

  --radius-md: 4px;
  --radius-lg: 7px;

  /* Höhe der Spaltenüberschriften einer Liste — die Eingabezeile klebt
     genau darunter. */
  --tabellenkopf-hoehe: 32px;

  /* Höhe von Seitenleistenkopf und Kopfleiste — beide, damit ihre
     Trennlinien eine Linie ergeben. Reicht für 23 px Überschrift samt
     Unterzeile; wer die Schriftgrößen dort ändert, prüft den Wert nach. */
  --kopfhoehe: 68px;

  --shadow-sm: 0 1px 2px color-mix(in srgb, #2b2b2d 14%, transparent);
  --shadow-lg: 0 12px 32px color-mix(in srgb, #2b2b2d 22%, transparent);

  --muted-55: color-mix(in srgb, var(--color-text) 55%, transparent);
  --muted-60: color-mix(in srgb, var(--color-text) 60%, transparent);
  --muted-65: color-mix(in srgb, var(--color-text) 65%, transparent);
  --muted-70: color-mix(in srgb, var(--color-text) 70%, transparent);
}

/* --- Tokens: Theme "Dunkel" ----------------------------------------------

   Der Entwurf verlangt beides: „Hell und dunkel beide sauber gestalten“
   (_Basisdaten/design-brief-svm-web.md). Der Prototyp hatte den Satz und
   eine Umschaltung; beim Übertragen in die Anwendung ist er zunächst
   weggefallen. Hier ist er wieder.

   Er steht **zweimal**, und das ist Absicht:

     1. `:root[data-theme="dunkel"]` — es wurde oben rechts umgeschaltet.
        Der Wert kommt aus dem Cookie und steht schon im ausgelieferten
        HTML, damit die Seite nicht erst hell aufblitzt.
     2. im `@media`-Block — das Betriebssystem sagt dunkel und niemand hat
        etwas anderes gewählt. `:not([data-theme="weiss"])` ist dabei die
        Übersteuerung: wer ausdrücklich hell will, bekommt hell, auch wenn
        das System dunkel steht.

   Zusammenfassen lassen sich die beiden nicht — eine Auswahlliste kann
   keinen Medienzustand abfragen, und `@media` erweitert keine
   Auswahlliste. Damit sie nicht auseinanderlaufen, vergleicht
   `pruefung/faelle/08-thema.php` sie Zeichen für Zeichen. Wer hier etwas
   ändert, ändert es in beiden Blöcken gleich.
   ------------------------------------------------------------------------ */

:root[data-theme="dunkel"] {
  color-scheme: dark;

  --color-bg: #191b1d;
  --color-surface: #212427;
  --color-text: #e7e8ea;
  --color-accent: #8fb2d6;
  --color-divider: color-mix(in srgb, #e7e8ea 20%, transparent);
  --sidebar-bg: #141618;

  /* Die Leiter dreht sich um: im hellen Satz führt sie von hell nach
     dunkel, hier von dunkel nach hell. 100 bleibt der Ton knapp neben dem
     Grund (gesperrte Felder, übernommene Zeilen), 900 der äußerste — er
     liegt jetzt *unter* dem Grund und trägt den Schleier hinter einem
     Dialog. Die ganze Leiter steht da, auch was heute niemand benutzt:
     sonst greift ein späterer Handgriff ins Helle und niemand sieht es.  */
  --color-neutral-100: #242729;
  --color-neutral-200: #2c3033;
  --color-neutral-300: #3a3f43;
  --color-neutral-600: #9a9b9e;
  --color-neutral-800: #c9cacd;
  --color-neutral-900: #0b0c0d;

  --color-accent-100: #243447;
  --color-accent-300: #5f7f9f;
  --color-accent-600: #7ba0c6;
  --color-accent-700: #a9c6e4;
  --color-accent-800: #c6dcf1;
  --color-accent-900: #e2eefb;

  /* Aufgehellt gegenüber dem Entwurf: dessen Werte (#2f6b4f, #8a5a12,
     #a3392f) sind auf dem Grund #191b1d nicht ausreichend lesbar (QA-07).
     Im hellen Satz bleiben sie unverändert. So schon im Prototyp, mit
     derselben Begründung — prototyp/README.md, Abweichung 2. */
  --color-ok: #74bf98;
  --color-warn: #d7a44f;
  --color-err: #e8887c;

  /* Ein Schatten aus dem Textton wäre hier ein heller Schein. Schatten
     bleiben schwarz und werden kräftiger: auf dunklem Grund trägt wenig
     Deckung nicht. */
  --shadow-sm: 0 1px 2px color-mix(in srgb, #000000 55%, transparent);
  --shadow-lg: 0 12px 32px color-mix(in srgb, #000000 60%, transparent);
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="weiss"]) {
    color-scheme: dark;

    --color-bg: #191b1d;
    --color-surface: #212427;
    --color-text: #e7e8ea;
    --color-accent: #8fb2d6;
    --color-divider: color-mix(in srgb, #e7e8ea 20%, transparent);
    --sidebar-bg: #141618;

    /* Die Leiter dreht sich um: im hellen Satz führt sie von hell nach
       dunkel, hier von dunkel nach hell. 100 bleibt der Ton knapp neben dem
       Grund (gesperrte Felder, übernommene Zeilen), 900 der äußerste — er
       liegt jetzt *unter* dem Grund und trägt den Schleier hinter einem
       Dialog. Die ganze Leiter steht da, auch was heute niemand benutzt:
       sonst greift ein späterer Handgriff ins Helle und niemand sieht es.  */
    --color-neutral-100: #242729;
    --color-neutral-200: #2c3033;
    --color-neutral-300: #3a3f43;
    --color-neutral-600: #9a9b9e;
    --color-neutral-800: #c9cacd;
    --color-neutral-900: #0b0c0d;

    --color-accent-100: #243447;
    --color-accent-300: #5f7f9f;
    --color-accent-600: #7ba0c6;
    --color-accent-700: #a9c6e4;
    --color-accent-800: #c6dcf1;
    --color-accent-900: #e2eefb;

    /* Aufgehellt gegenüber dem Entwurf: dessen Werte (#2f6b4f, #8a5a12,
       #a3392f) sind auf dem Grund #191b1d nicht ausreichend lesbar (QA-07).
       Im hellen Satz bleiben sie unverändert. So schon im Prototyp, mit
       derselben Begründung — prototyp/README.md, Abweichung 2. */
    --color-ok: #74bf98;
    --color-warn: #d7a44f;
    --color-err: #e8887c;

    /* Ein Schatten aus dem Textton wäre hier ein heller Schein. Schatten
       bleiben schwarz und werden kräftiger: auf dunklem Grund trägt wenig
       Deckung nicht. */
    --shadow-sm: 0 1px 2px color-mix(in srgb, #000000 55%, transparent);
    --shadow-lg: 0 12px 32px color-mix(in srgb, #000000 60%, transparent);
  }
}

/* --- Grundlagen --------------------------------------------------------- */

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

/* **Der Platz des Rollbalkens wird immer freigehalten.**

   Ohne das ist der Inhaltsbereich auf einer kurzen Seite rund fünfzehn Pixel
   breiter als auf einer langen — der Rollbalken kommt und geht mit der Länge.
   Bei jedem Wechsel zwischen zwei Bildschirmen rückt deshalb alles um diese
   Pixel: die Kopfleiste, die Filterzeile, die Spaltenbreiten. Bei Tabellen,
   deren Spalten sich den Rest teilen, ist es besonders auffällig, und beim
   Umschalten zwischen Verwaltungsmasken springt der Streifen darüber sogar
   von zwei Zeilen auf eine und zurück.

   `stable` reserviert die Rinne auch dann, wenn gerade nicht gerollt wird.
   Browser, die das nicht kennen, verhalten sich wie bisher — es geht also
   nichts verloren. */
html { scrollbar-gutter: stable; }

body {
  margin: 0;
  background: var(--color-bg);
  color: var(--color-text);
  font-family: var(--font-body);
  font-size: 15px;
  line-height: 1.55;
}

h1, h2, h3, h4 {
  font-family: var(--font-heading);
  font-weight: var(--font-heading-weight);
  line-height: 1.12;
  letter-spacing: -0.015em;
  margin: 0 0 var(--space-2);
}

p { margin: 0 0 var(--space-3); }
a { color: var(--color-accent); text-underline-offset: 3px; }
a:hover { color: var(--color-accent-700); }

:focus { outline: none; }
:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 2px; }
::selection { background: color-mix(in srgb, var(--color-accent) 30%, transparent); }

kbd {
  font: 500 10px/1 var(--font-mono);
  border: 1px solid var(--color-divider);
  padding: 3px 4px;
  color: var(--muted-60);
}

@keyframes skel { 0%, 100% { opacity: .35 } 50% { opacity: .85 } }

@media (prefers-reduced-motion: reduce) {
  * { animation: none !important; transition: none !important; }
}

/* --- Zahlen ------------------------------------------------------------- */

.num, .tnum {
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.num { text-align: right; }

.ellipsis {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.muted { color: var(--muted-55); }

/* --- Blueprint-Rahmen: Komponenten sind Drahtmodell-Objekte ------------- */

.blueprint {
  position: relative;
  border: 1px solid var(--color-divider);
  border-radius: 0;
}

/* --- Schaltflächen ------------------------------------------------------ */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  cursor: pointer;
  text-decoration: none;
  font-family: var(--font-heading);
  font-weight: var(--font-heading-weight);
  font-size: 14px;
  line-height: 1.2;
  color: var(--color-text);
  background: transparent;
  border: 1px solid var(--color-divider);
  border-radius: 0;
  height: 34px;
  padding: 0 14px;
  white-space: nowrap;
}
.btn-ghost { height: auto; min-height: 26px; }
.btn:disabled { opacity: .45; cursor: not-allowed; }
.btn-primary {
  background: var(--color-accent);
  color: var(--color-bg);
  border-color: var(--color-accent);
}
.btn-primary:hover { background: var(--color-accent-600); border-color: var(--color-accent-600); }
.btn-primary:active { background: var(--color-accent-700); border-color: var(--color-accent-700); }
.btn-secondary:hover { background: color-mix(in srgb, var(--color-text) 7%, transparent); }
.btn-secondary:active { background: color-mix(in srgb, var(--color-text) 14%, transparent); }
.btn-ghost {
  color: var(--color-accent);
  border-color: transparent;
  padding-inline: var(--space-1);
}
.btn-ghost:hover { background: color-mix(in srgb, var(--color-accent) 10%, transparent); }

/* --- Formularfelder ----------------------------------------------------- */

.field { min-width: 0; }
.field > label {
  display: block;
  font-size: 12px;
  margin-bottom: 5px;
  color: var(--muted-70);

  /* Beschriftungen wie „Veterinärkontrollnummer“ sind ein einziges Wort und
     haben keine Trennstelle — ohne das Folgende ragen sie aus ihrer Spalte
     heraus und überlagern die Beschriftung daneben. */
  overflow-wrap: break-word;
  hyphens: auto;
}

.input {
  /* Systemaussehen abschalten — sonst sehen Auswahlfelder auf jedem
     Betriebssystem anders aus als die Textfelder daneben. */
  appearance: none;
  -webkit-appearance: none;

  width: 100%;
  height: 34px;
  min-height: 34px;
  padding: 0 10px;
  margin: 0;
  font: inherit;
  font-size: 14px;
  line-height: 1.3;
  color: var(--color-text);
  caret-color: var(--color-accent);
  background: var(--color-surface);
  border: 1px solid var(--color-divider);
  border-radius: 0;
  box-shadow: none;
}

/* Mehrzeilig und Dateiauswahl brauchen mehr Platz. */
textarea.input { height: auto; min-height: 76px; padding: 7px 10px; resize: vertical; }
.input[type="file"] { height: auto; min-height: 34px; padding: 6px 10px; }

/* Auswahlfeld: eigener Pfeil, damit es zum Textfeld passt. */
select.input {
  padding-right: 30px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%237a7a7d' stroke-width='1.5'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 10px center;
  background-size: 10px 6px;
  cursor: pointer;
}
select.input::-ms-expand { display: none; }

.input:hover { border-color: color-mix(in srgb, var(--color-text) 45%, transparent); }
.input:focus-visible {
  border-color: var(--color-accent);
  outline: 1px solid var(--color-accent);
  outline-offset: -2px;
}
.input:disabled,
.input[readonly] { opacity: .7; cursor: not-allowed; background: var(--color-neutral-100); }
.input::placeholder { color: var(--muted-55); }

/* Feldbreiten als Klassen statt als Inline-Stil in jeder Ansicht —
   das war der Grund, warum die Leisten überall anders aussahen. */
.feld-xs { width: 90px; flex: 0 0 90px; }
.feld-s  { width: 120px; flex: 0 0 120px; }
.feld-m  { width: 160px; flex: 0 0 160px; }
.feld-l  { width: 210px; flex: 0 0 210px; }
.feld-xl { width: 280px; flex: 0 0 280px; }
.feld-voll { flex: 1 1 220px; min-width: 0; }

/* Segmentierte Auswahl */
.seg {
  display: inline-flex;
  overflow: hidden;
  border: 1px solid var(--color-divider);
  border-radius: 0;
}
.seg { height: 34px; }
.seg-opt {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 0 12px;
  font-size: 13px;
  cursor: pointer;
  white-space: nowrap;
}
.seg-opt + .seg-opt { border-left: 1px solid var(--color-divider); }
.seg-opt input {
  position: absolute;
  opacity: 0;
  width: 0;
  height: 0;
  pointer-events: none;
}
.seg-opt:has(input:checked) { background: var(--color-accent); color: var(--color-bg); }
.seg-opt:not(:has(input:checked)):hover { background: color-mix(in srgb, var(--color-text) 7%, transparent); }
.seg-opt:has(input:focus-visible) { outline: 2px solid var(--color-accent); outline-offset: -2px; }

input[type="checkbox"] {
  accent-color: var(--color-accent);
  width: 15px;
  height: 15px;
}

/* --- Tabellen ----------------------------------------------------------- */

.table { width: 100%; border-collapse: collapse; font-size: 14px; }

/* Der Kopf steht auf eigener Fläche. Vorher trennte ihn nur eine Linie von
   den Daten — bei zwölf Spalten war nicht mehr zu erkennen, wo die
   Überschriften aufhören. */
.table thead th {
  text-align: left;
  font-size: 11px;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--muted-70);
  padding: var(--space-2);
  background: var(--color-surface);
  border-bottom: 1px solid var(--color-divider);
  box-shadow: inset -1px 0 0 var(--color-divider);
  white-space: nowrap;
}
.table thead th:last-child { box-shadow: none; }

/* Sortierbare Überschriften bleiben blau: daran ist zu erkennen, dass sie
   anklickbar sind — die übrigen Überschriften sind es nicht. */

.table td {
  padding: var(--space-2);
  border-bottom: 1px solid color-mix(in srgb, var(--color-text) 8%, transparent);
}

/* Senkrechte Linien wie in einem Kontoauszug — bei vielen schmalen Spalten
   ist ohne sie nicht mehr abzulesen, welcher Wert zu welcher Spalte gehört. */
.table tbody td { box-shadow: inset -1px 0 0 color-mix(in srgb, var(--color-text) 6%, transparent); }
.table tbody td:last-child { box-shadow: none; }
.table tbody tr:hover { background: color-mix(in srgb, var(--color-text) 4%, transparent); }
.table tfoot td {
  border-bottom: none;
  border-top: 1px solid var(--color-divider);
  font-weight: 700;
}
.table tfoot .label {
  font-weight: 400;
  font-size: 11.5px;
  letter-spacing: .05em;
  text-transform: uppercase;
  color: var(--muted-60);
}

.tablescroll { overflow-x: auto; min-width: 0; }

/* Zeilenzustände — Markierung immer links als 3px-Balken.

   Der Balken hängt an der **ersten Zelle**, nicht am `<tr>`: WebKit zeichnet
   einen Schatten an einer Tabellenzeile bei `border-collapse: collapse` gar
   nicht, und in Safari fehlten die farbigen Balken deshalb vollständig. Die
   Zeile nennt nur noch ihre Farbe; die gemeinsame Regel dazu steht bei den
   Ergänzungen am Dateiende. */
tr.is-selected { background: var(--color-accent-100); --zeilenbalken: var(--color-accent); }
tr.is-error { --zeilenbalken: var(--color-err); }
tr.is-clickable { cursor: pointer; }
tr.is-strong { font-weight: 500; }

.val-err { color: var(--color-err); font-weight: 700; }
.val-warn { color: var(--color-warn); }
.val-ok { color: var(--color-ok); }

.st { font-size: 12px; }
.st-ok { color: var(--color-ok); }
.st-muted { color: var(--muted-55); }
.st-warn { color: var(--color-warn); }
.st-err { color: var(--color-err); }

/* --- Anwendungsgerüst --------------------------------------------------- */

.app {
  display: flex;
  min-height: 100vh;
  background: var(--color-bg);
}

.sidebar {
  width: 210px;
  flex: none;
  background: var(--sidebar-bg);
  border-right: 1px solid var(--color-divider);
  display: flex;
  flex-direction: column;
  position: sticky;
  top: 0;
  height: 100vh;
}
/* Seitenleistenkopf und Kopfleiste tragen dieselbe Höhe, damit ihre
   Trennlinien eine durchgehende Linie bilden. Vorher ergab sie sich aus dem
   Inhalt: 19 px Marke plus Unterzeile links, 23 px Überschrift plus
   Unterzeile rechts — gut zwei Pixel Unterschied, genug, dass die Linie
   sichtbar einen Absatz macht. Der Wert steht an einer Stelle
   (`--kopfhoehe`); wer eine der beiden Schriftgrößen ändert, ändert ihn mit. */
.sidebar-head {
  display: flex;
  align-items: center;
  gap: 8px;
  height: var(--kopfhoehe);
  padding: 0 10px 0 14px;
  border-bottom: 1px solid var(--color-divider);
}
.sidebar-marke { flex: 1; min-width: 0; overflow: hidden; }
.sidebar-brand {
  font-family: var(--font-heading);
  font-weight: 600;
  font-size: 19px;
  letter-spacing: -.01em;
  line-height: 1;
}
.sidebar-sub {
  font-size: 11px;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--muted-55);
  margin-top: 3px;
}
.sidebar-nav { padding: 8px 0; display: flex; flex-direction: column; }
.sidebar-foot {
  margin-top: auto;
  border-top: 1px solid var(--color-divider);
  font-size: 11px;
  line-height: 1.5;
  color: var(--muted-55);
}
.sidebar-benutzer { padding: 10px 14px 8px; overflow: hidden; }
.sidebar-benutzer-name { font-size: 12.5px; color: var(--color-text); white-space: nowrap; }
.sidebar-benutzer-rolle { white-space: nowrap; }
.sidebar-abmelden {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 14px;
  font-size: 12.5px;
  color: var(--color-text);
  text-decoration: none;
  border-top: 1px solid var(--color-divider);
}
.sidebar-abmelden:hover { background: color-mix(in srgb, var(--color-text) 5%, transparent); color: var(--color-text); }
.sidebar-abmelden .symbol { color: var(--muted-60); }

.nav-item {
  display: flex;
  align-items: center;
  gap: 10px;
  text-decoration: none;
  padding: 7px 14px;
  font-size: 13.5px;
  color: var(--color-text);
  background: none;
  border: 0;
  font-family: inherit;
  text-align: left;
  cursor: pointer;
  width: 100%;
}
.nav-symbol { flex: none; display: flex; width: 20px; height: 20px; color: var(--muted-60); }
.nav-item.is-active .nav-symbol,
.nav-item.is-branch .nav-symbol { color: var(--color-accent); }
.symbol { width: 20px; height: 20px; display: block; }
.nav-item:hover { background: color-mix(in srgb, var(--color-text) 5%, transparent); }
.nav-item.is-sub { padding: 5px 14px 5px 34px; font-size: 12.5px; color: var(--muted-70); }
.nav-item.is-branch { box-shadow: inset 3px 0 0 var(--color-accent); color: var(--color-accent-900); font-weight: 500; }
.nav-item.is-active {
  background: var(--color-accent-100);
  color: var(--color-accent-900);
  box-shadow: inset 3px 0 0 var(--color-accent);
  font-weight: 500;
}
.nav-key { flex: none; font: 500 10px/1 var(--font-mono); opacity: .45; }
.nav-label { flex: 1; }
.nav-badge { font-variant-numeric: tabular-nums; white-space: nowrap; font-size: 11px; opacity: .6; }

/* Der Knopf der Schublade gehört dem Telefon; auf breiten Bildschirmen ist
   die Leiste immer offen und er hätte nichts zu tun.

   **Die Rücknahme steht vor der `@media`-Regel, nicht danach** — dieselbe
   Falle wie bei `.stapel-hinweis`: bei gleicher Spezifität gewinnt die
   spätere Regel, unabhängig davon, dass die eine bedingt ist. Weiter unten
   geschrieben war dieses `display: none` in Wahrheit das stärkere, und der
   Knopf blieb auch am Telefon unsichtbar — die Navigation war damit gar
   nicht mehr zu erreichen. */
.leiste-mobil { display: none; }

.main { flex: 1; min-width: 0; display: flex; flex-direction: column; }

.topbar {
  display: flex;
  align-items: center;
  gap: 14px;
  height: var(--kopfhoehe);
  padding: 0 20px;
  border-bottom: 1px solid var(--color-divider);
}
.topbar h1 { font-size: 23px; margin: 0; line-height: 1.1; }
.topbar-sub { font-size: 11.5px; color: var(--muted-55); }
.topbar-tools { display: flex; align-items: center; gap: 8px; }

.content { flex: 1; min-width: 0; padding: 18px 20px 40px; }

/* --- Werkzeugleisten ---------------------------------------------------- */

.toolbar {
  display: flex;
  align-items: flex-end;
  gap: 10px;
  flex-wrap: wrap;
  padding-bottom: 12px;
  border-bottom: 1px solid var(--color-divider);
}
.toolbar .field { flex: none; }
.toolbar-actions { margin-left: auto; display: flex; gap: 8px; }

.legend {
  display: flex;
  gap: 18px;
  flex-wrap: wrap;
  margin-top: 10px;
  font-size: 11px;
  color: var(--muted-60);
}

.hint-line {
  margin-left: auto;
  font-size: 11.5px;
  color: var(--muted-55);
}

/* --- Bänder ------------------------------------------------------------- */

.banner {
  /* Hinweiskästen brauchen Luft nach oben und unten — sonst kleben sie an
     der Tabelle oder am Formular darüber. Der Abstand steht hier und nicht
     als Inline-Stil in dreiunddreißig Ansichten. */
  margin: 8px 0;
  border: 1px solid var(--color-divider);
  padding: 7px 12px;
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  font-size: 13px;
}
.banner-ok { border-color: var(--color-ok); }
.banner-ok .sym { color: var(--color-ok); font-weight: 700; }
.banner .spacer { margin-left: auto; display: flex; gap: 8px; }

/* --- Sammeländerung ------------------------------------------------------
   Bewusst so ruhig wie die Filterleiste im Kopf: dieselbe Fläche, dieselbe
   dünne Linie, Beschriftung über dem Feld. Vorher stand hier ein farbig
   abgesetztes Band, das sich vom übrigen Bildschirm löste. */

.sammelleiste {
  border: 1px solid var(--color-divider);
  background: var(--color-surface);
  margin-top: 12px;
}
.sammel-felder {
  display: flex;
  align-items: flex-end;
  gap: 10px;
  flex-wrap: wrap;
  padding: 10px 12px;
}
.sammel-felder .field { flex: none; }

/* Das Ankreuzfeld füllt dieselbe Höhe wie ein Eingabefeld daneben, damit die
   Zeile nicht verspringt. Die Beschriftung daran steht bewusst in einem
   eigenen Kasten: `.field > label` ist die stärkere Regel und würde sie
   sonst zu einem Block über dem Kästchen machen. */
.sammel-ankreuz {
  display: flex;
  align-items: center;
  gap: 8px;
  height: 34px;
  padding-right: 4px;
}
.sammel-ankreuz label {
  margin: 0;
  font-size: 13.5px;
  color: var(--color-text);
  cursor: pointer;
}

.sammel-tasten { margin-left: auto; display: flex; gap: 8px; align-items: flex-end; }

/* „Auswahl aufheben“ ist ein Geistknopf und damit niedriger als die
   umrandeten daneben. Unten ausgerichtet saß seine Beschriftung sichtbar
   tiefer als deren; mittig steht sie auf einer Linie. */
.sammel-tasten .btn-ghost { align-self: center; }

/* Fußzeile: Geltungsbereich und Zähler, abgesetzt wie die Filtermarken im Kopf. */
.sammel-fuss {
  display: flex;
  align-items: center;
  gap: 14px;
  flex-wrap: wrap;
  padding: 8px 12px;
  border-top: 1px solid var(--color-divider);
}
.sammel-alle { align-items: center; font-size: 12.5px; }
.sammel-alle input { margin-top: 0; }

.sammel-hinweis {
  margin: 0;
  font-size: 12px;
  color: var(--muted-60);
}

@media (max-width: 720px) {
  .sammel-tasten { margin-left: 0; width: 100%; }
  .sammel-tasten .btn { flex: 1; }
}

/* --- Kennzahlenleisten -------------------------------------------------- */

.statstrip {
  display: flex;
  border: 1px solid var(--color-divider);
  border-bottom: none;
}
.statstrip .stat { flex: 1; padding: 8px 12px; border-right: 1px solid var(--color-divider); }
.statstrip .stat:last-child { border-right: none; }
.statstrip .stat.is-accent { background: var(--color-accent-100); }
.statstrip .stat.is-accent .stat-label { color: var(--color-accent-800); }
.statstrip .stat.is-accent .stat-value { color: var(--color-accent-900); }
.stat-label {
  font-size: 10px;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--muted-55);
}
.stat-value {
  font-family: var(--font-heading);
  font-size: 22px;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  line-height: 1.2;
}

.summbar {
  position: sticky;
  bottom: 0;
  margin-top: 14px;
  padding: 10px 14px;
  display: flex;
  align-items: center;
  gap: 26px;
  flex-wrap: wrap;
  background: var(--color-bg);
  border: 1px solid var(--color-divider);
}
.summbar .stat-value { font-size: 24px; line-height: 1.1; }
.summbar .note { margin-left: auto; font-size: 12.5px; color: var(--color-warn); }

/* --- Erfassungsmaske ---------------------------------------------------- */

.formgrid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 12px;
  padding-bottom: 14px;
  border-bottom: 1px solid var(--color-divider);
}
.slots { display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px; margin-top: 14px; }
.slot { padding: 12px; }
.slot-info {
  margin-top: 10px;
  padding-top: 10px;
  border-top: 1px dashed var(--color-divider);
  font-size: 12.5px;
  line-height: 1.6;
}
.slot-name { font-family: var(--font-heading); font-size: 16px; }
.slot-row { display: flex; justify-content: space-between; font-variant-numeric: tabular-nums; white-space: nowrap; }
.slot-hint { margin-top: 6px; font-size: 12px; }

/* Ein Feld und der Knopf dazu in einer Zeile — Passwort setzen, Datei
   einspielen, zweiter Faktor.

   **`flex-end`, nicht `center`.** Ein `.field` ist zwei Zeilen hoch:
   Beschriftung oben, Eingabefeld darunter. Mittig ausgerichtet stand der
   Knopf auf halber Höhe des ganzen Blocks — also deutlich **über** dem Feld,
   zu dem er gehört, und auf gleicher Höhe mit dessen Beschriftung. Am unteren
   Rand ausgerichtet steht er auf der Linie des Eingabefeldes. Dieselbe
   Ausrichtung wie in `.toolbar` und `.kopf-filter`, wo Feld und Knopf aus
   demselben Grund nebeneinanderstehen.

   **Der Abstand zum Feld ist waagrecht größer als senkrecht** (20 statt 10):
   ein Knopf, der am Eingabefeld klebt, sieht aus, als gehörte er hinein.
   Senkrecht bleibt es bei 10 — das ist der Abstand zwischen zwei Zeilen,
   wenn die Leiste umbricht. */
.formbar {
  display: flex;
  align-items: flex-end;
  gap: 10px 20px;
  flex-wrap: wrap;
  margin-top: 16px;
  padding-top: 14px;
  border-top: 1px solid var(--color-divider);
}

/* --- Massenerfassung ---------------------------------------------------- */

/* Die Zahlenfelder der Massenerfassung.

   Sie waren rahmenlos und erst beim Überfahren zu sehen — gedacht als ruhiges
   Raster. Neben der Betriebswahl, die inzwischen ein gewöhnliches Feld mit
   Rahmen ist, sah das aus, als gäbe es dort gar nichts einzutippen. Jetzt
   tragen sie denselben Rahmen wie jedes andere Feld; niedriger und enger
   bleiben sie, damit acht Zeilen untereinander nicht auseinanderlaufen. */
.grid-input {
  min-height: 30px;
  height: 30px;
  padding: 2px 8px;
}
.grid-input.is-error { border-color: var(--color-err); }
.massen-betrieb { display: flex; align-items: center; gap: 8px; min-width: 0; }
.massen-lfbis { flex: none; white-space: nowrap; font-size: 11px; color: var(--muted-55); }
.seg-inrow { width: 100%; }
.seg-inrow .seg-opt { flex: 1; justify-content: center; padding: 4px 8px; }

/* Dieselbe Richtungswahl in der Eingabezeile der Meldungsliste. Dort teilt
   sie sich die Breite von „Gesendet“ und „Nummer“ — 58 + 68 px, und mehr ist
   nicht zu holen: was diese Spalte breiter macht, nehmen die Betriebsnamen
   daneben ab. Mit den Innenabständen der Massenerfassung lief
   „Eingang | Ausgang“ darüber hinaus. Höhe wie die Felder der Zeile
   (`.grid-input`, `.input` in der Eingabezeile), sonst steht sie um vier
   Pixel höher als das Datumsfeld daneben. */
.seg-eng { height: 30px; }
.seg-eng .seg-opt { padding: 0 4px; font-size: 12px; }

/* --- Kundendetail ------------------------------------------------------- */

.groupgrid {
  display: grid;
  /* Statt fest drei Spalten: so viele, wie mit 300 px Platz hineinpassen.
     Damit geht die Aufteilung 3 → 2 → 1, ohne dass ein Umbruchpunkt
     festgelegt werden müsste, und die Gruppen werden nie zu schmal. */
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: 14px;
  margin-top: 14px;
  align-items: start;
}
.group { padding: 12px; }
.group h3 {
  font-size: 13px;
  letter-spacing: .08em;
  text-transform: uppercase;
  margin: 0 0 10px;
  color: var(--muted-65);
}
.group-fields {
  display: grid;
  /* **Zwölf schmale Spalten**, aus denen sich jedes Feld nimmt, was es
     braucht. Nicht so viele Spalten, wie hineinpassen: die Anschrift steht
     paarweise, und das geht nur auf, wenn die Spaltenzahl nicht mit der
     Fensterbreite springt. Zwölf teilen sich in Hälften, Drittel und
     Viertel — genug, um Straße/Hausnummer und PLZ/Ort verschieden zu
     gewichten. Die Vorgabe ist die Hälfte, also zwei Felder je Zeile. */
  grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: 8px;

  /* Beschriftungen brauchen je nach Länge eine oder zwei Zeilen. Ohne das
     stünden die Eingabefelder einer Zeile auf verschiedener Höhe. */
  align-items: end;
}

/* Über die ganze Breite — auch dann, wenn nur eine Spalte übrig ist.
   „span 2“ allein ergäbe in einer einspaltigen Gruppe eine Leerspalte. */
.span2 { grid-column: 1 / -1; }

/* Breite eines Feldes im Zwölferraster. Ohne Angabe die halbe Zeile.
   `.span2` bleibt die ganze — der Name stammt aus der Zeit, als das Raster
   zwei Spalten hatte, und steht in mehreren Ansichten. */
.group-fields > .field { grid-column: span 6; }
.group-fields > .span2 { grid-column: 1 / -1; }
.group-fields > .feld-breit { grid-column: span 8; }
.group-fields > .feld-schmal { grid-column: span 4; }

/* --- Zustände ----------------------------------------------------------- */

.stategrid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.state-empty { padding: 34px 20px; text-align: center; }
.state-empty .headline { font-family: var(--font-heading); font-size: 20px; }
.state-empty p { font-size: 13px; max-width: 330px; margin: 6px auto 14px; color: var(--muted-65); }
.state-actions { display: flex; gap: 8px; justify-content: center; }
.skel-row {
  display: flex;
  gap: 8px;
  padding: 6px 0;
  border-bottom: 1px solid color-mix(in srgb, var(--color-text) 7%, transparent);
}
.skel { animation: skel 1.4s ease-in-out infinite; height: 12px; }
.skel-a { width: 16px; background: var(--color-neutral-300); }
.skel-b { width: 70px; background: var(--color-neutral-300); }
.skel-c { width: 120px; background: var(--color-neutral-200); }
.skel-d { flex: 1; background: var(--color-neutral-200); }
.state-msg { padding: 14px; display: flex; gap: 10px; }
.state-msg .sym { font-size: 16px; line-height: 1.2; }
.state-msg h4 { font-family: var(--font-heading); font-size: 19px; margin: 0; }
.state-msg p { font-size: 13px; margin: 4px 0 0; }
.state-msg .actions { display: flex; gap: 8px; margin-top: 12px; }
.kv { width: 100%; border-collapse: collapse; font-size: 12.5px; margin-top: 8px; }
.kv td { padding: 3px 0; }
.kv td:first-child { color: var(--muted-60); width: 110px; }

/* --- Dialog ------------------------------------------------------------- */

.dialog-backdrop {
  position: fixed;
  inset: 0;
  z-index: 50;
  display: grid;
  place-items: center;
  padding: var(--space-4);
  background: color-mix(in srgb, var(--color-neutral-900) 50%, transparent);
}
.dialog {
  width: min(560px, 100%);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  padding: var(--space-4);
  background: var(--color-bg);
  border: 1px solid var(--color-divider);
  box-shadow: var(--shadow-lg);
}
.dialog-head { display: flex; align-items: baseline; gap: 10px; }
.dialog-title { font-family: var(--font-heading); font-size: 20px; margin: 0; }
.dialog-actions { display: flex; justify-content: flex-end; gap: var(--space-2); margin-top: var(--space-2); }

/* --- Echte kleine Bildschirme ------------------------------------------- */

@media (max-width: 900px) {
  .formgrid { grid-template-columns: repeat(2, 1fr); }
  /* .groupgrid regelt sich über auto-fit selbst. */
  .slots, .reportgrid, .stategrid { grid-template-columns: 1fr; }
  .statstrip { flex-wrap: wrap; }
  .statstrip .stat { flex: 1 1 50%; }
}

@media (max-width: 720px) {
  .app { flex-direction: column; }
  .sidebar {
    width: 100%;
    height: auto;
    position: static;
    border-right: none;
    border-bottom: 1px solid var(--color-divider);
  }
  /* --- Die Navigation als Schublade ------------------------------------

     Vorher lag sie hier als waagrechte Reihe: `flex-direction: row` mit
     `overflow-x: auto`, dreizehn Punkte in einem seitlich rollenden
     Streifen. Drei Dinge waren daran falsch, und alle drei fielen erst am
     Gerät auf:

      - **Der Streifen stand bei jedem Seitenaufruf wieder ganz vorne.** Wer
        in der Verwaltung war, sah „Kunden, Neuer Kunde, Meldungen …“ und
        musste sich zu der Stelle zurückrollen, an der er gerade stand.
      - **Unterpunkte standen gleichrangig neben Hauptpunkten.** Waagrecht
        gibt es keine Einrückung; aus sieben Bereichen mit ihren Handgriffen
        wurde eine Kette aus dreizehn gleich aussehenden Wörtern.
      - **Abmelden fehlte ganz** — `.sidebar-foot` war ausgeblendet, und mit
        ihm der einzige Weg hinaus.

     Jetzt steht dort ein Knopf mit dem Namen des laufenden Bildschirms.
     Angekreuzt klappt darunter die vollständige Leiste auf, senkrecht wie
     auf dem großen Bildschirm — mit Einrückung, Zählern, Benutzer und
     Abmelden. Beim nächsten Seitenaufruf ist sie wieder zu, weil ein
     Ankreuzfeld jede Seite unangekreuzt beginnt.

     Ohne Skript: alles davon. `:checked ~` ist eine Auswahlregel, kein
     Verhalten. */
  .leiste-mobil {
    flex: none;
    display: flex;
    align-items: center;
    gap: 8px;
    min-height: 44px;
    padding: 0 12px;
    cursor: pointer;
    color: var(--color-text);
    border: 1px solid var(--color-divider);
  }
  .leiste-mobil-text {
    font-size: 13px;
    max-width: 40vw;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  /* Angekreuzt sieht der Knopf gedrückt aus — sonst wäre nicht zu erkennen,
     dass die Liste darunter zu ihm gehört. */
  .leiste-auf:checked ~ .sidebar-head .leiste-mobil {
    background: var(--color-accent-100);
    color: var(--color-accent-900);
    border-color: var(--color-accent);
  }

  .sidebar-nav,
  .sidebar-foot { display: none; }
  /* Höchstens zwei Drittel des Bildschirms, der Rest rollt in der Schublade
     selbst. Dreizehn Punkte plus Fußzeile sind auf einem Telefon höher als
     die Seite: ohne die Grenze schöbe die offene Leiste den Inhalt so weit
     nach unten, dass nach dem Zuklappen niemand mehr weiß, wo er war. */
  .leiste-auf:checked ~ .sidebar-nav {
    display: flex;
    max-height: 62vh;
    overflow-y: auto;
  }
  .leiste-auf:checked ~ .sidebar-foot { display: block; }

  /* Die Fußzeile steht hier unter der Liste und nicht am unteren Rand: die
     Leiste ist so hoch wie ihr Inhalt, und `margin-top: auto` hätte nichts,
     wogegen es schöbe. */
  .sidebar-foot { margin-top: 0; }

  /* Fingerbreite Ziele (QA-03) — dieselbe Zahl wie bei `.btn` weiter
     unten. Die Einrückung der Unterpunkte bleibt: sie ist der Grund, warum
     die senkrechte Liste lesbar ist. */
  .nav-item { width: 100%; white-space: normal; min-height: 44px; }
  .nav-item.is-sub { padding-top: 8px; padding-bottom: 8px; }

  /* Die Funktionstasten haben am Telefon keine Tastatur, auf der man sie
     drücken könnte. */
  .nav-key { display: none; }

  .content { padding: 14px 12px 32px; }
  .formgrid { grid-template-columns: 1fr; }
  .toolbar .field { flex: 1 1 100%; width: auto !important; }
  .toolbar-actions { margin-left: 0; width: 100%; }
  .toolbar-actions .btn { flex: 1; }
  /* Bedienung mit Handschuhen: größere Ziele auf dem Telefon (QA-03) */
  .btn { min-height: 44px; }
  .input { min-height: 44px; font-size: 16px; }
  input[type="checkbox"] { width: 20px; height: 20px; }
}

/* ==========================================================================
   Ergänzungen der Webanwendung

   Klassen, die es im Prototyp noch nicht gab, weil dort Anmeldung,
   Blättern, Filterleisten mit Wirkung und Formularrückmeldungen fehlten.
   Sie halten sich an dieselben Festlegungen: keine Rundungen, keine
   Schatten, Status nie allein über Farbe.
   ========================================================================== */

/* --- Rückmeldungen ------------------------------------------------------ */

.bannerstapel { display: flex; flex-direction: column; gap: 8px; margin-bottom: 14px; }
.banner-warn { border-color: var(--color-warn); }
.banner-warn .sym { color: var(--color-warn); font-weight: 700; }
.banner-err { border-color: var(--color-err); }
.banner-err .sym { color: var(--color-err); font-weight: 700; }
.banner .sym { flex: none; }
/* Bei 18 px verliert die feine Linienführung der Seitenleiste zu viel;
   eine Spur kräftiger bleibt das Rufzeichen im Dreieck lesbar. */
.banner .sym .symbol { width: 18px; height: 18px; stroke-width: 1.7; }

/* Der Text eines Bandes ist als Flex-Element zunächst so breit wie sein
   Inhalt. Beim Umbruch zählt diese Wunschbreite und nicht die geschrumpfte —
   ein langer Hinweis rutschte deshalb in die zweite Flex-Zeile und das
   Symbol stand allein darüber. flex: 1 1 0 nimmt ihm die Wunschbreite: er
   bleibt neben dem Symbol und bricht in sich um, das Symbol steht davor auf
   halber Höhe. */
.banner > span:not(.sym) { flex: 1 1 0; min-width: 0; }
.banner form { margin: 0; }

/* --- Anmeldung ---------------------------------------------------------- */

.anmeldeseite {
  min-height: 100vh;
  display: grid;
  place-items: center;
  padding: var(--space-6);
  background: var(--color-bg);
}
.anmeldekarte { width: min(380px, 100%); padding: var(--space-6); }
.anmeldekopf {
  padding-bottom: var(--space-3);
  margin-bottom: var(--space-4);
  border-bottom: 1px solid var(--color-divider);
}
.anmeldekarte .banner { margin-bottom: var(--space-3); }

/* Der Anmeldeknopf bekommt Luft, damit er nicht am Passwortfeld klebt.
   Nicht über `gap`: `.mform` ist kein Flex-Kasten, dort bliebe es wirkungslos.
   Und nicht nur am `.btn-primary` — der Wiederherstellungscode im zweiten
   Schritt hat einen gewöhnlichen `.btn`, und der klebte am Feld. */
.anmeldekarte .mform .btn { margin-top: var(--space-4); }

/* --- Aktive Filter ------------------------------------------------------ */

.filterchips {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  margin-top: 10px;
  font-size: 12px;
}
.chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 3px 8px;
  border: 1px solid var(--color-accent);
  background: var(--color-accent-100);
  color: var(--color-accent-900);
  text-decoration: none;
  white-space: nowrap;
}
.chip:hover { background: var(--color-accent-300); color: var(--color-accent-900); }

/* Eine Marke ohne Weg zum Entfernen: der Zeitraum steht immer, auch in der
   Vorgabe. Er lässt sich umstellen, aber nicht wegnehmen. */
.chip-fest {
  border-color: var(--color-divider);
  background: var(--color-surface);
  color: var(--muted-70);
}

/* Für Bildschirmleseprogramme, optisch entfernt */
.nurlesbar {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* --- Kleine Kennzeichnungen in Tabellenzellen --------------------------- */

.marke {
  display: inline-block;
  margin-left: 6px;
  padding: 1px 5px;
  font-size: 10.5px;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--muted-60);
  border: 1px solid var(--color-divider);
  vertical-align: middle;
}

/* Kassazeilen aus dem Rechnungsimport (AR/ER) — 4.5 */
tr.is-import td { background: var(--color-neutral-100); }

/* --- Kassabuch: die Farben der Zeile --------------------------------------
   Im Altprogramm trug die **ganze Zeile** ihre Farbe (`kassabuch.vb`):
   Einnahmen CadetBlue, Ausgaben OrangeRed. Hier stehen dafür die beiden
   Farben der Anwendung, dieselben wie überall für „gut“ und „Achtung“.

   Die Farbe ist nie der einzige Träger der Aussage: Einnahme und Ausgabe
   stehen ohnehin in verschiedenen Spalten, und die Ausgabe trägt ihr
   Minuszeichen. Auf einem schlecht eingestellten Bildschirm oder in einem
   Schwarzweißausdruck geht damit nichts verloren. */
tr.zeile-einnahme td { color: var(--color-ok); }
tr.zeile-ausgabe td { color: var(--color-err); }

/* Der laufende Saldo bleibt schwarz — er ist kein Zu- oder Abgang, sondern
   der Stand. Nur wenn er ins Minus läuft, färbt ihn `val-err` selbst. */
tr.zeile-einnahme td.saldo,
tr.zeile-ausgabe td.saldo { color: var(--color-text); }
tr.zeile-einnahme td.saldo.val-err,
tr.zeile-ausgabe td.saldo.val-err { color: var(--color-err); }

/* --- Spaltenbreiten des Kassabuchs ---------------------------------------
   Als Klassen, nicht als `style`-Merkmal — das verwirft die
   Inhaltsrichtlinie. */
.table-kassa { font-size: 13px; min-width: 1000px; }
/* Das Datumsfeld der Eingabezeile zeigt TT.MM.JJJJ und daneben das
   Kalendersymbol des Browsers — darunter wird es nicht lesbar. Die
   Textspalte gibt den Platz her: sie ist die einzige, die von sich aus
   dehnbar ist. */
.table-kassa .sp-datum { width: 116px; }
.table-kassa .sp-steuer { width: 66px; }
.table-kassa .sp-konto { width: 170px; font-size: 12.5px; }
.table-kassa .sp-betrag { width: 105px; }
.table-kassa .sp-saldo { width: 115px; }
.table-kassa .sp-aktionen { width: 108px; }
.table-kassa .sp-auswahl { width: 30px; }
.table-kassa .sp-text { max-width: 240px; }

/* Der Saldo ist die Zahl, auf die man schaut. */
.table-kassa .saldo { font-weight: 700; }
.table-kassa .saldo-stand { background: var(--color-accent-100); }

/* --- Kopfzeile und Eingabezeile bleiben stehen ---------------------------
   Beim Blättern durch einen vollen Monat verschwinden sonst als Erstes die
   Spaltenbezeichnungen und die leere Zeile — also genau das, was man
   braucht. `position: sticky` klebt sie an den oberen Rand des Fensters;
   ein eigener Rollbereich mit fester Höhe wäre der schlechtere Weg, weil er
   sich nach der Fensterhöhe richten müsste und die Spaltenbreiten von Kopf
   und Körper auseinanderlaufen ließe.

   Die Höhe der Kopfzeile steht fest, damit die Eingabezeile weiß, wie weit
   unten sie kleben muss. Wer an der Schrift der Überschriften dreht, prüft
   den Wert nach.

   Bei `border-collapse: collapse` malt eine klebende Zelle ihren Rahmen
   nicht mit — die Striche kommen deshalb als Schatten. */
/* --- Kopf bleibt stehen, die Zeilen laufen durch -------------------------

   `.fest-oben` ist der Block über der Tabelle — Kopfleiste, Aktionen,
   Filter, was immer dort steht. Er klebt am oberen Rand des Fensters;
   darunter kleben der Spaltenkopf und, wo es eine gibt, die Eingabezeile.

   Der Versatz kommt aus `--fest-hoehe`: die Höhe des Blocks wechselt
   (Filtermarken kommen und gehen, die Filterzeile bricht um), deshalb misst
   `svm.js` sie am Element mit `data-festhoehe` und schreibt sie fort. Ohne
   Skript bleibt der Block trotzdem stehen — der Spaltenkopf klebt dann am
   oberen Rand und verschwindet dahinter; bedienbar ist alles unverändert.

   Der Hintergrund muss deckend sein, sonst scheinen die durchlaufenden
   Zeilen hindurch. */
.fest-oben {
  position: sticky;
  top: 0;
  z-index: 8;
  background: var(--color-bg);
  padding-bottom: 2px;
}

.table-fest thead th {
  position: sticky;
  top: var(--fest-hoehe, 0px);
  z-index: 3;
  height: var(--tabellenkopf-hoehe);
  /* Der eigene Rahmen fällt weg: bei `border-collapse: collapse` gehört er
     der Tabelle und wandert nicht mit. Die Linie kommt als Schatten — beides
     zusammen wäre eine doppelte. */
  border-bottom: 0;
  box-shadow: inset -1px 0 0 var(--color-divider),
              inset 0 -1px 0 var(--color-divider);
}
.table-fest thead th:last-child {
  box-shadow: inset 0 -1px 0 var(--color-divider);
}

.table-fest tbody tr.eingabezeile td {
  position: sticky;
  top: calc(var(--fest-hoehe, 0px) + var(--tabellenkopf-hoehe));
  z-index: 2;
  border-bottom: 0;
  /* Senkrechte Trennlinie wie in jeder anderen Zeile, dazu die waagrechte
     als Schatten — einmal, nicht zweimal. */
  box-shadow: inset -1px 0 0 color-mix(in srgb, var(--color-text) 6%, transparent),
              inset 0 -1px 0 var(--color-divider);
}
.table-fest tbody tr.eingabezeile td:last-child {
  box-shadow: inset 0 -1px 0 var(--color-divider);
}

/* **Der Rahmen um eine solche Tabelle darf kein Rollbereich sein.**
   `overflow-x: auto` aus `.tablescroll` macht ihn dazu, und
   `top: var(--fest-hoehe)` misst dann von dessen Oberkante — der
   Spaltenkopf stand mitten in der Tabelle, drei Zeilen tief. Deshalb
   `tablescroll-offen` (`overflow: visible`) an jeder Liste mit festem Kopf.

   Ein eigener Rollbereich mit `max-height` war der erste Versuch und hat
   drei Fehler auf einmal gebracht: zwei Rollleisten übereinander, eine
   Lücke zwischen Filter und Spaltenkopf, und beim Blättern nach ganz oben
   einen angeschnittenen Spaltenkopf — die Höhe hätte auch die Kopfleiste
   der Seite und die Innenabstände mitrechnen müssen. Nicht wieder einbauen.

   Der Preis: eine zu breite Tabelle rollt nicht mehr in ihrem Rahmen. Dafür
   stapeln die beiden breiten Listen früher — siehe `.table-stapel`. */

/* --- Blättern ----------------------------------------------------------- */

.blaettern {
  display: flex;
  align-items: center;
  gap: 14px;
  margin-top: 12px;
  padding-top: 12px;
  border-top: 1px solid var(--color-divider);
  font-size: 12.5px;
}
.blaettern .is-aus { opacity: .4; pointer-events: none; }

/* --- Ankreuzfeld mit Erklärung ------------------------------------------ */

.ankreuz { display: flex; align-items: flex-start; gap: 9px; font-size: 13.5px; cursor: pointer; }
.ankreuz input { margin-top: 3px; flex: none; }
.ankreuz small { display: block; margin-top: 2px; font-size: 12px; line-height: 1.45; }

/* --- Abschnittsüberschrift innerhalb einer Seite ------------------------ */

.abschnitt {
  font-size: 13px;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--muted-65);
  margin: 22px 0 10px;
  padding-bottom: 6px;
  border-bottom: 1px solid var(--color-divider);
}

/* --- Aufzählung in Auskunftsseiten -------------------------------------- */

.punkte { margin: 10px 0 0; padding-left: 18px; font-size: 13px; line-height: 1.7; }
.punkte li { color: var(--muted-70); }

/* --- Massenerfassung: Zeile ohne Betrag --------------------------------- */

tr.is-warn { --zeilenbalken: var(--color-warn); }

/* --- Werkzeugleiste: Formulare in einer Reihe --------------------------- */

.toolbar-actions form { margin: 0; }
form.toolbar { margin: 0; }

/* --- Deaktivierte Felder in der Nur-Lesen-Rolle ------------------------- */

.input:disabled { background: var(--color-neutral-100); }

@media (max-width: 720px) {
  .startaktionen .btn { flex: 1 1 100%; }
  .blaettern { flex-wrap: wrap; }
  .anmeldekarte { padding: var(--space-4); }
}

/* ==========================================================================
   Verwaltung und Hilfe
   ========================================================================== */

/* --- Untermenü der Verwaltung -------------------------------------------

   **Ein Raster mit gleich breiten Spuren, keine Fließzeile.** Der Bereich hat
   mehr Bildschirme, als nebeneinander in eine Zeile passen: der Inhalt ist
   bei 1440 px Fensterbreite 1189 px breit, und schon ein Dutzend
   Beschriftungen misst mehr. Der Streifen läuft also über zwei Zeilen —
   daran ist nichts zu machen, und es ist auch nicht das Problem.

   Das Problem war die zweite Zeile als **Fließzeile**: sie begann dort, wo
   die erste zufällig aufhörte, und keine Beschriftung stand unter einer
   anderen. Gleich breite Spuren heißen: alle Zeilen brechen an derselben
   Stelle um, und jeder Punkt steht unter dem darüber. Dieselbe Bauart wie bei
   den gestapelten Listen, aus demselben Grund.

   An Abständen und Schriftgröße zu feilen, bis alle Punkte doch in eine Zeile
   passen, wäre die Ein-Pixel-Falle aus der Seitenleiste: bei der nächsten
   Verwaltungsmaske bräche es wieder um. Genau das ist inzwischen passiert —
   „Sicherungsziel“ ist dazugekommen.

   **150 px je Spur** — der längste Punkt („Datenübernahme“) misst samt
   Innenabstand rund 134 px. `auto-fill` und nicht `auto-fit`: `auto-fit`
   ließe die leeren Spuren der letzten Zeile einfallen und zöge deren Punkte
   breiter, womit die Flucht wieder dahin wäre. Dass die letzte Zeile rechts
   nicht aufgeht, ist der Preis dafür und richtig so.

   Wie jeder Eintrag zu einem Rahmen auf allen vier Seiten kommt, ohne dass
   eine Linie doppelt steht, steht unten an den Regeln. */
.untermenue {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: 0;
  /* **Oben und links am Kasten, unten und rechts am Punkt.** Zusammen hat
     jeder Eintrag damit einen Rahmen auf allen vier Seiten, und keine Linie
     steht doppelt:

       unten   eigener `border-bottom`
       rechts  eigener `border-right`
       oben    der `border-bottom` des Punktes darüber, in der ersten Zeile
               dieser `border-top`
       links   der `border-right` des linken Nachbarn, in der ersten Spalte
               dieser `border-left`

     Alle vier Kanten an den Punkt selbst zu hängen wäre der naheliegende Weg
     und gäbe zwischen zwei Nachbarn eine **zwei Pixel** dicke Linie — jeder
     brächte seine eigene mit. Mit negativen Rändern übereinandergezogen ragte
     die letzte Spalte aus dem Kasten heraus.

     **Warum unten am Punkt und nicht am Kasten** — genau andersherum war es
     zuerst, und daran ist es gescheitert: die letzte Zeile geht selten auf.
     Eine Bodenlinie am Kasten lief dann unter dem **leeren** Rest weiter, und
     zwischen ihr und dem letzten Punkt darüber stand ein oben offener Kasten.
     Am Punkt hört die Linie da auf, wo die Punkte aufhören; der Streifen
     endet in einer Stufe, und die ist ehrlich. Aus demselben Grund hat der
     Kasten **kein** `border-right`: in der letzten Zeile gäbe es nichts, das
     er abschlösse, und die vollen Zeilen darüber schließt ohnehin der
     `border-right` ihres letzten Punktes. */
  border-top: 1px solid var(--color-divider);
  border-left: 1px solid var(--color-divider);
  /* Etwas mehr Abstand nach unten als die 14 px von `.kopf`: der Streifen
     gehört nicht zum Bildschirm darunter, sondern führt zu ihm hin. Der Wert
     stand in fünf Ansichten als `style="margin-top:14px"` und wirkte unter
     `style-src 'self'` nirgends. */
  margin-bottom: 20px;
}
.untermenue-punkt {
  padding: 8px 12px;
  font-size: 13px;
  text-decoration: none;
  color: var(--color-text);
  /* **Abgesetzt vom Inhalt über die Fläche, nicht über eine weitere Linie.**
     Der Streifen stand als Kästchen aus lauter dünnen Strichen zwischen
     Kopfzeile und Inhalt und ging darin unter — Linien hat auf diesen
     Bildschirmen alles. `--color-surface` ist dieselbe Fläche, auf der auch
     ein Tabellenkopf steht: kein neuer Farbwert und kein neues Bauteil, und
     die Bedeutung ist dieselbe — „das hier ordnet, was darunter kommt“.

     Der gewählte Punkt bleibt in der Betonungsfarbe und hebt sich davon
     deutlicher ab als vom weißen Grund. */
  background: var(--color-surface);
  border-right: 1px solid var(--color-divider);
  border-bottom: 1px solid var(--color-divider);
  /* Ein Rasterkind behält von Haus aus `min-width: auto` und schrumpft nie
     unter seinen Inhalt — eine Spur wüchse dann mit, und die Flucht wäre
     dahin. Bei 0 gilt die Spur, und was doch einmal zu lang ist, wird
     abgeschnitten statt umzubrechen. */
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* Beim Überfahren eine Spur dunkler als das Band — deckend gemischt, nicht
   durchscheinend: über der Fläche ergäbe eine durchscheinende Auflage einen
   anderen Ton als über dem weißen Grund daneben. */
.untermenue-punkt:hover {
  background: color-mix(in srgb, var(--color-text) 8%, var(--color-surface));
}
.untermenue-punkt.is-active {
  background: var(--color-accent);
  color: var(--color-bg);
  font-weight: 500;
}

/* --- Kacheln der Übersicht ---------------------------------------------- */

.kacheln {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
  gap: 12px;
  margin-top: 16px;
}
.kachel {
  display: block;
  padding: 12px 14px;
  text-decoration: none;
  color: var(--color-text);
}
.kachel:hover {
  border-color: var(--color-accent);
  background: var(--color-accent-100);
  color: var(--color-text);
}
.kachel-titel { font-family: var(--font-heading); font-size: 16px; }
.kachel-wert {
  font-size: 12.5px;
  font-variant-numeric: tabular-nums;
  color: var(--color-accent-800);
  margin-top: 2px;
}
.kachel-text { font-size: 12px; line-height: 1.5; color: var(--muted-60); margin-top: 6px; }

/* --- Aufgaben mit Erklärung --------------------------------------------- */

.aufgaben {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: 12px;
}
.aufgabe { padding: 12px 14px; display: flex; flex-direction: column; }
.aufgabe h3 { font-size: 14px; margin: 0 0 6px; }
.aufgabe p { font-size: 12.5px; line-height: 1.55; color: var(--muted-70); flex: 1; }
.aufgabe .btn { align-self: flex-start; }

/* --- Einstellung mit Erklärungstext (FA-52) ----------------------------- */

.einstellung {
  padding: 12px 0;
  border-bottom: 1px solid color-mix(in srgb, var(--color-text) 8%, transparent);
  max-width: 620px;
}
.einstellung-kopf {
  display: flex;
  align-items: baseline;
  gap: 8px;
  flex-wrap: wrap;
  font-size: 14px;
  margin-bottom: 6px;
  cursor: pointer;
}
.einstellung-schluessel {
  font: 500 11px/1.4 var(--font-mono);
  color: var(--muted-55);
  border: 1px solid var(--color-divider);
  padding: 1px 5px;
}
.einstellung-erklaerung {
  font-size: 12.5px;
  line-height: 1.55;
  color: var(--muted-65);
  margin: 6px 0 0;
}

/* --- Formulare innerhalb einer Tabellenzeile ---------------------------- */

.zeilenform {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  margin: 0;
}
tr.ausklapp td { background: var(--color-surface); }

/* --- Code im Fließtext -------------------------------------------------- */

code {
  font: 500 12px/1.5 var(--font-mono);
  background: var(--color-neutral-100);
  border: 1px solid var(--color-divider);
  padding: 0 4px;
}

/* --- Hilfe -------------------------------------------------------------- */

.hilfe { display: grid; grid-template-columns: 230px 1fr; gap: 20px; align-items: start; }

.hilfe-themen {
  display: flex;
  flex-direction: column;
  border: 1px solid var(--color-divider);
  position: sticky;
  top: 12px;
}
.hilfe-thema {
  padding: 9px 12px;
  text-decoration: none;
  color: var(--color-text);
  border-bottom: 1px solid var(--color-divider);
}
.hilfe-thema:last-child { border-bottom: none; }
.hilfe-thema:hover { background: color-mix(in srgb, var(--color-text) 5%, transparent); }
.hilfe-thema.is-active {
  background: var(--color-accent-100);
  box-shadow: inset 3px 0 0 var(--color-accent);
}
.hilfe-thema-titel { display: block; font-size: 13.5px; font-weight: 500; }
.hilfe-thema-kurz { display: block; font-size: 11.5px; line-height: 1.4; color: var(--muted-60); margin-top: 2px; }

/* Gruppenüberschrift in der Themenliste („Anleitung“, „Für Administratoren“).
   Sie steht nur da, wo es mehr als eine Gruppe gibt — für die Rollen ohne
   Verwaltungsrecht fällt die zweite Gruppe samt Überschrift weg. */
.hilfe-gruppe {
  margin: 0;
  padding: 7px 12px 6px;
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted-60);
  background: var(--color-neutral-100);
  border-bottom: 1px solid var(--color-divider);
}
.hilfe-gruppe:not(:first-child) { border-top: 1px solid var(--color-divider); }

.hilfe-text { max-width: 760px; font-size: 14px; line-height: 1.65; }
.hilfe-text > :first-child { margin-top: 0; }

.hilfe-gliederung {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  padding-bottom: 10px;
  margin-bottom: 16px;
  border-bottom: 1px solid var(--color-divider);
  font-size: 12px;
}

.hilfe-h2 {
  font-size: 20px;
  margin: 28px 0 10px;
  padding-bottom: 6px;
  border-bottom: 1px solid var(--color-divider);
}
.hilfe-h3 { font-size: 16px; margin: 22px 0 8px; }
.hilfe-h4 { font-size: 14px; margin: 18px 0 6px; }
.hilfe-h5 { font-size: 13px; margin: 16px 0 6px; color: var(--muted-70); }

.hilfe-text p { margin: 0 0 12px; }
.hilfe-liste { margin: 0 0 12px; padding-left: 20px; }
.hilfe-liste li { margin-bottom: 5px; }
.hilfe-tabelle { margin: 0 0 16px; font-size: 13px; }
.hilfe-trenner { border: none; border-top: 1px solid var(--color-divider); margin: 24px 0; }

.hilfe-code {
  background: var(--color-surface);
  border: 1px solid var(--color-divider);
  padding: 10px 12px;
  overflow-x: auto;
  font: 500 12.5px/1.55 var(--font-mono);
  margin: 0 0 14px;
}
.hilfe-code code { background: none; border: none; padding: 0; }

.hilfe-zitat {
  margin: 0 0 14px;
  padding: 10px 14px;
  border: 1px solid var(--color-divider);
  box-shadow: inset 3px 0 0 var(--color-warn);
  background: var(--color-surface);
}
.hilfe-zitat p { margin: 0; font-size: 13px; }

@media (max-width: 900px) {
  .hilfe { grid-template-columns: 1fr; }
  .hilfe-themen { position: static; }
  .untermenue { flex-direction: column; }
  .untermenue-punkt { border-right: none; border-bottom: 1px solid var(--color-divider); }
}

/* ==========================================================================
   Einheitlicher Kopfbereich

   Überall gleich aufgebaut, damit man sich nicht je Bildschirm neu
   orientieren muss:

     1. Funktionen  — was man hier tun kann
     2. Trennlinie
     3. Filter      — was man hier sehen will

   Die alte .toolbar mischte beides; sie bleibt für den Übergang bestehen.
   ========================================================================== */

.kopf {
  border: 1px solid var(--color-divider);
  margin-bottom: 14px;
}

/* --- 1. Funktionen ------------------------------------------------------ */

.kopf-funktionen {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  padding: 10px 12px;
}
/* Nach links Zurück-Verweise, nach rechts die Hauptfunktionen. */
.kopf-funktionen .rechts { margin-left: auto; display: flex; gap: 8px; flex-wrap: wrap; }
.kopf-funktionen form { margin: 0; }
.kopf-hinweis { font-size: 12px; color: var(--muted-60); }

/* --- 2. Trennung -------------------------------------------------------- */

.kopf-funktionen + .kopf-filter { border-top: 1px solid var(--color-divider); }
.kopf-funktionen + .filter-klapp { border-top: 1px solid var(--color-divider); }

/* --- Filterleiste ein- und ausklappen -------------------------------------
   `<details>` statt Schalter und Skript: der Browser kann das selbst, und
   ohne JavaScript bleibt es bedienbar. Offen steht die Leiste, sobald ein
   wegklickbarer Filter gesetzt ist. */
.filter-klapp > summary {
  cursor: pointer;
  padding: 7px 12px;
  font-size: 12px;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--muted-65);
  list-style: none;
}
.filter-klapp > summary::-webkit-details-marker { display: none; }
.filter-klapp > summary::before {
  content: "▸";
  display: inline-block;
  margin-right: 6px;
  color: var(--muted-55);
}
.filter-klapp[open] > summary::before { content: "▾"; }
.filter-klapp > summary:hover { color: var(--color-text); }
.filter-klapp[open] > summary { border-bottom: 1px solid var(--color-divider); }

/* --- 3. Filter ---------------------------------------------------------- */

.kopf-filter {
  display: flex;
  align-items: flex-end;
  gap: 10px;
  flex-wrap: wrap;
  padding: 10px 12px;
  margin: 0;
  background: var(--color-surface);
}
.kopf-filter .field { flex: none; }
.kopf-filter > .btn { margin-left: 4px; }

/* Die aktiven Filter hängen unter der Leiste, nicht daneben. */
.kopf-filter-marken {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  padding: 8px 12px;
  border-top: 1px solid var(--color-divider);
  font-size: 12px;
}

@media (max-width: 720px) {
  .kopf-funktionen,
  .kopf-filter { padding: 10px; }
  .kopf-funktionen .rechts { margin-left: 0; width: 100%; }
  .kopf-funktionen .rechts .btn { flex: 1; }
  .kopf-filter .field,
  .feld-xs, .feld-s, .feld-m, .feld-l, .feld-xl { width: 100%; flex: 1 1 100%; }
  .kopf-filter > .btn { width: 100%; margin-left: 0; }
}

/* ==========================================================================
   Seitenleiste ein- und ausklappen

   Der Zustand steht in einem Cookie und wird schon beim Rendern gesetzt —
   so springt die Seite beim Laden nicht zusammen.
   ========================================================================== */

.leiste-schalter {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  padding: 0;
  cursor: pointer;
  color: var(--muted-60);
  background: transparent;
  border: 1px solid var(--color-divider);
}
.leiste-schalter:hover { background: color-mix(in srgb, var(--color-text) 7%, transparent); color: var(--color-text); }


/* --- eingeklappt --------------------------------------------------------

   Eingeklappt ist das Symbol alles, was bleibt: kein Text, keine Zahl, keine
   Einrückung. Was ausgeklappt die Beschriftung trägt, muss hier die Form
   tragen — sonst stehen zwölf gleich große Bilder untereinander und man
   klickt sich durch, statt zu zielen. Drei Dinge halten das lesbar: eine
   Trennlinie vor jedem Bereich, kleinere Symbole für die Unterpunkte und ein
   Punkt für den Zähler. */

.app.leiste-zu .sidebar { width: 52px; }
.app.leiste-zu .sidebar-marke,
.app.leiste-zu .sidebar-benutzer { display: none; }

/* Text, Zähler und Tastenkürzel verschwinden nur aus dem Bild, nicht aus der
   Seite. Mit `display: none` bliebe einem Vorlesegerät gar nichts — das SVG
   ist `aria-hidden`, der Linkname käme dann allein aus dem title-Merkmal. */
.app.leiste-zu .nav-label,
.app.leiste-zu .nav-badge,
.app.leiste-zu .nav-key {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.app.leiste-zu .sidebar-head { padding: 0; justify-content: center; }
.app.leiste-zu .nav-item,
.app.leiste-zu .sidebar-abmelden {
  position: relative;
  justify-content: center;
  padding-left: 0;
  padding-right: 0;
  gap: 0;
}

/* Jeder Hauptpunkt beginnt einen Bereich. Ausgeklappt sagt das die
   Einrückung der Unterpunkte darunter; eingeklappt fällt die weg. */
.app.leiste-zu .nav-item:not(.is-sub) {
  margin-top: 5px;
  padding-top: 12px;
  border-top: 1px solid var(--color-divider);
}
.app.leiste-zu .sidebar-nav .nav-item:first-child {
  margin-top: 0;
  padding-top: 7px;
  border-top: 0;
}

/* Unterpunkte bleiben Unterpunkte: kleiner und blasser. */
.app.leiste-zu .nav-item.is-sub { padding-top: 4px; padding-bottom: 4px; }
.app.leiste-zu .nav-item.is-sub .nav-symbol,
.app.leiste-zu .nav-item.is-sub .symbol { width: 16px; height: 16px; }
.app.leiste-zu .nav-item.is-sub:not(.is-active):not(.is-branch) .nav-symbol {
  color: var(--muted-55);
}

/* „3 offen“ ist die Zahl, wegen der man überhaupt hinsieht — eingeklappt
   blieb davon nichts übrig. Statt der Zahl steht ein Punkt am Symbol; die
   Zahl selbst bleibt für Vorlesegeräte im Text stehen. */
.app.leiste-zu .nav-symbol { position: relative; }
.app.leiste-zu .nav-item[data-zaehler] .nav-symbol::after {
  content: "";
  position: absolute;
  top: -2px;
  right: -4px;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--color-accent);
  border: 2px solid var(--sidebar-bg);
}

/* Der Schalter dreht sich um, sobald eingeklappt ist. */
.app.leiste-zu .leiste-schalter .symbol { transform: scaleX(-1); }

@media (max-width: 720px) {
  /* Auf dem Telefon liegt die Leiste ohnehin quer — nicht einklappbar. */
  .leiste-schalter { display: none; }
  .app.leiste-zu .sidebar { width: 100%; }
  .app.leiste-zu .sidebar-marke,
  .app.leiste-zu .sidebar-benutzer { display: block; }
  .app.leiste-zu .nav-label,
  .app.leiste-zu .nav-badge {
    position: static;
    width: auto;
    height: auto;
    overflow: visible;
    clip-path: none;
  }
  .app.leiste-zu .nav-item {
    justify-content: flex-start;
    padding: 7px 14px;
    gap: 10px;
    margin-top: 0;
    border-top: 0;
  }
  .app.leiste-zu .nav-item.is-sub .nav-symbol,
  .app.leiste-zu .nav-item.is-sub .symbol { width: 20px; height: 20px; }
  .app.leiste-zu .nav-item[data-zaehler] .nav-symbol::after { content: none; }
}

/* ==========================================================================
   Einrichtungsassistent
   ========================================================================== */

.einrichtung {
  min-height: 100vh;
  display: grid;
  place-items: start center;
  padding: var(--space-6);
  background: var(--color-bg);
}
.einrichtung-karte { width: min(560px, 100%); padding: var(--space-6); }
.einrichtung-kopf {
  padding-bottom: var(--space-3);
  border-bottom: 1px solid var(--color-divider);
}
.einrichtung-karte .banner { margin-bottom: var(--space-3); }
.einrichtung-karte p { font-size: 13.5px; line-height: 1.6; }
.einrichtung-fuss {
  margin: var(--space-6) 0 0;
  padding-top: var(--space-3);
  border-top: 1px solid var(--color-divider);
  font-size: 11.5px;
  color: var(--muted-55);
}
.einrichtung-mehr { font-size: 13px; }
.einrichtung-mehr summary { cursor: pointer; padding: 6px 0; color: var(--color-accent); }
.einrichtung-mehr > *:not(summary) { margin-top: 10px; }

/* --- Schrittanzeige ------------------------------------------------------ */

.schritte {
  display: flex;
  gap: 0;
  margin: var(--space-4) 0;
  padding: 0;
  list-style: none;
  border: 1px solid var(--color-divider);
}
.schritt {
  flex: 1;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 10px;
  border-right: 1px solid var(--color-divider);
  min-width: 0;
}
.schritt:last-child { border-right: none; }
.schritt-nummer {
  flex: none;
  width: 22px;
  height: 22px;
  display: grid;
  place-items: center;
  font: 600 12px/1 var(--font-heading);
  border: 1px solid var(--color-divider);
  color: var(--muted-60);
}
.schritt-text { min-width: 0; }
.schritt-titel { display: block; font-size: 12.5px; font-weight: 500; }
.schritt-kurz { display: block; font-size: 11px; color: var(--muted-55); line-height: 1.35; }

.schritt.is-jetzt { background: var(--color-accent-100); }
.schritt.is-jetzt .schritt-nummer { background: var(--color-accent); border-color: var(--color-accent); color: var(--color-bg); }
.schritt.is-fertig .schritt-nummer { border-color: var(--color-ok); color: var(--color-ok); }
.schritt.is-offen { opacity: .55; }

/* Erklärung unter einem Feld */
.feldhinweis { font-size: 12px; line-height: 1.5; color: var(--muted-60); margin: 5px 0 0; }

@media (max-width: 560px) {
  .schritte { flex-direction: column; }
  .schritt { border-right: none; border-bottom: 1px solid var(--color-divider); }
  .schritt:last-child { border-bottom: none; }
}

/* Hinweis unter einem Feld, das nicht stimmt (IBAN-Prüfung, FA-19) */
.fehler-text {
  display: block;
  margin-top: 4px;
  font-size: 12px;
  line-height: 1.45;
  color: var(--color-err);
}
/* `display: block` aus der Klasse schlägt das `[hidden]` des Browsers —
   ohne diese Zeile stünde ein weggeschalteter Hinweis weiter da. */
.fehler-text[hidden] { display: none; }
.input[aria-invalid="true"] { border-color: var(--color-err); }

/* Datumsfelder mit dem Kalender des Browsers.
   `.input` schaltet das Systemaussehen ab, damit Text- und Auswahlfelder
   gleich aussehen. Beim Datumsfeld muss das Kalendersymbol davon ausgenommen
   bleiben — sonst gäbe es keinen sichtbaren Weg zum Kalender, und die
   Schaltfläche ist genau das, was hier gewünscht war. */
.input[type="date"] {
  appearance: auto;
  -webkit-appearance: auto;
  padding-right: 8px;
}
.input[type="date"]::-webkit-calendar-picker-indicator {
  margin-left: 4px;
  opacity: .55;
  cursor: pointer;
}
.input[type="date"]::-webkit-calendar-picker-indicator:hover { opacity: 1; }
.input[type="date"]::-webkit-inner-spin-button { display: none; }

/* --- Breite Listen auf schmalen Bildschirmen ------------------------------
   Zwölf Spalten passen auf einem schmalen Bildschirm nicht nebeneinander.
   Statt die Tabelle seitwärts zu schieben — wo die halbe Zeile unsichtbar
   bleibt — wird aus jedem Datensatz eine Karte: die Werte stehen unter ihrer
   Spaltenbezeichnung. Die Bezeichnung kommt aus `data-spalte`, deshalb
   braucht es dafür kein Skript.

   **Ein Raster, keine Fließzeile.** Zuerst war die Zeile ein Flex-Kasten mit
   `flex-wrap`. Das ergab bei jedem Datensatz einen anderen Umbruch: eine
   Zeile mit kurzem Namen brachte sieben Felder unter, die daneben mit einem
   langen Namen nur fünf — untereinander stand nichts, und die Liste war
   nicht mehr zu überfliegen. Gleich breite Spuren (`repeat(auto-fill,
   minmax(…, 1fr))`) lösen beides auf einmal: jede Zeile hat gleich viele
   Spuren, jedes Feld steht unter dem gleichartigen der Zeile darüber, und
   **alle Zeilen brechen an derselben Stelle um**. */

/* Bis 1440 px, nicht bis 900: Kunden- und Rechnungsliste sind 1180 bzw.
   1020 px breit, und seit ihr Kopf klebt, rollen sie nicht mehr in einem
   eigenen Rahmen (`tablescroll-offen`, `overflow: visible`) — ihnen fehlt
   also der Rollbalken, der eine zu schmale Tabelle sonst auffinge.

   Die Breite bezieht sich auf den **Inhaltsbereich**, nicht auf das
   Fenster: die Seitenleiste nimmt 210 px plus 1 px Rahmen, der Inhalt 20 px
   Innenabstand auf jeder Seite — zusammen 251 px, die vom Fenster erst
   abgehen. Bei genau 1200 px Fensterbreite blieben der 1180 px breiten
   Kundenliste damit nur rund 950 px, gut 200 px zu wenig, und die rechten
   Spalten liefen unsichtbar über den Rand hinaus, ohne dass sich das
   bemerkbar machte — kein Rollbalken, einfach abgeschnitten. 1180 + 251
   ergibt rund 1430; mit etwas Luft also 1440. Gestapelt braucht es die
   Breite gar nicht. */

/* Verborgen, solange die Tabelle eine Tabelle ist: der Spaltenkopf sortiert
   dann selbst, und der Hinweis gälte einem Zustand, der gar nicht vorliegt.
   Eingeblendet wird beides erst in der Stapel-Regel unten — dieselbe Grenze
   wie `.table-stapel`, an einer Stelle gepflegt.

   **Diese Regel muss vor der `@media`-Regel stehen.** Eine unbedingte Regel
   schlägt eine Regel in einer zutreffenden `@media` bei gleicher Spezifität,
   wenn sie **später** im Quelltext steht — unabhängig davon, dass die
   `@media`-Regel bedingt ist. Stand die Rücknahme hier weiter unten, war
   sie in Wahrheit die stärkere und die Auswahlfelder blieben bei jeder
   Fensterbreite unsichtbar, selbst gestapelt. */
/* `.stapel-hinweis` sitzt zusätzlich zu `.banner` an demselben Element und
   schaltet nur die Sichtbarkeit — Rahmen, Innenabstand und Fläche kommen
   unverändert von `.banner`, demselben Kasten wie jeder andere Hinweis der
   Anwendung. */
.stapel-hinweis { display: none; }

/* Der äußere Kasten von `teile/sortierung-mobil.php` — die beiden
   `.field`-Kästen darin bleiben unangetastet und stapeln Bezeichnung und
   Auswahlfeld weiter wie jedes andere Feld der Filterleiste. `flex-basis:
   100%` reißt sie aus der Fließzeile der übrigen Filterfelder heraus, sonst
   stünden sie mal daneben, mal darunter, je nachdem, wie viel Platz die
   Felder davor noch lassen. */
.stapel-sortierung {
  display: none;
  flex-basis: 100%;
  gap: 10px;
}

/* Trennt „Suchen“ von der Sortierung darunter — der Knopf bleibt am
   gewohnten Platz gleich hinter den Filterfeldern, statt hinter Feldern
   verschoben zu werden, die es nur auf schmalen Bildschirmen gibt. */
.stapel-trenner {
  display: none;
  flex-basis: 100%;
  margin: 4px 0 0;
  border: none;
  border-top: 1px solid var(--color-divider);
}

@media (max-width: 1440px) {
  .tablescroll.stapel { overflow-x: visible; }

  /* Tabelle, Körper und Fuß hören auf, Tabelle zu sein. Sonst bemisst
     `table-layout` die Breite weiter nach dem *ungebrochenen* Inhalt, und
     die Karte läuft über den rechten Rand hinaus — unsichtbar, denn
     `overflow: visible` fängt nichts auf, es zeigt nur, was da ist. */
  .table-stapel { display: block; width: 100%; min-width: 0 !important; }
  .table-stapel thead { display: none; }
  .table-stapel tbody,
  .table-stapel tfoot { display: block; }

  /* **Eine verborgene Zeile bleibt verborgen.** `[hidden]` ist eine Regel
     des Browsers, und jede Regel aus diesem Stylesheet wiegt schwerer —
     `display: grid` an der Zeile darunter machte die eingeklappte
     Passwortzeile der Benutzerverwaltung auf schmalen Bildschirmen also
     sichtbar, und zwar bei **jedem** Zugang zugleich. Dieselbe Falle wie
     beim Themenschalter (`.thema-schalter[hidden]`). */
  .table-stapel tbody tr[hidden] { display: none; }

  .table-stapel tbody tr,
  .table-stapel tfoot tr {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(var(--stapel-spur, 165px), 1fr));
    gap: 7px 14px;
    padding: 10px 8px;
    border-bottom: 1px solid var(--color-divider);
  }

  .table-stapel tbody td,
  .table-stapel tfoot td {
    /* **`min-width: 0` ist der Kern.** Ein Raster- wie ein Flex-Kind hat von
       Haus aus `min-width: auto` und schrumpft damit nie unter seinen
       Inhalt. Eine Zelle mit `.ellipsis` (und damit `white-space: nowrap`)
       verlangte so die volle Textbreite, die Spur wuchs mit, und die Zeile
       lief hinaus — genau das war zu sehen. Erst bei 0 greift die Spur, und
       `.ellipsis` schneidet, wozu es da ist. */
    min-width: 0;
    /* Die Spaltenbreiten der Tabelle (`.sp-…`) gelten hier nicht mehr: die
       Breite macht die Spur. */
    width: auto !important;
    max-width: none !important;
    display: block;
    border: none;
    box-shadow: none;
    padding: 0;
  }

  /* Die Spaltenbezeichnung steht **über** dem Wert, nicht davor: in einer
     165 px schmalen Spur bliebe nebeneinander für den Wert nichts übrig. */
  .table-stapel tbody td::before,
  .table-stapel tfoot td::before {
    content: attr(data-spalte);
    display: block;
    font-size: 10.5px;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: var(--muted-55);
    margin-bottom: 3px;
  }

  /* Eine dünne strichlierte Linie unter jeder Bezeichnung, über die volle
     Breite ihrer Spur — sie trennt Überschrift und Wert optisch, wie eine
     Karteikarte. Nur bei einer echten Bezeichnung: der Platzhalter
     (`\00a0`) über dem Haken oder der Auswahlbox bekäme sonst eine Linie
     über nichts. */
  .table-stapel tbody td[data-spalte]:not([data-spalte=""])::before,
  .table-stapel tfoot td[data-spalte]:not([data-spalte=""])::before {
    padding-bottom: 3px;
    border-bottom: 1px dashed var(--color-divider);
  }
  /* Eine Zelle ohne Bezeichnung — Auswahlkästchen, Aktionsspalte, ein Knopf —
     bekommt statt der Bezeichnung einen **Platzhalter in Bezeichnungshöhe**,
     kein `content: none`. Sonst begänne ihr Inhalt um genau diese Zeile
     weiter oben als die Felder daneben: der Haken der Eingabezeile und der
     Knopf „Erstellen“ standen über der Mitte der Felder, zu denen sie
     gehören. Mit dem Platzhalter beginnt jede Zelle auf derselben Linie. */
  .table-stapel tbody td[data-spalte=""]::before,
  .table-stapel tfoot td[data-spalte=""]::before,
  .table-stapel tbody td:not([data-spalte])::before,
  .table-stapel tfoot td:not([data-spalte])::before { content: "\00a0"; }

  /* Die Kennung der Karte steht größer — aber in **einer** Spur wie jedes
     andere Feld. Über zwei Spuren gelegt (`span 2`) blieb die zweite bei
     einem kurzen Wert sichtbar leer: bei der Meldungs- und der
     Rechnungsnummer stand rechts neben der Zahl ein Loch, und es sah aus,
     als fehlte dort ein Feld. Lange Namen brechen stattdessen innerhalb
     ihrer Spur um — Platz nach unten ist auf einer Karte reichlich. */
  .table-stapel tbody td[data-hauptspalte] { font-size: 15px; }
  .table-stapel tbody td[data-volle-breite],
  .table-stapel tfoot td[data-volle-breite] { grid-column: 1 / -1; }

  /* Nur für das Tabellenraster da — gestapelt eine leere Spur. */
  .table-stapel .stapel-weg { display: none; }

  /* In der Tabelle wird ein zu langer Betriebsname abgeschnitten, weil die
     Zeile sonst umbräche. Auf einer Karte ist das falsch herum: Platz nach
     unten ist reichlich, und der abgeschnittene Name lässt sich auf einem
     Telefon nicht einmal überfahren, um den vollen zu sehen. Er bricht hier
     also um. `anywhere` deckt den Fall ab, dass ein einzelnes Wort breiter
     ist als die Spur — sonst wüchse sie mit und die Flucht wäre dahin. */
  .table-stapel tbody td.ellipsis {
    white-space: normal;
    overflow: visible;
    text-overflow: clip;
    overflow-wrap: anywhere;
  }

  /* Rechtsbündige Zahlen ergeben unter ihrer Bezeichnung keinen Sinn mehr. */
  .table-stapel tbody td.num,
  .table-stapel tfoot td.num { text-align: left; }

  /* Eingabefelder füllen ihre Spur. Ohne das behielte ein `.feld-…` seine
     Breite aus dem Tabellenraster und stünde schmal in einer breiten Spur. */
  .table-stapel tbody td .input,
  .table-stapel tbody td .kombi { width: 100%; min-width: 0; }

  /* Der klebende Spaltenkopf und die klebende Eingabezeile hängen an einem
     Tabellenraster, das es hier nicht mehr gibt: gestapelt stünde die
     Eingabezeile sonst über den Karten fest und verdeckte sie. */
  .table-stapel.table-fest tbody tr.eingabezeile td {
    position: static;
    box-shadow: none;
  }

  /* Der abgesetzte Grund gehört der **Zeile**, nicht der Zelle. Zwischen den
     Spuren steht eine Lücke, und je Zelle eingefärbt zerfiel die
     Eingabezeile in lauter einzelne Kacheln, statt als ein Block zu lesen. */
  .table-stapel tbody tr.eingabezeile { background: var(--color-accent-100); }
  .table-stapel tbody tr.eingabezeile td { background: none; }

  /* **Der Zeilenbalken gehört an die Karte, nicht an die erste Zelle.**
     In der Tabelle sitzt er dort, weil WebKit ihn an einem `<tr>` mit
     `border-collapse: collapse` verschluckt (siehe unten). Gestapelt ist die
     Zeile aber gar keine Tabellenzeile mehr, sondern ein Raster — das
     Problem gibt es nicht, und der Balken an der ersten Zelle wurde zu einem
     Strich neben dem Auswahlkästchen, der die Karte nicht mehr als Ganzes
     kennzeichnete. */
  .table-stapel tbody tr.is-selected,
  .table-stapel tbody tr.is-error,
  .table-stapel tbody tr.is-warn { box-shadow: inset 3px 0 0 var(--zeilenbalken); }

  /* Die Rücknahme an der Zelle braucht **beide** Formen und **`.table`
     dazu**. Den Balken setzt weiter unten zweimal eine Regel — einmal
     schlicht, einmal mit `:not(:last-child)`, das die senkrechte Trennlinie
     mitschreibt. Die zweite lautet `.table tbody tr.is-selected >
     td:first-child:not(:last-child)` und wiegt damit genau so schwer wie
     eine Rücknahme, die nur `.table-stapel` nennt — bei Gleichstand
     gewinnt die spätere, und das ist sie. Der Balken stand danach wieder am
     Kästchen, und zwei Striche standen da: einer an der Karte, einer am
     Kästchen. Beide Klassen zusammen wiegen schwerer und beenden das. */
  .table.table-stapel tbody tr.is-selected > td:first-child,
  .table.table-stapel tbody tr.is-error > td:first-child,
  .table.table-stapel tbody tr.is-warn > td:first-child,
  .table.table-stapel tbody tr.is-selected > td:first-child:not(:last-child),
  .table.table-stapel tbody tr.is-error > td:first-child:not(:last-child),
  .table.table-stapel tbody tr.is-warn > td:first-child:not(:last-child) { box-shadow: none; }

  /* Die Aktionszelle bleibt die letzte — wie in der Tabelle, wo sie die
     letzte Spalte ist. Festgelegt ist allein die **Spur**, nicht die Zeile:
     mit `grid-column: -2 / -1` rückt sie an den rechten Rand der Karte und
     sucht sich die erste Zeile, in der diese Spur noch frei ist. Bleibt in
     der letzten Feldzeile rechts Platz, steht sie dort; geht die Zeile genau
     auf, beginnt sie eine neue — dann aber am rechten Rand und nicht links
     angeschlagen mitten im Weiß.

     Die Zeile festzunageln (`grid-row: 1`) wäre der andere Weg und ist
     verworfen: der Haken saß dann mitten im Feldfluss. */
  .table-stapel tbody td.zeilenaktionen {
    grid-column: -2 / -1;
    align-self: start;
  }

  /* **Die Symbole einer Datenzeile verbrauchen keine Spur.** Als letzte von
     neun Zellen (Kassabuch) füllten die acht davor bei vier Spuren genau
     zwei Rasterzeilen, und Stift, Kopieren und Papierkorb bekamen eine
     dritte für sich allein. Absolut gesetzt fallen sie aus dem Raster
     heraus: die Felder fließen unverändert, und die Symbole sitzen rechts
     unten in der Karte — also neben dem letzten Wert statt unter ihm.

     Nur die Datenzeile. In der Eingabezeile bleibt der Haken im Fluss und
     auf Feldhöhe; unten rechts angeheftet stünde er unter den Feldern statt
     neben ihnen. */
  .table-stapel tbody tr { position: relative; }
  .table-stapel tbody tr:not(.eingabezeile) td.zeilenaktionen {
    position: absolute;
    right: 8px;
    bottom: 10px;
  }
  .table-stapel tbody tr:not(.eingabezeile) td.zeilenaktionen::before { content: none; }
  /* Der Haken beginnt am **linken** Rand seiner Spur, wie jedes Feld. An den
     rechten Rand geschoben (`flex-end`) stand er weit weg vom letzten Feld
     und sah aus, als gehörte er nicht mehr dazu. */
  .table-stapel tbody td.zeilenaktionen .zeilenaktionen-inhalt { justify-content: flex-start; }

  /* Der Hinweis über der Liste (`teile/stapel-hinweis.php`) und die
     Sortierung für schmale Bildschirme (`teile/sortierung-mobil.php`) sind
     sonst verborgen — siehe die Grundform vor dieser Regel — und erscheinen
     nur hier, wo die Tabelle wirklich stapelt. */
  .stapel-hinweis { display: flex; }
  .stapel-sortierung { display: flex; }
  .stapel-trenner { display: block; }
}

/* --- Kombinationsfeld: tippen und wählen in einem Feld -------------------
   Aus dem Auswahlfeld der Betriebswahl macht das Skript ein Feld, in das
   getippt wird, mit einer Liste darunter. Das <select> bleibt bestehen,
   trägt den Wert und wird abgeschickt — sichtbar ist es nicht mehr. */

.kombi { position: relative; }

.kombi-feld { width: 100%; }
.kombi-feld::placeholder { font-size: 12.5px; }

/* Nicht `display: none`: ein so verborgenes Feld wird zwar noch
   abgeschickt, aber die Browser behandeln es beim Fokus unterschiedlich.
   Aus dem Fluss nehmen ist eindeutiger. */
.kombi-verborgen {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  border: 0;
}

.kombi-liste {
  /* Die Liste hängt am Feld. Ist unter der Zeile kein Platz mehr, setzt das
     Skript `nach-oben` und sie klappt nach oben auf. */
  position: absolute;
  z-index: 60;
  top: 100%;
  left: 0;
  right: 0;
  margin: 2px 0 0;
  padding: 0;
  list-style: none;
  max-height: 260px;
  overflow-y: auto;
  background: var(--color-bg);
  border: 1px solid var(--color-accent);
  box-shadow: var(--shadow-lg);
}

.kombi-eintrag {
  padding: 6px 10px;
  font-size: 13.5px;
  cursor: pointer;
  border-bottom: 1px solid var(--color-divider);
}
.kombi-eintrag:last-child { border-bottom: none; }

.kombi-eintrag.is-aktiv { background: var(--color-accent-100); }
.kombi-eintrag.is-gewaehlt { font-weight: 600; }

/* „Auswahl aufheben“ — steht nur über der Liste, wenn etwas gewählt ist,
   und soll nicht wie ein Betrieb aussehen. */
.kombi-eintrag.kombi-leeren { color: var(--muted-65); font-style: italic; }

/* Ein Eintrag steht zweizeilig: oben der Name, darunter links der Ort und
   rechts die Nummer. Einzeilig („Bauer Anna, Mehrnbach · 1051“) brach er in
   einer schmalen Spalte — die Eingabezeile der Meldungsliste ist eine — über
   drei Zeilen um, und die Nummer stand allein unter dem Ortsnamen.

   Beschnitten wird mit …; den ganzen Text trägt der Eintrag im `title`.
   `min-width: 0` ist dafür die Bedingung: ein Flex-Kind gibt seine
   Inhaltsbreite sonst nicht her, und statt zu beschneiden schiebt es die
   Zeile auf. */
.kombi-eintrag-name,
.kombi-eintrag-ort {
  display: block;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.kombi-eintrag-neben {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
  margin-top: 1px;
  font-size: 12px;
  color: var(--muted-65);
}

/* Die Nummer gibt nicht nach: sie ist kurz und der eindeutige Teil — ein
   halb abgeschnittener Ortsname ist zu verschmerzen, eine halbe
   Kundennummer nicht. */
.kombi-eintrag-nummer { flex: none; }

.kombi-leer {
  padding: 8px 10px;
  font-size: 13px;
  color: var(--muted-60);
}

/* Aktionsleiste der Meldungsliste — dieselbe ruhige Fläche wie die
   Sammeländerung bei den Kunden. */
.sammel-titel { font-size: 13px; color: var(--muted-65); }
.sammel-felder .sammel-titel { align-self: center; }
.btn.is-aus { opacity: .4; pointer-events: none; }
.sammel-hinweis:empty { display: none; }
.sammel-fuss:has(.sammel-hinweis:empty) { display: none; }

/* ==========================================================================
   Berichte

   Die Vorschau ist nur der Rahmen: oben die Bedienleiste, darunter das
   eingebettete PDF. Den Bericht selbst zeichnet FPDF — hier gibt es nichts
   mehr nachzubauen, und damit auch nichts, was auseinanderdriften könnte.
   ========================================================================== */

.bericht-seite {
  margin: 0;
  height: 100vh;
  display: flex;
  flex-direction: column;
  background: var(--color-neutral-200);
}

.bericht-leiste {
  flex: none;
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  padding: 10px 16px;
  background: var(--color-bg);
  border-bottom: 1px solid var(--color-divider);
}
.bericht-titel { font-size: 13px; color: var(--muted-65); }
.bericht-tasten { margin-left: auto; display: flex; gap: 8px; }

/* Filterleiste über dem Bericht — ersetzt den Dialog, der im Altprogramm
   vor dem Bericht aufging. Aufbau wie die Filterleiste im Seitenkopf. */
.bericht-filter {
  flex: none;
  display: flex;
  align-items: flex-end;
  gap: 10px;
  flex-wrap: wrap;
  padding: 10px 16px;
  margin: 0;
  background: var(--color-surface);
  border-bottom: 1px solid var(--color-divider);
}
.bericht-filter .field { flex: none; }

/* Der Betrachter des Browsers füllt den Rest des Fensters. */
.bericht-rahmen {
  flex: 1 1 auto;
  width: 100%;
  border: none;
  min-height: 0;
}

.bericht-hinweis {
  margin: 0;
  padding: 10px 16px;
  font-size: 12.5px;
  color: var(--muted-60);
}

@media (max-width: 720px) {
  .bericht-tasten { margin-left: 0; width: 100%; }
  .bericht-tasten .btn { flex: 1; }
}


/* Erklärende Zeile zwischen Kopf und Tabelle — kein Kasten, nur Text. */
.hinweiszeile {
  margin: 12px 0 0;
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--muted-60);
}


/* --- Datumsauswahl (flatpickr) ------------------------------------------

   Die Bibliothek liegt unverändert unter assets/flatpickr/; angeglichen wird
   ausschließlich hier. flatpickr bringt eigene Farben, runde Ecken und eine
   blaue Betonung mit — die Anwendung hat keine runden Ecken und ein eigenes
   Blau. Geändert werden deshalb nur Farbe, Form und Schrift, nicht das
   Verhalten.
   ------------------------------------------------------------------------ */

.flatpickr-calendar {
  border-radius: 0;
  border: 1px solid var(--color-divider);
  /* flatpickr setzt hier #fff. Das ist die Fläche, auf der alles andere
     steht — bleibt sie fest, steht im dunklen Satz heller Text auf Weiß. */
  background: var(--color-bg);
  color: var(--color-text);
  box-shadow: 0 6px 22px color-mix(in srgb, var(--color-text) 16%, transparent);
  font-family: inherit;
  font-size: 13px;
}
.flatpickr-calendar.arrowTop::before,
.flatpickr-calendar.arrowTop::after,
.flatpickr-calendar.arrowBottom::before,
.flatpickr-calendar.arrowBottom::after { display: none; }

.flatpickr-months {
  background: var(--color-surface);
  border-bottom: 1px solid var(--color-divider);
  padding: 2px 0;
}
.flatpickr-current-month,
.flatpickr-current-month input.cur-year,
.flatpickr-monthDropdown-months {
  font-size: 13px;
  font-weight: 600;
  color: var(--color-text);
}
/* Monat als Auswahlfeld, Jahr als Zahlenfeld — beide sollen als
   anfassbar erkennbar sein, sonst sucht man die Umschaltung. */
.flatpickr-monthDropdown-months {
  background: transparent;
  border: 1px solid transparent;
  border-radius: 0;
  padding: 1px 4px;
  cursor: pointer;
}
.flatpickr-monthDropdown-months:hover,
.flatpickr-monthDropdown-months:focus {
  background: var(--color-bg);
  border-color: var(--color-divider);
}
.flatpickr-monthDropdown-months .flatpickr-monthDropdown-month { background: var(--color-bg); }
.numInputWrapper:hover { background: transparent; }
.numInputWrapper:hover .numInput.cur-year {
  background: var(--color-bg);
  border-color: var(--color-divider);
}
.numInput.cur-year {
  border: 1px solid transparent;
  padding: 1px 2px;
  font-variant-numeric: tabular-nums;
}
.flatpickr-months .flatpickr-prev-month,
.flatpickr-months .flatpickr-next-month { fill: var(--muted-65); padding: 7px 10px; }
.flatpickr-months .flatpickr-prev-month:hover svg,
.flatpickr-months .flatpickr-next-month:hover svg { fill: var(--color-accent); }

span.flatpickr-weekday {
  color: var(--muted-55);
  font-weight: 600;
  font-size: 11.5px;
  text-transform: uppercase;
  letter-spacing: .04em;
}

/* Die **Breite** eines Tages bleibt, wie flatpickr sie setzt (`max-width:
   39px`). Sie sieht nach Zierrat aus, hält aber das Raster zusammen: das
   Tagesfeld ist ein Flex-Kasten fester Breite (307,875 px) mit Umbruch, und
   jeder Tag hat `flex-basis: 14,2857 %`, begrenzt durch `max-width`. Bei
   39 px passen acht Tage (312 px) nicht mehr nebeneinander — es bleibt bei
   sieben Spalten. Ein einziges Pixel weniger, und acht passen hinein: der
   Kalender bricht achtspaltig um, während die Kopfzeile sieben Wochentage
   zeigt. Genau das stand hier mit `max-width: 38px`, und jedes Datum stand
   unter dem falschen Tag. Höhe und Farbe sind davon unberührt. */
.flatpickr-day {
  border-radius: 0;
  border-color: transparent;
  color: var(--color-text);
  height: 34px;
  line-height: 34px;
}
.flatpickr-day:hover,
.flatpickr-day:focus { background: var(--color-accent-100); border-color: transparent; }
.flatpickr-day.today { border-color: var(--color-accent); font-weight: 700; }
.flatpickr-day.selected,
.flatpickr-day.selected:hover {
  background: var(--color-accent);
  border-color: var(--color-accent);
  /* Nicht Weiß: im dunklen Satz ist der Akzent ein helles Blau, auf dem
     Weiß nicht mehr trägt. Der Grundton der Seite stimmt in beiden. */
  color: var(--color-bg);
}
.flatpickr-day.prevMonthDay,
.flatpickr-day.nextMonthDay,
.flatpickr-day.flatpickr-disabled { color: color-mix(in srgb, var(--color-text) 32%, transparent); }

/* Der Kopf des Kalenders und die Pfeile am Jahresfeld zeichnet flatpickr in
   festen Grautönen (`rgba(0,0,0,.9)`, `rgba(57,57,57,.6)`). Sie stehen hier
   nicht wegen des dunklen Satzes anders, sondern weil sie überhaupt aus den
   Tokens kommen sollen — dunkel wären sie sonst schlicht unsichtbar. */
.flatpickr-months .flatpickr-month { color: var(--color-text); fill: var(--color-text); }
.numInputWrapper span { border-color: var(--color-divider); }
.numInputWrapper span:hover { background: color-mix(in srgb, var(--color-text) 12%, transparent); }
.flatpickr-current-month .numInputWrapper span.arrowUp::after { border-bottom-color: var(--muted-70); }
.flatpickr-current-month .numInputWrapper span.arrowDown::after { border-top-color: var(--muted-70); }

/* Kalenderwochen — dafür ist die Spalte überhaupt eingeschaltet.
   Die Trennlinie kommt bei flatpickr als Schatten in #e6e6e6; hier steht
   ein Rahmen, der Schatten muss weg, sonst stehen zwei Linien. */
.flatpickr-weekwrapper { border-right: 1px solid var(--color-divider); }
.flatpickr-weekwrapper .flatpickr-weeks { box-shadow: none; }
.flatpickr-weekwrapper .flatpickr-weekday,
.flatpickr-weekwrapper span.flatpickr-day {
  color: var(--muted-55);
  font-size: 11.5px;
}

/* Das sichtbare Feld ist ein zweites Eingabefeld (altInput); es erbt die
   Klassen des Originals, muss aber wie jedes andere Feld aussehen. */
.flatpickr-anzeige { font-variant-numeric: tabular-nums; }


/* Monatswähler in der Filterleiste: Pfeil, Monat, Jahr, Pfeil in einer
   Reihe. Die Pfeile sind Verweise, keine Schaltflächen — damit funktionieren
   sie auch ohne Skript und lassen sich als Lesezeichen ablegen. */
.monatswahl {
  display: flex;
  align-items: center;
  gap: 4px;
  flex-wrap: wrap;
}
.monatswahl .btn {
  padding: 0 8px;
  min-width: 32px;
  height: 34px;
  font-size: 12px;
  border-color: var(--color-divider);
}
.monatswahl .btn:hover { background: color-mix(in srgb, var(--color-accent) 10%, transparent); }


/* --- Filterleisten: „ODER“ zwischen zwei Wegen -------------------------- */

/* Zwei Filterleisten übereinander brauchen ein Wort dazwischen, sonst sieht
   es aus, als müsste man beide ausfüllen. */
.filter-oder {
  margin: 0;
  padding: 6px 12px;
  background: var(--color-surface);
  font-family: var(--font-heading);
  font-weight: var(--font-heading-weight);
  font-size: 11px;
  letter-spacing: .12em;
  color: var(--muted-55);
}

/* --- Säulendiagramm ----------------------------------------------------- */

/* Gezeichnet wird als SVG; die Geometrie steht in Merkmalen, hier stehen nur
   Farbe und Größe. Ein Diagramm aus <div>-Balken bräuchte für jede Höhe einen
   Inline-Stil — den die Inhaltsrichtlinie nicht durchlässt. */
.saeulen {
  margin: 0 0 var(--space-4);
  border: 1px solid var(--color-divider);
  padding: 10px 12px 4px;
}
.saeulen-kopf {
  display: flex;
  align-items: baseline;
  gap: 12px;
  flex-wrap: wrap;
  margin-bottom: 8px;
}
.saeulen-titel {
  font-family: var(--font-heading);
  font-weight: var(--font-heading-weight);
  font-size: 13px;
}
.saeulen-legende {
  margin-left: auto;
  display: flex;
  align-items: center;
  flex-wrap: wrap;      /* drei Einträge: auf schmalen Schirmen umbrechen */
  gap: 6px;
  font-size: 12px;
  color: var(--muted-65);
}
.saeulen-legende svg { vertical-align: -1px; }

.saeulen-bild {
  display: block;
  width: 100%;
  height: auto;          /* gleichmäßig skalieren, siehe teile/saeulen.php */
  max-height: 240px;
}
.saeule-zu { fill: var(--color-accent); }
.saeule-ab { fill: var(--color-accent-300); }
.saeulen-raster { stroke: var(--color-divider); stroke-width: 1; }
.saeulen-grund { stroke: color-mix(in srgb, var(--color-text) 45%, transparent); stroke-width: 1; }
.saeulen-achse,
.saeulen-tag {
  fill: var(--muted-55);
  font-family: var(--font-body);
  font-size: 11px;
}
/* Die Anzahl an der Säule — etwas kleiner als die Achse, damit sie die
   Säulen begleitet und nicht mit ihnen um Aufmerksamkeit streitet. */
.saeulen-wert {
  fill: var(--muted-65);
  font-family: var(--font-body);
  font-size: 10px;
}
/* Rahmen um einen Tag, an dem Zugang und Abgang nicht übereinstimmen: rot,
   weil er auf etwas hinweist, das man sich ansehen soll — semantische Farbe,
   keine Dekoration. Ohne Füllung — bei einem Monat stehen bis zu 31 davon
   nebeneinander, und getönte Flächen ergäben ein Streifenmuster statt einer
   Markierung. `pointer-events` macht die leere Fläche trotzdem greifbar,
   damit der Zeiger überall in der Spalte den Hinweis findet; die Säulen
   liegen darüber und behalten ihren eigenen.

   Oben ist der Rahmen offen (ein Pfad, kein Rechteck, siehe
   `teile/saeulen.php`): die Kante liefe sonst quer durch die Zahlen über
   den Säulen. */
.saeulen-rahmen {
  fill: none;
  pointer-events: all;
  stroke: var(--color-err);
  stroke-width: 1;
  stroke-dasharray: 3 2;
}
.saeulen-fuss {
  margin: 2px 0 6px;
  font-size: 11.5px;
  color: var(--muted-55);
}

@media (max-width: 720px) {
  .saeulen-legende { margin-left: 0; }
}

/* --- Tabellenspalten ohne Inline-Stil ----------------------------------- */

.tabelle-schmal { font-size: 13px; }
.tabelle-schmal .spalte-datum    { width: 92px; }
.tabelle-schmal .spalte-richtung { width: 90px; }
.tabelle-schmal .spalte-satz     { width: 60px; }
.tabelle-schmal .spalte-anzahl   { width: 60px; text-align: right; }
.tabelle-schmal .spalte-betrag   { width: 120px; text-align: right; }

/* Die Tagessumme hebt sich von den Zeilen darüber ab. */
.zeile-summe td { font-weight: 600; background: var(--color-surface); }


/* --- Zeilenaktionen als Symbole ----------------------------------------- */

/* Mit beschrifteten Tasten brach jede Kassazeile um und wurde zweizeilig.
   Die Bedeutung steckt in `title` und `aria-label`, nicht in der Fläche. */
/* **Das Flex liegt im `<div>`, nicht im `<td>`.** Ein `display: flex` an
   einer Tabellenzelle nimmt ihr die Zelleneigenschaft: der Kasten richtet
   sich dann nach seinem Inhalt statt nach der Zeile, und Hintergrund wie
   unterer Rahmen enden mitten in der Zeile. In der Eingabezeile, die höher
   ist als ein Symbol, war das deutlich zu sehen. */
.zeilenaktionen { white-space: nowrap; }

.zeilenaktionen-inhalt {
  display: flex;
  align-items: center;
  gap: 2px;
}
.zeilenaktionen form { display: inline; margin: 0; }

.btn-symbol {
  width: 30px;
  height: 30px;
  min-height: 30px;
  padding: 0;
  border-color: transparent;
  color: var(--muted-65);
}
.btn-symbol .symbol { width: 17px; height: 17px; }
.btn-symbol:hover {
  background: color-mix(in srgb, var(--color-text) 8%, transparent);
  color: var(--color-text);
}

.abstand-oben { margin-top: 14px; }
.abstand-oben-klein { margin-top: 6px; }

/* Enger, wo der Kopf stehen bleibt: 14 px unter der Kopfleiste und noch
   einmal 14 über der Tabelle waren rund 30 px Luft — sichtbar zu viel,
   sobald der Block dauernd im Bild steht. */
.abstand-oben-eng { margin-top: 10px; }
.fest-oben > .kopf { margin-bottom: 10px; }

/* --- Ansicht einer Meldung -----------------------------------------------
   Fünf Karten nebeneinander: zwei mit Kennwerttabelle, drei mit einem
   Betrieb. Sie sollen wie ein Blatt wirken, also dieselbe Schriftgröße
   tragen. Die Erfassungsmaske benutzt `slot-name` und `slot-row` weiter in
   ihrer größeren Schrift — dort steht die Karte allein unter einem Feld und
   ist Kontrollanzeige, nicht Fließtext. */
.ansicht-fein { max-width: 1560px; }

/* **Fünf Karten, eine Zeile** — sobald das Fenster es hergibt.
   Hier stand `max-width: 1040px`, und damit war die Zeile nie zu haben: in
   1040 px passen bei 300 px Mindestbreite drei Spuren, also standen die
   fünf Karten auf **jedem** Bildschirm auf zwei Zeilen, die letzte allein
   neben drei leeren Spalten. Der Deckel bleibt — ohne ihn zöge ein
   27-Zoll-Schirm jede Karte auf 500 px —, er sitzt jetzt aber dort, wo
   fünf Karten hineinpassen.

   **Alle fünf Spuren sind gleich breit.** Gewichtet waren sie einmal — die
   beiden Kennwertkarten breiter, die drei Betriebskarten schmaler —, weil
   jene zweispaltige Tabellen tragen und diese nur Name, Anschrift und zwei
   Zahlen. Am Bildschirm war das Ergebnis eine Reihe aus zwei großen und
   drei kleinen Kästen, die aussah, als wären die drei kleinen von minderem
   Rang. Fünf gleiche Karten lesen sich als eine Reihe.

   Darunter bleibt es bei `auto-fit`: 3 → 2 → 1, ohne festen Umbruchpunkt.
   Die Grenze gilt dem **Fenster**, die Karten stehen im Inhaltsbereich und
   der ist um 251 px schmaler (Seitenleiste samt Rahmen und Innenabstand) —
   1400 px Fenster lassen also rund 1150 px, und darin stehen fünf Spuren
   mit rund 225 px. */
.groupgrid.ansicht-fein { grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); }
@media (min-width: 1400px) {
  .groupgrid.ansicht-fein { grid-template-columns: repeat(5, 1fr); }
}

/* Die Beschriftungsspalte der beiden Kennwerttabellen bekommt nur, was sie
   braucht. Ohne das teilte der Browser die Tabelle nach dem längsten Wort
   („Ausstellungsort“) und ließ dem Wert daneben zu wenig — „T —
   Transporteur“ brach auf zwei Zeilen, während in der Zeile darüber Luft
   stand. `width: 1%` heißt in einer Tabelle mit automatischer Aufteilung:
   so schmal wie möglich, ohne den Inhalt zu brechen. */
.ansicht-fein .kv td:first-child { width: 1%; padding-right: 10px; }
/* Der Name bleibt ein sichtbarer Verweis: er ist hier der einzige Weg zum
   Betrieb, anders als in den Listen, wo die ganze Zeile führt. */
.ansicht-fein .slot-name { font-size: 14px; }
.ansicht-fein .slot-row,
.ansicht-fein .betrieb-anschrift { font-size: 12.5px; }

/* --- Summenliste als Kreuztabelle ----------------------------------------
   Aufbau des Altberichts: links das Datum, dann je Richtung ein Block aus
   Summe und Rechnungsnummern. Die Nummern stehen rechtsbündig und kursiv —
   sie sind Beleg, nicht Rechenwert. */
.table-summen .mitte { text-align: center; }
.table-summen .sp-summe { width: 130px; }
.table-summen .sp-nummern { width: 150px; }
.table-summen .nummernbereich {
  font-style: italic;
  color: var(--muted-70);
  font-variant-numeric: tabular-nums;
}

/* Die Ankreuzfelder der Gruppe „Kennzeichen“ untereinander. */
.kennzeichen { display: flex; flex-direction: column; gap: 10px; }

/* Jahresübersicht im Kundenstammblatt. */
.table-jahre { font-size: 13px; }
.table-jahre .sp-jahr { width: 70px; }
.table-jahre .sp-richtung { width: 90px; }
.table-jahre .sp-anzahl { width: 100px; }
.table-jahre .sp-stueck { width: 100px; }
.table-jahre .sp-netto,
.table-jahre .sp-brutto { width: 130px; }

/* --- Spaltenbreiten der Meldungsliste ------------------------------------
   Bewusst als Klassen und nicht als `style="width:80px"` am `<th>`: die
   Inhaltsrichtlinie erlaubt kein `style`-Merkmal, die Breiten waren also nie
   in Kraft. Aufgefallen ist das erst mit der Eingabezeile — ohne Breite
   verteilt der Browser selbst, und ein Datumsfeld bekam ein Drittel der
   Tabelle. */

.table-meldungen {
  font-size: 13px;
  min-width: 1010px;
}

.table-meldungen .sp-auswahl { width: 30px; }
.table-meldungen .sp-gesendet { width: 58px; }
.table-meldungen .sp-nummer { width: 68px; }
/* Das Datumsfeld zeigt TT.MM.JJJJ und daneben das Kalendersymbol des
   Browsers — darunter wird es nicht lesbar. */
.table-meldungen .sp-datum { width: 116px; }
.table-meldungen .sp-stueck { width: 92px; }
.table-meldungen .sp-betrag { width: 112px; }
/* Ein Auswahlfeld braucht mehr als seinen Text: `select.input` hält rechts
   30 px für den Pfeil frei. Bei 68 px blieb für „ZU“ nichts mehr übrig. */
.table-meldungen .sp-grund { width: 84px; }
.table-meldungen .sp-datei { width: 120px; max-width: 120px; }
.table-meldungen .sp-rechnung { width: 92px; }

/* Die Betriebsnamen bekommen den Rest und werden abgeschnitten, statt die
   Zeile umbrechen zu lassen. */
.table-meldungen .sp-betrieb { max-width: 170px; }
.table-meldungen .sp-transport { max-width: 130px; }

/* Die Rechnungsliste — Breiten als Klassen, nicht als `style`-Merkmal.
   Unter `style-src 'self'` wirkt ein `style`-Merkmal ohnehin nicht. */
/* `table-layout: fixed` — sonst sind die Breiten unten nur Wünsche.

   Die Zellen von Kunde und Ort tragen `.ellipsis` und damit `white-space:
   nowrap`; in der automatischen Aufteilung ist die volle Textbreite dann
   ihre Mindestbreite, und der Browser räumt sie ihnen ein, egal was
   danebensteht. „Schwarzenberger-Mayrhofer“ bekam so ein Drittel der
   Tabelle, während vom Ort die Hälfte fehlte. Fest aufgeteilt gilt das
   Verhältnis der Breiten, und `.ellipsis` schneidet, was nicht passt —
   genau wozu es da ist. */
.table-rechnungen { font-size: 13px; min-width: 1020px; table-layout: fixed; }

.table-rechnungen .sp-auswahl { width: 30px; }
.table-rechnungen .sp-nummer { width: 90px; }
.table-rechnungen .sp-datum { width: 80px; }
.table-rechnungen .sp-art { width: 70px; }
.table-rechnungen .sp-menge { width: 60px; }
.table-rechnungen .sp-netto,
.table-rechnungen .sp-brutto { width: 100px; }

/* Name und Ort teilen sich, was übrig ist, und werden abgeschnitten, statt
   die Zeile umbrechen zu lassen.

   **`width`, nicht `max-width`.** In einer Tabelle mit automatischer
   Aufteilung ist `max-width` an einer Zelle wirkungslos: der Browser verteilt
   nach Inhalt. Der Kunde bekam so ein Drittel der Tabelle, während vom Ort
   „Lohnsburg am Kobernaußerwald“ die Hälfte abgeschnitten war. Mit einer
   Breitenangabe an beiden — und derselben Klasse auch am Spaltenkopf, sonst
   fehlt dem Browser der Anhaltspunkt — steht das Verhältnis fest. */
.table-rechnungen .sp-kunde { width: 230px; max-width: 230px; }
.table-rechnungen .sp-ort { width: 190px; max-width: 190px; }

/* --- Spaltenbreiten der Kundenliste --------------------------------------
   Wie bei der Meldungsliste: als Klassen, nicht als `style`-Merkmal. */

.table-kunden {
  font-size: 13px;
  min-width: 1180px;
}

.table-kunden .sp-auswahl { width: 30px; }
.table-kunden .sp-nummer { width: 70px; }
.table-kunden .sp-plz { width: 70px; }
.table-kunden .sp-land { width: 60px; }
.table-kunden .sp-lfbis { width: 100px; }
.table-kunden .sp-steuer { width: 70px; }
.table-kunden .sp-drucken { width: 80px; }
.table-kunden .sp-aktiv { width: 60px; }
.table-kunden .sp-letzte { width: 100px; }
.table-kunden .sp-strasse { max-width: 190px; }
.table-kunden .sp-ort { max-width: 180px; }

/* Ein Verweis, der nicht wie einer aussieht: die Meldungsnummer in der Liste.
   Die ganze Zeile ist anklickbar, ein zweiter blauer Weg daneben stiftet nur
   Verwirrung. Der Verweis bleibt trotzdem — ohne JavaScript ist er der
   einzige Weg in die Meldung. Beim Tastaturgang wird er wieder sichtbar. */
.stiller-verweis {
  color: inherit;
  text-decoration: none;
}
.stiller-verweis:focus-visible { text-decoration: underline; }

/* Der Haken ist die Schaltfläche „Speichern“ einer Eingabezeile. Als
   dünnes graues Symbol neben dem Abbrechen-Kreuz war nicht zu sehen, dass
   er die wichtigere der beiden Aktionen ist: kräftiger Strich und die
   Farbe der Anwendung. `stroke-width` gehört hierher und nicht ins SVG —
   im Merkmal stünde derselbe Wert für jedes Symbol. */
.btn-symbol.btn-bestaetigen { color: var(--color-accent); }
.btn-symbol.btn-bestaetigen .symbol {
  width: 19px;
  height: 19px;
  stroke-width: 3;
}
.btn-symbol.btn-bestaetigen:hover {
  background: var(--color-accent-100);
  color: var(--color-accent);
}


/* Abschließender Strich unter der Kennzahlenleiste, dazu Abstand zur
   Tabelle darunter — vorher klebte der Spaltenkopf daran. */
.statstrip {
  border-bottom: 1px solid var(--color-divider);
  margin-bottom: 14px;
}

/* Ankreuzfeld mit eigener Beschriftung im Formularraster. */
.ankreuzfeld { display: flex; align-items: center; gap: 8px; height: 34px; }
.ankreuzfeld label { font-size: 13px; color: var(--muted-70); margin: 0; }


/* --- Erfassungsmaske des Kassabuchs -------------------------------------

   Vier gleich breite Spalten machen ein Datums- oder Betragsfeld so breit
   wie ein Textfeld. Hier bekommt jede Spalte die Breite, die ihr Inhalt
   braucht; `feld-breit` nimmt den Rest der Zeile. */

.formgrid-erfassen {
  /* Kein Raster, sondern eine Reihe: **jedes Feld ist so breit, wie sein
     Inhalt es braucht** — bei einem Datum, einer Stückzahl oder der Auswahl
     brutto/netto weiß man das vorher. Ein Raster aus gleich breiten Spalten
     ließ zwischen den Feldern jedes Mal eine handbreite Lücke.

     Die Breiten kommen aus der Klassenleiter `.feld-xs` bis `.feld-xl`, wie
     überall in der Anwendung; auf schmalen Bildschirmen werden daraus von
     selbst volle Zeilen. */
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  align-items: flex-start;
}
.formgrid-erfassen > .field { min-width: 0; }

/* **Die Eingabefelder einer Reihe müssen auf einer Höhe liegen.** Sie liegen
   es nur, solange alle Beschriftungen darüber gleich hoch sind — bricht eine
   auf zwei Zeilen um, rutscht ihr Feld nach unten und die Reihe ist krumm.
   Genau so ist es passiert: „Passwort (hinterlegt — leer lassen, um es zu
   behalten)“ stand als Beschriftung da und schob sein Feld eine Zeile tiefer.

   Deshalb zwei Dinge: Beschriftungen bleiben **kurz und einzeilig**,
   Erklärendes steht als `.feldhinweis` **unter** dem Feld — und diese
   Mindesthöhe hält die Zeile auch dann gerade, wenn ein Feld gar keine
   Beschriftung trägt. */
.formgrid-erfassen > .field > label { min-height: 18px; }

/* Text und Konto nehmen den Rest der Zeile — sie sind die einzigen Felder,
   deren Inhalt nicht vorherzusehen ist. */
.formgrid-erfassen .feld-weit {
  flex: 1 1 300px;
  width: auto;
  min-width: 240px;
}

@media (max-width: 720px) {
  .formgrid-erfassen .feld-weit { flex: 1 1 100%; min-width: 0; }
}


/* --- Aufklappendes darf die Seite nicht wachsen lassen -------------------

   Kalender und Vorschlagslisten liegen absolut positioniert über der Seite.
   Auf einem kurzen Bildschirm — die Erfassungsmaske ist einer — reichen sie
   über den unteren Rand hinaus und lassen das Dokument mitwachsen: es
   erscheinen Bildlaufleisten, obwohl gar nicht mehr Inhalt da ist.

   Die Listen bekommen deshalb eine Höhe, die sich am Fenster orientiert,
   und der Seitenrumpf hält seine Breite. */

.kombi-liste { max-height: min(260px, 40vh); }

.flatpickr-calendar { max-width: calc(100vw - 24px); }

/* Ein zu breites Kind darf die Seite nicht aufschieben. `clip` beschneidet
   nur waagrecht und lässt senkrecht alles stehen — anders als `hidden`, das
   die andere Achse zum Rollen zwingen würde. Breite Tabellen rollen
   weiterhin in ihrem eigenen Rahmen (.tablescroll); der Kalender von
   flatpickr hängt am Seitenrumpf und ist davon nicht betroffen. */
.main { max-width: 100%; }
.content {
  max-width: 100%;
  overflow-x: clip;
}

/* Lange Einträge in einer Auswahlliste — der Kontenstamm hat 960 davon —
   ziehen eine Rasterspalte sonst auf ihre Breite auf. */
.formgrid > .field,
.formgrid > * { min-width: 0; }

/* Die Seitenleiste wächst mit jedem Unterpunkt. Sie soll in sich rollen,
   statt die ganze Seite länger zu machen — aber nur, solange sie senkrecht
   steht. Auf schmalen Bildschirmen ist sie eine waagrechte Leiste und rollt
   ohnehin zur Seite. */
@media (min-width: 721px) {
  .sidebar { overflow: hidden; }
  .sidebar-nav { flex: 1 1 auto; overflow-y: auto; min-height: 0; }
}


/* --- Erfassungsmasken: Schaltflächen oben, im Quelltext zuletzt ----------

   Die Schaltflächen sollen wie überall oben rechts stehen — beim Ausfüllen
   aber zuletzt angesprungen werden. Beides zugleich geht nur, wenn sie im
   Quelltext hinter den Feldern stehen und die Anzeige sie nach vorn holt:
   `order: -1`. Abgeschickt wird über das form-Merkmal, wie bei jeder anderen
   Schaltfläche außerhalb ihres Formulars. */

.maske { display: flex; flex-direction: column; }
.maske-kopf { order: -1; }

/* --- Feldbreiten nach Inhalt --------------------------------------------

   Ein Datum wird nicht breiter, wenn das Feld breiter wird, und ein Betrag
   auch nicht. Ohne Obergrenze zieht das Raster sie über die halbe Zeile —
   das sieht nach einem Fehler aus und lässt das Auge weit springen.

   `.flatpickr-anzeige` muss mit dabeistehen: flatpickr blendet das
   ursprüngliche Feld aus und setzt ein **Textfeld** daneben. Eine Regel auf
   `[type="date"]` greift dort nicht mehr — genau daran ist die erste Fassung
   dieser Begrenzung wirkungslos geblieben. */

.input[type="date"],
.input.flatpickr-anzeige,
.input.tnum,
.input[inputmode="numeric"],
.input[inputmode="decimal"],
.input.num {
  max-width: 220px;
}

/* In den Filterleisten bestimmt die Feldklasse die Breite; dort darf die
   Obergrenze nicht dazwischenfunken. */
.kopf-filter .input,
.bericht-filter .input,
.toolbar .input { max-width: none; }

/* Auch in den Gruppen des Stammblatts nicht: dort gibt schon die Spalte die
   Breite vor. Mit der Obergrenze standen die Amtlichen Nummern alle auf
   220 px in einer breiteren Spalte — rechts blieb bei jedem Feld ein Rest
   Luft, der wie ein Fehler aussah. Was wirklich kurz ist, wird unten einzeln
   begrenzt. */
.group-fields .input { max-width: none; }

/* Hausnummer, PLZ und die Bankleitzahl werden nicht länger, egal wie breit
   das Feld ist. Sie bekommen die Breite ihres Inhalts statt der Spalte. */
.group-fields .feld-kurz .input { max-width: 110px; }
.group-fields .feld-mittel .input { max-width: 170px; }

/* Die Fußleiste der Seitenleiste darf nicht schrumpfen, sonst wird
   „Abmelden“ abgeschnitten, sobald die Navigation lang wird. */
.sidebar-foot { flex: none; }


/* Wegklick-Taste der Rückmeldungen: rechts außen, unaufdringlich. */
.banner-zu {
  margin-left: auto;
  flex: none;
  border: 0;
  background: transparent;
  color: var(--muted-55);
  font-size: 17px;
  line-height: 1;
  padding: 2px 4px;
  cursor: pointer;
}
.banner-zu:hover { color: var(--color-text); }

/* Mehrere Meldungen stehen dichter beieinander als der Rest. */
.bannerstapel { margin-bottom: 6px; }
.bannerstapel .banner + .banner { margin-top: 4px; }


/* --- Erfassen in der Tabelle --------------------------------------------

   Die neue Zeile steht am Ende der Tabelle, die geänderte an ihrem Platz.
   Sie ist abgesetzt, damit man sie zwischen den erfassten Zeilen findet. */

/* Oben ausgerichtet, nicht mittig: sobald unter einem Feld ein Hinweis
   steht (unbekanntes Konto), wird die Zeile höher — mittig ausgerichtet
   rutschten dann alle übrigen Felder mit nach unten. */
.eingabezeile td {
  background: var(--color-accent-100);
  padding-top: 5px;
  padding-bottom: 5px;
  vertical-align: top;
}
.eingabezeile .input {
  height: 30px;
  min-height: 30px;
  max-width: none;
  font-size: 13px;
  background: var(--color-bg);
}
/* Ankreuzfeld und Haken sind niedriger als ein Eingabefeld. Auf dessen
   Höhe gebracht, stehen sie auf einer Linie mit der ersten Zeile der
   Felder — auch wenn darunter ein Hinweis erscheint. */
.eingabezeile .zeilenaktionen-inhalt { min-height: 30px; }
.eingabezeile .zeile-mona { min-height: 30px; }

/* Dasselbe für reinen Text in einer Eingabezeile — „neu“ und der
   Gedankenstrich für die Nummer, die es noch nicht gibt. Sie standen an der
   Oberkante der Zelle, während die Felder daneben ihre 30 px hoch waren. */
.eingabezeile .zeilentext {
  display: inline-flex;
  align-items: center;
  min-height: 30px;
}

/* Das Ankreuzfeld für die Monatsbuchung teilt sich die Saldospalte —
   dort steht bei einer neuen Zeile ohnehin noch nichts. */
.zeile-mona {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: 11.5px;
  color: var(--muted-60);
  white-space: nowrap;
}


/* Nach oben aufklappen, wenn unter dem Feld kein Platz mehr ist. */
.kombi-liste.nach-oben {
  top: auto;
  bottom: 100%;
  margin: 0 0 2px;
}

/* Die Kassabuchtabelle rollt nicht seitlich — sie muss deshalb auch nichts
   beschneiden, und die Aufklapplisten der Eingabezeile bleiben sichtbar. */
.tablescroll-offen { overflow: visible; }


/* --- Zweiter Faktor (FA-06) --------------------------------------------- */

/* Das Codefeld: sechs Ziffern, weit auseinander, damit man sie beim
   Abtippen nicht verliert. Ziffernbreite fest, sonst wandert der Cursor.

   Die Breite steht hier am Feld und **nicht** über die Leiter `.feld-*` am
   Rahmen: die setzt die Breite auf den Rahmen, das Feld erbt daraus
   `width: 100%` — bei `.feld-s` (120 px) blieb von sechs Ziffern die letzte
   draußen. In `ch` gerechnet, damit die Breite der Schriftgröße folgt;
   `letter-spacing` hängt an jede Ziffer noch einen Abstand an, deshalb reicht
   6ch nicht. */
.code-feld {
  font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
  font-size: 20px;
  letter-spacing: 6px;
  text-align: center;
  width: 12ch;
  min-width: 190px;
  max-width: 100%;
  font-variant-numeric: tabular-nums;
}

/* Einrichtung: Anleitung links, QR-Code rechts. Unter 860 px rutscht der
   Code unter den Text — auf dem Telefon liest ohnehin niemand den eigenen
   Bildschirm ab. */
.faktor-einrichtung {
  display: flex;
  align-items: flex-start;
  gap: 28px;
  flex-wrap: wrap;
}
.faktor-schritte { flex: 1 1 380px; min-width: 0; }
/* Der einzige Fleck, der auch im dunklen Satz weiß bleibt — und zwar mit
   Grund: der QR-Code lebt vom Gegensatz dunkler Module auf hellem Grund,
   und die Ruhezone rundherum gehört dazu. Umgekehrt lesen ihn viele
   Kameras nicht. Das Bild selbst bringt sein Weiß ohnehin mit
   (Kern\QrCode), hier steht nur der Rand darum. */
.faktor-qr {
  flex: 0 0 auto;
  border: 1px solid var(--color-divider);
  padding: 10px;
  background: #fff;
}
/* Der QR-Code bringt seine Maße als Merkmal mit; auf schmalen Bildschirmen
   darf er trotzdem nicht über den Rand hinauswachsen. */
.faktor-qr .qrcode { display: block; max-width: 100%; height: auto; }

@media (max-width: 860px) {
  .faktor-qr { order: -1; }
}

/* Das Geheimnis zum Abtippen — groß genug, um es vom Bildschirm auf ein
   Telefon zu übertragen. */
.geheimnis {
  font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
  font-size: 17px;
  letter-spacing: 1px;
  padding: 8px 10px;
  border: 1px solid var(--color-divider);
  background: var(--color-accent-100);
  display: inline-block;
  word-break: break-all;
}

/* Der Schlüssel und der Kopierknopf stehen nebeneinander in einer Zeile.
   `align-items: center` hält den Knopf auf der Höhe des Kastens, statt ihn
   an dessen Oberkante zu hängen.

   `min-width: 0` am Kasten: ein Flex-Element gibt von sich aus nicht unter
   seine Inhaltsbreite nach, und 32 Zeichen in Festbreitenschrift sind
   breiter als die Spalte. Ohne das schob es den Knopf in die nächste Zeile
   und lief selbst rechts hinaus. */
.geheimniszeile {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 0 0 4px;
}
/* Der Kasten gibt nach, der Knopf nicht: so bleibt „Kopieren“ **daneben**
   und der Schlüssel bricht in zwei Zeilen um, statt den Knopf nach unten zu
   schieben. `min-width: 0` hebt die selbstgesetzte Mindestbreite eines
   Flex-Elements auf — ohne sie gibt es unter seiner Inhaltsbreite gar nicht
   nach, und 32 Zeichen in Festbreitenschrift sind breiter als die Spalte. */
/* **Der Absatzabstand muss weg.** `align-items: center` mittet das
   **Randbox** eines Flex-Elements, nicht seinen Kasten: der Schlüssel steht
   in einem `<p>` und trägt aus `p { margin: 0 0 var(--space-3) }` einen
   Abstand nach unten. Gemittet wurde damit der Kasten **samt** dieser Luft,
   und „Kopieren“ saß sichtbar zu tief — beim Umbruch des Schlüssels in zwei
   Zeilen umso mehr. */
.geheimniszeile .geheimnis { min-width: 0; flex: 1 1 auto; margin: 0; }
.geheimniszeile .btn { flex: none; }
/* Das Symbol der Zwischenablage neben der Aufschrift. 20 px aus `.symbol`
   überragen die 12,5-px-Schrift von `.btn-klein`; den Abstand dazwischen
   bringt `gap: 6px` aus `.btn` schon mit. */
.geheimniszeile .btn .symbol { width: 15px; height: 15px; }

/* Ein Element, das da ist, aber nicht zu sehen — für das Textfeld, aus dem
   `document.execCommand("copy")` auswählt. `display: none` ginge nicht: aus
   einem nicht dargestellten Feld lässt sich nichts auswählen, und ohne
   Auswahl kopiert der Browser nichts. */
.abseits {
  position: absolute;
  left: -9999px;
  top: 0;
  opacity: 0;
  width: 1px;
  height: 1px;
}

/* Wiederherstellungscodes: zwei Spalten, gut ablesbar, druckbar. */
.codeliste {
  list-style: none;
  margin: 10px 0 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
  gap: 6px 18px;
}
.codeliste li {
  font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
  font-size: 16px;
  letter-spacing: 1px;
  padding: 4px 8px;
  border: 1px solid var(--color-divider);
}

/* Zwei nebeneinanderstehende Karten in „Mein Zugang“ — kein Raster aus
   vier Spalten wie .formgrid, sondern zwei, die den Platz teilen. */
.zugang-karten {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: 16px;
  max-width: 900px;
  margin-top: 14px;
}

/* Spaltenfluss statt Blockstapel: nur so lässt sich die Schaltfläche mit
   `margin-top: auto` an den unteren Rand schieben. Beide Karten enden dann
   auf derselben Linie, auch wenn der Text darüber verschieden lang umbricht
   — sonst sitzen zwei gleichwertige Knöpfe auf zwei Höhen. */
.zugang-karte {
  display: flex;
  flex-direction: column;
  padding: 16px 18px 18px;
}
.zugang-karte h3 { margin: 0 0 6px; font-size: 14px; }
/* `.hint-line` trägt `margin-left: auto` — gemeint für die `.formbar`, wo der
   Hinweis rechts neben der Schaltfläche steht. In einer Spalte schiebt genau
   das den Absatz an den rechten Rand; hier gehört er links an den Text. */
.zugang-karte > .hint-line { margin-left: 0; margin-bottom: 14px; }
/* Der Abstand wandert an das Eingabefeld statt an die Schaltfläche: dann
   fluchten in beiden Karten **Feld und Knopf**, nicht nur der Knopf. Der Text
   darüber darf verschieden lang umbrechen, ohne die Eingabe zu verschieben. */
.zugang-karte .field { margin-top: auto; }

/* Innerhalb einer Karte trennt schon der Rahmen — die Linie der `.formbar`
   wäre eine zweite Trennung auf engem Raum. Gebraucht wird hier nur der
   Abstand über der Schaltfläche. */
.zugang-karte .formbar {
  border-top: none;
  padding-top: 0;
  margin-top: 18px;
}

/* Zwei Striche übereinander: `.formgrid` schließt mit `border-bottom` ab,
   `.formbar` beginnt mit `border-top`. Stehen sie hintereinander — und das
   ist der übliche Aufbau einer Erfassungsmaske —, zieht der Browser beide
   Linien im Abstand von dreißig Pixeln. Gemeint war immer nur eine.
   Betrifft auch „Neuen Benutzer anlegen" und die Datenübernahme. */
.formgrid + .formbar {
  border-top: none;
  padding-top: 0;
}

/* Der Block mit den frisch erzeugten Codes steht allein, nicht im Raster. */
.zugang-codes { max-width: 900px; margin-bottom: 20px; }
.zugang-codes .codeliste { margin-bottom: 4px; }

/* Aufklappbarer Block ohne Skript (<details>) — für den
   Wiederherstellungscode unter dem Codefeld. */
.ausklapp-block {
  margin-top: 14px;
  border-top: 1px solid var(--color-divider);
  padding-top: 10px;
}
.ausklapp-block > summary {
  cursor: pointer;
  font-size: 13px;
  color: var(--color-accent);
}

/* Der Benutzername in der Seitenleiste führt zu „Mein Zugang“ — er ist
   damit ein Link und braucht dessen Aussehen abgeräumt. */
a.sidebar-benutzer { display: block; text-decoration: none; }
a.sidebar-benutzer:hover { background: color-mix(in srgb, var(--color-accent) 10%, transparent); }

/* Anmeldung ohne HTTPS (SA-10): die Karte zeigt statt des Formulars nur die
   Erklärung. Ihre Abstände hängen sonst an `.mform` — ohne Formular gäbe es
   hier keine, und der Knopf klebte an der Erklärung. */
.anmeldung-gesperrt { display: flex; flex-direction: column; gap: var(--space-3); }
.anmeldung-gesperrt p { margin: 0; }
.anmeldung-gesperrt .btn { align-self: flex-start; margin-top: var(--space-2); }


/* --- Maske „Buchung erfassen“ (Kassabuch) --------------------------------

   Zwei Reihen, und wo sie bricht, hängt nicht von der Fensterbreite ab:
   Datum, Text, Steuersatz und Konto beschreiben die Buchung, Einnahme,
   Ausgabe und die Monatsbuchung das Geld. Ohne den Umbruch landete auf einem
   breiten Bildschirm „Einnahme“ neben dem Konto — mit einer handbreiten
   Lücke davor, weil `.input.tnum` bei 220 px endet, das Feld darum herum
   aber weiterwächst. */

.formgrid-erfassen .umbruch {
  flex: 1 0 100%;
  height: 0;
  margin: 0;
  padding: 0;
}

/* Steht unter dem Raster nichts mehr, ist der Abschlussstrich eine Linie ins
   Leere. Der Auftraggeber hat genau so eine schon einmal wieder wegstreichen
   lassen: gemeint war Abstand, nicht Dekoration. */
.formgrid-erfassen.ohne-abschluss {
  border-bottom: none;
  padding-bottom: 0;
}


/* --- Sicherung (FA-53) ---------------------------------------------------

   Die drei Aufgabenkarten tragen Eingabefelder. Ohne `margin-top: auto`
   säßen Feld und Knopf je nach Länge der Erklärung darüber auf verschiedenen
   Höhen — zwei gleichwertige Karten sehen dann wie ein Versehen aus.
   Der Abstand zum Knopf ist Luft, keine Linie: die Karte ist schon
   gerahmt, eine zweite Trennung auf so engem Raum wäre zu viel. */

.aufgabe .aufgabe-felder {
  margin-top: auto;
  padding-top: var(--space-3);
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}
.aufgabe .aufgabe-felder + .btn { margin-top: var(--space-3); }

/* Der Sicherungsordner steht als Pfad in der Erklärung. Ein langer Pfad ist
   ein einziges Wort ohne Trennstelle — ohne diesen Umbruch schiebt er sich
   quer über die Nachbarkarte. */
.aufgabe code { overflow-wrap: anywhere; }

/* Der Prüfbericht ist eine Karte mit zwei kleinen Tabellen darin — links der
   Kopf der Sicherungsdatei, wie er im Klartext darin steht. */
.pruefbericht { padding: var(--space-3) var(--space-4); max-width: 720px; }
.pruefbericht h3 { font-size: 13px; margin: var(--space-4) 0 var(--space-2); }
.pruefbericht .table { font-size: 12.5px; }

/* ---------------------------------------------------------------------------
   Meldedatei senden (FA-24 bis FA-27)
   ------------------------------------------------------------------------ */

/* Die Vorschau zeigt die Datei so, wie sie hinausgeht: eine Zeile je Meldung,
   Semikolon an Semikolon. Umbrechen darf sie nicht — dann stünde ein Feld in
   der nächsten Zeile und die Zeilen ließen sich nicht mehr zählen. Sie rollt
   deshalb waagrecht in ihrem eigenen Rahmen (.tablescroll). */
.quelltext {
  margin: 0;
  padding: var(--space-3);
  font-family: var(--font-mono);
  font-size: 12px;
  line-height: 1.6;
  white-space: pre;
  background: var(--color-surface);
  border: 1px solid var(--color-divider);
  border-radius: var(--radius-md);
  color: var(--color-text);
}

/* Der graue Kasten ist so breit wie der Code darin, nicht so breit wie die
   Seite. `pre` ist ein Block und nähme sonst immer die volle Breite seines
   Rahmens — und das ging in beide Richtungen daneben:

   - Ein kurzer Verlauf (die Verbindungsprobe unter Verwaltung › Statistik
     Austria hat Zeilen von einer halben Handbreite) stand in einer grauen
     Fläche über den ganzen Bildschirm.
   - Eine lange Zeile (die Meldedatei-Vorschau, 39 Felder) lief über den
     Rahmen hinaus, der graue Grund aber nicht: beim Rollen nach rechts stand
     der Text auf Weiß, und rechts fehlte der Innenabstand.

   `max-content` löst beides — gerollt wird im Rahmen (`.tablescroll`), der
   Kasten wächst mit dem Inhalt. Nicht für `.verlauf-klapp > .quelltext`: der
   rollt in sich selbst und braucht seine 100 % als Bezug. */
.tablescroll > .quelltext { width: max-content; }

/* Auf der Sendeseite steht je Jahr eine Karte, und die Karten stehen
   nebeneinander. Ihre Texte sind verschieden lang — ohne diesen Abstand nach
   oben säße die Knopfreihe der einen auf halber Höhe der anderen. Dieselbe
   Regel wie bei .aufgabe-felder, und aus demselben Grund. */
.aufgabe .zeilenaktionen-inhalt {
  margin-top: auto;
  padding-top: var(--space-3);
  flex-wrap: wrap;
}

/* ---------------------------------------------------------------------------
   Doppelt angelegte Betriebe und das Zusammenführen
   ------------------------------------------------------------------------ */

/* Die Trefferliste im Warnband des Stammblatts: je Zeile ein Betrieb mit
   Nummer, Anschrift und dem Grund des Verdachts. Ohne eigene Aufzählungs-
   zeichen — im Band steht schon eines vor dem ganzen Absatz. */
.dublettenliste {
  list-style: none;
  margin: var(--space-2) 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}
.dublettenliste li {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-2);
}
.dublettenliste .muted { font-size: 12.5px; }

/* Kleiner als ein gewöhnlicher Knopf: er steht mitten in einem Fließtext
   und würde die Zeile sonst auseinanderziehen. */
.btn-klein {
  height: auto;
  min-height: 24px;
  padding: 0 var(--space-2);
  font-size: 12.5px;
}

.dubletten-hinweis { margin: 0 0 var(--space-3); max-width: 70ch; }

/* Je Verdachtsfall eine Karte. Der Abstand nach unten steht hier und nicht
   als Abstand nach oben an der nächsten — sonst klebte die erste Karte an
   der Kopfleiste. */
.dublettenpaar { margin-bottom: var(--space-4); }
.dublettenpaar h3 { display: flex; align-items: center; gap: var(--space-2); }

/* Gegenüberstellung: links das Merkmal, rechts die beiden Betriebe.
   Die Merkmalspalte bemisst sich am längsten Wort („Rechnungen“) und darf
   nicht mitwachsen — sonst stünden die beiden Seiten verschieden breit da
   und ließen sich nicht mehr vergleichen. */
.table-dubletten .sp-merkmal,
.table-uebernahme .sp-merkmal { width: 190px; }
.table-dubletten td:not(.label),
.table-uebernahme td:not(.label) { width: 50%; }

/* Was in beiden Datensätzen gleich ist, tritt zurück: der Blick soll auf
   dem Unterschied hängen bleiben. */
.table-dubletten .ist-gleich td { color: var(--muted-55); }

/* In der Übernahmetabelle ist jede Zelle ein Auswahlknopf mit seinem Wert
   daneben. `.ankreuz` hält den Knopf schon oben; der lange Wert (Zusatzzeile
   im Rechnungskopf) muss zusätzlich umbrechen dürfen, sonst schiebt er die
   Spalte auseinander. */
.table-uebernahme .ankreuz span { overflow-wrap: anywhere; }

/* Das Schrittprotokoll einer Übertragung, aufklappbar je Zeile des
   Sendearchivs. Ein fehlgeschlagener Versuch steht offen da — dort sucht
   jemand —, ein geglückter zugeklappt. `<details>` kann das ohne Skript. */
.verlauf-klapp { margin-top: var(--space-2); }
.verlauf-klapp > summary {
  cursor: pointer;
  font-size: 12px;
  color: var(--muted-65);
  padding: 2px 0;
}
.verlauf-klapp > summary:hover { color: var(--color-text); }
.verlauf-klapp > .quelltext {
  margin-top: var(--space-2);
  /* Die Tabellenzelle ist schmal; das Protokoll rollt in sich selbst, statt
     die Spalte auseinanderzuziehen. */
  max-width: 100%;
  overflow-x: auto;
}
.verlauf-leer { margin-top: var(--space-2); font-size: 12px; }

/* Die Kundennummer im Stammblatt: eigene Karte über den Gruppen, über die
   volle Breite. Sie steht außerhalb von `.groupgrid` und bringt deshalb den
   Abstand nach oben selbst mit; das Feld darin nimmt über `.span2` alle zwölf
   Spalten ein. Der Abstand ist derselbe wie der von `.groupgrid`. */
.kundennummer-karte { margin-top: 14px; }

/* --- Spaltenbreiten der Buchungskonten -----------------------------------
   Wie in Meldungs-, Kunden- und Rechnungsliste als Klassen: unter
   `style-src 'self'` wirkt ein `style`-Merkmal am `<th>` ohnehin nicht, und
   ohne wirksame Breite verteilt der Browser selbst — die Eingabefelder in
   der Zeile ziehen dann die Bezeichnungsspalte zusammen.

   Die Klasse hält 170 px: darin stehen „Speichern“ und „Löschen“
   nebeneinander, bei einem geschützten Konto stattdessen die Marke „vom
   Programm gebraucht“. */
.table-konten { font-size: 13px; min-width: 820px; }

.table-konten .sp-nummer { width: 110px; }
/* Ein Auswahlfeld braucht mehr als seinen Text: `select.input` hält rechts
   30 px für den Pfeil frei — „Lieferanten“ ist das längste Wort darin. */
.table-konten .sp-klasse { width: 160px; }
.table-konten .sp-gebucht { width: 110px; }
.table-konten .sp-aktionen { width: 170px; }

/* --- Meldedateien (Sendearchiv) ------------------------------------------

   Die Ergebnisspalte bekommt bewusst keine Breite: darin steht der Text vom
   Server und darunter das aufklappbare Schrittprotokoll — sie soll den Rest
   nehmen. Alles andere ist knapp gehalten, damit sie das kann. */
.table-meldedateien { font-size: 13px; min-width: 900px; }

.table-meldedateien .sp-zeitpunkt { width: 150px; }
.table-meldedateien .sp-datei { width: 190px; }
.table-meldedateien .sp-anzahl { width: 100px; }
.table-meldedateien .sp-benutzer { width: 130px; }
.table-meldedateien .sp-aktionen { width: 150px; }

/* --- Kleiner Wortschatz statt `style`-Merkmalen ---------------------------

   Unter `style-src 'self'` wendet der Browser **kein** `style`-Merkmal an —
   die Breiten, Abstände und Schriftgrößen, die früher dort standen, waren nie
   in Kraft. Die Seiten sahen trotzdem passabel aus, weil der Browser dann
   selbst verteilt; gemeint war es so aber nicht.

   Was mehrfach vorkam, steht hier als Klasse. Was nur an einer Tabelle
   vorkommt, steht bei dieser Tabelle als `.table-… .sp-…` — so wie bei
   Meldungs-, Kunden- und Rechnungsliste. */

/* Ein Formular, eine Kennwerttabelle oder eine Aufzählung soll nicht über die
   ganze Fensterbreite laufen: eine Zeile über 1600 px ist nicht mehr zu
   lesen, und ein Feldraster reißt auseinander. */
.lesebreite { max-width: 900px; }
.lesebreite-eng { max-width: 640px; }

/* Zwei Stufen unter der Grundschrift. `fein` für Werte, die neben dem
   eigentlichen Inhalt stehen (Zeitpunkte, Zustände in einer Liste), `klein`
   für Zwischenüberschriften und Marken. */
.text-fein { font-size: 12.5px; }
.text-klein { font-size: 12px; }

/* In einer senkrechten Flex-Spalte zieht sich ein Knopf sonst über die volle
   Breite — eine Schaltfläche über 900 px sieht aus wie ein Balken. */
.nicht-dehnen { align-self: flex-start; }

/* Der eine Knopf, auf den alles zuläuft: Anmelden und der zweite Faktor.
   Höher als die übrigen, damit er auf dem Telefon sicher zu treffen ist. */
.btn-gross { min-height: 44px; }

/* Die erste Überschrift in einer Karte bringt ihren Abstand nicht mit — den
   hat die Karte schon. */
.ohne-abstand-oben { margin-top: 0; }

/* --- Benutzerliste (Verwaltung) ------------------------------------------
   Acht Spalten, die letzte trägt bis zu vier Schaltflächen nebeneinander —
   ohne feste Breiten schob sie den Namen zusammen. */
.table-benutzer { font-size: 13px; min-width: 1010px; }

.table-benutzer .sp-ampel { width: 30px; }
.table-benutzer .sp-benutzer { width: 160px; }
/* Breiter als die früheren 150 px: seit Rollen einstellbar sind, steht dort
   nicht mehr nur „Erfassung“ oder „Nur-Lesen“, sondern auch „Mobil erfassen“
   und jeder selbst vergebene Name. Schon „Administrator“ war in der alten
   Breite um zwei Buchstaben abgeschnitten. */
.table-benutzer .sp-rolle { width: 205px; }
/* Breiter als die 140 px von früher: unter dem Zeitpunkt steht jetzt die
   zweite Zeile mit der Anwesenheit („zuletzt gesehen …“), und die trägt
   noch einmal einen Zeitpunkt. */
.table-benutzer .sp-anmeldung { width: 175px; }
.table-benutzer .sp-faktor { width: 110px; }
.table-benutzer .sp-zustand { width: 120px; }
.table-benutzer .sp-aktionen { width: 230px; }
/* Der Name bekommt, was übrig ist. Darin steht ein Eingabefeld — er wird
   also nicht mehr abgeschnitten, sondern das Feld nimmt die Spaltenbreite. */
.table-benutzer .sp-name { width: 250px; }
.table-benutzer .sp-name .input { min-height: 30px; width: 160px; }

/* Die Rollenwahl steht mitten in der Zeile: so hoch wie die übrigen
   Zeilenknöpfe, und breit genug für „Administrator“ samt Pfeil. */
.table-benutzer .rollenwahl { min-height: 30px; width: 140px; }

/* Die Aktionsspalte bricht nicht um — sonst wird jede zweite Zeile
   doppelt so hoch wie ihre Nachbarn. */
.table-benutzer td.aktionen { white-space: nowrap; }

/* Die aufgeklappte Passwortzeile sitzt schon in einer eigenen Tabellenzeile
   mit eigenem Hintergrund; der Trennstrich und der Abstand der `.formbar`
   wären dort ein zweiter Rahmen. */
.formbar-blank { margin-top: 0; padding-top: 0; border-top: none; }

/* --- Bericht der Datenübernahme (Verwaltung) -----------------------------
   Zwei schmale Berichtstabellen. Die Prüfsumme steht kleiner als der Rest:
   64 Zeichen, die niemand liest, sondern vergleicht. */
.kv-pruefsumme { font-size: 11.5px; }

.table-altbestand { font-size: 13px; min-width: 420px; }
.table-altbestand .sp-anzahl { width: 120px; }

.table-vergleich { font-size: 13px; min-width: 520px; }
.table-vergleich .sp-jahr { width: 80px; }
.table-vergleich .sp-art { width: 110px; }
.table-vergleich .sp-belege { width: 90px; }
.table-vergleich .sp-summe { width: 140px; }

/* --- Massenerfassung der Meldungen ---------------------------------------
   Acht Zeilen untereinander, alle über die Tastatur zu füllen. Die
   Betriebsspalte bekommt den Rest und wird abgeschnitten; ohne Grenze zieht
   ein langer Betriebsname die Zahlenspalten aus dem Bild. */
.table-massen { font-size: 13.5px; min-width: 1000px; }

.table-massen .sp-nr { width: 34px; }
.table-massen .sp-stueck { width: 90px; }
.table-massen .sp-betrag { width: 150px; }
.table-massen .sp-richtung { width: 170px; }
.table-massen .sp-weg { width: 40px; }
.table-massen .sp-betrieb { max-width: 360px; }

/* --- Nummernkreise (Verwaltung) ------------------------------------------
   Der Stand ist die Zahl, um die es geht — er steht fett. Die Erklärung
   darunter ist Beiwerk und tritt zurück. */
.table-kreise { font-size: 13px; min-width: 820px; }
.table-kreise .sp-jahr { width: 70px; }
.table-kreise .sp-stand { width: 110px; }
.table-kreise .sp-naechste { width: 170px; }
.table-kreise .sp-aendern { width: 260px; }
.table-kreise .kreis-stand { font-weight: 700; }
.table-kreise .kreis-erklaerung { font-size: 12px; margin-top: 2px; }
/* So hoch wie die Knöpfe daneben; breiter als sieben Ziffern muss sie nicht
   sein. */
.table-kreise .kreis-eingabe { width: 110px; min-height: 30px; }

.table-kreise-alle { font-size: 13px; min-width: 420px; }
.table-kreise-alle .sp-jahr { width: 80px; }
.table-kreise-alle .sp-stand { width: 110px; }

/* --- Offene Meldungen vor der Rechnungserstellung ------------------------
   Neun Spalten, drei davon mit Eingabefeldern; die beiden Betriebsnamen
   bekommen den Rest und werden abgeschnitten. */
.table-offene { font-size: 13px; min-width: 940px; }
.table-offene .sp-meldung { width: 70px; }
.table-offene .sp-stueck { width: 60px; }
.table-offene .sp-betrag { width: 100px; }
.table-offene .sp-bruttonetto { width: 100px; }
.table-offene .sp-kundeseite { width: 180px; }
.table-offene .sp-steuer { width: 130px; }
.table-offene .sp-aktionen { width: 100px; }
.table-offene .sp-betrieb { max-width: 190px; }

/* --- Änderungsprotokoll (Verwaltung) -------------------------------------
   Sieben Spalten, zwei davon mit dem alten und dem neuen Wert — die
   bekommen, was übrig ist. */
.table-protokoll { font-size: 12.5px; min-width: 1000px; }
.table-protokoll .sp-zeitpunkt { width: 130px; }
.table-protokoll .sp-benutzer { width: 120px; }
.table-protokoll .sp-aktion { width: 120px; }
.table-protokoll .sp-bereich { width: 100px; }
.table-protokoll .sp-datensatz { width: 80px; }
.table-protokoll .sp-feld { width: 110px; }
/* Die Absenderadresse kam mit dem Anmeldeverlauf dazu — 130 px reichen für
   eine IPv4 samt Rand; eine IPv6 bricht um, das ist auf einer Zeile mit
   sonst kurzen Werten verschmerzbar. */
.table-protokoll .sp-ip { width: 130px; }

/* --- Datenbank (Verwaltung) ----------------------------------------------
   Der Treiber ist die Auskunft, auf die es ankommt, und steht groß. Der
   Ablagepfad daneben ist lang: er bekommt die doppelte Breite und darf als
   einziger Kennwert umbrechen, statt aus der Karte zu laufen. */
.stat-treiber { font-size: 18px; }
.stat-breit { flex: 2; }
.stat-pfad { font-size: 13px; white-space: normal; }

.table-bestand { font-size: 13px; min-width: 420px; }
.table-bestand .sp-anzahl { width: 120px; }

/* --- Ortsverzeichnis (Verwaltung) ----------------------------------------
   Der Auszug aus der eingelesenen Liste. Schmal, weil dort nur PLZ, Ort und
   ein Wort zum Zustand stehen — er ist zum Nachsehen da, ob die beiden
   angegebenen Spalten die richtigen waren. */
.table-orte { font-size: 13px; min-width: 420px; }
.table-orte .sp-plz { width: 90px; }
.table-orte .sp-zustand { width: 140px; }

/* --- Umgebungsprüfung (Verwaltung) --------------------------------------- */
.table-umgebung { font-size: 13px; min-width: 640px; }
.table-umgebung .sp-ampel { width: 28px; }
.table-umgebung .sp-was { width: 230px; }
.table-umgebung .befund-hinweis { font-size: 12px; margin-top: 3px; }

/* --- Auswertungen: die Berichte in der Übersicht ------------------------- */
.table-berichte { font-size: 13px; min-width: 720px; }
.table-berichte .sp-bericht { width: 190px; }
.table-berichte .sp-zeitraum { width: 150px; }

/* Gegenstück zu `.abstand-oben-eng`, für den Hinweis über einem Feld. */
.abstand-unten-eng { margin-bottom: 12px; }

/* Der Titelblock in der Kopfleiste nimmt den Platz neben den Aktionen ein.
   `min-width: 0` gehört dazu: ohne das bricht ein langer Titel den Flex-Kasten
   auf, statt sich abschneiden zu lassen. */
.topbar-titel { flex: 1; min-width: 0; }

/* Die Rechnungsübernahme ins Kassabuch — Auswahlspalte wie in den Listen. */
.table-rechnungsuebernahme { min-width: 900px; }
.table-rechnungsuebernahme .sp-auswahl { width: 30px; }

/* --- Einstellungen (Verwaltung) ------------------------------------------
   Jedes Feld ist so breit wie sein Inhalt: ein Feld über die volle
   Kartenbreite für „ja/nein“ oder eine Jahreszahl liest sich wie ein
   Textfeld und lädt zum Hineinschreiben ein. Die Breiten kommen aus der
   Leiter `.feld-xs` … `.feld-xl` und stehen deshalb in der Ansicht — hier
   bleibt nur, was die Leiter nicht abdeckt.

   Die Klasse gehört ans Feld selbst, nicht an einen Rahmen darum: aus
   `.feld-s` am Rahmen erbte das Feld `width: 100%` und wäre wieder so breit
   wie die Karte.

   Der eigene Betrieb ist der eine Fall, für den die Leiter zu kurz ist —
   Kundennummer, Name und Ort in einem Auswahlfeld, das nicht abschneiden
   darf. Unter der Kartenbreite bleibt es trotzdem. */
.einstellung-betrieb { width: 100%; max-width: 420px; }

/* Die Kopfzeile der Meldedatei ist der längste Wert der Maske und wird Zeichen
   für Zeichen mit der alten data.db verglichen — sie bekommt die doppelte
   Breite eines gewöhnlichen Textfelds. Die Klasse steht doppelt (`.input`
   davor), weil `.input.tnum` sonst mit seiner Obergrenze von 220 px gewinnt:
   die gilt Ziffernfeldern wie Betrag und Stückzahl, nicht diesem hier. */
.input.einstellung-kopfzeile { width: 420px; max-width: 100%; }

/* Zwei Felder nebeneinander in der Einrichtung — Server und Port, Benutzer
   und Passwort. Die Klasse stand in der Ansicht, im Stylesheet gab es sie
   nie: die Felder standen deshalb untereinander. Ein Feld mit eigener Breite
   (`.feld-xs` für den Port) behält sie, alle übrigen teilen sich den Rest. */
.row {
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
  align-items: flex-start;
}
.row > .field { flex: 1 1 180px; }
.row > .feld-xs, .row > .feld-s, .row > .feld-m,
.row > .feld-l, .row > .feld-xl { flex-grow: 0; }

/* ---------------------------------------------------------------------------
   Meldedatei senden

   Bei **einem** offenen Jahr — dem Regelfall — stehen Übertragen und Datei
   herunterladen oben rechts wie auf jedem anderen Bildschirm, und die
   Kennzahlenleiste beschreibt die eine Datei. Nur über den Jahreswechsel
   werden es zwei Dateien; dann steht je Jahr eine Zeile mit eigenen
   Schaltflächen, weil oben nicht zu erkennen wäre, welche gemeint ist.

   Kein Raster aus Karten: in einem 280 px breiten Kästchen (`.aufgaben`)
   brach der Dateiname mitten im Namen um — „atm4014-26-“ / „0001.csv“.
   ------------------------------------------------------------------------ */

/* Ein Dateiname, ein Zeitraum, ein Server:Port — keine Kennzahl, sondern ein
   Text. In den 22 px der `.stat-value` liefe er aus der Kachel hinaus, und
   `white-space: nowrap` lässt ihn dabei nicht einmal umbrechen. */
.stat-schmal { font-size: 14px; }

.sendejahre { display: flex; flex-direction: column; gap: var(--space-2); }

.sendejahr {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-6);
  padding: var(--space-3) var(--space-4);
}
.sendejahr-text { min-width: 0; }
.sendejahr-text h3 { font-size: 14px; margin: 0 0 4px; }
.sendejahr-text p {
  font-size: 12.5px;
  line-height: 1.55;
  color: var(--muted-70);
  margin: 0;
}
.sendejahr-text p + p { margin-top: var(--space-2); }

/* Ein Dateiname ist ein Wort und hat keine Trennstelle. `.aufgabe code` bricht
   ihn absichtlich überall um — das ist für einen langen Pfad richtig und für
   „atm4014-26-0001.csv“ falsch: mittendrin getrennt liest sich der Name wie
   zwei. Hier steht genug Platz zur Verfügung, also bleibt er zusammen. */
.dateiname { white-space: nowrap; }

/* Übertragen oben, der Weg über die Datei darunter und leiser: er ist die
   Rückfallebene, nicht die gleichwertige Wahl. */
.sendejahr-aktionen {
  flex: none;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: var(--space-2);
}
.sendejahr-ersatz { display: flex; align-items: center; gap: var(--space-2); }

@media (max-width: 720px) {
  .sendejahr { flex-direction: column; gap: var(--space-3); }
  .sendejahr-aktionen { align-items: stretch; width: 100%; }
  .sendejahr-ersatz { justify-content: space-between; }
}


/* --- Umschalter hell / dunkel (Kopfleiste) -------------------------------

   Der Schalter oben rechts, gebaut wie der Leistenschalter in der
   Seitenleiste — gleiche Maße, gleiche Zurückhaltung. Er gehört in die
   Kopfleiste und nicht zu `.kopf-funktionen`: dort stehen die Aktionen des
   jeweiligen Bildschirms, und die Darstellung ist keine.
   ------------------------------------------------------------------------ */

.thema-schalter {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  padding: 0;
  cursor: pointer;
  color: var(--muted-60);
  background: transparent;
  border: 1px solid var(--color-divider);
}
.thema-schalter:hover {
  background: color-mix(in srgb, var(--color-text) 7%, transparent);
  color: var(--color-text);
}
/* `display: flex` aus der Klasse schlägt sonst das `[hidden]` des Browsers,
   und der Knopf stünde auch ohne Skript da — siehe `.fehler-text[hidden]`. */
.thema-schalter[hidden] { display: none; }

/* Alle drei Symbole stehen im Knopf, sichtbar ist das des geltenden
   Zustands. Damit setzt das Skript beim Umschalten nur ein Merkmal am
   <html> und tauscht kein SVG aus; und ohne Skript steht von vornherein
   das richtige da, weil der Server dasselbe Merkmal schon geschrieben hat. */
.thema-symbol { display: none; line-height: 0; }
:root[data-theme="system"] .thema-symbol.thema-system,
:root[data-theme="weiss"] .thema-symbol.thema-weiss,
:root[data-theme="dunkel"] .thema-symbol.thema-dunkel { display: block; }

/* Die Funktionstasten stehen rechtsbündig untereinander an der Trennlinie,
   der Zähler mit wenig Abstand davor, und die Ziffern wachsen nach vorne.

   1. Das Kürzel ist aus dem Fluss der Zeile genommen. Solange es ein
   Flex-Kind war, schob alles, was links davon breiter wurde, es nach außen:
   aus „1 offen“ wurde „123 offen“, und F11 stand 11 px weiter rechts als
   F10 darüber — bei drei Stellen schnitt die Leiste es sogar ab. Absolut
   gesetzt kann es das nicht mehr; es steht immer 6 px vor der Linie, in
   jeder Zeile gleich.

   2. `padding-right` hält seinen Platz frei — 36 px, so breit wie das
   Kürzel samt seinen 6 px Abstand zur Linie. Die Kurzform in
   `.nav-item.is-sub` setzt `padding` komplett und schlägt ein einzelnes
   `padding-right` — der Unterpunkt braucht die Regel deshalb eigens, sonst
   lief der Zähler in das Kürzel hinein. „Massenerfassung“ ist ein Wort und
   bricht nie; wird es einmal zu lang, ragt es in diesen reservierten Rand
   und nicht in das Kürzel.

   Der Wert stand einmal aus einem anderen Grund fest: aus
   210 − 1 Rahmen − 34 Einzug − 20 Symbol − 2 × 10 Abstand − 36 bleiben dem
   Beschriftungskasten 99 px, und „Meldung erfassen“ passt ab 100 px auf eine
   Zeile — die beiden Erfassungswege standen also mit **einem Pixel** Reserve
   zweizeilig. Das trug nicht: eine etwas schmaler ankommende Schrift, und
   der Eintrag fiel auf eine Zeile zurück, während seine Nachbarn zweizeilig
   blieben. Der Umbruch steht jetzt ausdrücklich an `[data-umbruch]` (siehe
   unten), und die 36 px begründen sich wieder allein aus dem Kürzel.

   3. Der Zähler hat feste Breite und steht rechtsbündig darin. Nur so
   endet er in jeder Zeile an derselben Kante — sonst stünde der Zähler der
   Meldungen 4 px neben dem der Rechnungen. 45 px sind „999 offen“ bei
   10 px Schrift; die Ziffern stehen ohnehin auf
   `font-variant-numeric: tabular-nums`, eine 1 ist also so breit wie eine 8.
   Die 10 px sind gerechnet, nicht geschmückt: bei 11 px misst er 49 px, und
   die Zeile „Rechnung erstellen“ liefe um genau diese 4 px über.

   `:not(:empty)` — der Zähler steht als leeres <span> in *jedem* Eintrag.
   Ohne die Einschränkung reservierten auch „Massenerfassung“ und
   „Buchungskonten“ 45 px, die sie nicht brauchen. Der negative Rand holt
   die letzten 6 px aus dem Abstand zur Beschriftung und wirkt ebenfalls nur
   in den zwei Zeilen mit Zähler; global geholt bekäme den Platz
   `.nav-label` — der Flex-Kasten, der den Rest der Zeile aufnimmt — und die
   zweizeiligen Einträge fielen auf eine Zeile zusammen.

   Nur für die senkrechte Leiste: unter 721 px ist sie eine waagrechte
   Reihe, dort ist das Kürzel ausgeblendet und ein reservierter Rand wäre
   nur ein Loch. */
@media (min-width: 721px) {
  .nav-item { position: relative; padding-right: 36px; }
  .nav-item.is-sub { padding-right: 36px; }

  .nav-key {
    position: absolute;
    right: 6px;
    top: 50%;
    transform: translateY(-50%);
  }

  .nav-badge:not(:empty) {
    min-width: 45px;
    margin-left: -6px;
    font-size: 10px;
    text-align: right;
  }

  /* Eingeklappt bleiben Kürzel und Zähler nur für Vorlesegeräte übrig, auf
     1 px zusammengezogen und weggeschnitten — dort darf nichts an den Rand
     geheftet und nichts wieder aufgezogen werden. */
  .app.leiste-zu .nav-key { right: auto; top: auto; transform: none; }
  .app.leiste-zu .nav-badge { min-width: 0; margin-left: 0; }

  /* Zweizeilig, weil es so gemeint ist — nicht, weil ein Pixel fehlt.

     Ein Wortabstand, der breiter ist als die ganze Leiste, lässt hinter dem
     ersten Wort nichts mehr übrig: der Zeilenumbruch fällt zwangsläufig auf
     die Lücke. Das gilt bei jeder Schrift und jeder Grundgröße, während eine
     ausgerechnete Kastenbreite nur bei genau der Schrift gilt, mit der sie
     ausgerechnet wurde. Über den Rand hinaus schiebt es nichts: ein
     Leerzeichen am Zeilenende zählt nicht zur Breite.

     Zwei Grenzen dafür. **Nur in der senkrechten Leiste** — unter 721 px ist
     sie eine waagrechte Reihe mit `white-space: nowrap`, dort fiele kein
     Umbruch, und der Abstand stünde als 210 px breites Loch mitten im Wort.
     Und **nicht eingeklappt**: dort ist die Beschriftung auf 1 px
     zusammengezogen und gehört den Vorlesegeräten. */
  .nav-item[data-umbruch] .nav-label { word-spacing: 210px; }
  .app.leiste-zu .nav-item[data-umbruch] .nav-label { word-spacing: normal; }
}

/* Ein Warnzeichen mitten im Text einer Tabellenzelle — heute neben dem
   „offen“ der Meldungsliste, wo der Betrag fehlt.

   `.symbol` ist ein Block mit 20 px Kantenlänge; unverändert eingesetzt
   fiele das Dreieck auf eine eigene Zeile unter das Wort und risse die
   Zeilenhöhe der ganzen Liste auf. Deshalb hier `inline-block`, auf
   Schriftgröße verkleinert und über `vertical-align` auf die Grundlinie
   gesetzt. Die Farbe kommt von `.val-err` daneben und wird nicht doppelt
   gesetzt — das Symbol zeichnet in `currentColor`. */
.warnzeichen { display: inline-block; vertical-align: -2px; }
.warnzeichen .symbol { width: 14px; height: 14px; display: inline-block; stroke-width: 1.8; }

/* --- Zeilenbalken, für Safari an der Zelle ------------------------------- */

/* `box-shadow` an einem `<tr>` bleibt in WebKit unsichtbar, sobald die
   Tabelle `border-collapse: collapse` benutzt — und das tun hier alle. In
   Chrome und Firefox war der Balken da, in Safari fehlte er ersatzlos. Er
   sitzt deshalb an der ersten Zelle der Zeile, die es überall gibt.

   Die senkrechte Trennlinie aus `.table tbody td` muss dabei mitgeschrieben
   werden: ein zweites `box-shadow` ersetzt das erste, es addiert sich nicht.
   Bei einer Zeile mit nur einer Zelle fiele sie weg, dafür `:not(:last-child)`.

   Die Farbe kommt über `--zeilenbalken` von der Zeile und wird geerbt; so
   genügt hier eine Regel für alle drei Zustände. */
tr.is-selected > td:first-child,
tr.is-error > td:first-child,
tr.is-warn > td:first-child {
  box-shadow: inset 3px 0 0 var(--zeilenbalken);
}
.table tbody tr.is-selected > td:first-child:not(:last-child),
.table tbody tr.is-error > td:first-child:not(:last-child),
.table tbody tr.is-warn > td:first-child:not(:last-child) {
  box-shadow: inset 3px 0 0 var(--zeilenbalken),
              inset -1px 0 0 color-mix(in srgb, var(--color-text) 6%, transparent);
}

/* --- Auswahl-Ankreuzfeld: die ganze Zelle ist der Klickbereich ---------- */

/* Das Kästchen selbst ist klein; ein Klick knapp daneben soll trotzdem
   treffen. svm.js toggelt es dann über die ganze Zelle — hier nur der
   Zeigercursor als sichtbarer Hinweis darauf. */
td:has(> input[type="checkbox"][name="wahl[]"]),
th:has(> input[type="checkbox"][data-auswahl-alle]) {
  cursor: pointer;
}

/* --- Rollen und Rechte -------------------------------------------------- */

/* Sechsundzwanzig Ankreuzfelder in acht Gruppen, je Rolle einmal. Als eine
   Spalte untereinander wäre der Bildschirm vier Bildschirmhöhen lang und
   nicht zu überblicken; nebeneinander stehen die Bereiche wie in der
   Seitenleiste.

   `auto-fill` mit `minmax`, dieselbe Bauart wie bei den gestapelten Listen:
   die Spurenzahl folgt der Breite, und alle Spuren sind gleich breit —
   sonst brächen die Gruppen an verschiedenen Stellen um und die Häkchen
   ständen nicht mehr untereinander. 280 px, weil die Erklärung darunter
   zwei Zeilen braucht und darunter unleserlich schmal würde.

   `align-items: start`: ohne das zöge jede Gruppe sich auf die Höhe der
   höchsten in ihrer Zeile, und unter „Auswertungen“ mit seinem einen Recht
   stünde ein Kasten voller Leere. */
.rechtegitter {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: var(--space-3);
  align-items: start;
  margin-top: var(--space-3);
}

.rechteblock {
  border: 1px solid var(--color-divider);
  padding: 10px 12px 12px;
  margin: 0;
  min-width: 0;
}
.rechteblock legend {
  font-size: 11px;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--muted-55);
  padding: 0 4px;
}

/* Kästchen links, Beschriftung daneben — und die Beschriftung ist zweizeilig:
   der Arbeitsschritt in Grundschrift, darunter, was er bedeutet.

   `align-items: start` am Kästchen: mittig ausgerichtet rutschte es bei
   einer dreizeiligen Erklärung in die Mitte des Absatzes und gehörte
   sichtbar zu nichts mehr. */
.rechtezeile {
  display: flex;
  align-items: start;
  gap: 8px;
  padding: 6px 0;
  border-top: 1px solid var(--color-divider);
}
.rechtezeile:first-of-type { border-top: 0; }
.rechtezeile input[type="checkbox"] { flex: none; margin-top: 2px; }
.rechtezeile label { display: block; min-width: 0; cursor: pointer; }
.rechtezeile .rechtetitel { display: block; font-size: 13px; }
.rechtezeile .muted { display: block; margin-top: 2px; }

/* --- Ampel: ein Rechteck statt eines Wortes -------------------------------
   „Gesendet: ja / offen / nein“ und „Zugang: frei / gesperrt“ waren gefärbte
   Wörter. Gelesen werden mussten sie trotzdem — und in einer Liste mit
   fünfzig Zeilen sucht man nicht nach einem Wort, sondern nach der Zeile,
   die aus der Reihe fällt. Eine Fläche sieht man beim Überfliegen.

   Ein **Quadrat**, kein Kreis: die Anwendung hat sonst keine runden Formen,
   und ein Punkt in einer Tabellenzelle sieht aus wie ein Aufzählungszeichen.
   Und kein liegendes Rechteck — das las sich als Balken einer Skala, als
   wäre die Fläche ein Anteil und nicht ein Zustand.

   Die Farbe kommt aus den Tokens und folgt damit dem Thema; im dunklen Satz
   sind die drei Werte heller und stehen auf dunklem Grund genauso deutlich
   da. Ein Rahmen ist deshalb nicht nötig.

   Der Klartext geht dabei nicht verloren — er steht im `title` und noch
   einmal in einem `.abseits`-Text darin (siehe `teile/ampel.php`). Deshalb
   `display: inline-block` und feste Maße: das eingeschlossene Wort ist
   weggeschnitten und darf die Fläche nicht aufziehen. */
.ampel {
  display: inline-block;
  width: 15px;
  height: 15px;
  border-radius: 2px;
  /* Auf die Mittellinie der Zeile gesetzt: ein Quadrat dieser Größe steht
     neben Text sonst sichtbar zu hoch. */
  vertical-align: -3px;
  /* Der weggeschnittene Text steht `position: absolute` — ohne diese Zeile
     bezöge er sich auf die Seite und zöge auf schmalen Bildschirmen eine
     waagrechte Rollleiste auf. */
  position: relative;
  overflow: hidden;
}
.ampel-ok { background: var(--color-ok); }
.ampel-warn { background: var(--color-warn); }
.ampel-err { background: var(--color-err); }

/* In der Legende steht dasselbe Quadrat vor seiner Erklärung. */
.legend .ampel { margin-right: 4px; }

/* **Mittig in der Spalte** — dort steht nichts als die Fläche, und
   linksbündig klebte sie am Rahmen der Nachbarspalte statt unter ihrer
   Überschrift zu stehen. Die Überschrift rückt mit, sonst stünden Wort und
   Fläche nebeneinander an verschiedenen Stellen.

   **Die Auswähler müssen `.table` nennen.** `.table thead th` setzt
   `text-align: left` und wiegt mit zwei Elementen und einer Klasse schwerer
   als ein `th.sp-gesendet` allein — die Überschrift blieb links stehen,
   während die Fläche darunter rückte. Bei Gleichstand gewinnt die spätere
   Regel, und diese hier steht am Dateiende.

   **Gestapelt zurückgenommen**: auf der Karte steht die Fläche unter ihrer
   Bezeichnung am linken Rand wie jeder andere Wert, und mittig in einer
   165 px breiten Spur schwämme sie ohne Bezug.

   **Die Rücknahme gehört in die `@media`-Regel, nicht an `.table-stapel`
   allein.** Die Klasse steht **immer** im Quelltext — sie kennzeichnet eine
   Tabelle, die stapeln *kann*, gestapelt wird erst unter 1440 px. An ihr
   festgemacht galt die Rücknahme deshalb bei jeder Fensterbreite, und die
   Fläche blieb links stehen. Für den Spaltenkopf braucht es dort nichts:
   gestapelt ist `thead` ohnehin `display: none`. */
.table td.ampel-zelle,
.table thead th.ampel-kopf { text-align: center; }

@media (max-width: 1440px) {
  .table.table-stapel td.ampel-zelle { text-align: left; }
}

/* --- Strichlierte Umrandung: dieselbe Form, ein anderer Bezug -------------
   Die Jahresübersicht im Kundenstammblatt zählt die Rechnungen **mit diesem
   Betrieb**. Beim eigenen Betrieb geht das nicht auf: er steht auf keiner
   Rechnung als Kunde, sondern als Aussteller — dort zählt sie deshalb den
   ganzen Bestand und ist die Übersicht des Hauses.

   Gleiche Spalten, gleiche Tabelle, anderer Bezug: genau der Fall, in dem
   man die Summe liest und für etwas anderes hält. Der strichlierte Rahmen
   sagt es, bevor die Überschrift gelesen ist — und ist derselbe Kasten,
   keine zweite Gestaltung.

   Nur die Kante wechselt, nicht die Farbe: eine zweite Farbe hieße
   „Warnung“, und es ist keine. */
.blueprint.gestrichelt { border-style: dashed; }

/* --- Benutzertabelle gestapelt --------------------------------------------
   Drei Dinge, die eine Karte von 165 px Spurbreite sonst sprengen. Alle drei
   stehen im `@media`-Block, weil `.table-stapel` **immer** im Quelltext
   steht: die Klasse kennzeichnet eine Tabelle, die stapeln *kann*.

   Die feste Grenze steht hier bei 1280 px — dieselbe Zahl wie das
   `data-stapel-ab` am Rahmen. Eine Media-Abfrage nimmt keine Variable;
   deshalb hängen beide Zahlen zusammen, und wer die eine ändert, ändert die
   andere mit. */
@media (max-width: 1280px) {
  /* **Die Zustandsfläche entfällt auf der Karte.** In der Tabelle ist sie
     die Abkürzung fürs Überfliegen von fünfzig Zeilen; auf einer Karte gibt
     es nichts zu überfliegen, und ein Quadrat allein in einer eigenen Spur,
     über sich nur den Platzhalter, sagt niemandem etwas. Was es bedeutet,
     steht ein paar Zeilen weiter unter „Zustand“ im Klartext — und dort
     ohnehin in derselben Farbe. */
  .table-benutzer.table-stapel td.ampel-zelle { display: none; }

  /* Eingabefeld und Auswahl tragen in der Tabelle feste Breiten (160 bzw.
     140 px), damit die Zeile nicht springt. Auf der Karte ist die Spur die
     Vorgabe — fest gesetzt ragte das Feld samt „setzen“ darüber hinaus. */
  .table-benutzer.table-stapel .sp-name .input,
  .table-benutzer.table-stapel .rollenwahl { width: 100%; min-width: 0; }

  /* `.zeilenform` ist ein Inline-Flex. Auf der Karte darf es die volle
     Breite nehmen, sonst steht das Feld auf halber Spur und der Knopf
     daneben im Nichts.

     **Nur außerhalb der Aktionsspalte.** Dort stecken drei solcher
     Formulare — entsperren, Faktor zurücksetzen, sperren —, und mit
     `width: 100%` nahm jedes eine eigene Zeile: vier Schaltflächen
     untereinander statt nebeneinander. Sie tragen kein Eingabefeld und
     brauchen die Breite nicht. */
  .table-benutzer.table-stapel td:not(.aktionen) .zeilenform {
    display: flex;
    width: 100%;
  }
  .table-benutzer.table-stapel .zeilenform .btn { flex: none; }

  /* In der Tabelle bricht die Aktionsspalte bewusst nicht um — sonst wird
     jede zweite Zeile doppelt so hoch. Auf der Karte steht sie über die
     volle Breite, und dort ist der Umbruch genau richtig: vier
     Schaltflächen nebeneinander passen auf kein Telefon. */
  .table-benutzer.table-stapel td.aktionen {
    white-space: normal;
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
  }

  /* Die aufgeklappte Passwortzeile ist eine eigene Karte über die volle
     Breite; ihr Formular stapelt wie jedes andere auf schmalem Schirm. */
  .table-benutzer.table-stapel tr.ausklapp .formbar { flex-wrap: wrap; }
  .table-benutzer.table-stapel tr.ausklapp .feld-xl { width: 100%; flex: 1 1 100%; }
}

/* --- Spaltenbreiten selbst einstellen -------------------------------------

   Der Griff am rechten Rand jeder Kopfzelle. Er entsteht in `svm.js` —
   ohne Skript gibt es ihn nicht, und die Listen stehen in ihren
   Vorgabebreiten da.

   Die Kopfzelle ist bei `.table-fest` ohnehin `position: sticky` und damit
   Bezugsrahmen für den Griff; die Regel hier setzt sie für die übrigen
   Fälle ausdrücklich auf `relative`, sonst hinge der Griff am nächsten
   Vorfahren mit Position — der Tabelle — und säße irgendwo.

   Er sitzt **innerhalb** der Zelle am rechten Rand, nicht über der
   Zellgrenze. Über die Grenze gelegt wäre er von beiden Seiten aus zu
   treffen und damit angenehmer — aber eine verstellte Tabelle bekommt aus
   dem erzeugten Stylesheet `overflow: hidden` an den Kopfzellen (sonst
   stünde eine schmal gezogene Überschrift quer über dem Nachbarn), und was
   hinausragt, wäre dann abgeschnitten. Neun Pixel innen sind breit genug.

   Sichtbar wird er erst beim Überfahren. Eine Liste mit vierzehn ständig
   sichtbaren Griffleisten im Kopf sähe aus wie ein Werkzeug, nicht wie eine
   Tabelle. */
/* **Nicht `.table thead th`.** Diese Regel wöge genau so schwer wie
   `.table-fest thead th { position: sticky }` weiter oben und stünde
   später — der feste Spaltenkopf wäre damit keiner mehr, und zwar in allen
   sechs Listen zugleich. Die Auswähler schließen einander deshalb aus. */
.table:not(.table-fest) thead th { position: relative; }

.spaltengriff {
  position: absolute;
  top: 0;
  right: 0;
  width: 9px;
  height: 100%;
  cursor: col-resize;
  /* Ohne das nimmt ein Zeigerzug am Griff die Auswahl des Kopftextes mit;
     `svm.js` ruft zwar `preventDefault()`, aber erst beim `pointerdown` —
     ein Doppelklick markiert vorher. */
  user-select: none;
  -webkit-user-select: none;
  touch-action: none;
}

/* Der Strich selbst: schmal, mittig im Griff, und nur beim Überfahren zu
   sehen. Über `::after`, damit die Trefferfläche breiter bleibt als das
   Bild — 3 px sind mit der Maus nicht zu treffen, 9 px schon. */
.spaltengriff::after {
  content: "";
  position: absolute;
  top: 4px;
  bottom: 4px;
  right: 1px;
  width: 3px;
  border-radius: 2px;
  background: transparent;
}
.spaltengriff:hover::after,
.spaltengriff:focus::after,
.spalten-ziehen .spaltengriff::after {
  background: var(--color-accent);
}
.spaltengriff:focus { outline: none; }

/* Während des Ziehens gilt der Zeiger für die ganze Seite: die Maus verlässt
   den 9 px schmalen Griff sofort, und ohne das flackerte der Zeiger
   zwischen Pfeil und Doppelpfeil. Dazu keine Textauswahl — sonst wird beim
   Ziehen die halbe Liste blau. */
.spalten-ziehen,
.spalten-ziehen * { cursor: col-resize !important; }
.spalten-ziehen { user-select: none; -webkit-user-select: none; }

/* Gestapelt ist der Spaltenkopf ausgeblendet und aus jeder Zeile eine Karte
   geworden — es gibt keine Spalten mehr, die sich verstellen ließen. Der
   Griff wäre dort ein Strich neben einer Bezeichnung. */
@media (max-width: 1440px) {
  .table-stapel .spaltengriff { display: none; }
}
