
  :root {
    /* Der Innenabstand eines Blocks (.panel, .kpi-card) und die Einrueckung
       der Kopfzeile und Bereichsleiste im Arbeitsbereich. Auf :root, weil die
       Blockregeln von ALLEN drei Huellen geteilt werden - Arbeitsbereich
       (.main), Kundenbereich (.kunde-main) und Plattform (.plattform-main).
       Bis zum 07.09.2026 stand die Variable auf .main: In den beiden anderen
       Huellen war sie unbekannt, die Angabe padding: 1.4rem var(--block-einzug)
       1.5rem wurde damit als GANZES ungueltig, und jeder Block klebte ohne
       Innenabstand am Rand (Befund des Auftraggebers, Leitlinie 5.9). Der
       Waechter test/seitenkonsistenz.test.js laesst das nicht mehr zu. */
    --block-einzug: 1.5rem;
    /* Die Hoehe des klebenden Seitenkopfs, in Kopfhoehe gemessen von
       oberflaeche.js (Leitlinie 5.30). Der Wert hier ist der Ausgangswert
       fuer die erste Zeichnung und der Rueckfall ohne JavaScript: Gemessen
       waren es am 29.09.2026 164 px, mehr mit einem Hinweis in der
       Titelzeile. Lieber etwas zu viel - zu wenig verdeckt das Sprungziel,
       zu viel laesst nur Luft darueber. */
    --kopf-hoehe: 11rem;
    --bg: #eef6f6;
    --bg-deep: #e1eff0;
    --blob-a: rgba(95, 197, 219, 0.30);
    --blob-b: rgba(248, 153, 33, 0.14);
    --surface: rgba(255, 255, 255, 0.62);
    --surface-solid: #ffffff;
    --surface-strong: rgba(255, 255, 255, 0.85);
    --border: rgba(1, 74, 80, 0.12);
    --border-strong: rgba(1, 74, 80, 0.2);
    --text: #12292b;
    --text-muted: #4c686b;
    --text-faint: #7c9598;
    --brand: #01919a;
    --brand-deep: #045a62;
    --brand-soft: #d3eef0;
    --cyan: #5fc5db;
    --accent: #f89921;
    --accent-deep: #c96f0a;
    --accent-soft: #fdecd2;
    --status-good-bg: #dcf3e6;
    --status-good-fg: #1f7a48;
    --status-info-bg: #d3eef0;
    --status-info-fg: #045a62;
    --status-critical-bg: #fbe1df;
    --status-critical-fg: #b23a2f;
    --shadow-color: 190, 40%, 30%;
    --sidebar-grad-top: #5fc5db;
    --sidebar-grad-bottom: #016d75;
    --sidebar-text: #eafbfc;
    --sidebar-text-muted: rgba(234, 251, 252, 0.68);
    --sidebar-active-bg: rgba(255, 255, 255, 0.16);
    --focus-ring: #01919a;
    /* Der gefuellte Knopf hat seit dem 08.09.2026 eine eigene Palette.
       Vorher lief er auf --cyan/--brand, und weisser Text stand am hellen
       Ende des Verlaufs mit 2,0:1 darauf - WCAG verlangt 4,5:1. Jetzt
       4,8:1 bis 10,0:1, und der Knopf hebt sich mit 4,4:1 vom Seitengrund
       ab (Befund und Entscheidung des Auftraggebers, Moeglichkeit A). */
    --knopf-von: #017f88;
    --knopf-bis: #024a52;
    --knopf-text: #ffffff;
  }

  @media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) {
      --bg: #0a1a1c;
      --bg-deep: #0d2226;
      --blob-a: rgba(95, 197, 219, 0.14);
      --blob-b: rgba(248, 153, 33, 0.10);
      --surface: rgba(19, 42, 46, 0.6);
      --surface-solid: #122a2d;
      --surface-strong: rgba(23, 50, 54, 0.9);
      --border: rgba(150, 210, 214, 0.14);
      --border-strong: rgba(150, 210, 214, 0.24);
      --text: #e7f5f5;
      --text-muted: #9fc0c2;
      --text-faint: #6f9295;
      --brand: #5fd3dd;
      --brand-deep: #bdeef2;
      --brand-soft: rgba(95, 211, 221, 0.14);
      --accent: #ffab52;
      --accent-deep: #ffcd94;
      --accent-soft: rgba(255, 171, 82, 0.14);
      --status-good-bg: rgba(58, 178, 118, 0.18);
      --status-good-fg: #7fe0ac;
      --status-info-bg: rgba(95, 211, 221, 0.16);
      --status-info-fg: #a6ecf1;
      --status-critical-bg: rgba(224, 96, 82, 0.2);
      --status-critical-fg: #ff9c8d;
      --shadow-color: 190, 60%, 2%;
      --sidebar-grad-top: #0f3438;
      --sidebar-grad-bottom: #04181a;
      --sidebar-text: #d9f2f3;
      --sidebar-text-muted: rgba(217, 242, 243, 0.6);
      --sidebar-active-bg: rgba(95, 211, 221, 0.14);
      --focus-ring: #5fd3dd;
      /* Umgekehrt zur hellen Fassung: helle Flaeche, dunkler Text. Derselbe
         dunkle Verlauf haette gegen den dunklen Blockgrund nur 1,5:1 - der
         Knopf waere kaum noch zu sehen. So: Text 9,4:1 bis 5,8:1, Knopf
         gegen den Seitengrund 6,2:1. */
      --knopf-von: #5fd3dd;
      --knopf-bis: #2ba7b4;
      --knopf-text: #062227;
    }
  }

  :root[data-theme="dark"] {
    --bg: #0a1a1c;
    --bg-deep: #0d2226;
    --blob-a: rgba(95, 197, 219, 0.14);
    --blob-b: rgba(248, 153, 33, 0.10);
    --surface: rgba(19, 42, 46, 0.6);
    --surface-solid: #122a2d;
    --surface-strong: rgba(23, 50, 54, 0.9);
    --border: rgba(150, 210, 214, 0.14);
    --border-strong: rgba(150, 210, 214, 0.24);
    --text: #e7f5f5;
    --text-muted: #9fc0c2;
    --text-faint: #6f9295;
    --brand: #5fd3dd;
    --brand-deep: #bdeef2;
    --brand-soft: rgba(95, 211, 221, 0.14);
    --accent: #ffab52;
    --accent-deep: #ffcd94;
    --accent-soft: rgba(255, 171, 82, 0.14);
    --status-good-bg: rgba(58, 178, 118, 0.18);
    --status-good-fg: #7fe0ac;
    --status-info-bg: rgba(95, 211, 221, 0.16);
    --status-info-fg: #a6ecf1;
    --status-critical-bg: rgba(224, 96, 82, 0.2);
    --status-critical-fg: #ff9c8d;
    --shadow-color: 190, 60%, 2%;
    --sidebar-grad-top: #0f3438;
    --sidebar-grad-bottom: #04181a;
    --sidebar-text: #d9f2f3;
    --sidebar-text-muted: rgba(217, 242, 243, 0.6);
    --sidebar-active-bg: rgba(95, 211, 221, 0.14);
    --focus-ring: #5fd3dd;
    /* Umgekehrt zur hellen Fassung: helle Flaeche, dunkler Text. Derselbe
       dunkle Verlauf haette gegen den dunklen Blockgrund nur 1,5:1 - der
       Knopf waere kaum noch zu sehen. So: Text 9,4:1 bis 5,8:1, Knopf
       gegen den Seitengrund 6,2:1. */
    --knopf-von: #5fd3dd;
    --knopf-bis: #2ba7b4;
    --knopf-text: #062227;
  }

  * { box-sizing: border-box; }

  body {
    margin: 0;
    background: var(--bg);
    color: var(--text);
    font-family: "Ruda", "Segoe UI", system-ui, sans-serif;
    -webkit-font-smoothing: antialiased;
  }

  a { color: inherit; }
  button, input, a { font-family: inherit; }
  a:focus-visible, button:focus-visible, .nav-item:focus-visible {
    outline: 2px solid var(--focus-ring);
    outline-offset: 2px;
  }

  /* ---------- Umgebungsband (views/umgebungsband.js) ---------- */
  /* Nur dev und demo tragen es, die Produktion nie. Es laeuft im Fluss ganz
     oben und bleibt beim Scrollen stehen - fixed haette es ueber den Inhalt
     gelegt, und ein Band, das die erste Zeile verdeckt, wird abgeschaltet.

     Die Hoehe steht als Variable auf :root mit 0rem und wird NUR gesetzt,
     wenn das Band wirklich da ist. So rechnen die Vollhoehen-Layouts in
     allen drei Huellen richtig, ohne dass eine von ihnen wissen muss, ob
     gerade ein Band existiert. */
  :root { --umgebungsband-hoehe: 0rem; }
  body:has(.umgebungsband) { --umgebungsband-hoehe: 1.7rem; }

  /* Dieselbe Bauweise fuer den Seitenkopf: Den klebenden Kopf hat bisher nur
     der Arbeitsbereich. Die Abstandsregel fuer Sprungziele gilt aber in allen
     drei Huellen (Leitlinie 5.9 Regel 9) - im Kundenbereich und in der
     Plattform waere ein Abstand in Kopfhoehe eine leere Wand ueber jedem
     Sprungziel. Wo kein Kopf klebt, gilt die alte Luft von 1.25rem.
     Ueber :has() statt ueber JavaScript, damit es auch ohne Skript stimmt. */
  body:not(:has(.seitenkopf)) { --kopf-hoehe: 1.25rem; }

  .umgebungsband {
    position: sticky; top: 0; z-index: 60;
    min-height: var(--umgebungsband-hoehe);
    padding: 0 0.6rem; text-align: center;
    display: flex; align-items: center; justify-content: center; gap: 0.5rem;
    font-size: 0.68rem; font-weight: 700; letter-spacing: 0.07em;
    text-transform: uppercase;
    color: #2a1c00; background: #f6c667;
    border-bottom: 1px solid rgba(0, 0, 0, 0.2);
  }
  .umgebungsband-punkt {
    width: 7px; height: 7px; border-radius: 50%;
    background: currentColor; opacity: 0.5; flex-shrink: 0;
  }
  /* Demo bekommt eine eigene Farbe: Zwei Umgebungen, die gleich aussehen,
     verwechselt man genauso wie zwei ohne Band. */
  .umgebungsband-demo { color: #062a33; background: #93dced; }
  .umgebungsband-unbekannt { color: #ffffff; background: #a8322a; }

  body:has(.umgebungsband) .app,
  body:has(.umgebungsband) .auth-page {
    min-height: calc(100vh - var(--umgebungsband-hoehe));
  }

  /* ---------- Auth page ---------- */

  .auth-page {
    min-height: 100vh;
    display: grid;
    place-items: center;
    padding: 1.5rem;
    position: relative;
    overflow: hidden;
    isolation: isolate;
  }

  .auth-page::before, .auth-page::after {
    content: "";
    position: absolute;
    z-index: -1;
    border-radius: 50%;
    filter: blur(80px);
  }
  .auth-page::before { width: 480px; height: 480px; top: -160px; right: -100px; background: var(--blob-a); }
  .auth-page::after { width: 420px; height: 420px; bottom: -160px; left: -80px; background: var(--blob-b); }

  .auth-card {
    width: 100%;
    max-width: 380px;
    background: var(--surface);
    backdrop-filter: blur(20px);
    -webkit-backdrop-filter: blur(20px);
    border: 1px solid var(--border);
    border-radius: 20px;
    padding: 2rem 1.9rem 1.75rem;
    box-shadow: 0 20px 50px -20px hsla(var(--shadow-color), 0.35);
  }

  .auth-brand {
    display: flex;
    align-items: center;
    gap: 0.65rem;
    margin-bottom: 1.6rem;
  }

  .auth-brand-mark {
    width: 38px;
    height: 38px;
    border-radius: 11px;
    background: linear-gradient(155deg, var(--knopf-von), var(--knopf-bis));
    display: grid;
    place-items: center;
    color: var(--knopf-text);
    flex-shrink: 0;
  }

  .auth-brand-name { font-weight: 900; font-size: 1.1rem; line-height: 1.1; }
  .auth-brand-sub { font-size: 0.72rem; color: var(--text-muted); letter-spacing: 0.04em; }

  .auth-card h1 {
    font-size: 1.15rem;
    font-weight: 700;
    margin: 0 0 0.3rem;
    text-wrap: balance;
  }

  .field { display: flex; flex-direction: column; gap: 0.35rem; margin-bottom: 1rem; }
  .field label { font-size: 0.78rem; font-weight: 700; color: var(--text-muted); }
  /* Ein Abschnitt in einem laengeren Formular (fremde Behaelter, Charge
     anlegen): Ueberschrift, ein Satz Erklaerung, dann die Felder. Bis zum
     05.09.2026 standen die Klassen in der Lagerplatz-Ansicht ohne Regel. */
  .form-section { margin-top: 1.25rem; padding-top: 1rem; border-top: 1px solid var(--border); }
  /* Steht der Abschnitt als Erstes im Panel (Kundenbereich "Meine
     Dokumente"), gibt es nichts, wovon er sich abtrennen muesste - die
     Linie stand dort als leerer Strich ueber der Ueberschrift (11.09.2026). */
  .panel > .form-section:first-child { margin-top: 0; padding-top: 0; border-top: none; }
  .form-section h3 { font-size: 0.92rem; margin: 0 0 0.25rem; }
  /* Ein zweispaltiges Formular ist EIN Raster, nicht zwei Spalten
     nebeneinander: Jedes Feld ist ein Rasterkind, das Raster bildet die
     Zeilen. Felder links und rechts stehen damit auf gleicher Hoehe - auch
     wenn eines davon einen Hinweis traegt oder eine Spalte mehr Felder hat.
     Zwei getrennte Spaltenkaesten (<div> links, <div> rechts) laufen
     dagegen nach der ersten Zeile auseinander (Bedientest 05.09., "die
     Eingabefelder sind nicht auf gleicher Hoehe"). Was beide Spalten
     einnehmen soll (Ueberschrift, Hinweis, Abschnitt), bekommt .span.
     Leitlinie 5.5. Auf dem Telefon einspaltig (unten). */
  .form-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0 1.5rem; align-items: start; }
  .form-grid > .span, .form-grid > h3, .form-grid > .form-section, .form-grid > .hint, .form-grid > .auth-error { grid-column: 1 / -1; }
  /* Eine Filterzeile ueber einer Liste: Suchfeld, Auswahl, Knopf in einer
     Reihe, auf dem Telefon untereinander. Angelegt am 01.10.2026 statt eines
     style-Attributs (Leitlinie 5.9 Regel 10) - das Vorbild in
     renderDokumenteListe schreibt die Abstaende noch von Hand, und genau
     deshalb laufen Regel und Handarbeit dort auseinander, sobald jemand
     den Abstand aendert. Das .field darin tragt keinen eigenen unteren
     Abstand: Die Zeile setzt ihn. */
  .filterzeile { display: flex; gap: 1rem; align-items: flex-end; flex-wrap: wrap; margin-bottom: 1.25rem; }
  .filterzeile > .field { margin: 0; }
  .filterzeile > .field.breit { flex: 1 1 18rem; }
  .filterzeile > a { align-self: center; font-size: 0.86rem; }
  .hint { margin: 0 0 0.85rem; font-size: 0.8rem; color: var(--text-muted); }
  .field .hint { margin: 0; font-size: 0.76rem; }
  /* Vorschau der gefuellten Fusszeile (Profil, Fallmail) - liest sich wie der
     Schluss der Mail, nicht wie ein Eingabefeld. */
  .fusszeile-vorschau { margin: 0.35rem 0 0; padding: 0.45rem 0.7rem; border-left: 3px solid var(--border-strong); white-space: pre-wrap; font-family: inherit; font-size: 0.8rem; color: var(--text-muted); }
  /* Eingabefeld, Auswahlliste und Textbereich teilen ALLE Aussenmasse.
     Vorher stand hier nur "input" - Auswahllisten fielen damit auf die
     Vorgaben des Browsers zurueck und standen sichtbar flacher als die Felder
     daneben, in einer anderen Schrift und mit einem anderen Rahmen.

     Dieselbe Sache, die der Kommentar bei .btn-primary/.btn-secondary
     beschreibt: Was nebeneinander steht, muss aus EINER Regel kommen. Sonst
     laeuft es auseinander, sobald jemand nur eines davon anfasst.

     Drei Punkte, die dafuer noetig sind und leicht vergessen werden:
       font-family  - eine Auswahlliste erbt die Seitenschrift NICHT, sie
                      nimmt sonst die Systemschrift
       appearance   - ohne sie ignorieren manche Browser die senkrechte
                      Polsterung, und die Hoehe stimmt trotz gleicher Angaben
                      nicht ueberein
       eigener Pfeil - der entfaellt mit appearance:none und muss ersetzt
                      werden, sonst sieht die Liste aus wie ein Eingabefeld
                      und niemand klickt sie an */
  input.suchfeld, select.auswahl,
  .field input:not([type="checkbox"]):not([type="radio"]),
  .field select,
  .field textarea {
    border: 1px solid var(--border-strong);
    background: var(--surface-solid);
    border-radius: 10px;
    padding: 0.65rem 0.8rem;
    font-size: 0.92rem;
    font-family: inherit;
    line-height: 1.35;
    color: var(--text);
    width: 100%;
    /* Eine feste Hoehe fuer alle einzeiligen Felder: Safari gibt Zahlen- und
       Datumsfeldern sonst eigene Innenmasse, und in einem zweispaltigen
       Formular stehen Text- und Zahlenfelder dann sichtbar verschieden hoch
       nebeneinander (Bedientest 05.09.). Rahmen und Polsterung sind in der
       Hoehe enthalten (box-sizing oben, global). */
    height: 2.75rem;
  }
  .field textarea { height: auto; min-height: 6rem; resize: vertical; }
  .field input[type="number"], .field input[type="date"] {
    -webkit-appearance: none;
    appearance: none;
  }

  select.auswahl,
  .field select {
    appearance: none;
    -webkit-appearance: none;
    /* Platz fuer den Pfeil, damit ein langer Eintrag nicht darunter laeuft. */
    padding-right: 2.1rem;
    cursor: pointer;
    /* Der Pfeil als eingebettete Grafik: keine zusaetzliche Anfrage, und er
       traegt currentColor - er folgt damit dem hellen wie dem dunklen Thema.
       Ein fest eingetragener Farbwert waere in einem der beiden falsch. */
    background-image: url("data:image/svg+xml;charset=UTF-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8' fill='none' stroke='%23808a8c' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M1 1.5 6 6.5 11 1.5'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 0.85rem center;
  }
  /* Mehrfachauswahl und aufgeklappte Listen brauchen keinen Pfeil und keine
     feste Hoehe - dort waere beides falsch. */
  .field select[multiple], .field select[size]:not([size="1"]) {
    background-image: none; padding-right: 0.8rem; cursor: default;
  }

  /* Kontrollkaestchen und Radios sind ausdruecklich ausgenommen: Sie stehen
     ebenfalls in einem .field (etwa "Abrechnung freischalten" in den
     Mandanteneinstellungen), haetten mit width:100% aber die volle
     Feldbreite und saehen mit Polsterung und Rahmen aus wie ein leeres
     Eingabefeld. Der Fokusrahmen gilt dagegen fuer alle - er ist eine
     Zugaenglichkeitsfrage, keine Gestaltungsfrage. */
  .field input:focus-visible,
  .field select:focus-visible,
  input.suchfeld:focus-visible, select.auswahl:focus-visible,
  .field textarea:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 1px; }

  /* Gemeinsame Aussenmasse beider Schaltflaechenarten. Vorher hatten sie
     unterschiedliche Polsterung (0.75/1 gegen 0.7/1.1), unterschiedliche
     Schriftgroesse (0.92 gegen 0.88), nur die eine einen Rahmen und nur die
     andere einen margin-top. Nebeneinander standen sie dadurch verschieden
     hoch UND vertikal versetzt. Alles Massgebliche steht deshalb jetzt an
     EINER Stelle; die Klassen darunter unterscheiden sich nur in der Farbe.
     Der Rahmen ist bei beiden da - bei der primaeren durchsichtig, damit sie
     ohne sichtbaren Rahmen exakt gleich hoch bleibt. */
  .btn-primary, .btn-secondary {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.4rem;
    padding: 0.7rem 1.1rem;
    border: 1px solid transparent;
    border-radius: 10px;
    font-family: inherit;
    font-size: 0.9rem;
    font-weight: 700;
    /* Fest gesetzt: Ohne line-height berechnen Safari und Chrome die Hoehe
       von <button> und <a> unterschiedlich - genau der Effekt, der im Safari
       auf dem Mac auffiel. */
    line-height: 1.25;
    text-decoration: none;
    cursor: pointer;
    white-space: nowrap;
  }

  .btn-primary {
    width: 100%;
    background: linear-gradient(135deg, var(--knopf-von), var(--knopf-bis));
    color: var(--knopf-text);
  }
  /* Der gefuellte Knopf ist der einzige, der sein Zeigen nicht ueber die
     Umrandung sagen konnte: filter: brightness(1.04) aendert den Ton um
     1,08:1 - nachgemessen am 08.09.2026 und damit unsichtbar, in der dunklen
     Fassung erst recht (Befund des Auftraggebers). Jetzt bekommt er densel-
     ben Rand wie .btn-secondary, nur aussen: --brand-deep steht in der hellen
     Fassung dunkel und in der dunklen hell und hebt sich deshalb in BEIDEN
     Fassungen vom Seitengrund ab (7,3:1 hell, 12,8:1 dunkel). */
  .btn-primary:hover { filter: brightness(1.06); box-shadow: 0 0 0 2px var(--knopf-text), 0 0 0 4px var(--brand-deep); }
  /* Dasselbe fuer das Profilzeichen - auch ein gefuellter, heller Knopf. */
  .avatar:hover { box-shadow: 0 0 0 2px var(--knopf-text), 0 0 0 4px var(--brand-deep); }

  /* Nebeneinander ist volle Breite immer falsch. Das stand bisher als
     style="width:auto" an 24 Stellen quer durch die Views - eine
     Nachbesserung, die man vergessen kann und die genau dann fehlt, wenn
     eine neue Seite dazukommt. */
  .form-actions .btn-primary,
  .form-actions .btn-secondary,
  .panel-head .btn-primary,
  .panel-head .btn-secondary { width: auto; }

  .hinweis-gut { margin: 0 0 0.85rem; padding: 0.6rem 0.8rem; border-radius: 8px; background: var(--status-good-bg); color: var(--status-good-fg); font-size: 0.86rem; }
  .auth-error {
    background: var(--status-critical-bg);
    color: var(--status-critical-fg);
    border-radius: 10px;
    padding: 0.6rem 0.8rem;
    font-size: 0.83rem;
    font-weight: 600;
    margin-bottom: 1rem;
  }

  .auth-hint {
    margin-top: 1.3rem;
    font-size: 0.76rem;
    color: var(--text-faint);
    line-height: 1.5;
  }

  /* ---------- App shell ---------- */

  /* gap: 0 ist Absicht, keine Vergesslichkeit: Seitenleiste und Hauptbereich
     stossen aneinander, die Trennung macht die Farbe (Leitlinie 5.9).  */
  .app { display: grid; grid-template-columns: minmax(220px, 260px) 1fr; min-height: 100vh; gap: 0; }

  .sidebar {
    background: linear-gradient(190deg, var(--sidebar-grad-top) 0%, var(--sidebar-grad-bottom) 85%);
    color: var(--sidebar-text);
    padding: 1.75rem 1.25rem 1.5rem;
    display: flex;
    flex-direction: column;
    gap: 2.25rem;
  }

  .brand { display: flex; align-items: center; gap: 0.65rem; padding: 0 0.25rem; }
  .brand-mark {
    width: 34px; height: 34px; border-radius: 10px;
    background: rgba(255, 255, 255, 0.16);
    display: grid; place-items: center; flex-shrink: 0;
  }
  .brand-name { font-weight: 900; font-size: 1.05rem; letter-spacing: 0.01em; line-height: 1.1; }
  .brand-sub { font-size: 0.7rem; color: var(--sidebar-text-muted); letter-spacing: 0.06em; text-transform: uppercase; }

  nav.primary-nav { display: flex; flex-direction: column; gap: 0.2rem; }
  .nav-label { font-size: 0.68rem; text-transform: uppercase; letter-spacing: 0.09em; color: var(--sidebar-text-muted); padding: 0.4rem 0.75rem 0.5rem; }

  .nav-item {
    display: flex; align-items: center; gap: 0.7rem;
    padding: 0.6rem 0.75rem; border-radius: 10px;
    color: var(--sidebar-text-muted); text-decoration: none;
    font-size: 0.9rem; font-weight: 700;
    transition: background 0.15s ease, color 0.15s ease;
  }
  .nav-item svg { flex-shrink: 0; opacity: 0.9; }
  .nav-item:hover { background: rgba(255, 255, 255, 0.1); color: var(--sidebar-text); }
  .nav-item.active { background: var(--sidebar-active-bg); color: var(--sidebar-text); box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.14); }
  /* Ein nicht gebuchtes Modul (konzept-plattform-abrechnung.md 4.4): bleibt
     sichtbar - der Weg fuehrt zur Buchung -, aber erkennbar gedaempft. */
  .nav-item.nicht-gebucht { opacity: 0.55; }
  .nav-item.nicht-gebucht::after { content: "✦"; margin-left: auto; font-size: 0.7rem; opacity: 0.8; }

  .sidebar-foot {
    margin-top: auto;
    padding: 0.9rem 0.9rem;
    border-radius: 12px;
    background: rgba(255, 255, 255, 0.08);
    font-size: 0.78rem;
    color: var(--sidebar-text-muted);
    line-height: 1.45;
  }
  .sidebar-foot strong { color: var(--sidebar-text); font-weight: 700; }

  /* min-width: 0 ist hier kein Schoenheitsfehler, sondern die Korrektur eines
     echten Fehlverhaltens: Als Grid-Element hat .main von Haus aus
     min-width:auto und kann damit nicht schmaler werden als sein breitester
     Inhalt. Eine breite Tabelle schob die Spalte ueber den Bildschirmrand
     hinaus - und weil overflow:hidden (noetig fuer die Farbschleier unten)
     das Ueberstehende abschneidet, liess es sich auch nicht heranscrollen.
     Auf dem iPhone quer war der rechte Teil damit unerreichbar. Mit
     min-width:0 bleibt .main in der Breite des Bildschirms, und .table-wrap
     scrollt wie vorgesehen in sich selbst. */
  /* Der Innenabstand der Bloecke, an einer Stelle - Panels und Kacheln
     benutzen denselben Wert, damit ihre Inhalte untereinander fluchten.
     Die Bezugslinie der SEITE ist dagegen die Kante der Bloecke, nicht ihr
     Text: Ueberschrift, Bereichsleiste und Blockrand beginnen alle am
     Seitenrand (Leitlinie 5.9, Abschnitt "Flucht").  */
  /* overflow: BEIDE Zeilen, und die Reihenfolge ist der ganze Punkt
     (29.09.2026). "hidden" macht aus .main einen Rollbereich - und ein
     klebendes Element klebt immer am naechsten Rollbereich. Der von .main
     rollt nie (er ist so hoch wie sein Inhalt), also klebte der Seitenkopf
     nirgends: gemessen topbar bei -306 px statt 0. "clip" schneidet genauso
     ab, macht aber KEINEN Rollbereich - dann ist das Fenster der naechste,
     und der Kopf klebt. Die "hidden"-Zeile bleibt als Rueckfall fuer
     Browser ohne "clip" (Safari vor 16): dort klebt der Kopf nicht, aber
     die breite Tafel bricht auch nicht aus (siehe .content-grid > *). */
  .main { position: relative; min-width: 0; padding: 2.25rem clamp(1.25rem, 4vw, 3rem) 3rem; overflow: hidden; overflow: clip; isolation: isolate; }
  .main::before, .main::after { content: ""; position: absolute; z-index: -1; border-radius: 50%; filter: blur(70px); pointer-events: none; }
  .main::before { width: 520px; height: 520px; top: -180px; right: -120px; background: var(--blob-a); }
  .main::after { width: 420px; height: 420px; bottom: -160px; left: 8%; background: var(--blob-b); }

  /* --- Der Seitenkopf bleibt stehen (Leitlinie 5.30) -------------------
     29.09.2026, Gregor ueber das Laborbuch: "dann kommt eine neue Seite, die
     keinen Kopfbereich hat ... haette ich erwartet, dass der Kopfbereich
     immer bleibt". Der Kopf fehlte nicht - die Seite war gesprungen: Der
     Verweis aus der Trefferliste traegt #erfassen, das Ziel lag 529 px
     unten, und damit war der Kopf (63 bis 227 px) aus dem Bild.

     Gegen einen Sprung hilft kein Abstand. scroll-padding-top verschiebt nur,
     WO das Ziel landet - der Kopf rollt trotzdem mit weg, weil er im Fluss
     steht. Entweder er klebt, oder er ist nach jedem Sprung fort; ein
     dritter Weg wurde gesucht und gibt es nicht.

     Die seitlichen Werte spiegeln das Polster von .main: Ohne sie deckt der
     Kopf nur die Textbreite, und der Inhalt rollt links und rechts an ihm
     vorbei. Negativer Rand nach aussen, gleich grosses Polster nach innen. */
  .seitenkopf {
    /* Unter das Umgebungsband, nicht dahinter: Das Band klebt selbst
       (sticky, top 0, z-index 60). Bei top: 0 lag der Titel darunter und war
       oben abgeschnitten - gemessen h1 bei 16 px, Bandunterkante bei 27 px.
       --umgebungsband-hoehe ist 0rem, wo kein Band ist (Produktion), und
       1.7rem auf dev und demo; die Zeile stimmt also in beiden Faellen. */
    position: sticky; top: var(--umgebungsband-hoehe); z-index: 40;
    margin: -2.25rem calc(-1 * clamp(1.25rem, 4vw, 3rem)) 0;
    padding: 1rem clamp(1.25rem, 4vw, 3rem) 0.35rem;
    background: var(--bg);
    border-bottom: 1px solid transparent;
  }
  /* Der Kopf deckt IMMER, nicht erst beim Rollen. Ein erster Entwurf liess
     ihn oben durchsichtig und schaltete die Farbe per Klasse zu, sobald man
     rollt - damit haette er die Farbschleier von .main::before/::after nicht
     verdeckt. Gemessen war die Klasse im Moment des Sprungs aber noch nicht
     gesetzt (das Rollereignis kommt einen Tick spaeter), und genau dann rollt
     der Inhalt sichtbar durch den Kopf. Eine Zustandsklasse, die einen Tick
     zu spaet kommt, taugt nicht fuer etwas, das Text verdecken muss.
     Die Klasse bleibt - sie zieht nur noch die Trennlinie. */
  .seitenkopf.klebt { border-bottom-color: var(--border); }

  /* Auf niedrigen Fenstern kostet der Kopf zu viel: gemessen 164 von 757 px
     (22 %). Unter 720 px Fensterhoehe rollt er wieder mit - dann ist der
     Platz fuer den Inhalt wichtiger als die Orientierung. */
  @media (max-height: 719px) {
    .seitenkopf { position: static; background: transparent; border-bottom-color: transparent;
      margin: 0; padding: 0; }
  }

  .topbar { display: flex; align-items: center; justify-content: space-between; gap: 1.5rem; margin-bottom: 2rem; flex-wrap: wrap; }

  /* Bereichsleiste unter der Kopfzeile. Sie kommt aus der Huelle und steht
     damit auf JEDER Seite des Bereichs (lib/untermenue.js, Leitlinie 5.10);
     bis zum 06.09.2026 baute sie jede Ansicht selbst - mit denselben Farben
     und Abstaenden, aber eben nicht ueberall.  */
  .unternav { display: flex; flex-wrap: wrap; gap: 1.5rem; font-size: 0.86rem; font-weight: 700; margin: -1.25rem 0 1.5rem; }
  .unternav a { color: var(--text-muted); text-decoration: none; border-bottom: 2px solid transparent; padding-bottom: 0.3rem; }
  .unternav a:hover { color: var(--text); }
  .unternav a.aktiv { color: var(--brand-deep); border-bottom-color: var(--brand); }
  /* Die zweite Leiste (24.09.2026): direkt unter der ersten, eine Stufe
     leiser - die erste zeigt den Bereich, die zweite die Sicht darin. */
  .unternav.hat-ebene2 { margin-bottom: 0.6rem; }
  .unternav.ebene2 { margin: 0 0 1.5rem; gap: 1rem; font-size: 0.82rem; font-weight: 600; padding-left: 0.1rem; }
  .unternav.ebene2 a { border-bottom-width: 2px; padding-bottom: 0.2rem; }
  /* Im Kundenbereich stehen Reiter unter dem Seitentitel, nicht in der Kopfzeile. */
  .kunde-main .unternav { margin: 0 0 1.25rem; }
  /* Korrekturhistorie am Progesteronwert (20260906230000): ein kleines Symbol,
     der Hinweis steht im title - Ueberfahren zeigt ihn. */
  .korrektur-marke { display: inline-block; margin-left: 0.25rem; font-size: 0.8em; color: var(--brand-deep); cursor: help; border-bottom: 1px dotted currentColor; }
  /* Legende unter einer Kurve mit mehreren Reihen (Kundenuebersicht). */
  .kurven-legende { list-style: none; margin: 0.75rem 0 0; padding: 0; display: flex; flex-wrap: wrap; gap: 0.5rem 1.5rem; font-size: 0.86rem; }
  .kurven-legende li { display: flex; align-items: center; gap: 0.45rem; }
  .kurven-legende .kurven-farbe { width: 0.9rem; height: 0.9rem; border-radius: 50%; flex: none; }

  /* Der Inhaltsbereich einer Seite. Seine Bloecke - meist mehrere .panel -
     stossen nicht aneinander, sondern haben einen Abstand aus EINER Regel.
     Vorher hatte jede Seite selbst dafuer zu sorgen, und wer wie die
     Benutzerverwaltung drei Panels untereinander ausgab, bekam keinen
     (Leitlinie 5.9).  */
  .seiteninhalt { display: flex; flex-direction: column; gap: 1.5rem; }

  /* Warum ein Block nie schmaler gemacht wird als seine Nachbarn:
     Der Grund, aus dem man das tut, ist fast immer ein Eingabefeld, das
     sonst ueber die halbe Seite laeuft. Dann gehoert die Grenze an das FELD,
     nicht an den Block - sonst stehen Kaesten verschiedener Breite
     untereinander, und die Seite wirkt zusammengewuerfelt (Befund
     /plattform, 06.09.2026, Leitlinie 5.9). Textfelder bleiben ausgenommen:
     Wer einen Text schreibt, will den Platz.  */
  .field > input, .field > select { max-width: 34rem; }
  /* Der Titelblock darf schrumpfen (Untertitel bricht um), die Werkzeuge
     rechts bleiben in der Zeile: Bis zum 07.09.2026 rutschten Umschalter,
     Thema und Abmelden auf Fallblatt, Posteingang und Bestand unter den
     Titel, auf Kunden und Charge nicht - je nach Laenge des Untertitels
     (Code-Review, Leitlinie 5.2).  */
  .greeting { flex: 1 1 0; min-width: 16rem; }
  .greeting h1 { font-weight: 900; font-size: clamp(1.4rem, 2.2vw, 1.85rem); margin: 0 0 0.2rem; text-wrap: balance; }
  /* Die Unterzeile hat IMMER die Hoehe von zwei Zeilen (24.09.2026, Gregor:
     "mal zweizeilig, mal einzeilig ... der Contentbereich springt"): Der
     Inhalt beginnt auf jeder Seite an derselben Stelle. Laengeres wird nach
     zwei Zeilen abgeschnitten - eine Unterzeile ist ein Satz, kein Absatz
     (Leitlinie 5.26, Waechter test/unterzeilen.test.js). */
  .greeting p { margin: 0; color: var(--text-muted); font-size: 0.92rem; line-height: 1.4; min-height: 2.8em;
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }

  .topbar-tools {
    display: flex; align-items: center; gap: 0.9rem;
    flex: 0 1 auto; margin-left: auto; max-width: 100%; flex-wrap: wrap;
  }

  /* --- Farbschema-Umschalter ------------------------------------------
     Drei Zustaende statt zwei: Sonne, Mond und "A" fuer automatisch. Der
     dritte ist der wichtigste - ohne ihn kann man eine einmal getroffene
     Wahl nie wieder an das Geraet zurueckgeben, und wer morgens hell und
     abends dunkel arbeitet, muesste zweimal taeglich von Hand schalten.
     Der Schieber ist reine Zier: Die Zustaende sind Schaltflaechen mit
     aria-pressed, damit auch ohne Blick auf die Farbe erkennbar ist,
     welcher gerade gilt. */
  .thema-schalter {
    position: relative;
    display: inline-flex;
    align-items: center;
    padding: 3px;
    border-radius: 999px;
    background: var(--surface-strong);
    border: 1px solid var(--border);
    gap: 0;
  }

  /* Die ersten beiden Abschnitte tragen ein Sinnbild und sind quadratisch,
     der dritte traegt das Wort "Auto" und ist entsprechend breiter. Feste
     Breiten, weil der Schieber darunter sie kennen muss. */
  .thema-schalter button {
    position: relative; z-index: 1;
    width: 30px; height: 26px;
    display: grid; place-items: center;
    border: none; background: none; cursor: pointer;
    border-radius: 999px;
    color: var(--text-faint);
    font: 700 0.72rem/1 inherit;
    transition: color 0.15s ease;
  }
  .thema-schalter button[data-thema="auto"] { width: 46px; letter-spacing: 0.02em; }
  .thema-schalter button:hover { color: var(--text-muted); }
  .thema-schalter button[aria-pressed="true"] { color: var(--brand-deep); }
  .thema-schalter svg { display: block; }

  /* Der gleitende Reiter. Er liegt hinter den Schaltflaechen und wird ueber
     data-aktiv am Behaelter verschoben - eine Angabe, kein Rechenweg. */
  .thema-schieber {
    position: absolute; top: 3px; left: 3px;
    width: 30px; height: 26px;
    border-radius: 999px;
    background: var(--brand-soft);
    box-shadow: 0 1px 2px hsl(var(--shadow-color) / 0.18);
    transition: transform 0.18s ease, width 0.18s ease;
    pointer-events: none;
  }
  .thema-schalter[data-aktiv="hell"]   .thema-schieber { transform: translateX(0);    width: 30px; }
  .thema-schalter[data-aktiv="dunkel"] .thema-schieber { transform: translateX(30px); width: 30px; }
  .thema-schalter[data-aktiv="auto"]   .thema-schieber { transform: translateX(60px); width: 46px; }

  /* --- Menueklappe (nur schmale Geraete) ------------------------------- */
  .menue-klappe {
    display: none;
    width: 38px; height: 38px;
    border-radius: 10px;
    border: 1px solid rgba(255, 255, 255, 0.22);
    background: rgba(255, 255, 255, 0.12);
    color: var(--sidebar-text);
    cursor: pointer;
    place-items: center;
    flex-shrink: 0;
  }
  .menue-klappe:hover { background: rgba(255, 255, 255, 0.2); }

  .role-chip {
    font-size: 0.72rem; font-weight: 700; padding: 0.4rem 0.75rem;
    border-radius: 999px; background: var(--brand-soft); color: var(--brand-deep);
    white-space: nowrap;
  }

  .avatar {
    width: 40px; height: 40px; border-radius: 12px;
    background: linear-gradient(155deg, var(--knopf-von), var(--knopf-bis));
    color: var(--knopf-text); display: grid; place-items: center;
    font-weight: 700; font-size: 0.85rem; text-decoration: none;
  }

  .logout-form { display: flex; margin: 0; }
  .logout-form button {
    display: grid; place-items: center; width: 40px; height: 40px;
    border-radius: 12px; background: var(--surface);
    backdrop-filter: blur(16px); -webkit-backdrop-filter: blur(16px);
    border: 1px solid var(--border); color: var(--brand-deep); cursor: pointer;
    padding: 0; margin: 0; line-height: 1;
  }
  .logout-form button:hover { color: var(--accent-deep); }

  /* ---------- KPI + panels (dashboard content) ---------- */

  .kpi-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 1rem; margin-bottom: 1.75rem; }
  .kpi-card {
    background: var(--surface); backdrop-filter: blur(18px); -webkit-backdrop-filter: blur(18px);
    border: 1px solid var(--border); border-radius: 16px; padding: 1.1rem var(--block-einzug);
    display: flex; flex-direction: column; gap: 0.55rem;
    box-shadow: 0 1px 2px hsla(var(--shadow-color), 0.06);
    transition: transform 0.18s ease, box-shadow 0.18s ease;
  }
  .kpi-card:hover { transform: translateY(-2px); box-shadow: 0 12px 24px -12px hsla(var(--shadow-color), 0.28); }
  .kpi-top { display: flex; align-items: center; justify-content: space-between; gap: 0.75rem; }
  .kpi-icon { width: 34px; height: 34px; border-radius: 10px; display: grid; place-items: center; flex: none; }
  .kpi-icon.brand { background: var(--brand-soft); color: var(--brand-deep); }
  .kpi-icon.accent { background: var(--accent-soft); color: var(--accent-deep); }
  .kpi-icon.good { background: var(--status-good-bg); color: var(--status-good-fg); }
  .kpi-icon.critical { background: var(--status-critical-bg); color: var(--status-critical-fg); }
  /* Der Hinweis unter dem Label ist ein SATZ, keine Kennzahl - deshalb die
     Textschrift und nicht die Ziffernschrift der Kachel. Monospace bleibt
     dem Wert vorbehalten, wo sie ihren Zweck hat: gleich breite Ziffern
     (Leitlinie 5.9). Er steht auf -0.15rem, weil das Label ueber ihm
     dieselbe Aussage einleitet und beide als ein Block lesen sollen.  */
  .kpi-hinweis { font-size: 0.76rem; line-height: 1.4; color: var(--text-muted); margin-top: -0.15rem; }
  .kpi-hinweis.warn { color: var(--status-critical-fg); }
  .kpi-value { font-family: "IBM Plex Mono", monospace; font-weight: 600; font-size: 1.9rem; font-variant-numeric: tabular-nums; line-height: 1; }
  .kpi-label { font-size: 0.82rem; color: var(--text-muted); }

  .content-grid { display: grid; grid-template-columns: 1.35fr 1fr; gap: 1.25rem; align-items: start; }
  /* Rasterkinder und Panels duerfen unter ihre Inhaltsbreite schrumpfen.
     Ohne min-width:0 nimmt ein Rasterkind die Breite seines breitesten
     Inhalts - eine Tafel mit 33 Fallspalten machte das Panel 8000 px breit,
     .main schneidet ab, und die Kaestchenzeile "Zeilen waehlen" lief rechts
     aus dem Bild, ohne dass man dorthin scrollen konnte (Gregor, 24.09.2026
     nachts). Mit min-width:0 bleibt das Panel so breit wie die Seite, und
     der breite Inhalt scrollt in seinem eigenen Kasten (.tafel-wrap,
     .table-wrap; Leitlinie 5.28). Bis heute stand die Regel nur in der
     Telefonbreite. */
  .content-grid > * { min-width: 0; }
  .panel {
    min-width: 0;
    background: var(--surface); backdrop-filter: blur(18px); -webkit-backdrop-filter: blur(18px);
    border: 1px solid var(--border); border-radius: 18px; padding: 1.4rem var(--block-einzug) 1.5rem;
    box-shadow: 0 1px 2px hsla(var(--shadow-color), 0.06);
  }
  /* Sprungziele: ein wenig Luft ueber dem Abschnitt, damit er nach dem Sprung
     nicht buendig am oberen Rand klebt und man sieht, dass darueber noch etwas
     ist. Ohne diese Zeile wirkt der Sprung wie ein abgeschnittener
     Seitenanfang.

     Bis zum 29.09.2026 stand hier .panel[id] - und traf damit 43 von 51
     Sprungzielen. Die uebrigen acht sind <form class="form-section"> oder
     tragen gar keine Klasse, darunter #erfassen im Laborbuch, das den Anlass
     gab. Ein Waechter haelt das jetzt nach (test/sprungziele.test.js).

     Der Wert deckt zusaetzlich den klebenden Seitenkopf ab: Ohne ihn landet
     das Ziel UNTER dem Kopf, also unsichtbar - der Sprung waere schlimmer
     als vorher. --kopf-hoehe misst oberflaeche.js; der Rueckfall von 11rem
     traegt den ueblichen Kopf auch ohne JavaScript.

     Formularfelder stehen mit in der Liste, weil der Browser auch beim
     Weitertasten rollt: Ohne sie verschwindet das gerade gewaehlte Feld
     eines langen Formulars unter dem Kopf. */
  :where([id], input, select, textarea, button, summary) {
    scroll-margin-top: calc(var(--umgebungsband-hoehe) + var(--kopf-hoehe) + 0.75rem);
  }
  /* Klebt der Kopf nicht (niedriges Fenster, Druck), genuegt die alte Luft. */
  @media (max-height: 719px), print {
    :where([id], input, select, textarea, button, summary) { scroll-margin-top: 1.25rem; }
  }

  /* Der Kopf einer Kachel traegt links Titel und Beschreibung, rechts oft
     einen Knopf ("Neue Charge"). Auf einem 320 Pixel breiten Schirm passt
     beides nicht nebeneinander - dann rutscht der Knopf unter den Text,
     statt aus der Kachel zu laufen (Leitlinie 5.20). */
  .panel-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 1rem; margin-bottom: 1.1rem; flex-wrap: wrap; }
  .panel-head > div:first-child { min-width: 0; flex: 1 1 12rem; }
  .panel-head h2 { margin: 0 0 0.15rem; font-weight: 700; font-size: 1.05rem; }
  .panel-head p { margin: 0; font-size: 0.82rem; color: var(--text-muted); }

  .chip {
    display: inline-flex; align-items: center; gap: 0.35rem; padding: 0.3rem 0.65rem;
    border-radius: 999px; font-size: 0.72rem; font-weight: 700; letter-spacing: 0.01em;
    /* EIGENE Zeilenhoehe, damit der Chip nicht mitwaechst, wenn die Umgebung
       eine groessere hat. Genau das ist am 10.09.2026 passiert: Um die
       Schriften in Tabellenzeilen auf eine Linie zu bringen, bekam
       table.stock td line-height:1.65 - und der Chip erbte sie, wurde von
       23,6 auf 28,6 px hoch und stand wieder genauso schief wie vorher.
       Ein Etikett hat eine feste Groesse; sie folgt seiner eigenen Schrift. */
    line-height: 1.2;
    /* Ein Etikett soll nicht mitten im Wort umbrechen - aber es darf auch
       nicht breiter werden als sein Platz. Ohne diese beiden Zeilen schob
       ein Chip mit Satzlaenge die Seite auf (iPhone, 09.09.2026). */
    white-space: nowrap; max-width: 100%; overflow-wrap: anywhere;
  }
  .chip::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: currentColor; }

  /* Ein KENNZEICHEN an einem Wert - nicht zu verwechseln mit .chip.
   *
   * Der Unterschied ist nicht kosmetisch: .chip ist ein Statusanzeiger und
   * steht allein in seiner Zelle ("Aktiv", "Abgelöst"); sein Punkt gehoert
   * zur Statusreihe. Ein Kennzeichen steht NEBEN Text in derselben Zelle
   * ("gregor@… Sie"). Dort ist der Punkt sinnlos, und inline-flex mit
   * align-items:center reisst es aus der Grundlinie des Textes daneben -
   * genau das war am 03.09. in der Benutzerliste zu sehen.
   *
   * inline-block mit vertical-align:baseline richtet die Grundlinie des
   * INHALTS aus, nicht die Unterkante des Kastens. Das Polster verschiebt
   * deshalb nur den Rahmen, und die Schrift bleibt auf der Linie. */
  .merkmal {
    display: inline-block; vertical-align: baseline; margin-left: 0.5rem;
    padding: 0.1em 0.5em; border-radius: 999px; line-height: 1.5;
    background: var(--brand-soft); color: var(--brand-deep);
    font-size: 0.72rem; font-weight: 600; letter-spacing: 0.01em; white-space: nowrap;
  }
  .chip.good { background: var(--status-good-bg); color: var(--status-good-fg); }
  .chip.info { background: var(--status-info-bg); color: var(--status-info-fg); }
  .chip.critical { background: var(--status-critical-bg); color: var(--status-critical-fg); }

  /* --- Eine Zelle, die mehr traegt als einen Wert -----------------------
     Angelegt 10.09.2026 nach dem Befund des Auftraggebers zur
     Rechnungsliste: "Kraut und Rueben, Abstaende der bunten Statusfelder
     inkonsistent."

     Er hatte recht, und die Ursache war ueberall dieselbe: Die Teile einer
     Zelle standen mit <br> untereinander, dazu einzelne margin-top an
     manchen von ihnen. Ein <br> erzeugt eine ZEILENHOEHE (rund 1.5em), ein
     margin-top von 0.3rem etwa ein Fuenftel davon - nebeneinander sieht das
     aus wie ein Versehen, und es ist auch eins.

     Der Stapel gibt jeder Zelle denselben Rhythmus: Etiketten oben,
     Erlaeuterung darunter, Bedienung zuletzt. Der Abstand kommt aus dem
     gap - EINE Zahl statt zehn verstreuter.  */
  .zellenstapel { display: flex; flex-direction: column; align-items: flex-start; gap: 0.4rem; }
  .zellenstapel > * { margin: 0 !important; }
  /* Mehrere Etiketten nebeneinander, solange Platz ist. Sie gehoeren
     zusammen (sie beschreiben denselben Beleg) und sollen deshalb nicht
     denselben Abstand haben wie die Bloecke darunter. */
  .etiketten { display: flex; flex-wrap: wrap; gap: 0.3rem; }

  /* Ein MANGEL ist kein Status: Er sagt nicht, wo der Beleg steht, sondern
     was ihm fehlt. Zusammengeklappt, damit er die Zeile nicht aufblaest -
     wer ihn braucht, klappt ihn auf. Der Pfeil der Vorgabe entfaellt; das
     Etikett selbst ist der Griff. */
  /* flex, damit Etikett und Pfeil auf einer Linie stehen: Ein <span class=chip>
     ist inline-flex mit max-width:100% - der Pfeil dahinter rutschte in eine
     eigene Zeile und sah aus wie ein Fehler (10.09.2026, erster Blick auf die
     ueberarbeitete Liste). An einem <summary> ist display unbedenklich; nur
     an einer Tabellenzelle waere es falsch (test/tabellenlayout.test.js). */
  .mangel > summary { display: flex; align-items: center; cursor: pointer; list-style: none; }
  .mangel > summary::-webkit-details-marker { display: none; }
  .mangel > summary::after {
    content: "";
    display: inline-block; width: 0.4rem; height: 0.4rem; margin-left: 0.45rem;
    border-right: 1.5px solid currentColor; border-bottom: 1.5px solid currentColor;
    transform: rotate(45deg) translate(-0.1rem, -0.1rem);
    opacity: 0.55;
  }
  .mangel[open] > summary::after { transform: rotate(-135deg) translate(-0.05rem, -0.05rem); }
  .mangel ul { margin: 0.4rem 0 0 1rem; padding: 0; font-size: 0.78rem; color: var(--text-muted); }
  .mangel ul li + li { margin-top: 0.15rem; }
  .mangel > div { margin-top: 0.4rem; font-size: 0.78rem; }

  .chart-legend { display: flex; gap: 1.1rem; flex-wrap: wrap; margin-top: 0.75rem; font-size: 0.78rem; color: var(--text-muted); }
  .chart-legend span { display: inline-flex; align-items: center; gap: 0.4rem; }
  .legend-swatch { width: 14px; height: 3px; border-radius: 2px; display: inline-block; }
  .legend-swatch.solid { background: var(--brand); }
  .legend-swatch.dashed { background: repeating-linear-gradient(90deg, var(--brand) 0 5px, transparent 5px 9px); }
  .legend-swatch.band { background: var(--accent); opacity: 0.55; height: 10px; border-radius: 3px; }
  svg.chart text { font-family: "IBM Plex Mono", monospace; fill: var(--text-faint); font-size: 10px; }

  table.stock { width: 100%; border-collapse: collapse; font-size: 0.86rem; }
  table.stock th {
    text-align: left; font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.06em;
    color: var(--text-faint); font-weight: 700; padding: 0 0.6rem 0.6rem; border-bottom: 1px solid var(--border);
  }
  /* OBEN, nicht mittig. Befund des Auftraggebers am 10.09.2026: "Koenntest Du
     die farbigen Felder so anordnen, dass diese bei allen Spalten immer auf
     gleicher Hoehe sind, so dass es nicht so unruhig aussieht?"
     
     vertical-align:middle war die Vorgabe, und sie ist bei Datentabellen die
     falsche, sobald Zellen verschieden hoch werden koennen: Eine Zelle mit
     drei Etiketten und die daneben mit einem zentrieren beide ihren Inhalt in
     derselben Zeilenhoehe - und landen dabei auf verschiedenen Hoehen. Das
     Auge liest eine Tabelle zeilenweise; ohne gemeinsame Startlinie sucht es
     in jeder Spalte neu.
     
     Mit top steht der jeweils ERSTE (und damit wichtigste) Wert jeder Zelle
     auf einer Linie mit der Rechnungsnummer. Bei einzeiligen Zellen aendert
     sich nichts - dort ist oben gleich mitte. */
  table.stock td {
    padding: 0.7rem 0.6rem; border-bottom: 1px solid var(--border); vertical-align: top;
    /* Die Zeilenhoehe ist an die HOEHE EINES CHIPS angeglichen - gemessen,
       nicht geschaetzt: Bei line-height:normal ist die Textzeile 16,5 px hoch,
       ein .chip 23,6 px. Beide Kaesten beginnen mit vertical-align:top an
       derselben Kante, ihre SCHRIFTEN sassen damit 3,5 px auseinander - genug,
       um als "unruhig" aufzufallen, zu wenig, um es zu benennen.
       1.65 x 13,76 px = 22,7 px; die Schriftmitten liegen jetzt einen halben
       Pixel auseinander. Am Chip zu schrauben waere der falsche Weg gewesen:
       Er ist ueberall gleich, die Zeilenhoehe gilt nur in Tabellen. */
    line-height: 1.65;
  }
  /* Erste und letzte Spalte buendig: sonst beginnt die Tabelle 0,6rem weiter
     rechts als die Ueberschrift darueber, und die Flucht im Block bricht
     (Leitlinie 5.9).  */
  table.stock th:first-child, table.stock td:first-child { padding-left: 0; }
  table.stock th:last-child, table.stock td:last-child { padding-right: 0; }
  table.stock tbody tr { transition: background 0.15s ease; }
  table.stock tbody tr:hover { background: var(--surface-strong); }

  /* Die letzte Zeile schliesst ohne Strich ab - sonst laege eine Linie
     unmittelbar ueber dem Rand des Panels.

     WICHTIG ist das "th" hier. Die Regel darueber gibt jedem th einen
     Rahmen; gedacht war sie fuer die KOPFzeile. In den Detailtabellen steht
     die Beschriftung aber als th IN der Zeile - und dort blieb der Rahmen
     stehen, waehrend das td daneben ihn verlor. Sichtbar war das als halber
     Strich, der genau an der Spaltengrenze endet und die letzte Zeile in der
     Luft haengen laesst. Vom Auftraggeber am 2026-09-03 bemerkt. */
  table.stock tbody tr:last-child td,
  table.stock tbody tr:last-child th { border-bottom: none; }

  /* Detailtabellen (Beschriftung links, Wert rechts) schliessen dagegen MIT
     Strich ab. Anders als eine Liste stehen sie mitten im Panel und haben
     unter sich meist noch eine Schaltflaeche oder ein Formular - ohne
     Abschluss liefe der letzte Wert optisch in das naechste Element.

     Eigene Klasse statt einer Erkennung ueber :has(th): ausdruecklich,
     ueberall gleich zu lesen und pruefbar. */
  table.stock.detail tbody tr:last-child td,
  table.stock.detail tbody tr:last-child th { border-bottom: 1px solid var(--border); }
  table.stock.detail tbody th {
    /* Beschriftungen brechen nicht um und behalten ihre Breite. */
    white-space: nowrap; vertical-align: middle; padding: 0.7rem 1rem 0.7rem 0.6rem;
  }
  table.stock.detail tbody tr:hover { background: transparent; }
  /* --- Kopierfeld ---------------------------------------------------
     Ein Wert, den jemand woandershin uebertragen muss. Der Wert bleibt
     markierbarer Text; der Knopf ist eine Zugabe. */
  .kopierfeld {
    display: flex; align-items: flex-start; gap: 0.5rem;
    background: var(--surface-solid); border: 1px solid var(--border-strong);
    border-radius: 8px; padding: 0.55rem 0.6rem 0.55rem 0.75rem;
  }
  .kopierfeld code {
    flex: 1; font-family: "IBM Plex Mono", monospace; font-size: 0.82rem;
    line-height: 1.5; color: var(--text);
    /* Lange Werte umbrechen statt die Seite breit zu machen. anywhere, weil
       eine URL ohne Leerzeichen sonst gar nicht bricht. */
    overflow-wrap: anywhere; word-break: break-word;
  }
  .kopierfeld.einzeilig code { white-space: nowrap; overflow-x: auto; }

  .kopierknopf {
    flex: none; position: relative; display: inline-flex; align-items: center; justify-content: center;
    width: 1.9rem; height: 1.9rem; padding: 0; cursor: pointer;
    background: transparent; border: 1px solid var(--border-strong); border-radius: 6px;
    color: var(--text-muted); transition: color .15s ease, border-color .15s ease, background .15s ease;
  }
  .kopierknopf:hover { color: var(--brand-deep); border-color: var(--brand); background: var(--surface-strong); }
  .kopierknopf:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 1px; }

  /* Die Rueckmeldung sitzt AM Knopf und nicht als Meldung oben auf der Seite:
     Wer gerade geklickt hat, sieht dorthin. */
  .kopiert-hinweis {
    position: absolute; right: calc(100% + 0.4rem); top: 50%; transform: translateY(-50%);
    white-space: nowrap; font-size: 0.72rem; font-weight: 700; letter-spacing: 0.02em;
    opacity: 0; pointer-events: none; transition: opacity .15s ease;
  }
  .kopierknopf.hat-kopiert { color: #146c43; border-color: #146c43; }
  .kopierknopf.hat-kopiert .kopiert-hinweis { opacity: 1; color: #146c43; }
  .kopierknopf.hat-kopiert .kopiert-hinweis::after { content: attr(data-kopiert); }

  /* Nicht gelungen heisst hier NICHT "kaputt": Die Zwischenablage steht nur
     in einem sicheren Kontext zur Verfuegung. Der Wert ist dann markiert und
     der Hinweis sagt, was zu tun ist. */
  .kopierknopf.kopieren-gescheitert { color: #b45309; border-color: #b45309; }
  .kopierknopf.kopieren-gescheitert .kopiert-hinweis {
    opacity: 1; color: #b45309; font-size: 0.7rem;
  }
  .kopierknopf.kopieren-gescheitert .kopiert-hinweis::after { content: attr(data-gescheitert); }

  .dog-name { font-weight: 700; }
  .dog-breed { color: var(--text-muted); font-size: 0.78rem; }
  td.num { font-family: "IBM Plex Mono", monospace; font-variant-numeric: tabular-nums; text-align: right; }
  /* Der Export-Knopf sitzt ueber der Tabelle, rechtsbuendig - dort, wo man
     ihn sucht, und ohne die Kopfzeile der Tabelle zu stoeren. Er wird per
     Skript eingesetzt (public/tabellen-export.js), damit ihn jede Liste
     bekommt, auch die, die ihr <table> noch von Hand schreiben. */
  /* Der Export fragt, was gemeint ist (public/tabellen-export.js): die
     Ansicht, die Treffer des Filters oder alles. Ein <dialog> bringt
     Fokusfang und Escape mit; die Wahl sind Knoepfe, keine Radiofelder mit
     Bestaetigen - das ist ein Klick weniger fuer dieselbe Entscheidung.  */
  .export-dialog {
    border: 1px solid var(--border); border-radius: 14px; padding: 1.25rem;
    max-width: 26rem; width: calc(100% - 2rem);
    background: var(--surface-solid); color: var(--text);
    box-shadow: 0 24px 48px -24px hsla(var(--shadow-color), 0.45);
  }
  .export-dialog::backdrop { background: hsla(var(--shadow-color), 0.35); }
  .export-dialog h2 { margin: 0 0 0.25rem; font-size: 1.05rem; }
  .export-dialog > p { margin: 0 0 1rem; font-size: 0.86rem; color: var(--text-muted); }
  .export-wahl {
    display: flex; flex-direction: column; gap: 0.2rem; width: 100%; text-align: left;
    border: 1px solid var(--border); border-radius: 10px; padding: 0.7rem 0.85rem;
    margin-bottom: 0.5rem; background: var(--surface); color: inherit; cursor: pointer;
    font: inherit;
  }
  .export-wahl:hover { border-color: var(--brand); background: var(--surface-strong); }
  .export-wahl strong { font-size: 0.92rem; }
  .export-wahl span { font-size: 0.78rem; color: var(--text-muted); }
  .export-abbrechen { display: block; margin: 0.25rem 0 0 auto; }
  .csv-leiste { display: flex; justify-content: flex-end; margin: 0 0 0.5rem; }

  .csv-export {
    font-size: 0.72rem; font-weight: 700; letter-spacing: 0.04em;
    padding: 0.35rem 0.7rem; border-radius: 8px;
    border: 1px solid var(--border-strong);
    background: var(--surface-strong); color: var(--text-muted);
    cursor: pointer;
    transition: color 0.15s ease, border-color 0.15s ease;
  }
  .csv-export:hover { color: var(--brand-deep); border-color: var(--brand); }

  /* Waagerecht scrollbar - mit sichtbarem Hinweis darauf.
     Ohne den Hinweis sieht eine abgeschnittene Tabelle auf dem Telefon aus
     wie ein Anzeigefehler: Man sieht, dass rechts etwas fehlt, aber nichts
     laedt zum Wischen ein. Genau so wurde es am 02.09. gemeldet.

     Die vier Verlaufsschichten sind der bekannte Kniff dafuer, ganz ohne
     Skript: Die beiden "local"-Schichten scrollen mit dem Inhalt mit und
     decken den Schatten ab, sobald man am jeweiligen Ende angekommen ist -
     der Schatten erscheint also nur, solange es dort wirklich noch etwas
     zu sehen gibt. */
  .table-wrap {
    overflow-x: auto;
    /* Der Rahmen scrollt nur dann, wenn ihn jemand begrenzt. Ohne max-width
       waechst er mit seinem Inhalt, schiebt die Kachel auf und am Ende die
       ganze Seite - auf dem iPhone lief der Kopfbereich deshalb aus dem Bild
       (Befund 09.09.2026). min-width:0 gehoert dazu, damit er auch als Flex-
       oder Rasterkind unter seine Inhaltsbreite darf. */
    max-width: 100%; min-width: 0;
    background:
      linear-gradient(to right, var(--surface-solid) 40%, transparent) left center / 28px 100% no-repeat local,
      linear-gradient(to left,  var(--surface-solid) 40%, transparent) right center / 28px 100% no-repeat local,
      radial-gradient(farthest-side at 0 50%, hsl(var(--shadow-color) / 0.22), transparent) left center / 14px 100% no-repeat scroll,
      radial-gradient(farthest-side at 100% 50%, hsl(var(--shadow-color) / 0.22), transparent) right center / 14px 100% no-repeat scroll;
  }
  table.stock th[hidden], table.stock td[hidden] { display: none; }
  table.stock th[data-spalte] { cursor: grab; user-select: none; }
  table.stock th[data-spalte]:hover { color: var(--brand-deep); }
  table.stock th.wird-gezogen { opacity: 0.4; }

  .spalten-steuerung { position: relative; display: inline-block; }
  .spalten-toggle {
    display: inline-flex; align-items: center; gap: 0.4rem; padding: 0.4rem 0.75rem;
    border-radius: 999px; border: 1px solid var(--border-strong); background: var(--surface-solid);
    color: var(--text-muted); font-size: 0.78rem; font-weight: 600; cursor: pointer;
  }
  .spalten-toggle:hover { color: var(--brand-deep); border-color: var(--brand); }
  .spalten-panel {
    position: absolute; right: 0; top: calc(100% + 0.4rem); z-index: 20; width: 15rem;
    background: var(--surface-solid); border: 1px solid var(--border-strong); border-radius: 0.75rem;
    padding: 0.75rem; box-shadow: 0 12px 28px hsla(var(--shadow-color), 0.22);
    display: flex; flex-direction: column; gap: 0.4rem;
  }
  /* Das Attribut hidden verliert gegen display:flex - die Vorgabe des
     Browsers ist nicht !important. Ohne diese Regel standen beide
     Auswahlfenster (Dashboard anpassen, Spalten) beim Laden offen
     (Bedientest 05.09.). */
  .spalten-panel[hidden] { display: none; }
  .spalten-panel-hint { margin: 0 0 0.2rem; font-size: 0.72rem; color: var(--text-faint); }
  .spalten-panel-item { display: flex; align-items: center; gap: 0.5rem; font-size: 0.82rem; cursor: pointer; }
  .spalten-reset {
    margin-top: 0.3rem; padding: 0.4rem 0.6rem; border-radius: 0.5rem; border: 1px dashed var(--border-strong);
    background: transparent; color: var(--text-muted); font-size: 0.78rem; cursor: pointer; text-align: left;
  }
  .spalten-reset:hover { color: var(--accent-deep); border-color: var(--accent); }

  .dashboard-toolbar { display: flex; justify-content: flex-end; margin-bottom: 0.9rem; }
  .dashboard-grid {
    display: grid;
    /* min(340px, 100%) statt 340px: Bei "auto-fit" ist die Mindestbreite
       eine HARTE Untergrenze - auf einem iPhone (390px abzueglich Raender)
       war die Spalte damit breiter als der Schirm, und die Seite lief nach
       rechts aus dem Bild (Befund 09.09.2026). */
    grid-template-columns: repeat(auto-fit, minmax(min(340px, 100%), 1fr));
    gap: 1.25rem; align-items: start;
  }
  /* Ein Rasterkind kann ohne min-width:0 nicht unter seine Inhaltsbreite
     schrumpfen. Eine Tabelle darin schiebt es sonst auf, statt in ihrem
     eigenen .table-wrap zu scrollen - dann laeuft die ganze Seite. */
  .dashboard-grid > * { min-width: 0; }
  /* Nur fuer Bildschirmleser: sichtbar fuer sie, unsichtbar fuer alle
     anderen. display:none oder visibility:hidden waeren falsch - die
     nehmen den Text auch aus der Vorlesung heraus. Deshalb der Umweg ueber
     ein Feld von einem Pixel ausserhalb des sichtbaren Bereichs
     (08.09.2026, Leitlinie 5.17). */
  .nur-fuer-vorleser {
    position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0;
  }
  /* Ein Knopf, den nur die Tastatur sieht.
     Formulare, die sich bei Aenderung selbst abschicken (der
     Mandantenumschalter, Filterleisten), brauchen fuer die Maus keinen Knopf -
     fuer die Tastatur schon: Wer mit den Pfeiltasten waehlt, muss die Wahl
     ausdruecklich bestaetigen koennen, und er muss SEHEN, womit
     (Leitlinie 5.18, 08.09.2026). Versteckt wie .nur-fuer-vorleser, aber beim
     Fokussieren springt er an seinen Platz zurueck. */
  .nur-bei-fokus {
    position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0;
  }
  .nur-bei-fokus:focus, .nur-bei-fokus:focus-visible {
    position: static; width: auto; height: auto; margin: 0 0 0 .4rem; padding: .3rem .7rem;
    overflow: visible; clip-path: none; border: 1px solid var(--border-strong);
    border-radius: 7px; background: var(--surface-strong); color: var(--text);
    font: inherit; font-size: .82rem; cursor: pointer;
    outline: 2px solid var(--focus-ring); outline-offset: 2px;
  }
  /* Die Sprungmarke: der erste Tabstopp jeder Huelle. Weggeklappt wie
     .nur-fuer-vorleser, aber beim Fokussieren springt sie sichtbar oben links
     an den Rand - wer sie mit der Maus nie sieht, verliert nichts, wer
     tabbt, spart 12 Stationen (Befund 08.09.2026, Leitlinie 5.18). */
  .sprungmarke {
    position: absolute; left: -9999px; top: 0; z-index: 200;
    width: 1px; height: 1px; overflow: hidden;
  }
  .sprungmarke:focus, .sprungmarke:focus-visible {
    left: .6rem; top: .6rem; width: auto; height: auto; overflow: visible;
    padding: .5rem .9rem; border-radius: 8px; border: 1px solid var(--border-strong);
    background: var(--surface); color: var(--text); font-weight: 700; text-decoration: none;
    outline: 2px solid var(--focus-ring); outline-offset: 2px;
    box-shadow: 0 6px 20px rgba(0,0,0,.18);
  }
  /* Das Sprungziel selbst darf keinen Fokusrahmen um den halben Bildschirm
     ziehen - es bekommt den Fokus nur, damit die naechste Tabulatortaste im
     Inhalt weitergeht. */
  #hauptinhalt:focus { outline: none; }

  /* ---------- Kommandopalette (Strg/Cmd+K, public/tastatur.js) ---------- */
  /* Ein <dialog>: Fokusfang, Hintergrundsperre und Escape kommen vom Browser,
     nicht aus nachgebautem Skript. */
  .palette::backdrop { background: rgba(15, 30, 35, .45); }
  .palette {
    width: min(34rem, calc(100vw - 2rem)); max-height: min(26rem, 80vh);
    margin: 12vh auto auto; padding: 0; border: 1px solid var(--border-strong);
    border-radius: 14px; background: var(--surface); color: var(--text);
    box-shadow: 0 24px 60px rgba(0,0,0,.28); overflow: hidden;
  }
  .palette-kopf { display: flex; align-items: center; gap: .6rem; padding: .7rem .8rem; border-bottom: 1px solid var(--border); }
  .palette-feld {
    flex: 1; min-width: 0; padding: .5rem .6rem; font: inherit; font-size: .95rem;
    border: 1px solid var(--border-strong); border-radius: 8px;
    background: var(--surface-strong); color: var(--text);
  }
  .palette-feld:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 1px; }
  .palette-liste { list-style: none; margin: 0; padding: .3rem; overflow-y: auto; max-height: 20rem; }
  .palette-option {
    display: flex; align-items: center; justify-content: space-between; gap: .8rem;
    padding: .45rem .6rem; border-radius: 8px; cursor: pointer; font-size: .9rem;
  }
  .palette-option .palette-ziel { color: var(--text-faint); font-size: .78rem; }
  .palette-option[aria-selected="true"] { background: var(--brand-soft); }
  .palette-leer { margin: 0; padding: .8rem; color: var(--text-muted); font-size: .88rem; }

  /* Mitteilungen der Plattform (20260908220000). Ein Band ueber dem Inhalt,
     das man wegklickt - kein Kasten, der zwischen den Bloecken untergeht. */
  .mitteilung {
    display: flex; align-items: flex-start; justify-content: space-between; gap: 1rem;
    margin: 0 0 1.25rem; padding: .8rem 1rem; border-radius: 10px;
    border: 1px solid var(--border-strong); background: var(--surface-strong); color: var(--text);
  }
  .mitteilung p { margin: .25rem 0 0; font-size: .88rem; color: var(--text-muted); }
  .mitteilung form { margin: 0; }
  .mitteilung-warnung { border-color: var(--status-critical-fg); background: var(--status-critical-bg); }
  .mitteilung-wartung { border-color: var(--accent); background: var(--accent-soft); }

  /* Der Kasten, in dem etwas VOR dem Absenden zu sehen ist (Vorschau einer
     Mitteilung an alle Mandanten, 08.09.2026). Abstand kommt vom
     .form-section, nicht von hier - Leitlinie 5.9. */
  .vorschau-kasten {
    border: 1px solid var(--brand); background: var(--brand-soft);
    border-radius: 10px; padding: 1rem;
  }

  /* Der Weg zur Kommandopalette in der Kopfleiste (08.09.2026). Sieht aus
     wie ein Suchfeld, ist aber ein Knopf: Getippt wird in der Palette. Auf
     schmalen Bildschirmen bleibt nur die Lupe - der Text und das Kuerzel
     sind dort weder noetig noch bedienbar. */
  .palette-knopf {
    display: inline-flex; align-items: center; gap: .45rem;
    padding: .35rem .6rem; border-radius: 999px;
    border: 1px solid var(--border-strong); background: var(--surface-strong);
    color: var(--text-muted); font: inherit; font-size: .82rem; cursor: pointer;
  }
  .palette-knopf:hover { color: var(--text); border-color: var(--brand); }
  .palette-knopf:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }
  .palette-knopf kbd {
    font-family: "IBM Plex Mono", monospace; font-size: .7rem; color: var(--text-faint);
    border: 1px solid var(--border); border-radius: 5px; padding: .05rem .3rem;
  }
  @media (max-width: 60rem) {
    .palette-knopf-text, .palette-knopf kbd { display: none; }
    .palette-knopf { padding: .35rem; }
  }

  .dashboard-panel-wrap { position: relative; }
  .dashboard-panel-wrap.full { grid-column: 1 / -1; }
  .dashboard-panel-wrap[hidden] { display: none; }
  /* Der Griff faengt das Ziehen selbst ab (public/listen-spalten.js). Damit
     Safari nicht sein eigenes Elementziehen startet - dabei klebte die
     Kachelueberschrift an der Maus (Auftraggeber, 07.09.2026) - darf an ihm
     nichts markierbar und nichts nativ ziehbar sein. */
  /* Der Griff liegt ueber dem Panel - deshalb muss der Panelkopf ihm Platz
     lassen. Ohne das stand er je nach Inhalt dicht an der Ueberschrift oder
     ueber einem Zaehler-Chip (Befund des Auftraggebers, 08.09.2026). Der
     reservierte Streifen ist so breit wie der Griff plus sein Abstand, damit
     es unabhaengig vom Inhalt immer gleich aussieht. */
  .dashboard-panel-wrap > .panel > .panel-head,
  .dashboard-panel-wrap > .panel > h2:first-child,
  .dashboard-panel-wrap > section > h2:first-child { padding-right: 2.6rem; }
  .dashboard-drag-handle {
    position: absolute; top: 0.6rem; right: 0.6rem; z-index: 5; cursor: grab;
    user-select: none; -webkit-user-select: none; -webkit-user-drag: none; touch-action: none;
    width: 1.6rem; height: 1.6rem; display: flex; align-items: center; justify-content: center;
    border-radius: 0.4rem; color: var(--text-faint); opacity: 0.5; transition: opacity 0.15s ease, color 0.15s ease;
    /* Als <button>: die Browservorgaben abraeumen, damit er aussieht wie zuvor. */
    background: none; border: none; padding: 0; font: inherit;
  }
  /* Mit der Tastatur ist der Griff nicht "beim Zeigen" da, sondern beim
     Fokussieren - sonst sieht man nicht, worauf man steht. */
  .dashboard-drag-handle:focus-visible {
    opacity: 1; color: var(--brand-deep); background: var(--surface-strong);
    outline: 2px solid var(--focus-ring); outline-offset: 2px;
  }
  .dashboard-panel-wrap:hover .dashboard-drag-handle { opacity: 1; }
  .dashboard-drag-handle:hover { color: var(--brand-deep); background: var(--surface-strong); }
  .dashboard-panel-wrap.wird-gezogen { opacity: 0.4; outline: 2px dashed var(--brand); outline-offset: 4px; }
  /* Waehrend des Ziehens (Zeigerereignisse, 05.09.): nichts markieren,
     der Griff bleibt sichtbar, der Zeiger zeigt "greift". */
  body.zieht { user-select: none; -webkit-user-select: none; cursor: grabbing; }
  body.zieht .dashboard-drag-handle { opacity: 1; }
  thead th.wird-gezogen { opacity: 0.5; }
  .panel.full { grid-column: 1 / -1; }

  .placeholder-panel {
    display: flex; flex-direction: column; gap: 0.5rem;
    align-items: flex-start; padding: 3rem 1.5rem;
  }
  .placeholder-panel h2 { margin: 0; font-weight: 700; font-size: 1.2rem; }
  .placeholder-panel p { margin: 0; color: var(--text-muted); font-size: 0.9rem; }

  /* ---------- 2FA / Profil ---------- */

  .btn-secondary {
    border-color: var(--border-strong);
    background: var(--surface-solid);
    color: var(--text);
  }
  .btn-secondary:hover { border-color: var(--accent); color: var(--accent-deep); }

  .btn-link {
    border: none; background: none; padding: 0; margin: 0;
    color: var(--text-muted); font-size: 0.82rem; font-weight: 600;
    cursor: pointer; text-decoration: underline;
  }
  .btn-link:hover { color: var(--brand-deep); }

  /* stretch statt center: So sind die Schaltflaechen zwingend gleich hoch,
     auch wenn eine Beschriftung einmal umbrechen sollte. */
  .form-actions { display: flex; align-items: center; gap: 0.75rem; margin-top: 0.5rem; flex-wrap: wrap; }
  /* Ein Satz, der eine Schaltflaeche erklaert, steht UNTER der Knopfzeile - und
     .hint hat kein margin-top, weil es sonst unter einem Eingabefeld sitzt, das
     seinen Abstand schon mitbringt. Ohne diese Regel klebt der Satz am Knopf
     (Gregor am Bild, 28.09.2026: "Ist der Abstand des Textes unter dem Button
     nicht viel zu klein?"). 0.5rem statt 0.75rem, weil der Satz zum Knopf
     gehoert und nicht von ihm getrennt werden soll - Leitlinie 5.9 Regel 2:
     Abstand ist die Aussage ueber Zugehoerigkeit. */
  .form-actions + .hint, .form-actions + .subtext { margin-top: 0.5rem; }
  /* Dieselbe Aussage, wenn die Knopfzeile das LETZTE Kind eines Formulars ist
     und der Satz dahinter steht: Der Nachbarschaftswaehler oben sieht ueber
     die Formulargrenze nicht hinweg. Drei Stellen hatten genau das
     (Befundseite, Fallakte, Kundenblatt) - der erklaerende Satz klebte am
     Knopf, obwohl die Regel darueber existierte. */
  form:has(> .form-actions:last-child) + .hint,
  form:has(> .form-actions:last-child) + .subtext { margin-top: 0.5rem; }
  /* Nach einer Knopfzeile folgt manchmal kein Satz, sondern der naechste
     Abschnitt - eine Tabelle, eine Tafel. Der gehoert nicht zum Knopf und
     bekommt darum den groesseren Schritt der Skala (Leitlinie 5.9 Regel 2).
     Ohne ihn setzte der Tabellenkopf auf der Knopfzeile auf, weil table.stock
     th oben kein Polster hat (Gutschrift-Detail). */
  .form-actions + .table-wrap { margin-top: 1rem; }
  /* Zwei Tabellen untereinander: dieselbe Aussage wie oben, und eine schmale
     traegt ihre Breite als Regel statt als style-Attribut (Regel 10). */
  .table-wrap + .table-wrap { margin-top: 1rem; }
  .table-wrap.schmal { max-width: 34rem; }
  /* Eine Spalte von Ankreuzzeilen (Typsystem: erlaubte Kindtypen, Farben).
     Bis zum 29.09.2026 war der Container ein Flex ohne gap, und der Abstand
     hing als margin-bottom:0.3rem an jedem Kind - Leitlinie 5.9 Regel 1
     (Abstand gehoert dem Container) und Regel 2 (0.3rem liegt nicht auf der
     Skala). Faellt eine Zeile weg, bleibt beim Kind-Abstand ein Rest unten
     stehen; beim gap nicht. */
  .ankreuzliste { display: flex; flex-direction: column; gap: 0.25rem; }
  /* Ein Absatz, auf den unmittelbar sein eigener Knopf folgt: Der Knopf
     gehoert zum Text, nicht zum naechsten Abschnitt (Leitlinie 5.9 Regel 2). */
  p.ohne-abstand-unten { margin: 0 0 0.25rem; }
  /* Ein Suchfeld ohne Beschriftung und eine Auswahl in einer Zeile (Posteingang,
     Lagerplaetze, Kopf des Kundenbereichs) tragen dieselbe Form wie ein Feld im
     Formular - sonst zeigt der Browser sein weisses Standardfeld (Befund 07.09.).  */
  input.suchfeld { min-width: 14rem; }

  /* ---- Bausteine fuer Bloecke und Formulare (Leitlinie 5.9; greg-106) ----
     Bis zum 07.09.2026 trugen die Rechte- und die Mandantenseite 80 Inline-
     Styles fuer Abstaende, Nebentexte und Zwischenueberschriften. Jetzt
     Regeln mit Namen - dieselbe Aussage an jeder Stelle. */
  /* ---- Zellen und Absaetze (Leitlinie 5.9, Regel 10): die haeufigsten
     Inline-Styles der Ansichten als benannte Regeln (07.09.2026). ---- */
  .rechts { text-align: right; }
  .ziffern { text-align: right; font-variant-numeric: tabular-nums; }
  .tabellenziffern { font-variant-numeric: tabular-nums; }
  .fett { font-weight: 700; }
  .nowrap { white-space: nowrap; }
  .gedaempft { color: var(--text-muted); }
  .ohne-rand { margin: 0; }
  .content-grid.einspaltig { grid-template-columns: 1fr; }
  .content-grid.schmal { max-width: 46rem; }
  .content-grid.eng { max-width: 30rem; }
  .content-grid.weit { max-width: 56rem; }
  /* Zeilenabstand NICHT auf 0: Zweispaltig entsteht selten eine zweite Zeile,
     und darum fiel es nie auf - aber unter 880px macht die Medienabfrage
     weiter unten eine Spalte daraus, und dann sitzt jedes Feld auf seinem
     Vorgaenger. Genau die Falle aus dem Kopf von test/abstaende.test.js:
     Ein Abstand, der nur aus der Anordnung kommt, ist keiner. */
  .content-grid.zweispaltig { grid-template-columns: 1fr 1fr; gap: 1.25rem 1.5rem; }
  .chip.meldung { margin-bottom: 1rem; }
  .nachgestellt { margin-top: 0.5rem; }
  .abgesetzt { margin-top: 1rem; }
  .vorangestellt { margin-bottom: 1rem; }
  .leise { color: var(--text-faint); }
  /* Ein Absatz in Nebentextgroesse unter einem Chip, Feld oder Knopf. */
  .nebentext { margin: 0.5rem 0 0; color: var(--text-muted); font-size: 0.8rem; line-height: 1.5; }
  .nebentext.abgesetzt { margin-top: 1rem; }
  /* Kleingedrucktes in einem farbigen Kasten - erbt die Farbe der Flaeche. */
  .kleingedruckt { margin: 0.75rem 0 0; font-size: 0.78rem; line-height: 1.55; }
  .kleingedruckt.titel { margin-bottom: 0.25rem; font-weight: 600; }
  .fliesstext { margin: 0 0 0.75rem; font-size: 0.9rem; line-height: 1.65; }
  .absatz-titel { margin: 1.25rem 0 0.25rem; font-size: 0.8rem; font-weight: 700; color: var(--text-muted); }
  .absatz-titel.erster { margin-top: 0; }
  .hinweisliste { margin: 0; padding-left: 1.1rem; color: var(--text-muted); font-size: 0.88rem; line-height: 1.65; }
  .notice-banner ul { margin: 0.5rem 0 0 1.1rem; font-size: 0.86rem; color: var(--text-muted); }
  .form-section h4 { font-size: 0.85rem; margin: 1rem 0 0.5rem; }
  .formular-schmal { max-width: 26rem; margin-bottom: 1.25rem; }
  .feldzeile { display: flex; gap: 0.75rem; align-items: flex-end; flex-wrap: wrap; }
  .feldzeile .field.kompakt { margin: 0; flex: 1 1 16rem; }
  .btn-primary.auto, .btn-secondary.auto { width: auto; }
  .kontrollkaestchen { display: flex; align-items: center; gap: 0.5rem; font-weight: 400; }
  .kontrollzeile { display: flex; align-items: center; gap: 0.5rem; margin-top: 0.5rem; }
  /* Drei Klassen tief, weil die allgemeine Feldregel (.field input[type=...])
     weiter unten steht und bei gleicher Spezifitaet gewinnen wuerde. */
  .field .kontrollzeile input[type="number"] { width: 5rem; flex: none; }
  .kontrollzeile label { font-weight: 400; }
  details.aufklapp { margin-top: 0.75rem; }
  details.aufklapp > summary { cursor: pointer; font-size: 0.8rem; color: var(--text-muted); }
  /* Ein Wortlaut je Sprache (Rechtstexte-Pflege, 27.09.2026). Nicht
     .aufklapp: Der ist eine Fussnote fuer Zusatzangaben, und im Bild vom
     27.09. sah der Block "English" genau so aus - wie etwas, das man
     uebersehen darf. Gemeint ist das Gegenteil: Wer die zweite Sprache nicht
     eintraegt, laesst seine fremdsprachigen Kunden auf Deutsch
     unterschreiben. Deshalb ein sichtbarer Rahmen, eine Kopfzeile in
     Lesegroesse und Luft zum naechsten Feld. */
  details.sprachblock { margin: 0.75rem 0; border: 1px solid var(--border); border-radius: 10px; background: var(--surface-solid); }
  details.sprachblock > summary { cursor: pointer; padding: 0.7rem 0.9rem; font-size: 0.92rem; font-weight: 600; color: var(--text); border-radius: 10px; }
  details.sprachblock > summary:hover { background: var(--brand-soft); }
  details.sprachblock[open] > summary { border-bottom: 1px solid var(--border); border-radius: 10px 10px 0 0; }
  details.sprachblock > summary .pflicht { font-weight: 400; color: var(--text-muted); }
  details.sprachblock > .inhalt { padding: 0.9rem; }
  details.sprachblock > .inhalt > .field:last-child { margin-bottom: 0; }
  /* Ein Wortlaut, der feststeht: Er wird ZITIERT, nicht als Feld angeboten.
     Ein deaktiviertes Textfeld waere schlimmer als keines - der Browser
     sendet es nicht mit, der Sprachzweig kaeme als entfernt an, und genau
     das wertet schuetze_rechtstext_fassung als Aenderung (20260927220100). */
  .wortlaut-fest { white-space: pre-wrap; max-height: 18rem; overflow: auto;
    padding: 0.7rem 0.9rem; border: 1px solid var(--border); border-radius: 10px;
    background: var(--surface-solid); font-size: 0.92rem; color: var(--text); }
  .vorschau-rahmen { width: 100%; height: 26rem; border: 1px solid var(--border-strong); border-radius: 10px; background: #fff; }
  .auth-error.gut { margin-top: 1rem; background: var(--status-good-bg); color: var(--status-good-fg); border: none; }
  /* Rechteverwaltung (views/rechte.js): eine Gruppe je Kategorie, der Baum
     eingerueckt je Tiefe - die Einrueckung ist die Aussage "Unterpunkt". */
  .rechte-gruppe { border: 1px solid var(--border); border-radius: 12px; padding: 1rem 1.25rem 1.25rem; margin: 0 0 1.25rem; }
  .rechte-gruppe legend { font-size: 0.8rem; font-weight: 700; color: var(--text-muted); padding: 0 0.5rem; }
  .rechte-zeile { display: flex; align-items: flex-start; gap: 0.75rem; padding: 0.35rem 0; cursor: pointer; }
  .rechte-zeile[data-tiefe="1"] { padding-left: 1.5rem; }
  .rechte-zeile[data-tiefe="2"] { padding-left: 3rem; }
  .rechte-zeile[data-tiefe="3"] { padding-left: 4.5rem; }
  .rechte-zeile[data-tiefe="4"] { padding-left: 6rem; }
  .rechte-zeile input[type="checkbox"] { margin-top: 0.25rem; }
  .rechte-text { min-width: 0; }
  .rechte-name { font-weight: 400; }
  .rechte-zeile[data-tiefe="0"] .rechte-name { font-weight: 600; }
  .rechte-fuss { font-size: 0.75rem; color: var(--text-muted); margin: 1.25rem 0 0; }
  /* Eine Reihe Filterknoepfe ueber einer Liste (Rechnungen, Samenbanken):
     der aktive als btn-primary, aber in Knopfbreite - nicht ueber die ganze
     Zeile, wie btn-primary in einem Formular (Befund 07.09.2026). */
  .filterleiste { display: flex; flex-wrap: wrap; gap: 0.5rem; margin: 0 0 1rem; }
  .filterleiste .btn-primary, .filterleiste .btn-secondary { width: auto; font-size: 0.82rem; padding: 0.35rem 0.75rem; }
  /* Ein Filter-FORMULAR ueber einer Liste - Felder, Schalter und Absendeknopf
     (Progesterontagebuch, Bestand, Faelle). Nicht zu verwechseln mit
     .filterleiste darueber: die ist eine Reihe Filterknoepfe ohne Formular.

     Anlass (Gregor, 28.09.2026, mit Seitenbild): "Warum sieht es wie Kraut
     und Rueben aus?" Das Formular war ein flex-Container, in dem JEDES Kind
     mitfloss - ein Hinweistext draengte sich zwischen Haekchen und Knopf,
     und sieben verschiedene inline-style-Attribute legten jedes Element
     einzeln fest. Wer eine Zeile ergaenzte, ergaenzte auch ihr Layout.

     Hier steht es EINMAL: Felder und Knopf in der ersten Zeile, Schalter in
     einer eigenen (.zeile), Hinweise auf ganzer Breite unter dem, worauf sie
     sich beziehen. Wer etwas ergaenzt, waehlt nur noch die Rolle. */
  .filterform { display: flex; flex-wrap: wrap; gap: 0.75rem 1rem; align-items: flex-end; }
  .filterform .field { margin: 0; }
  /* Ein Schalter samt Beschriftung - als Ganzes anklickbar, nie umbrechend. */
  .filterform .schalter { display: flex; align-items: center; gap: 0.4rem; margin: 0; white-space: nowrap; font-size: 0.86rem; }
  .filterform .schalter input { margin: 0; }
  /* Eine eigene Zeile fuer sich: die Schaltergruppe. */
  .filterform .zeile { flex: 1 1 100%; display: flex; flex-wrap: wrap; gap: 0.4rem 1.5rem; align-items: center; margin: 0; }
  /* Ein Hinweis gehoert unter seine Zeile, nicht neben den Knopf. */
  .filterform .hint { flex: 1 1 100%; margin: 0; }
  /* Ein Suchfeld braucht mehr Grundbreite als ein Datumsfeld: Sein Platzhalter
     nennt die vier Wege, auf denen man suchen kann ("Huendin, Chipnummer,
     Halter oder E-Mail"), und in der Standardbreite bricht er mittendrin ab
     (am Bild gesehen, 28.09.2026). Als benannte Regel statt als
     style-Attribut - Leitlinie 5.9 Regel 10. */
  .filterform .field.breit { flex: 1 1 18rem; }
  /* Der Hinweis in der Rechnungsliste, was der XRechnung fehlt (greg-109).  */
  .erechnung-hinweis { margin-top: 0.4rem; font-size: 0.78rem; }
  .erechnung-hinweis ul { margin: 0.25rem 0 0 1rem; padding: 0; color: var(--text-muted); }
  .erechnung-hinweis a { font-size: 0.78rem; }
  /* Was nach dem Absenden passiert, gehoert neben den Knopf - nicht in eine
     Fussnote, die niemand liest (Leitlinie 5.13).  */
  .form-danach { font-size: 0.78rem; color: var(--text-muted); flex: 1 1 14rem; }

  /* --- Einen Eintrag aus wenigen waehlen -------------------------------
     Karten statt Tabelle mit Aktionsspalte: Hier wird nicht verglichen,
     hier wird ausgewaehlt. Die ganze Karte ist der Klickbereich - ein
     "Fall anlegen"-Link am rechten Rand waere ein Ziel von 90 Pixeln in
     einer 900 Pixel breiten Zeile (Leitlinie 5.13).  */
  .wahl-suche { display: flex; gap: 1rem; align-items: flex-end; flex-wrap: wrap; margin-bottom: 1.25rem; }
  .wahl-liste { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.5rem; }
  /* --------------------------------------------------------------------
     Die Tafel der anstehenden Samenuebertragungen (18.09.2026, Konrad)
     --------------------------------------------------------------------
     Eine Matrix: links die Zeilenbeschriftungen, je Fall eine Spalte, unten
     die gemeinsame Datumsachse. Die erste Spalte bleibt beim seitlichen
     Blaettern stehen - ohne sie weiss nach drei Spalten niemand mehr, welche
     Zeile er liest. */
  .tafel-wrap { overflow-x: auto; max-height: 78vh; overflow-y: auto; }
  /* So breit wie ihr Inhalt, nie auf die Panelbreite gedehnt: table.stock
     setzt width:100%, und bei einem oder zwei Faellen verteilte der
     Browser die ganze Breite auf die wenigen Spalten - ungleich (Leitlinie
     5.27). Ein feiner Strich trennt die Fallspalten; am haftenden Kopf als
     Schatten, weil Rahmen dort beim Blaettern liegen bleiben. */
  table.tafel { width: auto; min-width: max-content; }
  table.tafel td + td { border-left: 1px solid var(--border); }
  table.tafel th.tafel-spalte + th.tafel-spalte { box-shadow: inset 1px 0 0 var(--border); }
  /* Langer Text bricht in der Zelle um, statt die Spalte auf Satzlaenge zu
     dehnen (min-width: max-content der Tabelle nimmt sonst jeden Satz als
     eine Zeile). inline-block, damit der Kasten in einer zentrierten Zelle
     der SUe-Tafel mittig steht und in der Versandtafel links. */
  table.tafel .tafel-text {
    display: inline-block; vertical-align: top; max-width: 16rem;
    white-space: normal; overflow-wrap: anywhere; text-align: left;
  }
  table.tafel .tafel-punkt select { max-width: 15rem; }
  /* Die Striche an den haftenden Zellen sind Schatten, keine Rahmen: Bei
     border-collapse bleibt ein Rahmen beim Blaettern liegen, waehrend die
     Zelle mitwandert (Chrome); ein inset-Schatten wandert mit. */
  /* Haftende Zellen brauchen einen DECKENDEN Grund: var(--surface) ist zu
     62 % durchsichtig, und beim Blaettern schien die naechste Zeile durch
     den Kopf - "Carla Whitfield" unter "#187", unlesbar (Seitenbild demo,
     25.09.2026). */
  table.tafel th[scope="row"] {
    position: sticky; left: 0; z-index: 2;
    background: var(--surface-solid); text-align: left; font-weight: 500;
    white-space: nowrap; padding-right: 1rem; box-shadow: inset -1px 0 0 var(--border);
  }
  table.tafel thead th { position: sticky; top: 0; z-index: 3; background: var(--surface-solid); }
  table.tafel thead th.tafel-kopf { left: 0; z-index: 4; box-shadow: inset -1px 0 0 var(--border); }
  /* Der Kopf einer Fallspalte ist kein Spaltentitel, sondern ein Datensatz
     (Nummer, Name, Typ): normale Schrift, keine Versalien. Die Regel fuer
     table.stock th machte aus "Import Hynes" ein gesperrtes IMPORT HYNES. */
  table.tafel thead th.tafel-spalte {
    text-transform: none; letter-spacing: 0; font-size: 0.86rem; font-weight: 400; color: var(--text);
  }
  .tafel-spalte { min-width: 7.5rem; vertical-align: bottom; }
  .tafel-huendin { font-weight: 600; font-size: 0.92rem; }
  .tafel-zelle { text-align: center; }
  .tafel-notiz { max-width: 14rem; white-space: normal; font-size: 0.82rem; }
  .tafel-punkt-name { font-size: 0.82rem; max-width: 12rem; white-space: normal !important; }

  /* Der Trenner zwischen Falldaten, Checkliste und Verlauf. */
  .tafel-trenner th, .tafel-trenner td {
    background: var(--surface-strong); font-weight: 600; font-size: 0.82rem;
    border-top: 2px solid var(--border);
  }
  /* Heute ist die Zeile, an der man sich orientiert. */
  .tafel-heute th, .tafel-heute td { background: var(--surface-strong); }
  /* Gruen ist der VEREINBARTE Termin - nicht die Empfehlung aus dem Verlauf
     (Konrads ausdrueckliche Trennung). */
  .tafel-zelle.vereinbart, td.vereinbart { background: var(--status-good-bg); }
  .tafel-zelle.eisprung { outline: 2px solid var(--brand); outline-offset: -2px; }
  .tafel-zelle.erledigt { background: var(--status-good-bg); }
  .tafel-wert { font-variant-numeric: tabular-nums; font-weight: 600; }
  .tafel-offen { color: var(--text-muted); }
  /* Trennt zwei Messungen desselben Tages - zurueckhaltend, damit die Werte
     selbst die Zelle tragen und nicht der Punkt dazwischen. NICHT
     "tafel-trenner": so heisst die Verlaufszeile der Matrix, und die Regel
     haette sie mit eingefaerbt (gefunden von test/tafel.test.js). */
  .tafel-werttrenner { color: var(--text-muted); padding: 0 0.25rem; }
  .tafel-marke {
    display: inline-block; font-size: 0.7rem; padding: 0 0.3rem;
    border-radius: 3px; background: var(--surface-strong); color: var(--text-muted);
  }
  .tafel-marke.gruen { background: var(--status-good-bg); color: var(--status-good-fg); }
  .tafel-punkt, .tafel-termin { display: flex; gap: 0.25rem; align-items: center; justify-content: center; }
  .tafel-punkt select { font-size: 0.8rem; padding: 0.1rem 0.2rem; }
  .tafel-termin input { font-size: 0.8rem; padding: 0.1rem 0.2rem; width: 8.5rem; }
  .tafel-rechner summary { cursor: pointer; }

  /* Die Checkliste eingeklappt: vierzehn Auswahlfelder je Spalte erschlugen
     die Tafel (Gregor, 19.09.2026). Sichtbar bleibt der Stand. */
  .tafel-liste summary { cursor: pointer; font-size: 0.8rem; white-space: nowrap; }
  .tafel-liste summary.vollstaendig { color: var(--status-good-fg); font-weight: 600; }
  .tafel-liste-inhalt {
    display: flex; flex-direction: column; gap: 0.35rem;
    margin-top: 0.4rem; min-width: 15rem; text-align: left;
  }
  .tafel-liste-zeile {
    display: flex; justify-content: space-between; align-items: center;
    gap: 0.5rem; font-size: 0.78rem;
  }
  .tafel-punkt.erledigt select { border-color: var(--status-good-fg); }

  /* --- Versandarbeitsplatz (24.09.2026) ------------------------------------
     Die Markierung ist ein Farbpunkt mit Wort, den nur die Setzende sieht;
     die Sendungszeile traegt darunter eine Detailzeile (Frachtbrief,
     Aktionen, Aendern) - nie ein Fenster darueber (Leitlinie 5.24). */
  .markierung {
    display: inline-block; vertical-align: middle; padding: 0.1em 0.6em 0.1em 1.4em; border-radius: 999px;
    font-size: 0.72rem; font-weight: 600; position: relative; background: var(--surface-strong); color: var(--text);
  }
  .markierung::before { content: ""; position: absolute; left: 0.5em; top: 50%; width: 0.6em; height: 0.6em; border-radius: 50%; transform: translateY(-50%); background: var(--text-muted); }
  .markierung-rot::before { background: #d64545; }
  .markierung-gelb::before { background: #e0b400; }
  .markierung-gruen::before { background: #3ab276; }
  .markierung-blau::before { background: #2f7fd6; }
  .markierung-grau::before { background: #8a8f98; }
  .markierung-formular { display: flex; gap: 0.75rem; align-items: flex-end; flex-wrap: wrap; margin: 0 0 1rem; }
  .partner-block {
    display: flex; flex-direction: column; gap: 0.25rem; font-size: 0.86rem; margin: 0 0 1rem;
    padding: 0.6rem 0.8rem; border: 1px solid var(--border); border-radius: 10px; background: var(--surface-strong);
  }
  .markierung-formular .field { flex: 0 1 12rem; }
  table.sendungen tr.sendung td { border-bottom: 0; vertical-align: top; }
  table.sendungen tr.sendung.dringend td:first-child { box-shadow: inset 3px 0 0 var(--status-critical-fg); }
  table.sendungen tr.sendung-details td { padding-top: 0; }
  .frachtbrief { display: flex; flex-direction: column; gap: 0.5rem; font-size: 0.86rem; }
  .frachtbrief-upload { display: flex; gap: 0.75rem; align-items: flex-end; flex-wrap: wrap; }
  .frachtbrief-upload .field { flex: 1 1 14rem; }
  .frachtbrief dl.vorschlag { display: grid; grid-template-columns: auto 1fr; gap: 0.25rem 1rem; margin: 0.5rem 0; font-size: 0.84rem; }
  .frachtbrief dl.vorschlag dt { color: var(--text-muted); }
  .frachtbrief dl.vorschlag dd { margin: 0; }
  .sendung-aktionen { display: flex; gap: 1rem; flex-wrap: wrap; margin: 0.5rem 0 0.25rem; }
  .sendung-aktionen form { margin: 0; }
  details.sendung-aendern > summary, details.sendung-neu > summary { cursor: pointer; font-size: 0.86rem; color: var(--brand-deep); margin-top: 0.4rem; }
  details.sendung-neu { margin-top: 1rem; }
  details.sendung-aendern .form-section, details.sendung-neu .form-section { margin-top: 0.5rem; }

  /* Die Versandtafel: dieselbe Matrix wie die SUe-Tafel, eine Spalte je
     Fall. Gregor am 24.09.2026 zur ersten Fassung: "Kraut und Rueben" -
     drei Ursachen, drei Regeln:
     1. Jede Fallspalte ist gleich breit (17rem), die Tabelle wird nicht auf
        die Panelbreite gedehnt. Bei zwei Faellen bekam sonst der eine
        1100 px und der andere 550. Langer Text bricht in der Zelle um
        (.tafel-text, Hoechstbreite 16rem) statt die Spalte zu dehnen.
     2. Die Gruppe (mehrere Faelle in EINER Sendung) haelt eine Klammer
        ueber den Koepfen - nur dort und nur ab zwei Faellen. Die Kante lag
        vorher auf JEDER Zelle jedes Falls mit Sendung, also fast ueberall,
        und zerschnitt die Tafel in Streifen.
     3. Ein feiner Strich zwischen den Spalten haelt das Auge in der
        Spalte, wenn es von der Beschriftung links nach rechts wandert. */
  .versandtafel th.tafel-spalte { width: 17rem; min-width: 17rem; }
  .versandtafel .tafel-spalte > div { max-width: 16rem; white-space: normal; overflow-wrap: anywhere; }
  .versandtafel th.tafel-gruppe { box-shadow: inset 0 3px 0 var(--brand); }
  .versandtafel th.tafel-spalte + th.tafel-gruppe:not(.tafel-gruppe-anfang) { box-shadow: inset 0 3px 0 var(--brand); }
  .versandtafel th.tafel-spalte + th.tafel-gruppe-anfang { box-shadow: inset 1px 0 0 var(--border), inset 0 3px 0 var(--brand); }
  .versandtafel .tafel-gruppe-marke {
    font-size: 0.7rem; font-weight: 700; letter-spacing: 0.05em; text-transform: uppercase;
    color: var(--brand-deep); margin: 0.2rem 0 0.35rem;
  }
  .versandtafel .tafel-fallnr { font-size: 0.8rem; }
  .versandtafel .tafel-fallnr a { font-weight: 600; }
  .versandtafel td .chip { white-space: nowrap; }
  .versandtafel .tafel-punkt { justify-content: flex-start; flex-wrap: wrap; }
  .versandtafel .tafel-zeile-wahl { margin-bottom: 0.75rem; }
  .versand-filter { display: flex; gap: 0.75rem; align-items: flex-end; flex-wrap: wrap; margin-bottom: 1rem; }
  .versand-filter .field { margin: 0; flex: 0 1 11rem; }
  .versand-filter button { width: auto; }
  .versand-status form, .versand-zustaendig form { display: inline; }
  .versand-status select, .versand-zustaendig select { font-size: 0.8rem; padding: 0.1rem 0.2rem; }

  /* Das Anfragen-Board: vier Spalten nach Bearbeitungsstand, Karten mit
     Statusknoepfen statt Ziehen (ohne JavaScript bedienbar). */
  .board { display: grid; grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); gap: 1rem; align-items: start; }
  .board-spalte { background: var(--surface-strong); border-radius: 12px; padding: 0.75rem; min-height: 8rem; }
  .board-spalte h3 { margin: 0 0 0.6rem; font-size: 0.9rem; display: flex; justify-content: space-between; align-items: center; gap: 0.5rem; }
  .board-karte { background: var(--surface); border: 1px solid var(--border); border-radius: 10px; padding: 0.6rem 0.75rem; margin-bottom: 0.6rem; font-size: 0.84rem; }
  .board-karte .board-titel { font-weight: 700; }
  .board-karte .board-titel a { text-decoration: none; }
  .board-karte .board-zeile { display: block; margin-top: 0.15rem; font-size: 0.8rem; color: var(--text-muted); }
  .board-karte .board-knoepfe { display: flex; gap: 0.5rem; flex-wrap: wrap; margin-top: 0.5rem; }
  .board-karte .board-knoepfe form { margin: 0; }
  .board-karte.wiedervorlage-faellig { border-color: var(--status-critical-fg); }
  /* Die letzte Notiz ist der Inhalt, nicht Beiwerk: in Textfarbe, nicht
     gedaempft. Ruhende Karten (vier Wochen ohne Bewegung) stehen unter
     einer Trennlinie und treten zurueck - Manuelas "AELTER" (25.09.2026). */
  .board-karte .board-zeile.board-notiz { color: var(--text); }
  .board-karte.ruht { opacity: 0.6; }
  .board-karte.ruht:hover { opacity: 1; }
  .board-trenner {
    display: flex; align-items: center; gap: 0.5rem; margin: 0.9rem 0 0.5rem; padding-top: 0.6rem;
    border-top: 1px dashed var(--border-strong); font-size: 0.7rem; letter-spacing: 0.06em;
    text-transform: uppercase; color: var(--text-faint);
  }
  .behaelter-frei { color: var(--status-good-fg); }

  .wahl-karte {
    display: flex; flex-direction: column; gap: 0.15rem; text-decoration: none; color: inherit;
    border: 1px solid var(--border); border-radius: 10px; padding: 0.7rem 0.9rem; background: var(--surface);
  }
  .wahl-karte:hover { border-color: var(--brand); background: var(--surface-strong); }
  .wahl-karte .wahl-zeile { font-size: 0.8rem; color: var(--text-muted); }
  .wahl-leer { color: var(--text-muted); margin: 0.5rem 0; }

  /* Der bereits gewaehlte Schritt bleibt sichtbar und aenderbar - sonst
     weiss man im zweiten Schritt nicht mehr, fuer wen man arbeitet.  */
  .gewaehlt-karte {
    display: flex; flex-wrap: wrap; gap: 0.5rem; align-items: baseline;
    border-radius: 10px; padding: 0.6rem 0.85rem; margin-bottom: 1.25rem;
    background: var(--surface-strong); border: 1px solid var(--border);
  }
  .gewaehlt-karte .gewaehlt-was { font-size: 0.72rem; letter-spacing: 0.06em; text-transform: uppercase; color: var(--text-faint); }
  .gewaehlt-karte a { margin-left: auto; font-size: 0.82rem; }

  /* Die Entscheidung, die alles Weitere bestimmt, steht als Karten da -
     jede mit einem Satz, was sie bedeutet. Eine Auswahlliste kann das
     nicht: Sie zeigt vier Woerter ohne Bedeutung.  */
  .typ-wahl { border: none; margin: 0 0 1.25rem; padding: 0; display: grid; gap: 0.5rem; grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); }
  .typ-wahl legend { font-size: 0.82rem; font-weight: 700; color: var(--text-muted); padding: 0; margin-bottom: 0.5rem; }
  .typ-karte {
    display: flex; gap: 0.6rem; align-items: flex-start; cursor: pointer;
    border: 1px solid var(--border); border-radius: 10px; padding: 0.7rem 0.85rem; background: var(--surface);
  }
  .typ-karte:hover { border-color: var(--brand-soft); background: var(--surface-strong); }
  .typ-karte:has(input:checked) { border-color: var(--brand); background: var(--brand-soft); }
  .typ-karte:has(input:focus-visible) { outline: 2px solid var(--focus-ring); outline-offset: 2px; }
  .typ-karte input { margin-top: 0.2rem; flex: none; }
  .typ-inhalt { display: flex; flex-direction: column; gap: 0.15rem; }
  .typ-inhalt span { font-size: 0.78rem; color: var(--text-muted); line-height: 1.4; }

  /* Taetigkeiten (docs/konzept-falltypen.md, Stufe 2). Dieselbe Bildsprache
     wie die Fallart-Wahl darueber - aber als Verweis, nicht als Auswahl.
     Deshalb eigene Regeln: Ein <a> in einer .typ-karte haette den Klickbereich
     der Karte gehabt und die Tastaturbedienung einer Zeile.  */
  .tat-gruppen { display: flex; flex-direction: column; gap: 1.1rem; }
  .tat-gruppe > h3 { font-size: 0.82rem; font-weight: 700; color: var(--text-muted); margin: 0 0 0.5rem; }
  .tat-karten { display: grid; gap: 0.5rem; grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); }
  .tat-karte {
    display: flex; flex-direction: column; gap: 0.15rem; text-decoration: none; color: inherit;
    border: 1px solid var(--border); border-radius: 10px; padding: 0.7rem 0.85rem; background: var(--surface);
  }
  .tat-karte:hover { border-color: var(--brand-soft); background: var(--surface-strong); }
  .tat-karte:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }
  .tat-karte strong { color: var(--brand); }
  .tat-karte span { font-size: 0.78rem; color: var(--text-muted); line-height: 1.4; }

  /* Randfaelle liegen zusammengefasst darunter, nicht zwischen den Feldern,
     die jeder braucht.  */
  .form-optional > summary { cursor: pointer; font-size: 0.92rem; font-weight: 700; margin-bottom: 0.5rem; }
  .form-optional > summary:hover { color: var(--brand-deep); }

  .mfa-status-row {
    display: flex; align-items: center; justify-content: space-between; gap: 1rem;
    padding: 1rem 1.1rem; margin: 1rem 0;
    background: var(--surface-strong); border: 1px solid var(--border); border-radius: 14px;
  }
  .mfa-status-row > div:first-child { display: flex; flex-direction: column; gap: 0.2rem; }
  .subtext { font-size: 0.8rem; color: var(--text-muted); }
  /* Auf einer farbigen Flaeche gilt die Farbe der Flaeche - auch fuer die
     Nebenangabe darin. .subtext hat eine feste graue Farbe; im aktiven
     Filterknopf ("Alle 0") blieb die Zahl damit dunkel auf Tuerkis, waehrend
     das Wort daneben weiss war (Befund 06.09.2026, Leitlinie 5.9). Die
     Nebenrolle traegt hier die Deckkraft, nicht eine zweite Farbe.  */
  .btn-primary .subtext { color: inherit; opacity: 0.75; }
  .mfa-status-row .subtext { font-size: 0.82rem; color: var(--text-muted); }

  .qr-box {
    display: grid; place-items: center; padding: 1.25rem;
    background: #fff; border: 1px solid var(--border); border-radius: 14px; margin-bottom: 1rem;
  }
  .qr-box img { width: 190px; height: 190px; }

  .mfa-secret {
    font-size: 0.82rem; color: var(--text-muted); text-align: center;
    margin: 0 0 1.3rem;
  }
  .mfa-secret code {
    font-family: "IBM Plex Mono", monospace; background: var(--surface-strong);
    padding: 0.15rem 0.4rem; border-radius: 6px; color: var(--text);
  }

  /* --- Posteingang: Arbeitsliste statt Tabelle ------------------------
     Jede Nachricht ist eine Karte mit vier Zonen in immer derselben
     Reihenfolge: wer/wann, worum es geht, was zu tun ist, und die Aktionen.
     Der Zustand steht links als Streifen - offen faellt auf, Erledigtes
     tritt zurueck (views/postfach.js, Leitlinien 5.9 und 5.13).  */
  .post-liste { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.85rem; }
  .post-karte {
    display: flex; flex-direction: column; gap: 0.55rem;
    border: 1px solid var(--border); border-left: 3px solid var(--border);
    border-radius: 12px; padding: 0.85rem 1rem; background: var(--surface-solid);
  }
  .post-karte[data-stand="offen"] { border-left-color: var(--accent); }
  .post-karte[data-stand="zugeordnet"] { border-left-color: var(--status-good-fg); }
  .post-karte[data-stand="erledigt"] { opacity: 0.62; }
  .post-kopf { display: flex; flex-wrap: wrap; gap: 0.75rem; align-items: baseline; justify-content: space-between; }
  .post-wer { display: flex; flex-wrap: wrap; gap: 0.5rem; align-items: baseline; min-width: 0; }
  .post-zustand { display: flex; flex-wrap: wrap; gap: 0.5rem; align-items: center; }
  .post-zeit { font-size: 0.78rem; color: var(--text-faint); white-space: nowrap; }
  .post-betreff { margin: 0; font-size: 1rem; font-weight: 700; line-height: 1.35; }
  .post-anhang { font-size: 0.78rem; font-weight: 400; color: var(--text-muted); white-space: nowrap; }

  /* Die Aufgabe: der einzige farbige Block der Karte - deshalb faellt er
     auf, und deshalb steht er nur da, wenn wirklich etwas zu tun ist.  */
  .post-aufgabe {
    display: flex; flex-direction: column; gap: 0.4rem;
    border-radius: 10px; padding: 0.6rem 0.75rem;
    background: var(--surface-strong); border: 1px solid var(--border);
  }
  .post-aufgabe.ist-offen { background: var(--accent-soft); border-color: transparent; }
  .post-aufgabe-titel { margin: 0; font-size: 0.82rem; font-weight: 700; color: var(--text-muted); }
  .post-aufgabe.ist-offen .post-aufgabe-titel { color: var(--accent-deep); }
  .post-werte { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.3rem; }
  .wert-zeile {
    display: grid; gap: 0.75rem; align-items: baseline;
    grid-template-columns: minmax(6rem, auto) minmax(7rem, 1fr) minmax(7rem, auto) minmax(12rem, auto);
    padding: 0.3rem 0; border-top: 1px solid var(--border);
  }
  .wert-zeile:first-child { border-top: none; }
  .wert-zahl { font-weight: 700; font-variant-numeric: tabular-nums; white-space: nowrap; }
  .wert-einheit { font-weight: 400; font-size: 0.82rem; color: var(--text-muted); }
  .wert-hund, .wert-zeit { display: flex; flex-direction: column; gap: 0.1rem; font-size: 0.86rem; }
  .wert-stand { display: flex; flex-wrap: wrap; gap: 0.4rem; align-items: center; justify-self: end; }
  .wert-grund { font-size: 0.78rem; color: var(--accent-deep); font-weight: 700; }
  .wert-formular { display: flex; flex-wrap: wrap; gap: 0.4rem; align-items: center; }
  .wert-formular select { max-width: 15rem; }

  /* Anhaenge: Chips, die anklickbar aussehen und es auch sind.  */
  .anhaenge { list-style: none; padding: 0; margin: 0; display: flex; gap: 0.4rem; flex-wrap: wrap; }
  .anhang-chip { text-decoration: none; border: 1px solid var(--border); }
  .anhang-chip:hover { border-color: var(--brand); color: var(--brand-deep); }
  /* Ein Suchfeld mit Knopf in einem Formularraster (Lagerplatz-, Kundensuche
     im Buchungsformular, Leitlinie 5.14). Der Knopf gehoert ueber das
     form-Attribut zu einer GET-Form ausserhalb des Vorgangsformulars.  */
  .feld-mit-knopf { display: flex; gap: 0.5rem; align-items: stretch; }
  .feld-mit-knopf input { flex: 1 1 auto; min-width: 0; }
  .feld-mit-knopf button { flex: 0 0 auto; width: auto; }
  .anhang-mehr { color: var(--text-muted); }
  /* Ein Vermerk statt einer Datei: nicht anklickbar, gedaempft, der Grund im
     Titel (20260905780000).  */
  .anhang-fehlt { cursor: help; color: var(--text-muted); border-style: dashed; }
  .post-anhang-hinweis { margin: 0; font-size: 0.78rem; color: var(--status-critical-fg); }
  .post-anhang-titel { margin: 0.75rem 0 0.35rem; font-size: 0.82rem; font-weight: 700; color: var(--text-muted); }

  .post-mehr summary { cursor: pointer; font-size: 0.82rem; color: var(--text-muted); }
  .post-mehr summary:hover { color: var(--text); }
  .post-mehr[open] summary { margin-bottom: 0.5rem; }
  .post-text { white-space: pre-wrap; font-size: 0.86rem; line-height: 1.5; max-height: 22rem; overflow: auto; color: var(--text); }
  /* Die Zusammenfassung der KI steht offen in der Karte. Sie ist Fliesstext
     mit einem Kennzeichen davor, kein Kasten: Ein zweiter farbiger Block
     neben der Aufgabe wuerde beide entwerten (Leitlinie 5.9).  */
  .post-ki-text { margin: 0; font-size: 0.9rem; line-height: 1.5; }
  .post-ki-fuss { margin: 0.5rem 0 0; font-size: 0.76rem; color: var(--text-faint); }
  .post-ki-lauft { margin: 0; }

  /* Die Aktionen stehen immer unten und immer in derselben Reihenfolge -
     erst das Zuordnen (der Regelfall), dann die Ausnahmen.  */
  .post-aktionen { display: flex; flex-wrap: wrap; gap: 0.75rem; align-items: center; padding-top: 0.2rem; }
  .post-zuordnen { display: flex; flex-wrap: wrap; gap: 0.4rem; align-items: center; }
  .post-zuordnen select { max-width: 18rem; }
  .post-leer { text-align: center; color: var(--text-muted); padding: 2rem 0; margin: 0; }

  .notice-banner {
    display: flex; align-items: center; justify-content: space-between; gap: 1rem; flex-wrap: wrap;
    background: var(--accent-soft); border: 1px solid var(--border);
    border-radius: 14px; padding: 0.9rem 1.2rem; margin-bottom: 1.25rem;
  }
  .notice-banner p { margin: 0; font-size: 0.86rem; color: var(--text); }
  .notice-banner strong { color: var(--accent-deep); }
  .notice-banner-actions { display: flex; align-items: center; gap: 1rem; flex-shrink: 0; }

  @media (max-width: 880px) {
    /* grid-template-rows ist hier noetig, nicht kosmetisch: Ein Raster mit
       min-height:100vh und automatischen Zeilen verteilt den freien Platz auf
       beide Zeilen - die Seitenleiste waere damit ueber den halben Bildschirm
       hoch, auch wenn nur eine Zeile darin steht. "auto 1fr" gibt ihr die
       Hoehe ihres Inhalts und den Rest dem Arbeitsbereich. */
    .app { grid-template-columns: 1fr; grid-template-rows: auto 1fr; }

    /* Auf dem Telefon ist der Innenabstand kleiner: 1,5rem waeren dort
       Platz, der dem Text fehlt. Auf :root wie die Grundregel - fuer alle
       drei Huellen, nicht nur fuer .main (Befund 07.09.2026, 5.9).  */
    :root { --block-einzug: 1rem; }

    /* Bis 2026-09-02 wurde die Seitenleiste hier zu einer waagerecht
       scrollenden Leiste. Das zeigte alle Punkte gleichzeitig, aber keinen
       davon vollstaendig - man musste blind seitwaerts wischen, um zu sehen,
       was es ueberhaupt gibt. Stattdessen jetzt eine Klappe: geschlossen
       nimmt sie eine Zeile ein, geoeffnet zeigt sie die Punkte untereinander
       und damit lesbar. */
    .sidebar {
      flex-direction: column;
      gap: 0;
      padding: 0.85rem 1rem;
    }
    .sidebar .brand { padding: 0; }
    .menue-klappe { display: grid; }

    .sidebar-kopf {
      display: flex; align-items: center; justify-content: space-between;
      gap: 1rem; width: 100%;
    }

    nav.primary-nav { display: none; padding-top: 0.85rem; }
    .app.nav-offen nav.primary-nav { display: flex; }
    .nav-label { display: none; }

    /* Die Fusszeile der Seitenleiste (Adresse und Rolle) haengt an der
       Navigation: Sie zeigt dasselbe wie die Kopfleiste rechts und ist
       geschlossen nur Wiederholung. */
    .sidebar-foot { display: none; margin-top: 0.85rem; }
    .app.nav-offen .sidebar-foot { margin-top: 0.85rem; }
    .app.nav-offen .sidebar-foot { display: block; }

    /* !important ist hier bewusst gesetzt und die einzige Regel im ganzen
       Stylesheet, die es braucht. Grund: Acht Ansichten geben ihre
       Spaltenaufteilung als style-Attribut mit
       (style="grid-template-columns:1fr 1fr"), und ein Inline-Stil schlaegt
       jede Regel aus dem Stylesheet. Ohne !important blieben die Formulare
       auf dem Telefon zweispaltig - gemessen am 02.09.: 722px Inhalt in
       einem 500px breiten Bereich, den overflow:hidden dann abschneidet.
       Genau das war der Fehler "auf dem iPhone laesst sich nicht waagerecht
       scrollen": Es gab nichts zu scrollen, das Fehlende war weggeschnitten.

       Die saubere Loesung waeren Klassen statt style-Attribute in den
       Ansichten. Das ist der eigentliche Mangel und gehoert nachgezogen;
       bis dahin faengt diese Regel alle acht Dateien auf einmal ab - auch
       die Stelle in samenbankTypsystem.js, die ihre Spalten erst zur
       Laufzeit zusammensetzt und sich schlecht in eine Klasse uebersetzen
       laesst. */
    .content-grid { grid-template-columns: 1fr !important; }
    .form-grid { grid-template-columns: 1fr; }

    /* min-width:0 fuer Rasterkinder und Panels steht seit dem 24.09.2026 in
       der allgemeinen Regel (.content-grid > *, .panel) - es gilt in jeder
       Breite, nicht nur auf dem Telefon. */

    /* Die Werkzeuge rechts oben duerfen umbrechen, statt die Kopfzeile zu
       sprengen. Auf schmalen Schirmen beginnen sie links: "margin-left:auto"
       draengt sie sonst an den rechten Rand, und was nicht mehr passt, liegt
       dahinter im Nichts. */
    .topbar { gap: 0.9rem; }
    .topbar-tools { flex-wrap: wrap; gap: 0.6rem; margin-left: 0; width: 100%; }
    /* Die Mandantenwahl traegt den laengsten Text der Leiste. Sie darf
       schrumpfen - ein abgeschnittener Name ist besser als eine Zeile, die
       aus dem Bild laeuft. */
    .topbar-tools select { max-width: 100%; }
    .greeting { min-width: 0; }
  }

  /* --- Kopfbereich auf dem Handy --------------------------------------
     Befund 09.09.2026 (iPhone, demo): Die Werkzeugleiste war 542 Pixel breit
     auf einem 390 Pixel breiten Schirm. Sie lief nicht etwa ueber - ein
     Vorfahre schnitt sie ab, und "Administrator" endete mitten im Wort.

     Die Breite war nur das Symptom. Die eigentliche Frage ist, was auf einem
     Handy ueberhaupt in die Kopfzeile gehoert:

     * Der Palettenknopf ("Springen zu", Strg+K) ist ein TASTATURWEG. Auf
       einem Geraet ohne Tastatur ist er ein Knopf, der Platz kostet und
       nichts kann - er verschwindet.
     * Der Rollenchip ("Administrator") ist eine Auskunft, keine Handlung.
       Sie steht im Profil, wo man sie sucht, wenn man sie braucht.
     * Was bleibt, sind die drei Dinge, die man unterwegs tut: den Mandanten
       wechseln, das Thema umstellen, sich abmelden - plus der Weg ins
       Profil.

     Damit passt die Zeile: Die Mandantenwahl nimmt den freien Platz und
     kuerzt ihren Text notfalls, die uebrigen Ziele behalten ihre Groesse.
     44 Pixel Kantenlaenge sind die Empfehlung fuer Bedienziele auf
     Beruehrungsschirmen; darunter trifft man daneben. */
  @media (max-width: 40rem) {
    .palette-knopf { display: none; }
    .role-chip { display: none; }
    .topbar-tools { gap: 0.5rem; }
    /* Die Mandantenwahl bekommt eine eigene Zeile. Der Versuch, sie mit den
       drei Knoepfen in eine zu zwaengen, endete bei 390 Pixeln damit, dass
       der Abmeldeknopf allein in der zweiten Zeile stand - zwei Zeilen sind
       es dann ohnehin, und so sind sie wenigstens geordnet: oben die
       Auswahl, darunter die Bedienelemente. */
    .topbar-tools .mandant-umschalter { flex: 1 1 100%; min-width: 0; }
    .topbar-tools .mandant-umschalter select { width: 100%; min-width: 0; text-overflow: ellipsis; }
    .avatar, .logout-form button { min-width: 2.75rem; min-height: 2.75rem; }
    .thema-schalter button { min-height: 2.25rem; }
    /* Der Reiter muss mit den hoeheren Knoepfen wachsen, sonst sitzt er oben
       am Rand statt hinter dem Sinnbild (Gregor, 16.09.2026, iPhone). */
    .thema-schieber { height: 2.25rem; }
    /* Die Begruessung darf schmal werden - sie ist Text, kein Bedienelement. */
    .greeting { flex: 1 1 100%; min-width: 0; }
  }

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

  /* --- Unterschriftsfeld (Zustimmungs-Gate) ---------------------------- */
  .unterschrift-rahmen {
    border: 1px dashed var(--border-strong); border-radius: 12px; background: #fff;
    display: inline-block; touch-action: none;
  }
  .unterschrift-rahmen canvas { display: block; width: 100%; max-width: 600px; height: 200px; cursor: crosshair; }

  .mandant-umschalter select {
    border: 1px solid var(--border-strong); background: var(--surface-solid); color: var(--text);
    border-radius: 999px; padding: 0.35rem 0.7rem; font-size: 0.78rem; font-weight: 600; cursor: pointer;
  }
  .mandant-umschalter select:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 1px; }

  /* --- Newsletter-Editor (Baustufe D) -------------------------------- */
  /* Aktionsspalte. KEIN display:flex auf der Zelle: Ein <td> mit flex
     verlaesst das Tabellenlayout - der Browser haengt eine anonyme Zelle
     darum, die Zelle ist nur noch so hoch wie ihr Inhalt und ihre
     Trennlinie sitzt hoeher als die der Nachbarzellen. Sichtbar wurde das
     am 06.09.2026 auf /samenbank/lagerplaetze, wo die Zeilen zwei- und
     dreizeilig sind. Die Zelle bleibt Tabellenzelle, ihre Kinder werden
     inline nebeneinander gesetzt (Leitlinie 5.8).  */
  /* Aktionen am Zeilenende. Befund des Auftraggebers am 10.09.2026 zur
     Rechnungsliste: Bei VIER Aktionen ("Versenden / PDF / XRechnung /
     Gutschrift") brechen sie um, und untereinander klebten sie aneinander -
     der Abstand sass nur LINKS.

     Der erste Versuch war display:flex mit gap. Das ist hier falsch, und
     test/tabellenlayout.test.js sagt auch warum: display an einem <td>
     nimmt der Zelle das Tabellenlayout, die Zeilen liegen dann nicht mehr
     auf einer Hoehe. Die Zelle bleibt also eine Zelle; den senkrechten
     Abstand gibt die Zeilenhoehe, den waagerechten die Kinder. */
  /* Der senkrechte Abstand steckt in der HOEHE der Kinder, nicht in der
     line-height der Zelle: Eine grosszuegige Zeilenhoehe wuerde die erste
     Aktionszeile tiefer setzen als die erste Zeile der Nachbarspalten - und
     genau die gemeinsame Startlinie ist der Zweck von vertical-align:top
     darueber. */
  .table-actions { text-align: right; line-height: 1.2; }
  .table-actions > * {
    display: inline-flex; align-items: center; vertical-align: top;
    min-height: 1.55rem;
  }
  .table-actions > * + * { margin-left: 0.75rem; }
  .table-empty { text-align: center; color: var(--text-muted); padding: 1.5rem 0 !important; }

  /* Die Steuerzeile am Ende einer Liste (lib/nachladen.js). Sie ist ein
     Link - ohne JavaScript fuehrt ein Klick zum naechsten Block, mit
     JavaScript laedt sie sich selbst nach, sobald sie in Sicht kommt.  */
  tr.nachladen td { text-align: center; padding: 1rem 0 !important; }
  tr.nachladen a { font-weight: 700; font-size: 0.86rem; }
  tr.nachladen .nachladen-rest { display: block; font-size: 0.78rem; color: var(--text-faint); margin-top: 0.25rem; }
  tr.nachladen[data-laedt] a { opacity: 0.5; }
  tr.nachladen[data-fehler] a::after { content: " ↻"; }

  .nl-titel-input, .nl-betreff-input {
    display: block; border: none; background: transparent; font-family: inherit; color: var(--text);
    padding: 0.1rem 0; margin: 0 0 0.15rem; width: 100%;
  }
  .nl-titel-input { font-weight: 700; font-size: 1.05rem; }
  .nl-betreff-input { font-size: 0.82rem; color: var(--text-muted); }
  .nl-titel-input:focus-visible, .nl-betreff-input:focus-visible {
    outline: 2px solid var(--focus-ring); outline-offset: 2px; border-radius: 4px;
  }

  .nl-editor { display: grid; grid-template-columns: 15rem 1fr 20rem; gap: 1.25rem; align-items: start; margin-top: 0.5rem; }
  .nl-sidebar-block { margin-bottom: 1.25rem; }
  .nl-sidebar-block h3 { margin: 0 0 0.2rem; font-size: 0.82rem; }
  .nl-palette, .nl-platzhalter { display: flex; flex-direction: column; gap: 0.4rem; margin-top: 0.5rem; }
  .nl-palette-item, .nl-platzhalter-item {
    border: 1px solid var(--border-strong); background: var(--surface-solid); border-radius: 10px;
    padding: 0.5rem 0.7rem; font-size: 0.8rem; text-align: left; cursor: pointer; color: var(--text);
  }
  .nl-palette-item:hover, .nl-platzhalter-item:hover { border-color: var(--brand); color: var(--brand-deep); }
  .nl-palette-item:disabled, .nl-platzhalter-item:disabled { opacity: 0.4; cursor: not-allowed; }

  .nl-canvas-wrap { min-height: 20rem; }
  .nl-canvas { display: flex; flex-direction: column; gap: 0.75rem; min-height: 8rem; }
  .nl-canvas-empty { text-align: center; padding: 2rem 1rem; border: 1px dashed var(--border-strong); border-radius: 12px; }

  .nl-block { border: 1px solid var(--border); border-radius: 12px; background: var(--surface-solid); overflow: hidden; }
  .nl-block-head {
    display: flex; align-items: center; gap: 0.5rem; padding: 0.4rem 0.6rem;
    background: var(--surface-strong); border-bottom: 1px solid var(--border); font-size: 0.75rem;
  }
  .nl-block-handle { cursor: grab; color: var(--text-faint); }
  .nl-block-typ { font-weight: 700; color: var(--text-muted); flex: 1; }
  .nl-block-remove {
    border: none; background: transparent; color: var(--text-faint); cursor: pointer; font-size: 0.85rem;
    padding: 0.1rem 0.35rem; border-radius: 6px;
  }
  .nl-block-remove:hover { color: var(--status-critical-fg); background: var(--status-critical-bg); }
  .nl-block-body { padding: 0.75rem 0.9rem; display: flex; flex-direction: column; gap: 0.5rem; }
  .nl-block-body input {
    border: 1px solid var(--border-strong); border-radius: 8px; padding: 0.45rem 0.6rem;
    font-family: inherit; font-size: 0.85rem; background: var(--surface-solid); color: var(--text);
  }
  .nl-block-toolbar { display: flex; gap: 0.35rem; }
  .nl-block-toolbar button {
    border: 1px solid var(--border-strong); background: var(--surface-solid); border-radius: 6px;
    width: 1.8rem; height: 1.8rem; cursor: pointer; font-size: 0.8rem; color: var(--text);
  }
  .nl-block-content {
    border: 1px solid var(--border-strong); border-radius: 8px; padding: 0.6rem 0.7rem;
    min-height: 3rem; font-size: 0.85rem; line-height: 1.5;
  }
  .nl-block-content:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 1px; }
  .nl-block-trenner-vorschau { border-top: 1px solid var(--border-strong); margin: 0.5rem 0; }

  .nl-preview-wrap { border: 1px solid var(--border); border-radius: 12px; overflow: hidden; background: var(--surface-solid); }
  .nl-preview-head {
    display: flex; align-items: center; justify-content: space-between; gap: 0.5rem;
    padding: 0.6rem 0.8rem; border-bottom: 1px solid var(--border);
  }
  .nl-preview-head h3 { margin: 0; font-size: 0.82rem; }
  .nl-preview-frame { width: 100%; height: 32rem; border: none; }

  @media (max-width: 1180px) {
    .nl-editor { grid-template-columns: 1fr; }
  }
  /* -------------------------------------------------------------------
     Zustellbox der Rechnungsmail
     -------------------------------------------------------------------
     Die Stufen liegen auf einer Linie, weil sie eine Abfolge sind und keine
     Aufzaehlung: Angenommen -> Zugestellt -> Geoeffnet. Erreichte Stufen
     tragen Farbe, noch nicht erreichte nur einen Umriss - der Blick findet
     damit ohne Lesen, wie weit die Mail gekommen ist.

     Gebaut als <details>, nicht mit Skript: Das Aufklappen funktioniert
     ohne JavaScript, ist ueber die Tastatur bedienbar und braucht keinen
     eigenen Zustand, der mit dem Serverzustand auseinanderlaufen koennte. */
  .zustellung { border: 1px solid var(--border); border-radius: 12px; background: var(--surface); }
  .zustellung > summary {
    list-style: none; cursor: pointer; padding: 0.55rem 0.7rem;
    display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap;
  }
  .zustellung > summary::-webkit-details-marker { display: none; }
  .zustellung > summary:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; border-radius: 12px; }
  .zustellung .aufklapp {
    margin-left: auto; font-size: 0.72rem; color: var(--text-muted); white-space: nowrap;
    display: inline-flex; align-items: center; gap: 0.25rem;
  }
  .zustellung .aufklapp::after { content: "▾"; transition: transform .15s ease; }
  .zustellung[open] .aufklapp::after { transform: rotate(180deg); }

  .zustellweg { display: flex; align-items: flex-start; gap: 0; flex-wrap: wrap; }
  .zustellstufe {
    display: flex; flex-direction: column; align-items: center; gap: 0.15rem;
    /* 6.6rem statt 5.4: Seit dem 10.09.2026 traegt die Zeitmarke ein "Uhr"
       (Leitlinie 6a.5). "10.09., 21:28 Uhr" passt nicht in 5.4rem und waere
       auf zwei Zeilen gebrochen - die Stufen der Leiste haetten dann
       verschiedene Hoehen. */
    min-width: 6.6rem; position: relative; padding: 0 0.15rem;
  }
  /* Verbindungslinie zur naechsten Stufe. Sie sitzt auf halber Punkthoehe
     und endet vor dem naechsten Punkt, damit sie nicht durch ihn laeuft. */
  .zustellstufe:not(:last-child)::after {
    content: ""; position: absolute; top: 5px; left: calc(50% + 8px); right: calc(-50% + 8px);
    height: 2px; background: var(--border-strong);
  }
  .zustellstufe.erreicht:not(:last-child)::after { background: var(--status-good-fg); }
  .zustellpunkt {
    width: 12px; height: 12px; border-radius: 50%;
    border: 2px solid var(--border-strong); background: var(--surface-solid); z-index: 1;
  }
  .zustellstufe.erreicht .zustellpunkt { border-color: var(--status-good-fg); background: var(--status-good-fg); }
  .zustellstufe.gestoert .zustellpunkt { border-color: var(--status-critical-fg); background: var(--status-critical-fg); }
  .zustellstufe .stufe-label { font-size: 0.7rem; font-weight: 700; color: var(--text-faint); text-align: center; }
  .zustellstufe.erreicht .stufe-label { color: var(--text); }
  .zustellstufe.gestoert .stufe-label { color: var(--status-critical-fg); }
  .zustellstufe .stufe-zeit { font-size: 0.66rem; color: var(--text-muted); font-variant-numeric: tabular-nums; text-align: center; white-space: nowrap; }

  .zustell-inhalt { border-top: 1px solid var(--border); padding: 0.7rem; }
  .zustell-inhalt table { width: 100%; border-collapse: collapse; font-size: 0.76rem; }
  .zustell-inhalt th {
    text-align: left; font-size: 0.68rem; text-transform: uppercase; letter-spacing: 0.04em;
    color: var(--text-faint); padding: 0 0.5rem 0.35rem 0; font-weight: 700;
  }
  .zustell-inhalt td { padding: 0.35rem 0.5rem 0.35rem 0; border-top: 1px solid var(--border); vertical-align: top; }
  .zustell-inhalt td.zeit { font-variant-numeric: tabular-nums; white-space: nowrap; }
  .zustell-inhalt .smtp-antwort { color: var(--text-muted); word-break: break-word; }

  .chip.warn { background: var(--accent-soft); color: var(--accent-deep); }
  .zustell-hinweis { margin: .6rem 0 0; font-size: .74rem; line-height: 1.5; color: var(--text-muted); }


  /* -------------------------------------------------------------------
     Druckfassung
     -------------------------------------------------------------------
     Auftrag 08.09.2026: "Der Besitzer soll sich das auch fuer seinen Samen
     schoen ausdrucken koennen." Gedruckt wird der Inhalt, nicht die
     Anwendung: Menue, Kopfleiste, Filter, Knoepfe und die Nachladezeile
     haben auf Papier keinen Sinn - man kann sie nicht anklicken.

     Bewusst hell und ohne Flaechen: Ein dunkles Thema wuerde eine
     Tonerpatrone kosten und waere schlechter lesbar. Deshalb setzt der
     Druck die Farben zurueck, statt sie zu uebernehmen. */
  @media print {
    :root, :root[data-theme="dark"], :root:not([data-theme="light"]) {
      --bg: #ffffff; --surface: #ffffff; --surface-strong: #ffffff;
      --text: #000000; --text-muted: #333333; --text-faint: #555555;
      --border: #999999; --border-strong: #666666;
    }
    body { background: #ffffff; color: #000000; }
    /* Der Seitenkopf klebt auf Papier nicht: Ein klebendes Element landet im
       Ausdruck entweder auf jeder Seite oder an einer zufaelligen Stelle -
       und seine negativen Raender wuerden den Titel ueber den Rand ziehen. */
    .seitenkopf { position: static; margin: 0; padding: 0; background: none;
      border-bottom: none; }
    .sidebar, .topbar-tools, .unternav, .umgebungsband, .sprungmarke,
    .csv-leiste, .nachladen, .palette, .palette-knopf, .mitteilungsband,
    .form-actions, [data-drucken] { display: none !important; }
    /* Filter- und Suchformulare sind auf Papier ein leeres Kaestchen mit
       einem Knopf, den niemand druecken kann. Sie gehen ueber die Methode:
       Ein Formular mit "get" filtert eine Ansicht, eines mit "post" aendert
       etwas - und Aendern gibt es beim Drucken ohnehin nicht. */
    form[method="get"] { display: none !important; }
    /* Dasselbe gilt fuer die Handlungsknoepfe in der Kopfzeile eines
       Bereichs ("Neue Charge", "Neuer Fall"). */
    .panel-head .btn-primary, .panel-head .btn-secondary { display: none !important; }
    /* Die Aktionsspalte wird leer, bleibt aber eine Zelle: "display:none" an
       einem <td> nimmt der Zeile das Tabellenlayout (test/tabellen.test.js).
       Auf Papier gibt es nichts zu klicken - der Platz darf trotzdem stehen. */
    .table-actions > * { display: none !important; }
    .app { display: block; }
    .main, .seiteninhalt { padding: 0; margin: 0; }
    .panel { border: none; box-shadow: none; padding: 0; margin: 0 0 1rem; break-inside: auto; }
    .panel-head { border-bottom: 1px solid #999; padding-bottom: .3rem; }
    .greeting h1 { font-size: 16pt; margin: 0 0 .2rem; }
    .greeting p { font-size: 10pt; margin: 0; }
    table.stock { font-size: 9.5pt; width: 100%; }
    table.stock th { border-bottom: 1px solid #666; }
    /* Eine Zeile soll nicht ueber den Seitenrand zerfallen, und die
       Kopfzeile wiederholt sich auf jeder Seite - sonst weiss man ab
       Seite zwei nicht mehr, welche Spalte welche ist. */
    table.stock tr { break-inside: avoid; }
    table.stock thead { display: table-header-group; }
    .chip { border: 1px solid #666; background: none !important; color: #000 !important; }
    a { color: #000; text-decoration: none; }
    .druck-fuss { display: block; margin-top: .8rem; font-size: 8.5pt; color: #333; }
  }
  .druck-fuss { display: none; }
