/* ── Die Farben: eine Quelle, zwei Modi (Issue #65) ───────────────────────
 *
 * Jede Farbe dieser Oberfläche steht **hier** und nirgendwo sonst. Weiter unten
 * gibt es keinen festen Farbwert mehr – nur noch `var(--…)`. Das ist die
 * Bedingung dafür, dass ein zweiter Modus überhaupt möglich ist: Wären die
 * Werte über 5000 Zeilen verstreut, bräuchte jede Regel eine zweite Fassung,
 * und die erste Regel ohne zweite wäre der helle Fremdkörper im dunklen Bild.
 *
 * Die Namen sagen, **wozu** eine Farbe da ist, nicht wie sie aussieht:
 * `--flaeche-karte` und nicht `--weiss`. Nur so kann derselbe Name im Dunkeln
 * etwas anderes bedeuten, ohne zu lügen.
 *
 * **Keine Invertierung.** Der dunkle Modus ist kein umgedrehter heller: Die
 * Flächen sind gestuft (Seite dunkler als Karte, Karte dunkler als Eingabefeld),
 * damit die Hierarchie erhalten bleibt, und nirgends steht reines Schwarz.
 * Umgekehrt werden Signalfarben im Dunkeln aufgehellt statt abgedunkelt – ein
 * Rot, das auf Weiß gut liest, verschwindet auf Dunkelgrau.
 *
 * **Was der helle Modus war, bleibt er.** Die Werte unter `:root` sind exakt
 * die, die vorher im Stylesheet standen. Zusammengelegt wurde nur, was sich
 * nicht unterscheiden ließ – etwa fünf fast gleiche Weißtöne für dieselbe
 * zurückgenommene Fläche.
 *
 * **Umgeschaltet wird nichts von Hand.** Es gibt keinen Schalter und keine
 * Einstellung; es gilt, was das Gerät sagt (`prefers-color-scheme`). Ein
 * Schalter wäre ein zweiter Zustand, den man speichern, übertragen und
 * beantworten müsste – der Auftrag verlangt ihn ausdrücklich nicht.
 *
 * `color-scheme` steht dabei nicht zur Zierde: Es sagt dem Browser, in welchem
 * Modus er seine **eigenen** Teile zeichnen soll – Scrollbalken, Datumswähler,
 * die aufgeklappte Liste eines `<select>`, den Hintergrund eines `<dialog>`.
 * Ohne diese eine Zeile bliebe genau das hell und stünde weiß im dunklen Bild.
 */
:root {
    color-scheme: light;

    /* Flächen, von der Seite nach innen gestuft */
    --flaeche-seite: #f4f5f7;
    --flaeche-karte: #fff;
    --flaeche-still: #fafbff;          /* zurückgenommen: Kind, intern, inaktiv */
    --flaeche-zart: #f8f9ff;           /* Hover, Hinweiskasten, Tagesspalte */
    --flaeche-sanft: #f3f4f6;          /* Merkmal, Badge, gesperrtes Feld */
    --flaeche-gewaehlt: #eef0ff;       /* ausgewählte Option */
    --flaeche-neutral: #e5e7eb;        /* neutrale Schaltfläche */
    --flaeche-neutral-stark: #d1d5db;  /* dieselbe beim Überfahren */
    --flaeche-eingabe: #fff;           /* Eingabefelder */
    --flaeche-gesperrt: #f3f4f6;       /* deaktivierte und schreibgeschützte */
    --flaeche-bild: #eef0f4;           /* Platzhalterfläche hinter Porträts */

    /* Text, von stark nach still */
    --text-stark: #1a1a2e;
    --text-lesetext: #1f2937;          /* Fließtext des Handbuchs */
    --text-normal: #374151;
    --text-mittel: #4b5563;
    --text-leise: #6b7280;
    --text-still: #9ca3af;
    --text-platzhalter: #9ca3af;       /* Vorbelegung in einem leeren Feld */
    --platzhalter-text: #b6bcc8;       /* Titelanfang und Silhouette */

    /* Rahmen und Trenner */
    --rand: #e5e7eb;
    --rand-stark: #d1d5db;
    --rand-sanft: #d7dbe3;
    --rand-zart: #f0f0f4;
    --rand-still: #9ca3af;
    --rand-akzent: #c7cbf5;

    /* Der Kopfbereich – in beiden Modi dunkel, deshalb eigene Namen */
    --kopf-flaeche: #1a1a2e;
    --kopf-text: #a8b2d8;
    --kopf-text-hell: #e8ecf8;
    --kopf-akzent: #e63946;

    /*
     * Und seine Höhe am Schreibtisch (Issue #164).
     *
     * Das ist keine Farbe, steht aber aus demselben Grund hier: Zwei Häuser
     * dieses Bestands zeichnen ihre eigene Kopfleiste – die Redaktion
     * (`.hauptmenue`) und die Entwicklungszentrale (`.zen-kopf`) –, und
     * Martin legt sie nebeneinander. Bis hierher entschied jede für sich:
     * links ein 68 px großes Portrait plus 0,6 rem Polster (knapp 88 px),
     * rechts ein 38 px hohes Logo plus 0,75 rem (62 px). Beide Zahlen hatten
     * ihren Grund, und nebeneinander sahen sie aus wie zwei Programme.
     *
     * Jetzt treffen sie sich in der Mitte. **Die Höhe kommt aus dieser einen
     * Zahl** und nicht mehr aus dem, was in der jeweiligen Leiste zufällig am
     * höchsten ist: Beide Köpfe tragen sie als `min-height`, und beide halten
     * ihren Inhalt darunter – das Portrait misst 56 px, das Logo 46 px, und
     * in beiden Fällen bleibt zwischen Bild und Kante noch Polster. Ein Maß,
     * das aus dem Inhalt fiele, wäre beim nächsten größeren Bild still wieder
     * verschieden.
     *
     * Das Telefon nimmt sie ausdrücklich nicht mit: Dort ist die Leiste
     * mehrzeilig und so flach wie möglich. Sie gilt am Schreibtisch, und dort
     * misst `automatisierung/qm-kopfhoehe.mjs` beide Köpfe im echten Browser
     * gegeneinander – mit einem Bildpunkt Toleranz.
     */
    --kopf-hoehe: 72px;

    /* Gefüllte Flächen: primäre Knöpfe, aktive Schalter, Tabellenkopf */
    --fuellung: #1a1a2e;
    --fuellung-text: #fff;
    --fuellung-text-leise: #c7cbf5;
    /*
     * Die leise Zusatzzeile in einem Spaltenkopf. Sie braucht ein eigenes
     * Token: `--text-still` liest sich auf Weiß und auf dem tiefdunklen
     * Kopf des hellen Modus, aber nicht mehr auf der aufgehellten Füllung
     * des dunklen – dort kam sie auf 2,8:1 statt der 5,6:1, die sie im
     * hellen Modus hat. Im Browser gemessen (`tests/bildbeleg_dunkelmodus`).
     */
    --fuellung-text-still: #9ca3af;

    /* Zustände: Fokus, Auswahl, Bedienfarbe */
    --kontur: #1a1a2e;
    --akzent: #e63946;                 /* Fläche und Kante der Marke */
    --akzent-text: #e63946;            /* dieselbe Marke als Schrift */
    --verweis: #1d4ed8;                /* Verweise im Fließtext */

    /* Erfolg */
    --erfolg-flaeche: #dcfce7;
    --erfolg-rand: #86efac;
    --erfolg-text: #166534;
    --erfolg-kraeftig: #16a34a;
    --erfolg-leise: #4d7c5f;
    --erfolg-zart-flaeche: #ecfdf5;
    --erfolg-zart-rand: #6ee7b7;
    --erfolg-zart-text: #047857;
    --erfolg-zart-kante: #059669;

    /* Fehler und Gefahr */
    --fehler-flaeche: #fee2e2;
    --fehler-flaeche-zart: #fdecec;
    --fehler-flaeche-stark: #fca5a5;
    --fehler-rand: #fca5a5;
    --fehler-text: #b91c1c;
    --fehler-kraeftig: #dc2626;
    --gefahr-flaeche-zart: #fff5f5;    /* Löschbereich, Konflikt, Warnkasten */
    --gefahr-rand-zart: #f0d8d8;

    /* Warnung und Hervorhebung */
    --warnung-flaeche: #fef3c7;
    --warnung-flaeche-zart: #fef9c3;
    --warnung-flaeche-blass: #fffbeb;
    --warnung-text: #92400e;
    --warnung-text-tief: #854d0e;
    --warnung-mittel: #b45309;
    --warnung-kraeftig: #d97706;
    --treffer-flaeche: #fde68a;        /* der Suchtreffer im Handbuch */
    --band-ausfall-flaeche: #fdf6ec;
    --band-ausfall-rand: #ead9bd;
    --band-ausfall-text: #7c5a1e;

    /* Einordnung: Rolle, Hinweis, Kategorie */
    --info-flaeche: #e0e7ff;
    --info-flaeche-zart: #eef2ff;
    --info-text: #3730a3;
    --info-text-tief: #1e3a8a;
    --info-marke: #4c51bf;
    --info-kante: #4338ca;
    --initialen-text: #6b70c4;
    --blau-flaeche: #dbeafe;
    --blau-text: #1e40af;
    --violett-flaeche: #f3e8ff;
    --violett-text: #6b21a8;
    --hinweis-kante: #2563eb;
    --hinweis-flaeche: #eff6ff;
    --hinweis-text: #1d4ed8;
    --voraussetzung-kante: #7c3aed;
    --voraussetzung-flaeche: #f5f3ff;
    --voraussetzung-text: #6d28d9;

    /*
     * Die Zeichen der Dashboard-Kacheln, je fachlicher Gruppe (Issue #341).
     *
     * Eine eng begrenzte Ausnahme und keine zweite Palette: Sie gilt
     * ausschließlich für das Symbol einer Kachel und seine kleine Unterlage,
     * nirgends sonst. Drei Familien – Blau für „Schnell starten", Petrol für
     * „Inhalte & Stammdaten", Violett für die GUDE KI –, und „Verwaltung &
     * Technik" bleibt neutral. Kein Rot, kein Grün, kein Orange: Marke und
     * Zustände behalten ihre Farben für sich.
     *
     * Nachgeschärft nach Martins Sichtprüfung vom 16.10.2026: Die erste
     * Fassung war zu dezent. Die Symbole stehen jetzt kräftig und gesättigt,
     * die Unterlage ist deutlich in derselben Familie getönt – auf den ersten
     * Blick erkennbar, aber weiterhin nur an dieser einen Stelle.
     *
     * Dieselben Werte stehen in `app/marke.py` (`ZEICHENFARBEN`), und dort
     * rechnet das Designhandbuch ihre Kontraste nach: Zeichen auf Unterlage
     * mindestens 4,2 : 1 hell und 5,4 : 1 dunkel.
     */
    --zeichen-start: #1a5fe6;
    --zeichen-start-flaeche: #d9e5fd;
    --zeichen-inhalte: #007a87;
    --zeichen-inhalte-flaeche: #cfeff2;
    --zeichen-ki: #7b3fe4;
    --zeichen-ki-flaeche: #e9ddfd;

    /* Ausgefallene Termine und freie Zeiten – beides tritt zurück */
    --ausfall-flaeche: #fbfbfd;
    --ausfall-rand: #cbd0dd;
    --ausfall-rand-hover: #a3aabd;
    --ausfall-text: #9096a6;
    --ausfall-marke-flaeche: #e6e8ee;
    --ausfall-marke-rand: #e0e2e8;
    --ausfall-marke-text: #5d6472;
    --frei-streifen-hell: #fafbfd;
    --frei-streifen-dunkel: #f1f3f7;
    --frei-text: #8b93a3;

    /* Das Wochenraster */
    --raster-heute: #fbfcff;
    --raster-linie-stunde: #dfe2ec;
    --raster-linie-halb: #f1f2f7;
    --raster-spaltenrand: #eceef5;
    --block-link-farbe: #c7cbd6;

    /*
     * Die Jetzt-Linie im Wochenraster (Issue #155).
     *
     * „Hell" heißt hier zurückgenommen und nicht wörtlich weiß: Auf der hellen
     * Fläche des Tagesrasters wäre echtes Weiß unsichtbar, und eine Linie, die
     * man nur über einem farbigen Block sieht, beantwortet die Frage „wo stehen
     * wir gerade?" gerade dort nicht, wo nichts läuft. Genommen ist deshalb der
     * hellste Ton, der sich auf der Spalte noch abzeichnet; im dunklen Modus
     * ist es dann tatsächlich ein Weißton.
     *
     * Der heutige Tag bekommt Gelb – ein Fokus, den der Blick von allein
     * findet, ohne dass die übrige Woche ihre Ruhe verliert. Dieselbe Farbe
     * trägt die Uhrzeit-Marke in der Zeitachse.
     *
     * Nachgeschärft nach Martins Sichtprüfung: Der zurückgenommene Teil war zu
     * schwach, um über dem Raster überhaupt aufzufallen. Er steht jetzt bei
     * 88 % Deckkraft – im Dunkeln ein Weiß, im Hellen ein deutlich tieferer
     * Grauton –, der heutige Abschnitt trägt sein Gelb voll und bekommt mit
     * `--jetzt-glanz` einen Hauch Schein, damit er sich auch auf einer farbigen
     * Karte absetzt. Ein Schein von wenigen Pixeln, kein Leuchtstift.
     */
    --jetzt-linie: rgba(122, 132, 152, 0.88);
    --jetzt-linie-heute: #eab308;
    --jetzt-glanz: rgba(161, 98, 7, 0.45);
    --jetzt-marke-flaeche: #fde047;
    --jetzt-marke-text: #713f12;

    /*
     * Konturen an Farbflächen. Sie machen einen sehr hellen Typenton auf Weiß
     * als Fläche erkennbar – und müssen sich im Dunkeln umdrehen, sonst
     * verschwände ein dunkler Ton auf dunklem Grund spurlos.
     */
    --kontur-farbklotz: rgba(0,0,0,0.1);
    --kontur-farbfeld: rgba(0, 0, 0, 0.25);

    /*
     * Die Unterlage der Screenshots im Handbuch. Sie bleibt in beiden Modi
     * hell: Die Bilder zeigen die helle Oberfläche, und ein dunkler Rand um
     * ein helles Bild sähe aus wie ein Fehler im Bild. Ausdrücklich wird
     * **nicht** invertiert – ein umgefärbter Screenshot zeigte etwas, das es
     * so nirgends gibt.
     */
    --bild-unterlage: #fff;

    /* Schatten. Im Dunkeln tiefer, sonst wären sie nicht zu sehen. */
    --schatten-zart: 0 1px 2px rgba(0, 0, 0, 0.04);
    --schatten-liste: 0 1px 3px rgba(0, 0, 0, 0.06);
    --schatten-karte: 0 1px 4px rgba(0,0,0,0.07);
    --schatten-block: 0 2px 10px rgba(0,0,0,0.14);
    --schatten-hoch: 0 4px 24px rgba(0,0,0,0.10);
    --schatten-dialog: 0 8px 32px rgba(0,0,0,0.15);
    --schatten-vorschlaege: 0 12px 28px rgba(15, 23, 42, 0.14);

    /* Abdunkelung hinter einem modalen Dialog (Issue #377). */
    --dialog-schleier: rgba(0, 0, 0, 0.4);
}

/*
 * Der dunkle Modus. Dieselben Namen, andere Werte – und keine einzige Regel,
 * die es nur hier gäbe.
 *
 * Die Stufung ist bewusst flach gehalten: Seite #14161c, Karte #1b1f27,
 * Eingabefeld #1e222b. Reines Schwarz kommt nicht vor, weil dann jede Kante
 * gleich weit entfernt wäre und die Ebenen ununterscheidbar würden.
 *
 * Die Textstufen sind nachgerechnet und liegen alle über dem 4,5:1 der
 * WCAG-Stufe AA auf der Kartenfläche – auch die leiseste (#868f9e, 5,0:1).
 * Das ist der Punkt, an dem ein dunkler Modus üblicherweise scheitert: grauer
 * Text auf fast gleichem grauem Grund.
 *
 * Das Radio-GUDE-Rot bleibt als Fläche unverändert (#e63946, weiße Schrift
 * darauf), wird als Schrift aber aufgehellt (#ff7b83): Auf #1b1f27 käme das
 * Original nur auf 3,3:1 und wäre für kleine Schrift zu schwach.
 */
@media (prefers-color-scheme: dark) {
    :root {
        color-scheme: dark;

        --flaeche-seite: #14161c;
        --flaeche-karte: #1b1f27;
        --flaeche-still: #20242d;
        --flaeche-zart: #232833;
        --flaeche-sanft: #262b35;
        --flaeche-gewaehlt: #2b3145;
        --flaeche-neutral: #333a46;
        --flaeche-neutral-stark: #414957;
        --flaeche-eingabe: #1e222b;
        --flaeche-gesperrt: #23272f;
        --flaeche-bild: #262b35;

        --text-stark: #e9ecf3;
        --text-lesetext: #dfe4ec;
        --text-normal: #d0d7e1;
        --text-mittel: #b9c1cd;
        --text-leise: #9fa8b7;
        /* Nachgerechnet an der gedämpftesten Fläche (`--flaeche-sanft`):
           4,9:1 statt der 4,4:1, auf die ein direkt übersetztes Grau
           gekommen wäre. Das ist die Stufe, an der ein dunkler Modus
           sonst zum grauen Text auf grauem Grund wird. */
        --text-still: #8f98a7;
        --text-platzhalter: #8f98a7;
        --platzhalter-text: #6f7887;

        --rand: #2e333d;
        --rand-stark: #3d4453;
        --rand-sanft: #3a404e;
        --rand-zart: #272b34;
        --rand-still: #4d5563;
        --rand-akzent: #4d5488;

        /* Der Kopf bleibt der Kopf – nur eine Spur tiefer als die Seite. */
        --kopf-flaeche: #101319;

        /* `--fuellung-text` und `--fuellung-text-leise` fehlen hier mit
           Absicht: Weiß auf der Füllung bleibt Weiß. Eine Zuweisung, die
           denselben Wert noch einmal setzt, sähe aus wie eine
           Entscheidung und wäre keine. */
        --fuellung: #3d4762;
        --fuellung-text-still: #b7bfcc;

        --kontur: #9aa5cf;
        --akzent-text: #ff7b83;
        --verweis: #8ab4f8;

        --erfolg-flaeche: #12331f;
        --erfolg-rand: #26663f;
        --erfolg-text: #86efac;
        --erfolg-kraeftig: #2fb35f;
        --erfolg-leise: #7fb99a;
        --erfolg-zart-flaeche: #10302a;
        --erfolg-zart-rand: #2a6b56;
        --erfolg-zart-text: #6fd8b2;
        --erfolg-zart-kante: #2fae83;

        --fehler-flaeche: #3a1a1a;
        --fehler-flaeche-zart: #331a1b;
        --fehler-flaeche-stark: #5e2727;
        --fehler-rand: #7d2c2c;
        --fehler-text: #ffa8a8;
        --fehler-kraeftig: #f05252;
        --gefahr-flaeche-zart: #2e1c1c;
        --gefahr-rand-zart: #4c3030;

        --warnung-flaeche: #3a2f0c;
        --warnung-flaeche-zart: #36300f;
        --warnung-flaeche-blass: #2f2913;
        --warnung-text: #f5c273;
        --warnung-text-tief: #f0c274;
        --warnung-mittel: #f0b45c;
        --warnung-kraeftig: #e79a35;
        /* Deutlich tiefer als ein bloß abgedunkeltes Gelb: Der Treffer erbt
           seine Schriftfarbe vom Text um ihn herum, und die ist hier hell.
           Bei #6d5411 kam er auf 3,9:1 – nachgemessen und korrigiert. */
        --treffer-flaeche: #5c460a;
        --band-ausfall-flaeche: #33290f;
        --band-ausfall-rand: #5a4a22;
        --band-ausfall-text: #e8c98a;

        --info-flaeche: #29315a;
        --info-flaeche-zart: #222a48;
        --info-text: #b6bef7;
        --info-text-tief: #c2d3ff;
        --info-marke: #a3a9f0;
        --info-kante: #7b83e8;
        --initialen-text: #a7ade9;
        --blau-flaeche: #1e3050;
        --blau-text: #a8c6f7;
        --violett-flaeche: #2f2544;
        --violett-text: #d6b7f5;
        --hinweis-kante: #4d86e8;
        --hinweis-flaeche: #182740;
        --hinweis-text: #9dc0ff;
        --voraussetzung-kante: #9a6ef0;
        --voraussetzung-flaeche: #261f3d;
        --voraussetzung-text: #c3aaf7;

        /* Dieselben drei Familien, leuchtend aufgehellt auf einer deutlich
           getönten dunklen Unterlage – kein blasses Symbol auf Grau. */
        --zeichen-start: #7faeff;
        --zeichen-start-flaeche: #1d3363;
        --zeichen-inhalte: #3fd0dc;
        --zeichen-inhalte-flaeche: #0f3c44;
        --zeichen-ki: #b794ff;
        --zeichen-ki-flaeche: #352663;

        --ausfall-flaeche: #1d2129;
        --ausfall-rand: #3a4150;
        --ausfall-rand-hover: #525b6d;
        --ausfall-text: #7c8494;
        --ausfall-marke-flaeche: #2b303b;
        --ausfall-marke-rand: #363c48;
        --ausfall-marke-text: #99a2b2;
        --frei-streifen-hell: #1e222a;
        --frei-streifen-dunkel: #262b34;
        --frei-text: #7f8898;

        --raster-heute: #202632;
        --raster-linie-stunde: #333a47;
        --raster-linie-halb: #262b34;
        --raster-spaltenrand: #2a2f39;
        --block-link-farbe: #5a6273;

        --jetzt-linie: rgba(255, 255, 255, 0.88);
        --jetzt-linie-heute: #facc15;
        --jetzt-glanz: rgba(250, 204, 21, 0.5);
        --jetzt-marke-flaeche: #facc15;
        --jetzt-marke-text: #3f2d05;

        --kontur-farbklotz: rgba(255, 255, 255, 0.28);
        --kontur-farbfeld: rgba(255, 255, 255, 0.45);

        --schatten-zart: 0 1px 2px rgba(0, 0, 0, 0.45);
        --schatten-liste: 0 1px 3px rgba(0, 0, 0, 0.5);
        --schatten-karte: 0 1px 4px rgba(0, 0, 0, 0.55);
        --schatten-block: 0 2px 10px rgba(0, 0, 0, 0.6);
        --schatten-hoch: 0 4px 24px rgba(0, 0, 0, 0.6);
        --schatten-dialog: 0 8px 32px rgba(0, 0, 0, 0.65);
        --schatten-vorschlaege: 0 12px 28px rgba(0, 0, 0, 0.6);

        --dialog-schleier: rgba(0, 0, 0, 0.6);
    }
}

/* ── Grundlayout ─────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

/*
 * Der Platz des senkrechten Scrollbalkens bleibt dauerhaft frei.
 *
 * Der Sendeplan ist in der Tagesansicht kürzer als in der Wochenansicht. Ohne
 * diese Zeile erscheint der Balken beim Wechsel und verschwindet wieder; die
 * nutzbare Breite ändert sich um seine Stärke, und der gesamte Inhalt – vor
 * allem die Schalterleiste oben – springt waagerecht mit.
 *
 * `scrollbar-gutter: stable` ist genau dafür gedacht und die standardsaubere
 * Lösung an der richtigen Ebene: Der Streifen wird reserviert, der Balken aber
 * nur gezeigt, wenn er gebraucht wird. Es wird nichts erzwungen und keine
 * zweite, waagerechte Leiste erzeugt. Browser ohne Unterstützung verwerfen die
 * Zeile und verhalten sich wie bisher.
 */
html { scrollbar-gutter: stable; }

body {
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
    background: var(--flaeche-seite);
    color: var(--text-stark);
    min-height: 100vh;
}

/* ── Hauptmenü ───────────────────────────────────────────── */
/*
 * `min-height` ist seit Issue #164 das Maß dieser Leiste, nicht mehr das Logo.
 *
 * Vorher bestimmte der höchste Inhalt die Höhe: 38 px Logo plus zweimal
 * 0,75 rem Polster waren 62 px. Die Entwicklungszentrale kam auf demselben
 * Weg auf knapp 88 px, und nebeneinander gelegt sahen die beiden Häuser aus
 * wie zwei Programme. Jetzt tragen beide dieselbe Zahl (`--kopf-hoehe`, siehe
 * dort) – und weil der Inhalt darunter bleibt, ist es wirklich dieselbe und
 * keine, die sich beim nächsten etwas höheren Element auseinanderbewegt.
 *
 * Auf dem Telefon wird sie im 640-px-Block wieder aufgehoben: Dort ist die
 * Leiste mehrzeilig, und eine Mindesthöhe von 72 px wäre dort verschenkter
 * Bildschirm.
 */
.hauptmenue {
    display: flex;
    justify-content: space-between;
    align-items: center;
    background: var(--kopf-flaeche);
    color: var(--fuellung-text);
    padding: 0.75rem 1.5rem;
    min-height: var(--kopf-hoehe);
    position: sticky;
    top: 0;
    z-index: 100;
}

/*
 * 46 px statt 38 (Issue #164).
 *
 * „Das Beste aus beiden bisherigen Varianten" ist Martins Wortlaut: Links
 * wird das Portrait der Zentrale etwas kleiner, rechts das Radio-GUDE-Logo
 * etwas größer. 46 px lassen in der 72 px hohen Leiste oben und unten 13 px
 * Luft – das Logo steht darin vollständig und mittig, und die Leiste bleibt
 * eine Arbeitsoberfläche und kein Aushängeschild.
 *
 * Die Breite kommt weiterhin aus dem Seitenverhältnis (`width: auto`); die
 * Datei ist quadratisch, das Logo ist also 8 px breiter als vorher. Die
 * Navigation daneben rechnet damit – `static/menue.js` misst, was übrig ist,
 * und räumt notfalls einen Bereich mehr unter „Mehr".
 */
.logo-bild {
    height: 46px;
    width: auto;
    display: block;
}

.untertitel {
    margin-left: 0.6rem;
    font-size: 0.9rem;
    color: var(--kopf-text);
}

.menue-rechts {
    display: flex;
    align-items: center;
    gap: 1rem;
    /* Ohne diese Zeile kann die Zone nicht schmaler werden als ihr Inhalt –
       und ein langer Anzeigename schob den ganzen Kopf in eine zweite Zeile.
       Siehe die Begründung bei `.benutzer`. */
    min-width: 0;
}

/*
 * Der Name darf als Letzter nachgeben.
 *
 * Im Kopf stehen drei Zonen: Logo und Produktname links, die Bereiche in der
 * Mitte, Name, Rolle und `Abmelden` rechts. Wird es eng, gibt zuerst die Mitte
 * nach – `menue.js` räumt Bereiche unter „Mehr". Reicht auch das nicht, weil
 * jemand einen sehr langen Anzeigenamen trägt, dann kürzt der Name sich selbst
 * mit Auslassungspunkten, statt den Kopf in eine zweite Zeile zu schieben.
 *
 * Vollständig steht er trotzdem noch da: als `title` am selben Element, und
 * sobald das Fenster wieder breit genug ist, auch wieder sichtbar. Rolle und
 * `Abmelden` bleiben unangetastet – sie sind kurz und werden gebraucht.
 */
.benutzer {
    font-size: 0.9rem;
    color: var(--kopf-text);
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.abmelden {
    font-size: 0.85rem;
    color: var(--kopf-akzent);
    text-decoration: none;
    border: 1px solid var(--kopf-akzent);
    padding: 0.25rem 0.6rem;
    border-radius: 4px;
    transition: background 0.15s;
}

.abmelden:hover { background: var(--kopf-akzent); color: var(--fuellung-text); }

/* ── Login ───────────────────────────────────────────────── */
.login-wrapper {
    display: flex;
    justify-content: center;
    align-items: center;
    min-height: 100vh;
    padding: 2rem;
}

.login-box {
    background: var(--flaeche-karte);
    border-radius: 10px;
    box-shadow: var(--schatten-hoch);
    padding: 2.5rem 2rem;
    width: 100%;
    max-width: 380px;
}

.login-box h1 {
    font-size: 1.6rem;
    color: var(--akzent-text);
    margin-bottom: 0.2rem;
}

.login-untertitel {
    color: var(--text-leise);
    margin-bottom: 1.8rem;
    font-size: 0.95rem;
}

.fehler-meldung {
    background: var(--fehler-flaeche);
    color: var(--fehler-text);
    border: 1px solid var(--fehler-rand);
    border-radius: 6px;
    padding: 0.7rem 1rem;
    margin-bottom: 1.2rem;
    font-size: 0.9rem;
}

.formfeld {
    margin-bottom: 1.1rem;
}

.formfeld label {
    display: block;
    font-size: 0.85rem;
    font-weight: 600;
    color: var(--text-normal);
    margin-bottom: 0.35rem;
}

.formfeld input {
    width: 100%;
    padding: 0.6rem 0.8rem;
    border: 1px solid var(--rand-stark);
    border-radius: 6px;
    font-size: 0.95rem;
    transition: border-color 0.15s;
}

.formfeld input:focus {
    outline: none;
    border-color: var(--kontur);
}

.anmelde-button {
    width: 100%;
    padding: 0.7rem;
    background: var(--fuellung);
    color: var(--fuellung-text);
    border: none;
    border-radius: 6px;
    font-size: 1rem;
    font-weight: 600;
    cursor: pointer;
    margin-top: 0.5rem;
    transition: background 0.15s;
}

.anmelde-button:hover { background: var(--akzent); }

/*
 * Der zweite Weg unter der Anmeldeschaltfläche: „Passwort vergessen?" und die
 * Rückwege der Reset-Seiten (Issue #69). Mittig und leiser als der Knopf – wer
 * sein Passwort weiß, soll ihn gar nicht erst lesen müssen.
 */
.login-nebenweg {
    margin-top: 1.1rem;
    text-align: center;
    font-size: 0.88rem;
    color: var(--text-leise);
}

.login-nebenweg a { color: var(--akzent-text); }

/* Der erklärende Satz über einem Formular in der Anmeldekarte. */
.formular-einleitung {
    font-size: 0.88rem;
    color: var(--text-leise);
    margin-bottom: 1.2rem;
    line-height: 1.5;
}

/*
 * Die ruhige Schwester von `.fehler-meldung` in derselben Karte: eine
 * Auskunft, keine Störung. Sie trägt die neutrale Antwort auf eine
 * Reset-Anforderung – die darf nicht nach Fehler aussehen und nicht nach
 * Erfolg, denn sie sagt bewusst nicht, ob es das Konto gibt.
 */
.hinweis-meldung {
    background: var(--flaeche-zart);
    color: var(--text-normal);
    border: 1px solid var(--rand-sanft);
    border-radius: 6px;
    padding: 0.7rem 1rem;
    margin-bottom: 1.2rem;
    font-size: 0.9rem;
    line-height: 1.5;
}

/* ── Passwortfeld mit Augensymbol (Issue #69) ────────────── */
/*
 * Der Knopf liegt im Feld und nicht daneben: Daneben kostete er auf dem
 * Telefon die halbe Zeile, und das Feld selbst ist dort das Knappste, was es
 * gibt. Der zusätzliche rechte Innenabstand hält den Text von ihm fern.
 *
 * Ohne JavaScript gibt es den Knopf nicht – er steht `hidden` im Dokument und
 * wird erst von `static/passwort-auge.js` eingeblendet. Das Feld funktioniert
 * dann unverändert, nur ohne Umschalter.
 */
.passwortfeld {
    position: relative;
    display: block;
}

/*
 * Drei Selektoren für eine Regel, und der Grund ist Gewicht, nicht Geschmack:
 * `.formular-zeile input[type="password"]` weiter unten setzt `padding` als
 * Kurzform und wiegt schwerer als eine Klasse allein – der rechte Abstand für
 * das Auge wäre dort wieder weg, und der eingegebene Text liefe unter das
 * Symbol. Die beiden längeren Selektoren nennen die zwei Umgebungen, in denen
 * ein Passwortfeld vorkommt, und wiegen damit schwerer.
 */
.passwortfeld .passwortfeld-eingabe,
.formfeld .passwortfeld .passwortfeld-eingabe,
.formular-zeile .passwortfeld .passwortfeld-eingabe {
    width: 100%;
    padding-right: 2.9rem;
}

.passwort-auge {
    position: absolute;
    top: 50%;
    right: 0.3rem;
    transform: translateY(-50%);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.2rem;
    height: 2.2rem;
    padding: 0;
    background: none;
    border: 1px solid transparent;
    border-radius: 6px;
    color: var(--text-leise);
    cursor: pointer;
}

.passwort-auge:hover { color: var(--text-stark); }

.passwort-auge:focus-visible {
    outline: 2px solid var(--kontur);
    outline-offset: 1px;
}

/* Steht nach den Maßen und gewinnt trotzdem – ein Attributselektor wiegt
   schwerer als eine Klasse, unabhängig von der Reihenfolge. */
.passwort-auge[hidden] { display: none; }

.auge-symbol {
    width: 1.2rem;
    height: 1.2rem;
}

/*
 * Welches der beiden Symbole zu sehen ist, hängt an genau einem Zustand:
 * `aria-pressed` am Knopf. Solange das Passwort verdeckt ist, steht dort das
 * offene Auge – es sagt, was ein Klick bewirken würde. Ist es sichtbar, steht
 * dort das durchgestrichene.
 *
 * Bewusst über das Stylesheet und nicht über ein `hidden` am `<svg>`: Ein
 * SVG-Element kennt die Eigenschaft `hidden` nicht, `element.hidden = true`
 * legte dort nur ein Feld am Objekt an und ließe das Bild stehen.
 */
.auge-verbergen { display: none; }
.passwort-auge[aria-pressed="true"] .auge-anzeigen { display: none; }
.passwort-auge[aria-pressed="true"] .auge-verbergen { display: block; }

/* ── Mein Konto (Issue #69) ──────────────────────────────── */
.konto-uebersicht { margin-bottom: 1.4rem; }

.konto-angaben {
    display: grid;
    grid-template-columns: auto 1fr;
    gap: 0.4rem 1rem;
    margin: 0.7rem 0 0.9rem;
    font-size: 0.92rem;
}

.konto-angaben dt {
    color: var(--text-leise);
    font-weight: 600;
}

.konto-angaben dd { margin: 0; }

/*
 * Der eigene Name in der Kopfzeile führt nach „Mein Konto". Der Verweis liegt
 * um `.benutzer` herum und erbt dessen Farbe – sichtbar wird er erst beim
 * Überfahren. `min-width: 0` muss mit: Ohne ihn könnte der Name in der
 * Kopfzeile nicht mehr kürzen (Issue #63).
 */
.benutzer-konto {
    display: flex;
    align-items: center;
    min-width: 0;
    color: inherit;
    text-decoration: none;
    border-radius: 4px;
}

.benutzer-konto:hover .benutzer { text-decoration: underline; }

.benutzer-konto:focus-visible {
    outline: 2px solid var(--kopf-akzent);
    outline-offset: 2px;
}

/*
 * Nachahmen (Issue #238) – der Einstieg im Benutzermenü und die Kennzeichnung
 * im Kopf.
 *
 * Der Einstieg steht neben „Abmelden" und sieht aus wie dieses, nur in der
 * ruhigen Kopffarbe statt im Akzent: Er ist eine Werkzeugfunktion und keine
 * Aufforderung. Die Kennzeichnung „nachgeahmt von …" ist gestrichelt umrandet
 * – erkennbar eine Ausnahme, aber kein Alarm. Der Kopf ist in beiden
 * Farbschemata dunkel; beide benutzen deshalb dieselben Kopffarben wie Name
 * und Rolle daneben.
 */
.nachahmen-einstieg {
    font-size: 0.85rem;
    color: var(--kopf-text);
    text-decoration: none;
    border: 1px solid var(--kopf-text);
    padding: 0.25rem 0.6rem;
    border-radius: 4px;
    white-space: nowrap;
    transition: background 0.15s;
}

.nachahmen-einstieg:hover { background: var(--kopf-text); color: var(--kopf-flaeche); }

.nachahmen-einstieg:focus-visible {
    outline: 2px solid var(--kopf-akzent);
    outline-offset: 2px;
}

.nachahmung-marke {
    font-size: 0.72rem;
    color: var(--kopf-text);
    border: 1px dashed var(--kopf-akzent);
    border-radius: 999px;
    padding: 0.1rem 0.5rem;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    min-width: 0;
}

/*
 * Das Band über jeder Seite, solange eine Nachahmung läuft.
 *
 * Die Farbe ist die der Auskunft (`--info-…`) und nicht die der Warnung oder
 * des Fehlers: Es ist nichts kaputt, aber man soll es nicht übersehen. Die
 * Kante links trägt die Auffälligkeit, die Fläche bleibt ruhig – in hell wie
 * in dunkel aus denselben Tokens.
 */
.nachahmung-band {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 0.6rem 1rem;
    /* Auch in den festen Arbeitsansichten (`seite-fest`), in denen `<main>`
       ein Flex-Kasten ist: volle Breite, nie gestaucht. */
    box-sizing: border-box;
    width: 100%;
    flex: 0 0 auto;
    max-width: 1200px;
    margin: 0 auto 1.2rem;
    padding: 0.75rem 1rem;
    background: var(--info-flaeche);
    color: var(--info-text-tief);
    border: 1px solid var(--info-kante);
    border-left-width: 5px;
    border-radius: 6px;
}

.nachahmung-band-text {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
    min-width: 0;
    font-size: 0.92rem;
}

.nachahmung-band-text strong { font-size: 1rem; }

.nachahmung-band-aktion { margin: 0; }

.nachahmung-band-aktion .schaltflaeche {
    background: var(--flaeche-karte);
    color: var(--info-text-tief);
    border: 1px solid var(--info-kante);
    white-space: nowrap;
}

.nachahmung-band-aktion .schaltflaeche:hover { background: var(--info-flaeche-zart); }

/* Die Auswahl unter /konto/nachahmen. */
.nachahmen-suche { margin: 1rem 0; }

.nachahmen-suche label {
    display: block;
    margin-bottom: 0.35rem;
    font-weight: 600;
    color: var(--text-normal);
}

.nachahmen-suche-zeile { display: flex; gap: 0.5rem; }

.nachahmen-suche-zeile input { flex: 1 1 auto; min-width: 0; }

.nachahmen-liste {
    list-style: none;
    margin: 0;
    padding: 0;
    border: 1px solid var(--flaeche-neutral);
    border-radius: 6px;
    background: var(--flaeche-karte);
}

.nachahmen-eintrag {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    padding: 0.6rem 0.9rem;
    border-top: 1px solid var(--flaeche-neutral);
}

.nachahmen-eintrag:first-child { border-top: none; }

.nachahmen-eintrag[hidden] { display: none; }

.nachahmen-eintrag form { margin: 0; }

.nachahmen-person {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.2rem 0.6rem;
    min-width: 0;
}

.nachahmen-name { color: var(--text-normal); font-weight: 600; }

.nachahmen-rolle { color: var(--text-normal); font-size: 0.82rem; opacity: 0.8; }

/* Die Auskunft über den Mailversand in der Benutzerverwaltung. */
.mailstand-bereit { margin-bottom: 1.2rem; }

/* ── Dashboard ───────────────────────────────────────────── */
main { padding: 2rem 1.5rem; }

/*
 * Der Inhaltsbereich ist seit Issue #134 fokussierbar (`tabindex="-1"`), damit
 * die Schale den Fokus nach einem Bereichswechsel dorthin setzen kann. Sehen
 * soll man davon nichts: Der Fokusring gehört an Bedienelemente, die man
 * angesteuert hat, und nicht um eine ganze Seite.
 *
 * `:focus` und nicht `:focus-visible` – ein programmatisch gesetzter Fokus ist
 * für den Browser kein Tastaturfokus, und ein Ring erschiene je nach Browser
 * doch. Für die Tastatur ändert sich dadurch nichts: In die Tabulatorreihe
 * kommt der Bereich mit `-1` ohnehin nicht.
 */
main[tabindex="-1"]:focus { outline: none; }

/*
 * Während die Schale die nächste Seite holt.
 *
 * Bis dahin steht alles da, wo es war – das ist der Zweck von Issue #134. Genau
 * deshalb braucht es ein Zeichen, dass etwas passiert: Beim Neuladen zeigte der
 * Browser seinen eigenen Ladefortschritt, jetzt zeigt er keinen.
 *
 * Ein Zeiger und sonst nichts. Eine Fortschrittsleiste wäre ein neues Element
 * in einer Leiste, an der dieser Auftrag ausdrücklich nichts ändern soll, und
 * jedes Abdunkeln des Inhalts wäre wieder ein Flackern. Wie es um die Seite
 * steht, sagt zusätzlich `aria-busy` am Körper – für alle, die nichts sehen.
 *
 * Die zweite Zeile ist nicht überflüssig: Ein Verweis trägt vom Browser aus
 * `cursor: pointer`, und geerbt wird nur, was nicht selbst gesetzt ist – ohne
 * sie zeigte ausgerechnet der angeklickte Bereich weiter die Hand.
 */
html.schale-laedt,
html.schale-laedt a[href] { cursor: progress; }

/* ── Die einfache Karte ───────────────────────────────────────────────────
 *
 * Ein ruhiger Kasten mit Überschrift und Satz. Benutzt wird er von den
 * mAirList-Ansichten, „Mein Konto" und der Hinweisseite von Guder Draht.
 *
 * Bis Issue #157 stand darunter eine zweite Fassung `.karte-aktiv` – sie war
 * ausschließlich für das Dashboard da und hat dort Textfarbe und Verweise
 * aufgehellt. Das Dashboard hat seitdem eigene Kacheln (siehe unten), und eine
 * Variante ohne Benutzer wäre eine Regel, die niemand mehr lesen kann.
 */
.karte {
    background: var(--flaeche-karte);
    border: 1px solid var(--rand);
    border-radius: 8px;
    padding: 1.2rem 1.4rem;
}

.karte h3 {
    font-size: 1rem;
    margin-bottom: 0.4rem;
    color: var(--text-stark);
}

.karte p {
    font-size: 0.85rem;
    color: var(--text-still);
}

/* ── Das Dashboard: Kopf, Abschnitte, Kacheln (Issue #157) ─────────────────
 *
 * Bis zu diesem Auftrag waren es sechs gleich aussehende Textkästen unter einer
 * Begrüßung, und einer davon – „Stammdaten" – trug vier unterstrichene Verweise
 * in einer Zeile. Was daraus geworden ist, steht in drei Regeln und nicht in
 * einer Bildsprache:
 *
 *   * **Die ganze Kachel ist der Weg.** Der Verweis im Titel legt sich als
 *     unsichtbare Fläche über die Kachel (`.kachel-haupt::after`). Kein
 *     JavaScript, kein `<a>` um andere Verweise herum, und der Fokusrahmen
 *     liegt genau auf derselben Fläche – also um die ganze Kachel.
 *   * **Ein Zeichen je Kachel, ruhig und funktional.** Es steht in einer
 *     zurückgenommenen Fläche links vom Titel und ist `aria-hidden`: Die
 *     Beschriftung bleibt der Text. Seit Issue #341 trägt es – und nur es –
 *     die kräftige Farbe seiner fachlichen Gruppe; siehe `--zeichen-*`.
 *   * **Nur Tokens.** Kein eigener Farbwert, also auch keine zweite Fassung für
 *     den dunklen Modus. Rot ist Akzent und nicht Dekoration – es erscheint
 *     genau an einer Stelle, am Wegzeichen unter dem Zeiger.
 */
.dashboard-wrapper { max-width: 1120px; margin: 0 auto; }

/*
 * Begrüßung und Rollenmarke stehen in einer Zeile und rücken erst auf dem
 * Telefon untereinander. Eine große Hero-Fläche wäre verschenkte Arbeitsfläche:
 * Diese Seite soll Wege zeigen und nicht sich selbst.
 */
.dashboard-kopf {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem 0.8rem;
    margin-bottom: 1.6rem;
}

.dashboard-wrapper h2 {
    font-size: 1.4rem;
    margin-bottom: 0;
}

.rolle-badge {
    display: inline-block;
    font-size: 0.75rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    padding: 0.2rem 0.6rem;
    border-radius: 999px;
    background: var(--info-flaeche);
    color: var(--info-text);
}

.rolle-badge.rolle-admin { background: var(--fehler-flaeche); color: var(--fehler-text); }
.rolle-badge.rolle-redakteur { background: var(--erfolg-flaeche); color: var(--erfolg-text); }
.rolle-badge.rolle-moderator { background: var(--warnung-flaeche-zart); color: var(--warnung-text-tief); }

.dashboard-abschnitt { margin-bottom: 2rem; }
.dashboard-abschnitt:last-child { margin-bottom: 0; }

/*
 * Die Abschnittsüberschrift ist eine Marke und keine zweite Schlagzeile: klein,
 * versal, leise. Sie ordnet, ohne mit den Kacheln um Aufmerksamkeit zu ringen –
 * und eine Rolle mit wenigen Rechten bekommt dadurch keine Seite, die aus
 * Überschriften besteht.
 */
.abschnitt-titel {
    font-size: 0.78rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.07em;
    color: var(--text-leise);
    margin-bottom: 0.7rem;
}

/*
 * Ein Raster, das sich selbst füllt: Am Schreibtisch stehen vier Kacheln
 * nebeneinander, auf einem kleinen Laptop drei, auf dem Tablet zwei, auf dem
 * Telefon eine. Das braucht keinen einzigen zusätzlichen Haltepunkt – und weil
 * die zweite Spalte `minmax(0, 1fr)` ist, kann kein langer Name die Kachel
 * breiter machen als das Raster.
 */
.kachelfeld {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
    gap: 0.9rem;
}

.kachel {
    position: relative;
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) auto;
    /*
     * Die Kacheln einer Reihe sind alle so hoch wie die höchste – das ist das
     * Raster und richtig so. Ihr Inhalt darf deshalb aber nicht mitwachsen:
     * Ohne diese Zeile verteilte der Browser die übrige Höhe auf die Zeilen der
     * Kachel, und die Titel einer Reihe stünden auf vier verschiedenen Höhen.
     */
    align-content: start;
    column-gap: 0.85rem;
    padding: 1rem 1.1rem;
    border: 1px solid var(--rand);
    border-radius: 10px;
    background: var(--flaeche-karte);
}

/*
 * Feste Plätze statt automatischer Verteilung: Zeichen, Titel und Wegzeichen
 * stehen in derselben Zeile; der Satz darunter und die kleinen Nebenaktionen
 * darunter nehmen die **ganze** Breite. Ein Satz, der neben dem Zeichen
 * eingerückt bliebe, hätte in einer 15-rem-Spalte noch gut die Hälfte davon –
 * zwei Wörter je Zeile, und aus zwei Zeilen würden fünf.
 *
 * Eine Kachel ohne Nebenaktionen lässt die dritte Zeile einfach leer; dadurch
 * sehen Kacheln mit und ohne Zusatz gleich gebaut aus.
 */
.kachel-zeichen  { grid-area: 1 / 1; }
.kachel-titel    { grid-area: 1 / 2; align-self: center; }
.kachel-weg      { grid-area: 1 / 3; align-self: center; }
.kachel-kurz     { grid-area: 2 / 1 / auto / -1; }
.kachel-aktionen { grid-area: 3 / 1 / auto / -1; }

.kachel-zeichen {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 38px;
    height: 38px;
    border-radius: 10px;
    background: var(--kachelzeichen-flaeche, var(--flaeche-sanft));
}

.kachel-zeichen svg {
    width: 20px;
    height: 20px;
    fill: var(--kachelzeichen, var(--text-mittel));
}

/*
 * Die Gruppenfarbe der Zeichen (Issue #341). Gewählt wird sie an der
 * fachlichen Gruppe des Kachelfeldes (`data-zeichengruppe` aus
 * `app/dashboard.py`) – nie an Titel, Reihenfolge oder Position. Gesetzt
 * werden nur die beiden Werte, die die Zeichen oben lesen; Kachel, Titel,
 * Rahmen und Wegzeichen bleiben, wie sie waren. Eine Gruppe ohne eigene
 * Zeile hier („verwaltung") behält die neutralen Werte.
 */
.kachelfeld[data-zeichengruppe="start"] {
    --kachelzeichen: var(--zeichen-start);
    --kachelzeichen-flaeche: var(--zeichen-start-flaeche);
}

.kachelfeld[data-zeichengruppe="inhalte"] {
    --kachelzeichen: var(--zeichen-inhalte);
    --kachelzeichen-flaeche: var(--zeichen-inhalte-flaeche);
}

.kachelfeld[data-zeichengruppe="ki"] {
    --kachelzeichen: var(--zeichen-ki);
    --kachelzeichen-flaeche: var(--zeichen-ki-flaeche);
}

.kachel-titel {
    font-size: 1rem;
    font-weight: 600;
    color: var(--text-stark);
}

.kachel-haupt {
    color: inherit;
    text-decoration: none;
    /* „mAirList-Lerndaten" ist länger als die Spalte schmal werden kann. */
    overflow-wrap: break-word;
}

/*
 * Die Fläche, die die ganze Kachel zum Weg macht. Sie liegt über dem Satz und
 * dem Zeichen und unter den Nebenaktionen – die sind später im Dokument und
 * tragen zusätzlich `z-index`.
 */
.kachel-haupt::after {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: 10px;
}

.kachel-kurz {
    margin-top: 0.4rem;
    font-size: 0.85rem;
    line-height: 1.45;
    color: var(--text-normal);
    overflow-wrap: break-word;
}

.kachel-weg {
    display: flex;
    align-items: center;
}

.kachel-weg svg {
    width: 18px;
    height: 18px;
    fill: var(--text-still);
}

.kachel:hover {
    border-color: var(--rand-stark);
    background: var(--flaeche-zart);
}

.kachel:hover .kachel-weg svg { fill: var(--akzent-text); }

/*
 * Der Fokusrahmen sitzt auf derselben Fläche, die auch der Zeiger trifft. Ohne
 * diesen Umweg läge er um die drei Wörter des Titels und nicht um die Kachel –
 * und wer mit der Tastatur arbeitet, sähe nicht, was er gerade auslöst.
 */
.kachel-haupt:focus-visible { outline: none; }

.kachel-haupt:focus-visible::after {
    outline: 2px solid var(--kontur);
    outline-offset: 2px;
}

/*
 * Die Nebenaktionen. Sie sind ausdrücklich klein und rund: Es sind Abkürzungen
 * neben dem Hauptziel und keine zweiten Kacheln. Auf dem Telefon bekommen sie
 * ihre 44 px – siehe den Telefonblock weiter unten.
 */
.kachel-aktionen {
    position: relative;
    z-index: 1;
    display: flex;
    flex-wrap: wrap;
    gap: 0.35rem;
    margin-top: 0.6rem;
}

.kachel-aktion {
    display: inline-flex;
    align-items: center;
    padding: 0.2rem 0.6rem;
    border: 1px solid var(--rand);
    border-radius: 999px;
    background: var(--flaeche-still);
    font-size: 0.78rem;
    color: var(--text-normal);
    text-decoration: none;
}

.kachel-aktion:hover,
.kachel-aktion:focus-visible {
    border-color: var(--rand-stark);
    background: var(--flaeche-sanft);
    color: var(--text-stark);
}

/*
 * „Führt hinaus" – dasselbe Zeichen und dieselbe Begründung wie bei den
 * externen Bereichen der Kopfleiste (`.menue-extern::after`): Der Pfeil kommt
 * aus dem Stylesheet und nicht als Element ins Dokument, damit die Beschriftung
 * genau einen Textknoten behält. Was beim Klicken passiert, sagt zusätzlich das
 * `aria-label` am Verweis.
 *
 * Die ganze Kachel braucht ihn nicht: Sie trägt ihn rechts als Wegzeichen.
 */
.kachel-aktion-extern::after {
    content: "\2197";
    margin-left: 0.25rem;
    font-size: 0.85em;
}

/*
 * Der Abschnitt „Uploads": ein Überblick statt eines Kachelfeldes (Issue #203).
 *
 * Er steht in derselben Reihe von Abschnitten wie die Kacheln, unter derselben
 * leisen Überschrift, und er trägt dieselbe Karte: derselbe Rand, dieselbe
 * Rundung, dieselbe Fläche, dieselben Innenabstände wie `.kachel`. Das ist die
 * Auflage „bestehendes Dashboard-Raster und visuelle Hierarchie beibehalten" –
 * nicht ein neuer Kastentyp, sondern eine Kachel in voller Breite, in der etwas
 * mehr steht als ein Satz.
 *
 * Gebaut ausschließlich aus Tokens; der dunkle Modus gilt damit ohne eine
 * einzige eigene Regel (Issue #85).
 */
.uploadblick {
    display: grid;
    gap: 0.7rem;
    padding: 1rem 1.1rem;
    border: 1px solid var(--rand);
    border-radius: 10px;
    background: var(--flaeche-karte);
}

.uploadblick-kopf {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.35rem 0.7rem;
    margin: 0;
}

/*
 * Der Zustand der Anbindung als kleine Marke – dieselbe Form wie die Marke an
 * einer Protokollzeile unter `/uploads`.
 */
.uploadblick-marke {
    padding: 0.1rem 0.5rem;
    border-radius: 999px;
    font-size: 0.72rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    background: var(--erfolg-flaeche);
    color: var(--erfolg-text);
}

/*
 * „Nicht bereit" trägt die Warnfarbe und nicht das Fehlerrot: Auf der
 * Startseite ist das eine Auskunft und kein Störfall – der Auftrag verlangt
 * „Fehlerstatus klar, aber nicht alarmistisch darstellen". Was zu tun ist,
 * steht als Satz daneben, und er kommt aus der Fachlogik.
 */
.uploadblick-marke-still {
    background: var(--warnung-flaeche-zart);
    color: var(--warnung-text-tief);
}

.uploadblick-satz {
    font-size: 0.85rem;
    line-height: 1.45;
    color: var(--text-leise);
}

.uploadblick-zahlen {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-wrap: wrap;
    gap: 0.35rem 1.6rem;
}

.uploadblick-zahlen li {
    display: flex;
    align-items: baseline;
    gap: 0.35rem;
}

.uploadblick-zahl {
    font-size: 1.35rem;
    font-weight: 700;
    line-height: 1.1;
    color: var(--text-stark);
}

/* Nur die Zahl kippt, nie die Fläche – wie bei den dringenden Gruppen der
   Attention-Zentrale. */
.uploadblick-zahl-fehler { color: var(--fehler-text); }

.uploadblick-wort {
    font-size: 0.82rem;
    color: var(--text-leise);
}

.uploadblick-leer,
.uploadblick-hinweis {
    margin: 0;
    font-size: 0.85rem;
    line-height: 1.45;
    color: var(--text-leise);
}

.uploadblick-liste {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
}

.uploadblick-zeile {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.2rem 0.6rem;
    padding: 0.4rem 0.65rem;
    border: 1px solid var(--rand);
    border-radius: 8px;
    background: var(--flaeche-still);
}

.uploadblick-datei {
    font-size: 0.87rem;
    font-weight: 600;
    color: var(--text-normal);
    /* „Bauernregel Oktober.mp3" ist länger als die Spalte schmal werden kann. */
    overflow-wrap: anywhere;
}

.uploadblick-typ,
.uploadblick-zeit {
    font-size: 0.8rem;
    color: var(--text-leise);
}

/*
 * Der Stand rutscht an den rechten Rand, solange die Zeile in eine Zeile passt;
 * auf dem Telefon bricht sie um, und dann steht er links unter dem Namen. Ein
 * `margin-left: auto` und keine zweite Spaltenregel.
 */
.uploadblick-stand {
    margin-left: auto;
    padding: 0.1rem 0.45rem;
    border-radius: 999px;
    font-size: 0.72rem;
    font-weight: 700;
    background: var(--erfolg-flaeche);
    color: var(--erfolg-text);
}

.uploadblick-stand-fehler {
    background: var(--fehler-flaeche);
    color: var(--fehler-text);
}

.uploadblick-wege {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.4rem;
    margin: 0;
}

/*
 * Der Einstieg. Er ist der einzige Hauptweg dieses Abschnitts und sieht
 * deshalb kräftiger aus als die kleine Nebenaktion daneben – aber nicht wie
 * eine Schaltfläche: Auf dieser Seite wird nichts abgeschickt, hier wird
 * gegangen.
 */
.uploadblick-einstieg {
    display: inline-flex;
    align-items: center;
    padding: 0.3rem 0.8rem;
    border: 1px solid var(--rand-stark);
    border-radius: 999px;
    background: var(--flaeche-sanft);
    font-size: 0.85rem;
    font-weight: 600;
    color: var(--text-stark);
    text-decoration: none;
}

.uploadblick-einstieg::after {
    content: "\203A";
    margin-left: 0.4rem;
    font-size: 1.1em;
    line-height: 1;
    color: var(--text-leise);
}

.uploadblick-einstieg:hover,
.uploadblick-einstieg:focus-visible {
    background: var(--flaeche-zart);
    border-color: var(--rand-still);
}

.uploadblick-einstieg:hover::after,
.uploadblick-einstieg:focus-visible::after { color: var(--akzent-text); }

/* Die Verwaltung ist eine Abkürzung daneben – dieselbe Form wie die
   Nebenaktionen an einer Kachel. */
.uploadblick-aktion {
    display: inline-flex;
    align-items: center;
    padding: 0.2rem 0.6rem;
    border: 1px solid var(--rand);
    border-radius: 999px;
    background: var(--flaeche-still);
    font-size: 0.78rem;
    color: var(--text-normal);
    text-decoration: none;
}

.uploadblick-aktion:hover,
.uploadblick-aktion:focus-visible {
    border-color: var(--rand-stark);
    background: var(--flaeche-sanft);
    color: var(--text-stark);
}

/*
 * Uploads links, Archiv rechts (Issue #545).
 *
 * Die zwei Karten teilen sich die Breite, die der Überblick bisher allein
 * belegte. Beide tragen dieselben Maße (`.uploadblick`, `.kachel`); die
 * Archivkachel nimmt nur den Platz, den sie braucht, und der Überblick den
 * Rest. Unter 640 px – dem Telefonpunkt dieses Stylesheets – stehen sie
 * untereinander. Ohne Archivrecht bleibt der Überblick allein und breit, ohne
 * eine freigehaltene zweite Spalte.
 */
.blickfeld {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(15rem, 22rem);
    gap: 0.9rem;
    align-items: stretch;
}

.blickfeld-allein { grid-template-columns: minmax(0, 1fr); }

/* `.kachelfeld` füllt sonst selbst auf; das gemeinsame Feld hat feste Plätze. */
.kachelfeld.blickfeld { grid-template-columns: minmax(0, 1fr) minmax(15rem, 22rem); }

.kachel-archiv { align-content: start; }
.kachel-archiv .kachel-titel { margin: 0; }

.kachel-einstiegszeile {
    grid-area: 3 / 1 / auto / -1;
    margin: 0.7rem 0 0;
}

/* Der Einstieg trägt die Form des Einstiegs im Uploadüberblick; seine
   Fläche legt sich wie bei jeder Kachel über die ganze Karte
   (`.kachel-haupt::after`). */
.kachel-einstieg {
    display: inline-flex;
    align-items: center;
    padding: 0.3rem 0.8rem;
    border: 1px solid var(--rand-stark);
    border-radius: 999px;
    background: var(--flaeche-sanft);
    font-size: 0.85rem;
    font-weight: 600;
    color: var(--text-stark);
}

.kachel-einstieg-pfeil {
    margin-left: 0.4rem;
    font-size: 1.1em;
    line-height: 1;
    color: var(--text-leise);
}

.kachel-archiv:hover .kachel-einstieg { background: var(--flaeche-zart); }
.kachel-archiv:hover .kachel-einstieg-pfeil { color: var(--akzent-text); }

@media (max-width: 640px) {
    .blickfeld,
    .kachelfeld.blickfeld { grid-template-columns: minmax(0, 1fr); }
    .kachel-einstieg { min-height: 44px; }
}

/*
 * Der Einstieg ins Studio (Issue #138).
 *
 * Er steht über den Kacheln und sieht anders aus als sie, und beides ist
 * dasselbe Argument: Die Kacheln führen in die Bereiche der Redaktion, dieser
 * Weg führt aus ihr heraus – in eine zweite, konzentrierte Arbeitsoberfläche.
 * Eine weitere Kachel in derselben Reihe sagte „noch ein Bereich"; die
 * durchgehende Zeile mit der Kante in der Marke sagt, was es ist.
 *
 * Mit dem neuen Dashboard teilt er die Maße statt der Form: dieselbe Rundung,
 * dieselbe Kante, derselbe Zeichenkasten in der Größe daneben. Die rote Kante
 * links und die gefüllte Sprechblase bleiben – sie sind der Unterschied.
 *
 * Gebaut ausschließlich aus den Tokens des Stylesheets – damit gilt der
 * dunkle Modus hier ohne eine einzige eigene Regel (Issue #85).
 */
.studio-einstieg {
    display: flex;
    align-items: center;
    gap: 0.85rem;
    min-height: 44px;
    margin-bottom: 2rem;
    padding: 1rem 1.1rem;
    border: 1px solid var(--rand);
    border-left: 4px solid var(--akzent);
    border-radius: 10px;
    background: var(--flaeche-karte);
    color: var(--text-stark);
    text-decoration: none;
}

.studio-einstieg:hover {
    border-color: var(--rand-stark);
    border-left-color: var(--akzent);
    background: var(--flaeche-zart);
}

.studio-einstieg:focus-visible {
    border-left-color: var(--akzent);
    outline: 2px solid var(--kontur);
    outline-offset: 2px;
}

.studio-einstieg-zeichen {
    display: flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    width: 38px;
    height: 38px;
    border-radius: 10px;
    background: var(--fuellung);
}

.studio-einstieg-zeichen svg {
    width: 20px;
    height: 20px;
    fill: var(--fuellung-text);
}

.studio-einstieg-text {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
    flex: 1 1 auto;
    min-width: 0;
}

.studio-einstieg-text strong {
    font-size: 1rem;
    color: var(--text-stark);
}

.studio-einstieg-text span {
    font-size: 0.85rem;
    line-height: 1.4;
    color: var(--text-normal);
}

.studio-einstieg-pfeil {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
}

.studio-einstieg-pfeil svg {
    width: 20px;
    height: 20px;
    fill: var(--text-still);
}

/*
 * Auf dem Telefon bricht der Satz um, und ein mittig stehendes Zeichen neben
 * drei Zeilen Text sieht verrutscht aus. Dort steht es deshalb oben an der
 * ersten Zeile – am Schreibtisch bleibt die Zeile einzeilig und mittig.
 */
@media (max-width: 640px) {
    .studio-einstieg { align-items: flex-start; }
    .studio-einstieg-pfeil { align-self: center; }
}

/*
 * Der Hinweis, warum jemand hier steht und nicht dort, wo er hinwollte. Er
 * benutzt die vorhandene ruhige Meldung der Anmeldemaske und braucht nur die
 * Breite und den Abstand des Dashboards dazu – eine zweite Meldungsform
 * daneben wäre ein zweiter Kasten für dieselbe Aussage.
 */
.dashboard-hinweis {
    margin-bottom: 1.6rem;
}

.etappe-hinweis {
    font-size: 0.85rem;
    color: var(--text-leise);
    border-top: 1px solid var(--rand);
    padding-top: 1rem;
}

/* ── Hauptnavigation (Stammdaten-Links) ──────────────────── */
.hauptmenue {
    flex-wrap: wrap;
    gap: 0.5rem;
}

.menue-links {
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

.menue-nav {
    display: flex;
    align-items: center;
    gap: 0.25rem;
    flex: 1;
    justify-content: center;
    /* Ohne beides blieb der Streifen 775 px breit und schob auf dem Telefon
       die ganze Seite über den Bildschirm hinaus. `min-width: 0` hebt die
       Vorgabe auf, dass ein Flex-Kind nie schmaler wird als sein Inhalt. */
    flex-wrap: wrap;
    min-width: 0;
}

.menue-link {
    font-size: 0.875rem;
    color: var(--kopf-text);
    text-decoration: none;
    padding: 0.3rem 0.75rem;
    border-radius: 4px;
    transition: background 0.15s, color 0.15s;
}

.menue-link:hover { background: rgba(255,255,255,0.1); color: var(--fuellung-text); }
.menue-link.aktiv { background: rgba(230,57,70,0.2); color: var(--kopf-akzent); font-weight: 600; }

/* ── Die Aktivmarkierung verschiebt nichts (Issue #163) ────────────────────
 *
 * Der Befund kam aus dem Vergleich zweier Vollbilder, `/aufgaben` gegen
 * `/dashboard`: Die Namen in der Leiste standen nicht an derselben Stelle.
 * Schuld war die Zeile darüber. `font-weight: 600` macht denselben Text
 * breiter, alle Bereiche liegen in einer gemeinsamen Flex-Zeile – und damit
 * rückt bei jedem Bereichswechsel alles hinter dem markierten Punkt zur Seite.
 * Ein Menü, das beim Umschalten springt.
 *
 * Gelöst wird das nicht mit festen Breiten je Bereich: Die Beschriftungen
 * kommen aus der Rechtematrix, sie ändern sich mit jedem neuen Bereich, und
 * eine Tabelle von Zahlen daneben wäre beim nächsten Namen still falsch.
 * Stattdessen **reserviert jeder Verweis von Anfang an die Breite seiner
 * fetten Beschriftung**: Ein unsichtbarer Messabdruck desselben Titels in
 * `600` steht als eigene Zeile im Kasten, ist 0 px hoch und wird abgeschnitten
 * – er kostet keine sichtbare Höhe und keinen zweiten Umbruch, aber der
 * Browser rechnet seine Breite mit. Der Kasten ist damit immer so breit wie
 * sein fetter Zustand, ganz gleich, ob er gerade markiert ist.
 *
 * Das ist zugleich die Zusage an „Mehr": `static/menue.js` misst, wie viel
 * die Leiste braucht (`getBoundingClientRect().width`), und rechnet daraus,
 * welcher Bereich noch hineinpasst. Solange diese Breite vom Zustand abhinge,
 * könnte ein Bereich allein wegen der Fettschrift in die Klappliste wandern –
 * und beim nächsten Wechsel wieder heraus. Jetzt misst das Skript auf jeder
 * Seite dieselbe Zahl.
 *
 * Der Titel kommt aus `data-titel` am Verweis (`app/templates/base.html`) und
 * nicht aus dem Textknoten: `content: attr(…)` kann nur ein Attribut lesen.
 * Er ist deshalb die einzige Stelle, an der die Beschriftung zweimal steht –
 * im Dokument, nicht im Stylesheet, und aus derselben Quelle wie der
 * sichtbare Text.
 *
 * `visibility: hidden` nimmt den Abdruck aus der Vorlesereihenfolge; im Text
 * der Seite steht er ohnehin nicht, denn ein Pseudoelement ist kein Knoten.
 *
 * Der Sammelpunkt „Mehr" steht mit in dieser Regel, und zwar über die Hülle um
 * sein Wort (`.menue-mehr-wort`): Auch er wird fett, sobald der Bereich, in
 * dem man steht, gerade unter ihm liegt. Er steht am Ende einer mittig
 * gesetzten Zeile – wird er breiter, rückt alles davor zur Seite, und
 * `static/menue.js` rechnet zugleich mit einer anderen Zahl. Warum das Wort
 * eine eigene Hülle braucht, steht in `app/templates/base.html`: Der Knopf
 * selbst ist ein Flex-Kasten, und darin stünde der Abdruck neben dem Wort
 * statt unter ihm.
 */
.menue-link[data-titel]::before,
.menue-mehr-wort[data-titel]::before {
    content: attr(data-titel);
    display: block;
    height: 0;
    overflow: hidden;
    visibility: hidden;
    white-space: nowrap;
    font-weight: 600;
}

/*
 * Ein externer Bereich trägt rechts noch den Pfeil `↗` (`.menue-extern::after`,
 * Issue #105), und der steht **neben** der Beschriftung in derselben Zeile.
 * Der Messabdruck darüber kennt nur Text und kann ihn nicht mitzeichnen –
 * ohne diese Zeile wäre die Breite des externen Bereichs genau um den Pfeil
 * und seine Fettschrift wieder vom Zustand abhängig (gemessen: 11,45 px).
 *
 * 0.95 rem sind die beiden Zahlen von dort: 0.25 rem Abstand und 0.7 rem
 * Kasten. Dass der Pfeil einen festen Kasten hat, ist die Voraussetzung dafür,
 * dass hier eine Zahl stehen kann statt einer Schätzung.
 */
.menue-extern[data-titel]::before { padding-right: 0.95rem; }

/*
 * Der Menüschalter gehört dem Telefon.
 *
 * Auf dem Schreibtisch ist Platz für alle Bereiche nebeneinander – dort gibt
 * es ihn nicht, weder sichtbar noch als Sprungziel für die Tastatur, und die
 * Navigation steht unverändert offen da. Erst die Medienabfrage für schmale
 * Geräte weiter unten weckt ihn.
 */
.menue-umschalter,
.menue-schalter { display: none; }

/* ── „Mehr": die Navigation bleibt eine Zeile ──────────────────────────────
 *
 * Elf Bereiche brauchen 1113 px; mit Logo, Produktname, Benutzer, Rolle und
 * „Abmelden" sind es rund 1605 px. Unterhalb davon – also auf jedem Laptop –
 * brach die Leiste in eine zweite Zeile um und der Kopf wuchs von 62 auf
 * 79 px, bei 1024 px auf 109 px (Issue #63). Was nicht mehr in die Zeile
 * passt, hängt `menue.js` deshalb unter einen Sammelpunkt „Mehr".
 *
 * Drei Regeln machen das überhaupt möglich:
 *
 *   * **Der Kopf bricht nicht mehr um.** Ein Flex-Kasten verteilt Zeilen
 *     **vor** dem Schrumpfen. Solange `flex-wrap: wrap` gilt, wandert die
 *     Benutzerzone bei einem langen Anzeigenamen in eine zweite Zeile, statt
 *     den Namen zu kürzen – und dann hilft kein Sammelpunkt mehr.
 *   * **Die Leiste bricht nicht um, sondern schneidet ab.** Nur so ist
 *     „passt nicht" ein Zustand, den man rechnen kann, statt eines zweiten
 *     Kopfes, der schon gezeichnet ist. Der Zuschnitt ist dabei nie zu sehen:
 *     Das Skript räumt auf, bevor der Browser zeichnet.
 *   * **Die Bereiche schrumpfen nicht mit.** Eine Beschriftung, die
 *     abgeschnitten wird, ist kein gewonnener Platz, sondern ein neuer Fehler.
 *
 * Alles drei gilt nur mit JavaScript (`html.js`): Ohne Skript gäbe es
 * niemanden, der den Überhang einsammelt, und dann ist Umbrechen das kleinere
 * Übel. Für das Telefon nimmt der Block ganz unten alles wieder zurück – dort
 * trägt weiterhin die Klappnavigation aus Issue #35, und es kommt kein
 * zweiter Breakpoint hinzu.
 */
html.js .hauptmenue { flex-wrap: nowrap; }

html.js .menue-nav {
    flex-wrap: nowrap;
    overflow: hidden;
    /*
     * Der Boden unter der Leiste: Platz für „Mehr" (gemessen 73 px), und
     * keinen Schritt weniger.
     *
     * Die Leiste nimmt sonst genau den Platz, der übrig bleibt – bei einem
     * sehr langen Anzeigenamen und einem schmalen Fenster wäre das null, und
     * die Navigation verschwände ersatzlos. Mit diesem Boden bleibt sie
     * immer erreichbar; was dann fehlt, nimmt sich der Name (siehe dort).
     */
    min-width: 6rem;
}

html.js .menue-nav > * {
    flex: 0 0 auto;
    white-space: nowrap;
}

/*
 * Der Sammelpunkt sieht aus wie ein Bereich, weil er einer ist – er trägt
 * dieselbe Klasse `menue-link` und damit auch deren Zustand `aktiv`. Hier
 * steht nur, was eine Schaltfläche von einem Verweis unterscheidet.
 */
.menue-mehr {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    background: none;
    border: 0;
    font-family: inherit;
    font-size: 0.875rem;
    line-height: inherit;
    cursor: pointer;
}

/* Ein Dreieck aus Rahmen – kein Zeichen, das je nach Schrift anders aussieht. */
.menue-mehr-pfeil {
    border: 4px solid transparent;
    border-top-color: currentColor;
    border-bottom-width: 0;
    transition: transform 0.15s;
}

.menue-mehr[aria-expanded="true"] .menue-mehr-pfeil { transform: rotate(180deg); }

/*
 * Die Klappliste hängt am Kopf und nicht in der Leiste – die schneidet ihren
 * Überhang ab und würde die Liste mit abschneiden. Ihre waagerechte Lage setzt
 * das Skript, damit sie unter „Mehr" steht und trotzdem im Kopf bleibt.
 */
.menue-mehr-liste {
    position: absolute;
    top: 100%;
    left: 0;
    z-index: 110;
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
    min-width: 12rem;
    padding: 0.4rem;
    background: var(--kopf-flaeche);
    border: 1px solid rgba(255, 255, 255, 0.14);
    border-radius: 0 0 8px 8px;
    box-shadow: 0 10px 28px rgba(0, 0, 0, 0.35);
}

.menue-mehr-liste .menue-link {
    display: block;
    white-space: nowrap;
}

/* `hidden` muss auch gegen ein `display` aus einer Regel gelten. */
.menue-mehr[hidden],
.menue-mehr-liste[hidden] { display: none; }

/*
 * Der Fokus wird nach innen gezeichnet. Die Leiste schneidet ihren Überhang
 * ab; ein Ring außerhalb des Kastens wäre am Rand der Leiste halb weg – und
 * ein halb sichtbarer Fokus ist für die Tastatur kein Fokus.
 */
.menue-link:focus-visible,
.menue-mehr:focus-visible {
    outline: 2px solid var(--kopf-akzent);
    outline-offset: -2px;
}

/* ── Stammdaten-Seiten – gemeinsames Layout ───────────────── */
/*
 * Die seitliche Polsterung steht als Variable, weil die Auswahlleiste des
 * Sendeplans denselben Wert braucht: Sie steht außerhalb des Flusses und muss
 * ihre linke und rechte Kante selbst setzen – aus dieser einen Angabe statt aus
 * einer zweiten Zahl. Auf dem Telefon wird sie weiter unten kleiner gesetzt,
 * und die Leiste folgt von selbst mit.
 */
.seite-wrapper {
    --seiten-polster: 0.5rem;

    max-width: 1100px;
    margin: 0 auto;
    padding: 0 var(--seiten-polster);
}

/* ── Feststehender Kopf, scrollender Inhalt ───────────────────────────────
 *
 * Eine Arbeitsansicht besteht aus zwei Teilen: oben, was man ständig braucht –
 * Überschrift, Schalter, Filter, Spaltenköpfe –, darunter der Inhalt. Beim
 * Scrollen soll der obere Teil stehen bleiben.
 *
 * Erreicht wird das nicht mit `position: sticky` an vielen Einzelteilen,
 * sondern mit einer Aufteilung: Die Seite ist genau so hoch wie das Fenster,
 * und **nur** der Inhaltsbereich scrollt. Das hat drei Vorteile gegenüber
 * gestapelten Sticky-Elementen:
 *
 *   * Es gibt keine Sticky-Zonen, die einander überlagern könnten, und keine
 *     Kette von `top`-Werten, die zusammen die Höhe der Elemente darüber
 *     ergeben müssten – Werte, die bei jedem responsiven Umbruch neu falsch
 *     wären.
 *   * Der Inhalt kann nicht unter den Kopf geraten, weil er gar nicht dorthin
 *     kann.
 *   * Es ist dieselbe Aufteilung, die die Wochenansicht seit jeher hat: Kopf
 *     außerhalb, Raster mit eigenem Scrollbereich darin. Statt sie ein zweites
 *     Mal nachzubauen, wird sie hier verallgemeinert.
 *
 * Angeschaltet wird das je Seite über `seite-fest` am `<body>`. Ohne die Klasse
 * ändert sich nichts – Personen, Benutzer und alle Formulare scrollen weiter
 * wie gewohnt.
 */
.seite-fest {
    height: 100vh;
    overflow: hidden;
    display: flex;
    flex-direction: column;
}
/* `min-height: 0` an jeder Stufe: Ohne das darf ein Flex-Element nicht unter
   die Höhe seines Inhalts schrumpfen, und der Scrollbereich säße außerhalb des
   Fensters statt darin. */
.seite-fest main,
.seite-fest .seite-wrapper {
    flex: 1 1 auto;
    min-height: 0;
    display: flex;
    flex-direction: column;
}
/*
 * `width: 100%` ist hier nicht überflüssig: In einer Flex-Spalte richtet sich
 * ein Element mit `margin: 0 auto` quer nicht mehr nach der verfügbaren
 * Breite, sondern nach seinem Inhalt – die Sendungsliste stand dadurch 190 px
 * weiter rechts als die Elementliste. Mit `width: 100%` bleibt die Breite die
 * gewohnte und `max-width` die Grenze.
 */
.seite-fest .seite-wrapper { width: 100%; }

/*
 * Eine gemeinsame Arbeitsbreite für die Arbeitsansichten.
 *
 * Vorher begann die Kopfzeile in jeder Ansicht woanders: bei 24 px im
 * Sendeplan, bei 32 px in der Planung und bei 170 px in Elementen und
 * Sendungen, die auf 1100 px zentriert waren. Beim Wechsel sprang der
 * Seitenkopf also um bis zu 146 px zur Seite – genau das, was hier ruhig
 * werden soll.
 *
 * Gebunden an `seite-fest` und damit an dieselbe Entscheidung: Personen,
 * Benutzer und alle Formulare behalten ihre zentrierte Lesebreite, weil ein
 * Formular von begrenzter Zeilenlänge profitiert und eine Arbeitsfläche von
 * Platz.
 */
.seite-fest .seite-wrapper {
    max-width: none;
    margin: 0;
}

/*
 * Der scrollende Teil. Er nimmt den Platz, der nach dem Kopf übrig bleibt –
 * ohne dass irgendwo eine Höhe geraten werden müsste.
 */
/*
 * Das Raster der Wochenansicht ist derselbe Fall: ein Scrollbereich, der den
 * Rest der Seite ausfüllen soll. Es hatte dafür `max-height: calc(100vh -
 * 15rem)` – einen Schätzwert für alles, was darüber steht.
 *
 * Gemessen war er um rund 10 px zu großzügig: Das Raster ragte ein Stück über
 * das Fenster hinaus, und die Seite scrollte zusätzlich zum Raster. Zwei
 * Scrollrichtungen für dieselbe Bewegung, und der Kopf rutschte dabei ein
 * wenig mit. Jetzt füllt das Raster genau den Rest – ohne Schätzwert, der bei
 * der nächsten Kopfänderung wieder falsch wäre.
 */
.seite-fest .raster {
    flex: 1 1 auto;
    min-height: 0;
    max-height: none;
}

.arbeitsflaeche-scroll {
    flex: 1 1 auto;
    min-height: 0;
    overflow: auto;
    /* Der Platz des Scrollbalkens bleibt reserviert, damit die Spalten beim
       Wachsen der Liste nicht seitlich springen – dieselbe Regel wie am
       Dokument (#4). */
    scrollbar-gutter: stable;
}

.seite-kopf {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 1.5rem;
    /* Auf schmalen Geräten rutscht die Schaltfläche unter die Überschrift,
       statt rechts aus dem Bildschirm zu laufen. */
    flex-wrap: wrap;
    gap: 0.75rem;
}

.seite-kopf h2 {
    font-size: 1.4rem;
}

/* ── Meldungen (Erfolg / Fehler) ─────────────────────────── */
.meldung {
    padding: 0.75rem 1rem;
    border-radius: 6px;
    margin-bottom: 1.2rem;
    font-size: 0.9rem;
}

.meldung-erfolg {
    background: var(--erfolg-flaeche);
    color: var(--erfolg-text);
    border: 1px solid var(--erfolg-rand);
}

.meldung-fehler {
    background: var(--fehler-flaeche);
    color: var(--fehler-text);
    border: 1px solid var(--fehler-rand);
}

/* ── Datentabelle ─────────────────────────────────────────── */
/*
 * Kein `overflow: hidden` mehr. Es rundete die Ecken, hätte aber die
 * Spaltenköpfe am Feststehen gehindert: Ein Vorfahre mit `overflow: hidden`
 * schneidet ein `position: sticky` ab, noch bevor es wirken kann. Die Rundung
 * übernehmen jetzt die Eckzellen selbst.
 */
.datentabelle {
    width: 100%;
    border-collapse: separate;
    border-spacing: 0;
    background: var(--flaeche-karte);
    border-radius: 8px;
    box-shadow: var(--schatten-karte);
    font-size: 0.9rem;
}

.datentabelle thead {
    background: var(--fuellung);
    color: var(--fuellung-text);
}

/*
 * Die Spaltenköpfe bleiben beim Scrollen stehen.
 *
 * `top: 0` bezieht sich auf den nächsten scrollenden Vorfahren, und das ist
 * `.arbeitsflaeche-scroll` – nicht das Fenster. Deshalb braucht es hier keinen
 * Versatz um die Höhe der Menüleiste oder des Seitenkopfs: Beide liegen
 * außerhalb des Scrollbereichs und können gar nicht überlagert werden.
 *
 * `background` gehört an die Zelle, nicht nur an `thead`: Ein `sticky`
 * gesetztes Element trägt seinen Hintergrund selbst, sonst scheint der Inhalt
 * darunter durch.
 */
.datentabelle thead th {
    position: sticky;
    top: 0;
    z-index: 2;
    background: var(--fuellung);
}
.datentabelle thead th:first-child { border-top-left-radius: 8px; }
.datentabelle thead th:last-child { border-top-right-radius: 8px; }
.datentabelle tbody tr:last-child td:first-child { border-bottom-left-radius: 8px; }
.datentabelle tbody tr:last-child td:last-child { border-bottom-right-radius: 8px; }

.datentabelle th {
    padding: 0.7rem 1rem;
    text-align: left;
    font-weight: 600;
    font-size: 0.8rem;
    text-transform: uppercase;
    letter-spacing: 0.04em;
}

.datentabelle td {
    padding: 0.65rem 1rem;
    border-bottom: 1px solid var(--rand-zart);
    vertical-align: middle;
}

.datentabelle tbody tr:last-child td { border-bottom: none; }
.datentabelle tbody tr:hover { background: var(--flaeche-zart); }

/* ── Listenkarten ─────────────────────────────────────────────
 *
 * Dieselben Daten wie die Tabelle darüber, nur als eine Karte je Datensatz.
 * Getragen wird das von Planung, Elementen, Sendungen und Programmpunkt-Typen
 * (Issue #45).
 *
 * Am Schreibtisch ist die Kartenliste nicht da – nicht „klein“, nicht
 * „gestapelt“, sondern `display: none`. Dort bleibt die Tabelle die Ansicht,
 * und sie soll es bleiben: Elf Spalten nebeneinander sind auf 1600 px die
 * bessere Form, und der Mobile-Audit (#35) verlangt für dieses Paket
 * ausdrücklich, dass sich am Schreibtisch nichts ändert.
 *
 * Umgekehrt wird unter 640 px die Tabelle ausgeblendet und die Kartenliste
 * eingeblendet; beide Regeln stehen im Telefon-Block weiter unten, zusammen
 * mit der Gestaltung der Karte selbst.
 *
 * Die Tabelle bleibt dabei **im Dokument**. Sie wird nicht ersetzt, sondern
 * verborgen – so steht es im Audit unter „Seiteneffekte“, und dort stehen auch
 * die Suiten, die ihr Vorhandensein ausdrücklich prüfen.
 */
.listenkarten { display: none; }

/* Elemente-Baum */
.zeile-parent { background: var(--flaeche-karte); }
.zeile-kind { background: var(--flaeche-still); }
.zeile-kind td { font-size: 0.88rem; }

.kind-einzug { padding-left: 2rem !important; }
.baum-linie { color: var(--text-still); margin-right: 0.4rem; }
.baum-marker { color: var(--text-still); margin-right: 0.4rem; font-size: 0.7rem; }
.kinder-anzahl { font-size: 0.8rem; color: var(--text-leise); margin-left: 0.5rem; }

.aktionen { white-space: nowrap; }

/*
 * Mehrere Moderatorinnen und Moderatoren in einer Zelle – Issue #131.
 *
 * Jeder Name ist ein eigener Block und steht damit in einer eigenen Zeile,
 * in der Tabelle wie in der Karte. Ein `<br>` zwischen den Namen täte optisch
 * dasselbe, ließe aber den Vorlesedienst die Namen in einem Zug lesen.
 *
 * Die schmalste mögliche Breite der Spalte ist so der längste **einzelne**
 * Name und nicht ihre Summe: Eine zweite Person schiebt die Tabelle nicht
 * auseinander. `overflow-wrap` nimmt den Rest – ein Doppelname bricht im
 * Notfall innerhalb des Namens um, statt die Zelle zu sprengen.
 */
.moderationsname { display: block; overflow-wrap: anywhere; }

/* ── Schaltflächen ────────────────────────────────────────── */
.schaltflaeche {
    display: inline-block;
    padding: 0.45rem 0.9rem;
    border-radius: 5px;
    font-size: 0.875rem;
    font-weight: 500;
    cursor: pointer;
    text-decoration: none;
    border: none;
    transition: background 0.15s, opacity 0.15s;
}

.schaltflaeche-primaer  { background: var(--fuellung); color: var(--fuellung-text); }
.schaltflaeche-primaer:hover  { background: var(--akzent); }

.schaltflaeche-neutral  { background: var(--flaeche-neutral); color: var(--text-normal); }
.schaltflaeche-neutral:hover  { background: var(--flaeche-neutral-stark); }

.schaltflaeche-gefahr   { background: var(--fehler-flaeche); color: var(--fehler-text); }
.schaltflaeche-gefahr:hover   { background: var(--fehler-flaeche-stark); }

.schaltflaeche-klein { padding: 0.25rem 0.6rem; font-size: 0.8rem; }
.schaltflaeche:disabled { opacity: 0.4; cursor: not-allowed; }

/* ── Badges & Kästchen ────────────────────────────────────── */
.farbklotz {
    display: inline-block;
    width: 1.4rem;
    height: 1.4rem;
    border-radius: 4px;
    border: 1px solid var(--kontur-farbklotz);
    vertical-align: middle;
}

.kategorie-badge {
    display: inline-block;
    padding: 0.15rem 0.5rem;
    border-radius: 999px;
    font-size: 0.75rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
}
.kategorie-wort     { background: var(--blau-flaeche); color: var(--blau-text); }
.kategorie-werbung  { background: var(--warnung-flaeche-zart); color: var(--warnung-text-tief); }
.kategorie-service  { background: var(--erfolg-flaeche); color: var(--erfolg-text); }
.kategorie-musik    { background: var(--violett-flaeche); color: var(--violett-text); }

.badge { display: inline-block; padding: 0.15rem 0.5rem; border-radius: 999px; font-size: 0.78rem; font-weight: 600; }
.badge-gruen { background: var(--erfolg-flaeche); color: var(--erfolg-text); }
.badge-grau  { background: var(--flaeche-sanft); color: var(--text-leise); }

.typ-badge {
    display: inline-block;
    padding: 0.15rem 0.5rem 0.15rem 0.4rem;
    border-radius: 4px;
    font-size: 0.82rem;
}

/* ── Formular ─────────────────────────────────────────────── */
.formular {
    background: var(--flaeche-karte);
    border-radius: 8px;
    box-shadow: var(--schatten-karte);
    padding: 1.8rem 2rem;
    /*
     * Die Flucht der Seite – dieselbe Zahl, die alles liest, was sich an
     * diesem Kasten ausrichtet (siehe `--flucht` weiter unten). Vorher stand
     * hier 720 px als eigene Zahl; die Kachel darunter kannte sie nicht und
     * war deshalb so breit wie der Wrapper.
     */
    max-width: var(--flucht);
}

/*
 * Folgt eine Sonderaktion unmittelbar auf das Formular, rückt der Kasten unten
 * enger zusammen. Das untere Polster von 1,8 rem ist als Abschluss gedacht –
 * hier schließt aber noch etwas an, und mit ihm stand die Aktion 38 px tiefer
 * und las sich wie ein eigener Abschnitt.
 */
.formular-anschluss { padding-bottom: 0.9rem; }

.formular-abschnitt {
    border: 1px solid var(--rand);
    border-radius: 6px;
    padding: 1.2rem 1.4rem;
    margin-bottom: 1.5rem;
}

.formular-abschnitt legend {
    padding: 0 0.5rem;
    font-weight: 600;
    font-size: 0.95rem;
    color: var(--text-stark);
}

.abschnitt-hinweis {
    font-size: 0.84rem;
    color: var(--text-leise);
    margin-bottom: 1rem;
}

.formular-zeile {
    margin-bottom: 1.1rem;
}

.formular-zeile label {
    display: block;
    font-size: 0.85rem;
    font-weight: 600;
    color: var(--text-normal);
    margin-bottom: 0.3rem;
}

/*
 * Die vier Feldarten, die in einer Formularzeile vorkommen. `email` und
 * `password` sind seit Issue #69 dabei: Beide gab es schon in der
 * Benutzerverwaltung, standen aber nicht in dieser Liste und wurden deshalb
 * unbestylt gezeichnet – ein kantiges Kästchen neben lauter gerundeten
 * Feldern. Mit „Mein Konto" und den Reset-Seiten stehen jetzt drei
 * Passwortfelder untereinander, und derselbe Unterschied wäre dort nicht mehr
 * zu übersehen.
 */
.formular-zeile input[type="text"],
.formular-zeile input[type="url"],
.formular-zeile input[type="number"],
.formular-zeile input[type="email"],
.formular-zeile input[type="password"],
/* `date` und `time` kommen mit den Fristen der Redaktionsaufgaben dazu
   (Issue #71). Ohne sie zeichnete der Browser genau dort sein eigenes,
   kantiges Feld – neben zwei gerundeten in derselben Zeile. */
.formular-zeile input[type="date"],
.formular-zeile input[type="time"],
.formular-zeile select,
.formular-zeile textarea {
    width: 100%;
    padding: 0.55rem 0.8rem;
    border: 1px solid var(--rand-stark);
    border-radius: 6px;
    font-size: 0.92rem;
    font-family: inherit;
    transition: border-color 0.15s;
    background: var(--flaeche-eingabe);
}

.formular-zeile input:focus,
.formular-zeile select:focus,
.formular-zeile textarea:focus {
    outline: none;
    border-color: var(--kontur);
}

.checkbox-label {
    display: flex !important;
    align-items: center;
    gap: 0.5rem;
    font-weight: 500 !important;
    cursor: pointer;
}

.checkbox-label input[type="checkbox"] {
    width: auto;
    margin: 0;
}

.farbe-eingabe {
    display: flex;
    align-items: center;
    gap: 0.75rem;
}

.farbe-eingabe input[type="color"] {
    width: 3rem;
    height: 2.2rem;
    padding: 0.1rem;
    border-radius: 4px;
    cursor: pointer;
}

.farbe-vorschau {
    font-size: 0.85rem;
    color: var(--text-leise);
    font-family: monospace;
}

.hinweis {
    display: block;
    font-size: 0.8rem;
    color: var(--text-leise);
    margin-top: 0.3rem;
}

.pflicht { color: var(--akzent-text); }

.formular-aktionen {
    display: flex;
    gap: 0.75rem;
    margin-top: 1.5rem;
}

/* ── Sonstige Hilfselemente ───────────────────────────────── */
.dauer-readonly {
    display: inline-block;
    padding: 0.55rem 0.8rem;
    background: var(--flaeche-sanft);
    border: 1px solid var(--rand);
    border-radius: 6px;
    font-size: 0.92rem;
    color: var(--text-leise);
}

.leer-hinweis {
    color: var(--text-leise);
    padding: 2rem;
    text-align: center;
    background: var(--flaeche-karte);
    border-radius: 8px;
    border: 1px dashed var(--rand-stark);
}

.leer-wert { color: var(--text-still); }
.kurztext { color: var(--text-normal); }
.link-extern { color: var(--text-stark); font-size: 0.85rem; }

/* ── Select mit "+ Neu"-Button ───────────────────────────── */
.select-mit-neu {
    display: flex;
    gap: 0.5rem;
    align-items: center;
}
.select-mit-neu select { flex: 1; }

/* ── Schnell-Dialog ──────────────────────────────────────── */
.schnell-dialog {
    position: fixed;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    margin: 0;
    border: 1px solid var(--rand-stark);
    border-radius: 10px;
    padding: 1.5rem;
    width: 100%;
    /* 420 px am Schreibtisch – und auf einem schmalen Gerät ein Rand von je
       1 rem, statt Kante an Kante mit dem Display. Ohne die zweite Grenze war
       der Dialog auf einem 390-px-Telefon genau 390 px breit; die abgerundeten
       Ecken lagen dann außerhalb des sichtbaren Bereichs. */
    max-width: min(420px, calc(100vw - 2rem));
    box-shadow: var(--schatten-dialog);
    /* Ohne diese beiden Zeilen zeichnete der Browser den Dialog in seiner
       eigenen Farbe (`Canvas`) – im Dunkeln richtig, aber eben nicht in der
       Farbe der Karten daneben. Die Fläche gehört zur Oberfläche. */
    background: var(--flaeche-karte);
    color: var(--text-normal);
}
.schnell-dialog h3 {
    font-size: 1.1rem;
    margin-bottom: 1rem;
    color: var(--text-stark);
}
/*
 * Die Angaben, die ein Bestätigungsdialog nennen muss: Bezeichnung links,
 * Wert rechts, zwei Spalten, keine Tabelle. Man soll auf einen Blick sehen,
 * welchen Termin es trifft – „Wirklich?" allein beantwortet niemand richtig.
 */
.dialog-angaben {
    display: grid;
    grid-template-columns: auto 1fr;
    column-gap: 0.9rem;
    row-gap: 0.2rem;
    margin: 0 0 0.9rem;
    padding: 0.55rem 0.7rem;
    border-radius: 6px;
    background: var(--flaeche-zart);
    font-size: 0.87rem;
}
.dialog-angaben dt { color: var(--text-leise); }
.dialog-angaben dd { margin: 0; color: var(--text-stark); font-weight: 500; overflow-wrap: anywhere; }

.dialog-hinweis { font-size: 0.85rem; line-height: 1.45; color: var(--text-normal); margin: 0; }

/* Abbrechen links, die zerstörende Aktion rechts – und beide am rechten Rand,
   wo die Hand nach dem Lesen ohnehin steht. */
.dialog-aktionen { justify-content: flex-end; flex-wrap: wrap; }

.schnell-dialog::backdrop {
    background: rgba(0,0,0,0.4);
}

/* ── Programm-Verwaltung ──────────────────────────────────── */
.seite-wrapper-breit { max-width: 1400px; }

/*
 * Und die Gegenrichtung, seit Issue #69: „Mein Konto" besteht aus zwei kurzen
 * Formularen und drei Angaben darüber. Über die volle Breite gezogen stünde
 * ein einzeiliges Eingabefeld 900 px weit da – das liest sich schlecht und
 * tippt sich schlechter. Die Klasse steht hier neben ihrer Schwester und nicht
 * bei den Konto-Regeln weiter oben: Beide überschreiben `.seite-wrapper` und
 * müssen dafür nach ihm kommen.
 */
.seite-wrapper-schmal { max-width: 640px; }

/*
 * Die Breite des Programmformulars: eine Zahl, zwei Leser.
 *
 * Der Formularkasten und die Sonderaktion darunter (`.einzelaktion`, weiter
 * unten) sind Geschwister im selben Wrapper und sollen dieselben Außenkanten
 * haben – links wie rechts. Stünde die Zahl zweimal im Stylesheet, liefen die
 * beiden Kanten beim nächsten Eingriff auseinander; genau das war der Befund
 * zu Issue #66. Deshalb steht sie hier einmal und wird zweimal gelesen.
 *
 * Sie ist kein Farbtoken und gehört deshalb nicht in den Farbkopf oben – die
 * dunkle Medienabfrage hat mit einer Breite nichts zu schaffen.
 */
:root { --formular-breite: 860px; }

/* ── Die Flucht einer Seite ───────────────────────────────────────────────
 *
 * Issue #66 hat das Problem für **eine** Seite gelöst; Issue #76 sagt, dass es
 * dasselbe Problem überall ist: Aktions-, Warn- und Löschkacheln standen unter
 * dem Formular, waren aber schmaler oder breiter als es und fluchteten links
 * und rechts nicht mit ihm. Im Rollenformular lag der rote Kasten „Rolle
 * löschen“ 300 px weiter rechts als das Formular darüber, in der
 * Benutzermaske 380 px. Die Ursache war jedes Mal dieselbe: Der Formularkasten
 * hatte eine Höchstbreite, sein Nachbar keine – und übernahm damit die des
 * Wrappers.
 *
 * Statt das je Kachel nachzuziehen, hat die Seite jetzt **eine Flucht**: die
 * linke und rechte Kante, an der alles ausgerichtet ist, was untereinander
 * steht. Sie ist eine Zahl (`--flucht`), sie steht je Seite genau einmal, und
 * gelesen wird sie von zwei Stellen:
 *
 *   * `.formular`      – der Formularkasten selbst (weiter oben)
 *   * `.formularflucht` – alles, was sich an ihm ausrichten soll: der
 *     Fehlerhinweis darüber, der Löschbereich darunter, die Absagekachel,
 *     jede weitere Kachel dieser Art.
 *
 * Der Normalfall sind 720 px – die Breite eines Formulars mit Eingabefeldern
 * untereinander. Drei Seiten brauchen mehr – das Programmformular (860 px),
 * die Rollenmaske und der Testdialog der GUDE KI (je 1100 px) –, und alle
 * drei sagen es an genau einer Stelle: an ihrem Wrapper, abgeleitet aus der
 * Klasse ihres Formulars. Damit gibt es weiterhin nur einen Ort je Seite, an
 * dem die Breite steht, und die Kachel darunter kann gar nicht mehr eine
 * andere haben.
 *
 * `:has()` steht hier bewusst statt einer zusätzlichen Klasse am Wrapper: Eine
 * zweite Klasse wäre eine zweite Angabe derselben Sache, und genau solche
 * Paare laufen beim nächsten Eingriff auseinander.
 */
:root { --flucht: 720px; }
.seite-wrapper:has(> .formular-breit)  { --flucht: var(--formular-breite); }
/*
 * Das Rollenformular ist breiter als die anderen: `.formular` deckelt bei
 * 720 px – richtig für ein paar Eingabefelder untereinander, zu eng für eine
 * Matrix mit fünf Spalten. Bei 720 px blieben für den Bereichsnamen 200 px,
 * und jede Bereichsbeschreibung stand über fünf Zeilen. 1100 px ist die
 * gewohnte Lesebreite dieser Anwendung (`.seite-wrapper`); die Maske selbst
 * steht im breiten Wrapper von 1400 px, damit ihr Kopf Platz hat.
 */
/*
 * Und derselbe Fall ein zweites Mal – der Testdialog der GUDE KI (#175).
 *
 * Er steht hier **in derselben Regel** und nicht in einer eigenen: Die Zahl
 * ist dieselbe Inhaltsbreite, und zwei Angaben von 1100 px liefen beim
 * nächsten Eingriff auseinander. Der Grund ist ein anderer als bei der
 * Rollenmaske: Unter der Frage steht das Ergebnis derselben Anfrage –
 * Antwort, technischer Beleg, verwendetes Wissen, Live-Kontext –, und das
 * nimmt die Inhaltsbreite der Seite. Ein Formular von 720 px darüber hätte
 * eine Kante, die mitten im Ablauf um 360 px springt.
 */
/*
 * Und ein drittes Mal – die beiden Upload-Flächen (Issue #206, Nacharbeit zur
 * Abnahme vom 28.09.2026).
 *
 * Wieder **in derselben Regel**: Es ist dieselbe Inhaltsbreite, und eine zweite
 * 1100 daneben liefe beim nächsten Eingriff auseinander. Der Grund ist derselbe
 * wie beim Testdialog: Unter dem Formular steht nicht ein Feld mehr, sondern das
 * Ergebnis – bei `/uploads` die Dateikarten mit Abspieler und die
 * Kontrollansicht in Spalten, in der Workflow-Maske die Platzhalterliste und die
 * Vorschau. Martins Befund dazu: „nutzt aktuell nur die schmale
 * Formularflucht, obwohl rechts viel Platz frei bleibt."
 *
 * Und ein viertes Mal, aus genau demselben Grund und wieder **in derselben
 * Regel** (Issue #218): Die Monatslieferung steht in einer Klammer aus zwei
 * Formularen – Monatsauswahl und Übergabe –, weil es ein Formular in einem
 * Formular in HTML nicht gibt. Damit ist die Klammer das direkte Kind des
 * Wrappers und nicht mehr die Uploadfläche selbst; ohne die Zeile für
 * `.upl-monatsflaeche` unten fiele die ganze Fläche auf die schmale Flucht
 * zurück, obwohl darin eine Liste aus dreißig Zeilen mit Abspieler steht.
 */
.seite-wrapper:has(> .formular-ki),
.seite-wrapper:has(> .upl-monatsflaeche),
.seite-wrapper:has(> .formular-upload),
.seite-wrapper:has(> .formular-rollen) { --flucht: 1100px; }

/*
 * Die gemeinsame Regel. Sie enthält absichtlich nichts als die Breite: Farbe,
 * Rahmen und Innenabstände bleiben Sache der jeweiligen Kachel – es geht um
 * Außenbreite und Flucht, nicht darum, alles gleich aussehen zu lassen.
 *
 * Eine Höchstbreite und keine feste: Unterhalb der Flucht füllt die Kachel die
 * Zeile wie das Formular, auf dem Telefon also die volle Breite.
 */
.formularflucht { max-width: var(--flucht); }

/* Datum- und Zeitfelder wie die übrigen Eingabefelder gestalten */
.formular-zeile input[type="date"],
.formular-zeile input[type="time"] {
    width: 100%;
    padding: 0.55rem 0.8rem;
    border: 1px solid var(--rand-stark);
    border-radius: 6px;
    font-size: 0.92rem;
    font-family: inherit;
    background: var(--flaeche-eingabe);
}
.formular-zeile input[type="date"]:focus,
.formular-zeile input[type="time"]:focus { outline: none; border-color: var(--kontur); }
.formular-zeile input[readonly] { background: var(--flaeche-sanft); color: var(--text-leise); }

/* Zwei Felder nebeneinander (gültig ab / gültig bis) */
.formular-zeile-doppelt {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 1.2rem;
}
@media (max-width: 640px) {
    .formular-zeile-doppelt { grid-template-columns: 1fr; }
}

/* Umschalter "vorhandener Inhalt" / "neuer Inhalt" */
.auswahl-schalter {
    display: flex;
    flex-wrap: wrap;
    gap: 1.5rem;
    background: var(--flaeche-zart);
    border: 1px solid var(--rand);
    border-radius: 6px;
    padding: 0.7rem 1rem;
}
.auswahl-schalter input[type="radio"] { width: auto; margin: 0; }

/* Wochentags-Auswahl */
.wochentage-auswahl {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
}
.wochentag-feld {
    display: flex !important;
    align-items: center;
    gap: 0.4rem;
    border: 1px solid var(--rand-stark);
    border-radius: 6px;
    padding: 0.4rem 0.75rem;
    background: var(--flaeche-karte);
    cursor: pointer;
    font-size: 0.85rem;
    font-weight: 500 !important;
    margin-bottom: 0 !important;
    user-select: none;
}
.wochentag-feld input[type="checkbox"] { width: auto; margin: 0; }
.wochentag-feld:has(input:checked) { border-color: var(--kontur); background: var(--flaeche-gewaehlt); }

/* Programm-Tabelle */
/*
 * Bestandsklasse ohne eigene Wirkung mehr. Sie trug den waagerechten
 * Scrollbereich für die breite Planungstabelle; den übernimmt jetzt
 * `.arbeitsflaeche-scroll`, der ohnehin in beide Richtungen scrollt. Zwei
 * ineinanderliegende Scrollbereiche wären nicht nur überflüssig gewesen – der
 * innere hätte die Spaltenköpfe am Feststehen gehindert.
 */
.tabelle-scroll { min-width: 0; }

/*
 * Ein Kasten, der nur auf dem Telefon einer ist.
 *
 * Die Typenliste (`/typen`) hat keinen `.arbeitsflaeche-scroll` – anders als
 * Planung, Elemente und Sendungen. Ihre sieben Spalten brauchen rund 1150 px;
 * bei 390 px wanderte der Überlauf deshalb an das Dokument selbst: 767 px
 * waagerechtes Scrollen der ganzen Seite, Kopfzeile inbegriffen. Gemessen im
 * Mobile-Audit zu Issue #35.
 *
 * Das eigentliche Gegenmittel ist die Kartenliste aus Paket 3 (#45), und sie
 * steht inzwischen: Unter 640 px ist die Tabelle ausgeblendet, der Kasten
 * umschließt dort nichts Sichtbares mehr. Er bleibt trotzdem – er kostet
 * nichts und ist die Rückfallebene, falls die Tabelle auf einem schmalen
 * Gerät je wieder erscheint.
 *
 * Warum die Regel in der Medienabfrage steht und nicht hier: Ein
 * Scrollbereich schneidet `position: sticky` ab. Am Schreibtisch passt die
 * Tabelle ohnehin in die Seitenbreite – dort gäbe es nichts zu scrollen, wohl
 * aber feststehende Spaltenköpfe zu verlieren. Also bleibt der Kasten dort ein
 * gewöhnliches `div`.
 */
.tabelle-quer { min-width: 0; }
.programm-tabelle { min-width: 1150px; font-size: 0.86rem; }
.zelle-zeit strong { font-variant-numeric: tabular-nums; }
.toleranz { display: block; font-size: 0.72rem; color: var(--text-leise); }
.zeittext { font-style: italic; color: var(--text-normal); }
.sendung-hinweis { display: block; font-size: 0.75rem; color: var(--text-leise); }
.programm-tabelle .badge { margin-left: 0.3rem; }

.badge.sidebar-voll         { background: var(--blau-flaeche); color: var(--blau-text); }
.badge.sidebar-nur_vorschau { background: var(--warnung-flaeche-zart); color: var(--warnung-text-tief); }
.badge.sidebar-aus          { background: var(--flaeche-sanft); color: var(--text-leise); }

.tabellen-fussnote { font-size: 0.8rem; color: var(--text-leise); margin-top: 0.9rem; }

/* ── Abschnittsüberschrift einer Liste ─────────────────────── */
.block-ueberschrift {
    font-size: 0.85rem;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--text-leise);
    border-bottom: 1px solid var(--rand);
    padding-bottom: 0.5rem;
    margin-bottom: 1.2rem;
}
.block-ueberschrift .anzahl {
    background: var(--flaeche-neutral);
    color: var(--text-normal);
    border-radius: 999px;
    padding: 0.1rem 0.5rem;
    font-size: 0.78rem;
    margin-left: 0.4rem;
}

.badge-offen { background: var(--warnung-flaeche); color: var(--warnung-text); }

/* ── Sendeplan: Tages- und Wochenansicht ──────────────────── */
/* ── Kompakter Kopfbereich des Sendeplans ─────────────────────────────────
 *
 * Der Sendeplan ist das Arbeitswerkzeug; das Raster soll den Bildschirm
 * ausfüllen. Vorher standen darüber eine Titelzone (46 px), ein
 * Navigationsband (46 px) und eine Werkzeugleiste mit eingebauter
 * Statistikzeile (60 px), dazwischen dreimal 16–19 px Abstand: gut 203 px auf
 * 1440x900, gemessen am 27.08.2026.
 *
 * Verdichtet, nicht beschnitten: Es fehlt keine Funktion. Titelzone und
 * Navigationsband sagen beide, *welcher Zeitraum* – sie sind jetzt eine Zeile.
 * Die Statistik hat die Werkzeugleiste verlassen, weil ihre Länge dort die
 * Geometrie der Bedienelemente veränderte.
 */

/* Der Sendeplan braucht oben weniger Luft als eine Formularseite. Die Klasse
   sitzt am `main` und gilt nur für ihn – alle anderen Seiten behalten ihr
   Polster. */
/* Waagerecht knapper als die übrigen Seiten: Eine Arbeitsfläche ist kein
   Lesetext. Die 16 px, die das je Seite freigibt, entscheiden bei 1024 px
   darüber, ob die Werkzeugleiste des Sendeplans in eine Zeile passt – sie war
   dort um 0,4 px zu breit.

   Seit #14 tragen alle fünf Arbeitsansichten dieselbe Polsterung, damit der
   Seitenkopf beim Wechsel nicht senkrecht springt: Er stand im Sendeplan bei
   73 px, in den Listen bei 94 px. */
.hauptbereich-arbeit { padding: 0.7rem 1rem 0.8rem; }

/*
 * Zwei Zonen: links das Blättern samt Zeitraum, rechts Datum und
 * Ansichtsumschalter.
 *
 * `margin-left: auto` statt `justify-content: space-between`. Der Unterschied
 * zeigt sich erst beim Umbruch: `space-between` stellt eine allein
 * umgebrochene Zone an den **linken** Rand – die Umschalter sprangen dadurch
 * bei 1245 px um 633 px nach links. Mit `margin-left: auto` bleibt die rechte
 * Zone rechts, ob sie nun neben dem Blätterbereich steht oder unter ihm.
 */
.plan-kopfzeile {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.5rem 0.8rem;
    margin-bottom: 0.5rem;
}
.plan-blaettern { display: flex; align-items: center; gap: 0.4rem; flex-wrap: wrap; }

/*
 * Die rechte Zone. Sie darf umbrechen – aber immer nach demselben Muster:
 * Das Datum rutscht nach oben weg, die Umschalter bleiben unten rechts
 * stehen. Das ergibt sich aus der Reihenfolge im Markup und daraus, dass eine
 * umbrechende Zeile ihre letzte Zeile rechts ausrichtet.
 */
.plan-rechts {
    margin-left: auto;
    display: flex;
    align-items: center;
    justify-content: flex-end;
    flex-wrap: wrap;
    gap: 0.5rem 0.8rem;
}

/*
 * Die beiden Umschaltergruppen bleiben zusammen. Getrennt umgebrochen stünde
 * „Tag | Woche" allein in einer Zeile und wechselte dabei seine Position –
 * genau der Sprung, den diese Zone verhindern soll. Sie sind zusammen rund
 * 340 px breit und passen damit auch bei 850 px in eine Zeile.
 */
.plan-ansichten {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    flex-wrap: nowrap;
}

/* Weiterhin die Überschrift der Seite, nur in Arbeitsgröße statt in
   Plakatgröße. Sie steht zwischen den Blätterschaltflächen und nennt den
   Zeitraum, den man gerade sieht. */
/*
 * `tabular-nums`: Ziffern gleicher Breite. Ohne sie ist „2027" um gut einen
 * Pixel breiter als „2026" – gemessen am 27.08.2026 –, und die
 * Datumsangaben verschöben beim Blättern alles rechts von sich. Mit ihr ist
 * jedes `tt.mm.–tt.mm.jjjj` gleich breit, ganz ohne reservierten Platz.
 */
.plan-zeitraum {
    font-size: 1rem;
    font-weight: 600;
    color: var(--text-stark);
    margin: 0 0.3rem;
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
    display: flex;
    align-items: baseline;
}

/*
 * Ein Kasten, so breit wie seine breiteste denkbare Beschriftung.
 *
 * Alle Varianten liegen in derselben Gitterzelle übereinander; die Zelle nimmt
 * die Breite der breitesten an. Gemessen wird das vom Browser – eine geratene
 * Breite in `em` wäre bei anderer Schrift oder Zoomstufe wieder falsch.
 *
 * `align-items: baseline` hält die sichtbare Variante auf der Grundlinie der
 * Zeile; ohne das säße sie in der Zelle oben.
 */
.stabil {
    display: inline-grid;
    align-items: baseline;
    justify-items: start;
}
.stabil > * { grid-area: 1 / 1; white-space: nowrap; }
.stabil-platz { visibility: hidden; }
.plan-zusatz { font-weight: 400; color: var(--text-leise); font-size: 0.88rem; }
/* „heute" als Marke statt als eigener Absatz – der kostete eine ganze Zeile. */
/*
 * Sie steht in jeder Woche im Markup und ist außerhalb der aktuellen nur
 * unsichtbar. Weggelassen gab sie ihre Breite frei und schob die
 * Blätterschaltflächen um 58,9 px – bei jedem Sprung in die aktuelle Woche
 * hinein und wieder heraus.
 *
 * Reservierter Platz kostet etwas, also ist die Marke so knapp gehalten, wie
 * sie lesbar bleibt: ohne Sperrung, mit engem Innenabstand.
 */
.plan-heute {
    flex: 0 0 auto;
    margin-left: 0.4rem;
    padding: 0.05rem 0.3rem;
    border-radius: 3px;
    background: var(--info-flaeche-zart);
    color: var(--info-marke);
    font-size: 0.72rem;
    font-weight: 600;
    text-transform: uppercase;
}
.plan-heute-still { visibility: hidden; }

/* ── Was es nur auf dem Telefon gibt ──────────────────────────────────────
 *
 * Fünf Bausteine aus Issue #44 stehen unverändert im Markup der
 * Sendeplan-Ansichten und sind am Schreibtisch schlicht nicht da: der
 * Tagesstreifen und die beiden Schalter „Ansicht" und „Details" mit ihren
 * Kästchen.
 *
 * `display: none` und nicht `visibility: hidden`: Sie reservieren keinen
 * Platz, sie sollen am Schreibtisch gar nicht vorkommen. Gemessen bedeutet das
 * null Abweichung – ein Element ohne Kasten verschiebt nichts.
 *
 * Sie stehen hier zusammen und nicht bei ihren jeweiligen Nachbarn, weil sie
 * eine gemeinsame Aussage haben: Der Schreibtisch bleibt, wie er war. Was aus
 * ihnen auf dem Telefon wird, steht im Block `@media (max-width: 640px)`.
 *
 * `.termin-nebensache` fehlt in dieser Liste mit Absicht: Die Angaben, die sie
 * auszeichnet, sind am Schreibtisch sichtbar wie zuvor. Die Klasse trägt dort
 * keine einzige Regel.
 */
.tagesstreifen,
.plan-werkzeug-schalter,
.plan-werkzeug-taste,
.plan-nebensachen-schalter,
.plan-nebensache-taste { display: none; }

/* Die Werkzeugleiste: eine Zeile, wo sie hinpasst, sonst kontrollierter
   Umbruch. Sie trägt nur noch Bedienelemente. */
/* 0,6 rem statt 0,8: Die Leiste war bei 1024 px um sieben Pixel zu breit für
   eine Zeile – gemessen am 27.08.2026. Zusammen mit dem etwas knapperen
   Innenabstand der Schalter passt sie dort jetzt hinein. Die Höhe der Schalter
   und damit die Trefferfläche bleibt unverändert. */
.plan-werkzeuge {
    display: flex;
    align-items: center;
    gap: 0.5rem 0.6rem;
    flex-wrap: wrap;
    margin-bottom: 0.35rem;
    /* Eine feste Zeilenhöhe, weil die Tagesansicht keine Zeitskala hat: Ihr
       höchstes Bedienelement ist einen Pixel niedriger als das der
       Wochenansicht, und der Rasterbeginn wanderte beim Umschalten genau um
       diesen Pixel. Reserviert ist er ohnehin. */
    min-height: 2rem;
}
.plan-werkzeuge:empty { display: none; }

/* Beschriftung vor einer Schaltergruppe. Sie trägt das Wort, das sonst in
   jedem einzelnen Schalter stünde – einmal statt zweimal, und die Gruppe
   bleibt trotzdem als Ganzes benannt (`aria-labelledby`). */
.werkzeug-feld { display: inline-flex; align-items: center; gap: 0.4rem; }
.werkzeug-titel { font-size: 0.8rem; color: var(--text-leise); white-space: nowrap; }
/* Unsichtbar, aber der Platz bleibt – und aus Tabreihenfolge und Vorlesefluss
   ist der Inhalt damit ebenfalls heraus. */
.werkzeug-feld-still { visibility: hidden; }

/*
 * Die Fußnotenzeile: links die Einordnung, rechts die Zahlen.
 *
 * Immer genau eine Zeile hoch. Das ist der Preis dafür, dass kein Umschalter
 * etwas verschiebt – Vorschau-Erklärung, Dauer-Hinweis und Statistik sind
 * unterschiedlich lang und bräuchten sonst je nach Zustand eine Zeile mehr
 * oder weniger. Was nicht hineinpasst, endet mit Auslassungspunkten; der
 * vollständige Text steht im `title`.
 */
.plan-fussnote {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 1rem;
    margin: 0 0 0.45rem;
    font-size: 0.82rem;
    line-height: 1.35;
    color: var(--text-normal);
    min-height: 1.35em;
}
.plan-notiz {
    flex: 1 1 auto;
    min-width: 0;
    color: var(--warnung-text);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.plan-statistik { flex: 0 0 auto; white-space: nowrap; }
.plan-statistik .still { color: var(--text-still); }

/* Nur für Vorlesesoftware – die Beschriftung des Datumsfeldes braucht keinen
   Platz mehr, das Feld erklärt sich selbst. */
.nur-vorlesen {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
}

.sendeplan-steuerung { display: flex; gap: 0.6rem; flex-wrap: wrap; }
.schalter-gruppe {
    display: inline-flex;
    border: 1px solid var(--rand-stark);
    border-radius: 6px;
    overflow: hidden;
    background: var(--flaeche-karte);
}
.schalter {
    /* Waagerecht knapper, senkrecht unverändert: Die Trefferfläche für Finger
       hängt an der Höhe, die Breite einer Werkzeugleiste an der Breite. */
    padding: 0.4rem 0.75rem;
    font-size: 0.85rem;
    color: var(--text-normal);
    text-decoration: none;
    border-right: 1px solid var(--rand);
    /*
     * Ein fester Schriftschnitt für alle Zustände.
     *
     * Zuvor trug allein der aktive Schalter 600 und alle übrigen 400. Fetter
     * Text ist breiter: „Öffentliche Vorschau" maß aktiv 162,91 px und inaktiv
     * 152,39 px, „Intern" 65,95 gegen 62,95. Beim Umschalten wuchs die Gruppe
     * dadurch um 7,5 px, und weil sie rechtsbündig steht, schob sie die
     * Gruppe „Tag/Woche" daneben um genau diese 7,51 px nach links – im
     * Browser gemessen.
     *
     * Ein konstantes Gewicht löst das an der Ursache, ohne mit Margins oder
     * Transformationen nachzuhelfen. Erkennbar bleibt der aktive Zustand
     * ohnehin: dunkle Fläche, weiße Schrift.
     */
    font-weight: 500;
}
.schalter:last-child { border-right: none; }
.schalter:hover { background: var(--flaeche-zart); }
/* Farbe und Fläche tragen den Zustand – nie die Maße. */
.schalter-aktiv { background: var(--fuellung); color: var(--fuellung-text); }
.schalter-aktiv:hover { background: var(--fuellung); }
.schalter:focus-visible { outline: 2px solid var(--kontur); outline-offset: -2px; }

/* Eine eigene Zone: Sie darf als Ganzes umbrechen, aber nicht in sich. */
.datumswahl { display: flex; align-items: center; gap: 0.4rem; flex-wrap: nowrap; }

/*
 * Wege, die es nur ohne JavaScript braucht. Ausgeblendet wird über die Klasse
 * am `<html>` und damit im Stylesheet – vor dem ersten Zeichnen. Ein Skript am
 * Seitenende zeigte sie erst und nahm sie dann weg; in diesem Moment konnte
 * die Zeile umbrechen.
 */
.js .datum-los,
.js .skala-los { display: none; }
.datumswahl label { font-size: 0.82rem; color: var(--text-leise); }
.datumswahl input[type="date"] {
    padding: 0.3rem 0.5rem;
    border: 1px solid var(--rand-stark);
    border-radius: 5px;
    font-size: 0.85rem;
    font-family: inherit;
}

/* ── Suche im Sendeplan (Issue #224) ─────────────────────────────────────
 *
 * Kompakt im Kopf, die Lupe im Feld, die Vorschläge direkt darunter – das
 * Bediengefühl des WordPress-Sendeplans in den Mitteln dieser Oberfläche.
 *
 * Ohne JavaScript ausgeblendet: Das Feld lebt von der Suche beim Tippen. Die
 * Klasse `js` am `<html>` setzt `base.html` vor dem ersten Zeichnen, es
 * rutscht also nichts nach.
 *
 * Liste und Meldung liegen über dem Plan und nicht im Fluss: Beim Tippen
 * wächst und schrumpft keine Zeile. Die feste Breite des Feldes sorgt dafür,
 * dass der Wechsel zwischen Intern und Vorschau nichts verschiebt.
 */
.plan-suche {
    display: none;
    position: relative;
    align-items: center;
    flex: 0 0 auto;
}
.js .plan-suche { display: flex; }
.plan-suche-lupe {
    position: absolute;
    left: 0.55rem;
    width: 0.95rem;
    height: 0.95rem;
    color: var(--text-leise);
    pointer-events: none;
}
.plan-suche-eingabe {
    width: clamp(10rem, 16vw, 15rem);
    padding: 0.3rem 0.5rem 0.3rem 1.9rem;
    border: 1px solid var(--rand-stark);
    border-radius: 5px;
    font-size: 0.85rem;
    font-family: inherit;
    background: var(--flaeche-karte);
    color: var(--text-stark);
}
.plan-suche-eingabe:focus-visible { outline: 2px solid var(--kontur); outline-offset: 1px; }

.plan-suche-liste,
.plan-suche-meldung {
    position: absolute;
    top: calc(100% + 0.3rem);
    left: 0;
    z-index: 60;
    background: var(--flaeche-karte);
    border: 1px solid var(--rand-stark);
    border-radius: 8px;
    /* Derselbe Schatten wie an jeder anderen Vorschlagsliste. */
    box-shadow: var(--schatten-vorschlaege);
}
.plan-suche-liste {
    min-width: 100%;
    width: max-content;
    max-width: min(24rem, 92vw);
    max-height: 20rem;
    overflow-y: auto;
    margin: 0;
    padding: 0.25rem;
    list-style: none;
}
.plan-suche-liste[hidden],
.plan-suche-meldung[hidden] { display: none; }
.plan-suche-vorschlag {
    display: flex;
    flex-direction: column;
    gap: 0.1rem;
    min-height: 2.25rem;
    padding: 0.45rem 0.6rem;
    border-radius: 6px;
    cursor: pointer;
}
.plan-suche-vorschlag:hover,
.plan-suche-vorschlag[aria-selected="true"] { background: var(--flaeche-gewaehlt); }
.plan-suche-titel { font-size: 0.9rem; font-weight: 600; color: var(--text-stark); }
.plan-suche-zeit {
    font-size: 0.78rem;
    color: var(--text-leise);
    font-variant-numeric: tabular-nums;
}
.plan-suche-intern {
    margin-left: 0.4rem;
    padding: 0.05rem 0.45rem;
    border-radius: 999px;
    font-size: 0.7rem;
    font-weight: 500;
    background: var(--flaeche-sanft);
    color: var(--text-leise);
}
.plan-suche-meldung {
    width: max-content;
    max-width: min(22rem, 92vw);
    margin: 0;
    padding: 0.45rem 0.65rem;
    font-size: 0.8rem;
    color: var(--text-mittel);
}

/* Tagesansicht */
.terminliste { list-style: none; display: flex; flex-direction: column; gap: 0.4rem; }
.termin {
    display: flex;
    align-items: stretch;
    gap: 0.9rem;
    background: var(--flaeche-karte);
    border: 1px solid var(--rand);
    border-radius: 8px;
    padding: 0.7rem 0.9rem;
}
.termin:hover { border-color: var(--rand-akzent); }
.termin-intern { background: var(--flaeche-still); }

.termin-zeit { min-width: 6.5rem; display: flex; flex-direction: column; }
.zeit-haupt { font-size: 1.05rem; font-weight: 700; color: var(--text-stark); font-variant-numeric: tabular-nums; }
.zeit-toleranz, .zeit-ende { font-size: 0.72rem; color: var(--text-still); }

.termin-farbe { width: 4px; border-radius: 2px; flex-shrink: 0; }

.termin-inhalt { flex: 1; min-width: 0; }
.termin-titel { font-size: 0.98rem; font-weight: 600; color: var(--text-stark); }
.termin-moderator { font-size: 0.85rem; color: var(--text-leise); margin-top: 0.1rem; }
.termin-merkmale { display: flex; flex-wrap: wrap; gap: 0.4rem; margin-top: 0.4rem; }
.merkmal {
    font-size: 0.74rem;
    background: var(--flaeche-sanft);
    color: var(--text-mittel);
    border-radius: 999px;
    padding: 0.1rem 0.5rem;
}
.merkmal-still { background: transparent; color: var(--text-still); padding-left: 0; }
.merkmal-zeittext { background: var(--warnung-flaeche-zart); color: var(--warnung-text-tief); }
.merkmal-verborgen { background: var(--flaeche-sanft); color: var(--text-leise); }   /* kein Fehler – nur intern */
.termin-aktion { display: flex; align-items: center; }

.badge-wdh { background: var(--info-flaeche); color: var(--info-text); margin-left: 0.4rem; }

/*
 * Im Wochenraster ist das Kennzeichen kleiner und enger – es soll im knappen
 * Block neben den Titel passen, nicht ihn verdrängen. Die runde Form und die
 * Farbe bleiben, damit es dasselbe Zeichen ist wie überall sonst.
 */
.block .badge-wdh {
    /* Rechts stabil: Das Kennzeichen schrumpft nie und sitzt am Rand. */
    flex: 0 0 auto;
    padding: 0 0.3rem;
    font-size: 0.85em;
    /*
     * Die Zeilenhöhe wird geerbt und nicht eigens gesetzt. Mit einem eigenen
     * Wert war der Zeilenkasten des Kennzeichens höher als der des Titels: Im
     * knappen Block – 24 px, in denen Uhrzeit und Titel schon fast aufgehen –
     * wuchs die Titelzeile dadurch über den Kasten hinaus, und das Kennzeichen
     * wurde unten angeschnitten. Geerbt bleibt es innerhalb der Zeile, ohne
     * einen Millimeter Höhe zu kosten.
     */
    line-height: inherit;
    margin-left: auto;
}
.block-knapp .badge-wdh,
.block-spuren-viele .badge-wdh { padding: 0 0.22rem; font-size: 0.9em; }
.badge-hinweis { background: var(--warnung-flaeche); color: var(--warnung-text); margin-left: 0.4rem; }

.musikmix-hinweis {
    margin-top: 1.4rem;
    padding: 0.8rem 1rem;
    background: var(--flaeche-sanft);
    border-radius: 8px;
    font-size: 0.88rem;
    color: var(--text-mittel);
}

/* ── Bestätigung der internen Zielzeit ─────────────────────── */

.zeit-eingabe {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.5rem;
}
.zeit-eingabe input[type="time"] { width: auto; }
.zeit-bestaetigt {
    font-size: 0.82rem;
    font-weight: 600;
    color: var(--erfolg-kraeftig);
}
/* Ohne gewählte Uhrzeit ist es keine Bestätigung, sondern ein Hinweis. */
.zeit-bestaetigt-leer { color: var(--warnung-mittel); font-weight: 500; }

/* ── Gleiche Inhalte hervorheben ───────────────────────────── */

/*
 * Ein Klick auf einen Programmpunkt zeigt, wo derselbe Inhalt in der gerade
 * sichtbaren Ansicht noch vorkommt. Zusammengehörig sind die Vorkommen über
 * die Inhaltskennung, nie über den Titel.
 *
 * Die Auswahl arbeitet mit dem Gegensatz zum Umfeld statt mit einer eigenen
 * Farbe: Die Gruppe bleibt voll da und bekommt eine deutliche, aber neutrale
 * Kontur, alles andere tritt zurück. Bewusst kein Rot – das trägt hier bereits
 * den Konflikt.
 *
 * Zurückgenommen, nicht ausgeblendet: Bei dieser Deckkraft bleibt jede Zeile
 * lesbar und der Plan überblickbar. Die Regeln hängen ausschließlich am
 * Planbehälter – Zeitachse, Tagesköpfe, Werkzeugleiste und Navigation liegen
 * außerhalb und bleiben unberührt.
 */
[data-rgs-plan] { --rgs-abblendung: 0.35; }

[data-rgs-plan].hat-inhalt .block:not(.ist-inhalt),
[data-rgs-plan].hat-inhalt .termin:not(.ist-inhalt) {
    opacity: var(--rgs-abblendung, 0.35);
    transition: opacity 0.18s ease;
}

/* Die Kontur liegt als innerer Schein im Kasten und verschiebt deshalb nichts. */
[data-rgs-plan].hat-inhalt .block.ist-inhalt,
[data-rgs-plan].hat-inhalt .termin.ist-inhalt {
    opacity: 1;
    box-shadow: inset 0 0 0 2px var(--kontur);
    /* Über den abgeblendeten Nachbarn, damit die Kontur nirgends angeschnitten wird. */
    z-index: 5;
}
/* Der Hoverschatten des Wochenblocks darf die Kontur nicht ersetzen. */
[data-rgs-plan].hat-inhalt .block.ist-inhalt:hover {
    box-shadow: inset 0 0 0 2px var(--kontur), var(--schatten-block);
}

/*
 * Der angesprungene Suchtreffer (Issue #224) – ein Akzent auf **einem** Termin
 * der bestehenden Auswahl, kein zweiter Auswahlzustand: Er gilt nur, solange
 * dieser Termin zur Auswahl gehört. Die Markenfarbe als Kontur und leiser
 * Schein, wie im WordPress-Sendeplan; beides liegt im bzw. um den Kasten und
 * verschiebt nichts.
 */
[data-rgs-plan].hat-inhalt .block.ist-inhalt.ist-treffer,
[data-rgs-plan].hat-inhalt .termin.ist-inhalt.ist-treffer,
[data-rgs-plan].hat-inhalt .block.ist-inhalt.ist-treffer:hover {
    box-shadow: inset 0 0 0 2px var(--akzent),
                0 0 0 3px color-mix(in srgb, var(--akzent) 30%, transparent);
    z-index: 6;
}

/* Bedienbar heißt auch: sichtbar, sobald man mit der Tastatur dort ist. */
[data-rgs-inhalt] { cursor: pointer; }
[data-rgs-inhalt]:focus-visible {
    outline: 2px solid var(--kontur);
    outline-offset: -2px;
}

@media (prefers-reduced-motion: reduce) {
    [data-rgs-plan].hat-inhalt .block:not(.ist-inhalt),
    [data-rgs-plan].hat-inhalt .termin:not(.ist-inhalt) { transition: none; }
}

/* Eine Einordnung, keine Warnung und kein Erfolg: Sie sagt, was gerade gilt. */
.meldung-hinweis {
    background: var(--flaeche-zart);
    border: 1px solid var(--rand-sanft);
    color: var(--text-normal);
}

/* ── Ausgefallene Termine ──────────────────────────────────── */

/*
 * Ein gestrichener Termin ist Planungsinformation, kein Programm. Er muss
 * deshalb erkennbar sein, ohne wie ein Programmpunkt auszusehen: gestrichelte
 * Kante statt voller, gedämpfte Farbe, durchgestrichener Titel.
 *
 * Er liegt über den freien Streifen (z-index 0) und unter den echten Blöcken
 * (z-index 2): Wo zur selben Zeit etwas läuft, gehört dem Laufenden die
 * Aufmerksamkeit. Dass die Zeit trotzdem als frei gilt, entscheidet nicht das
 * CSS, sondern die Rechnung – ein Ausfall belegt keine Sendezeit.
 */
.block-ausfall {
    z-index: 1;
    background: var(--ausfall-flaeche);
    border: 1px dashed var(--ausfall-rand);
    border-left: 3px dashed var(--ausfall-rand);
    color: var(--ausfall-text);
    box-shadow: none;
}
.block-ausfall .block-titel { text-decoration: line-through; }
.block-ausfall:hover { z-index: 6; border-color: var(--ausfall-rand-hover); }

.badge-ausfall {
    flex: 0 0 auto;
    line-height: inherit;
    margin-left: auto;
    background: var(--flaeche-sanft);
    color: var(--text-leise);
    border: 1px solid var(--ausfall-marke-rand);
}

/*
 * Im Wochenraster ist die Marke kleiner und enger – dieselbe Behandlung, die
 * das Wiederholungskennzeichen in #11 bekommen hat, und aus demselben Grund.
 *
 * `.badge` setzt eine **absolute** Schriftgröße von 0,78 rem. Ein knapper Block
 * steht aber bei 0,6 rem: Die Marke war damit größer als der Text um sie herum,
 * 19,88 px hoch und höher als eine Textzeile. Sie schob den Titel 5,4 px tiefer
 * als im normalen Block derselben Höhe – gemessen am 28.08.2026 –, und dort
 * wurde er unten angeschnitten.
 *
 * `0.85em` statt `0.78rem` ist der ganze Unterschied: Die Marke wächst und
 * schrumpft jetzt mit ihrem Block. Dazu die geerbte Zeilenhöhe und kein
 * Rahmen, der zwei weitere Pixel kostet. Die Farbe bleibt und trägt allein –
 * sie hebt sich auch ohne Kante vom hellen Untergrund des Platzhalters ab.
 */
.block .badge-ausfall {
    padding: 0 0.3rem;
    font-size: 0.85em;
    border: 0;
    background: var(--ausfall-marke-flaeche);
    color: var(--ausfall-marke-text);
}

/*
 * Die Wiederherstellen-Schaltfläche sitzt wie der Stift in der Ecke – und
 * bleibt es auch im knappsten Block.
 *
 * Ich hatte sie dort zunächst ausgeblendet, weil sie im engen Slot zu drängen
 * schien. Nachgesehen: `.block-link` ist absolut positioniert und bis zum
 * Überfahren unsichtbar. Sie nimmt also gar keinen Platz in der Zeile und
 * drängt nichts. Das Ausblenden hätte eine funktionierende Aktion aus einer
 * Vermutung heraus entfernt.
 */
.block-ausfall-aktion { display: contents; }
.block-ausfall-aktion .block-link {
    background: none;
    border: 0;
    cursor: pointer;
    font: inherit;
}

/* In der Tagesliste ist es eine eigene Zeile, kein Untergrund. */
.termin-ausfall {
    background: var(--ausfall-flaeche);
    border-style: dashed;
    color: var(--ausfall-text);
}
.termin-ausfall .ausfall-titel { text-decoration: line-through; }
.termin-ausfall .termin-zeit { color: var(--ausfall-text); }
.ausfall-aktion { display: flex; align-items: center; padding-right: 0.8rem; }

/*
 * Die Rückmeldung einer Aktion, mit ihrer Umkehrung daneben. Sichtbar, aber
 * nicht aufdringlich: Es ist eine Bestätigung, keine Warnung.
 */
.plan-band {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    flex-wrap: wrap;
    margin-bottom: 0.5rem;
    padding: 0.5rem 0.8rem;
    border-radius: 6px;
    font-size: 0.86rem;
    background: var(--flaeche-zart);
    border: 1px solid var(--rand-sanft);
    color: var(--text-normal);
}
/*
 * `display: flex` schlägt das eingebaute `[hidden] { display: none }` des
 * Browsers – ein verstecktes Band stünde sonst weiter da. Deshalb hier
 * ausdrücklich; das Auswahlband unten wird genau so ein- und ausgeblendet.
 */
.plan-band[hidden] { display: none; }
.plan-band-ausgefallen { background: var(--band-ausfall-flaeche); border-color: var(--band-ausfall-rand); color: var(--band-ausfall-text); }
.plan-band-text { flex: 1 1 auto; min-width: 0; }
.plan-band-aktion { flex: 0 0 auto; }

/*
 * Welche Sendung gerade hervorgehoben ist – Issue #73.
 *
 * Dieselbe Zeile wie die Rückmeldung darüber, nur mit der Kontur der Auswahl
 * als linker Kante. Damit sagt die Farbe dasselbe wie der innere Schein an den
 * markierten Blöcken: *das* ist die Auswahl. Beide holen sie aus `--kontur` und
 * folgen damit von selbst dem hellen wie dem dunklen Modus.
 *
 * Es ist eine Einordnung und keine Meldung: kein Erfolgsgrün, kein Warngelb.
 */
.plan-band-auswahl { border-left: 3px solid var(--kontur); }

/*
 * Sein Platz ist die Unterkante der Arbeitsfläche – an jeder Breite.
 *
 * Bis zur Nacharbeit zu Issue #91 war das eine Frage des Geräts: Auf dem
 * Telefon stand das Band seit #85 außerhalb des Flusses, am Schreibtisch bekam
 * es mit `flex: 1 0 100%` die ganze Zeilenbreite der Kopfzeile und brach damit
 * in eine eigene Zeile um. Gemessen kostete das dort 49 px – die Kopfzeile
 * wuchs beim Auswählen von 62 auf 111 px, der ganze Plan rutschte um genau
 * diesen Betrag nach unten und beim Aufheben wieder zurück. Es ist derselbe
 * Sprung, den #85 auf dem Telefon abgeschafft hat, nur eine Bildschirmgröße
 * weiter; also gilt jetzt überall dieselbe Antwort.
 *
 * Zwei Regeln tragen sie. Die Leiste steht `position: absolute` an der
 * Unterkante der Arbeitsfläche, deren Unterkante zugleich die der Ansicht ist –
 * die Seite ist genau fensterhoch (`.seite-fest`). Und ihr Platz wird unten
 * frei gemacht statt oben (siehe die `:has()`-Regeln unten): Der Scrollbereich
 * wird kürzer, sein Inhalt beginnt aber weiter an derselben Stelle. Kein Kasten
 * wandert, weder beim Setzen noch beim Wechseln noch beim Aufheben.
 *
 * Die linke und rechte Kante kommen aus derselben Polsterung, die
 * `.seite-wrapper` setzt: Die Leiste steht damit auf den Kanten der Karten
 * darüber und nicht ein paar Pixel daneben.
 *
 * Sichtbar unterscheiden sich Schreibtisch und Telefon weiterhin – dort die
 * beschriftete Schaltfläche „Auswahl aufheben“ und das Wort „Auswahl:“, hier
 * das „×“ mit 44-px-Tippziel. Das steht in `@media (max-width: 640px)`; die
 * Lage der Leiste steht hier und nur hier.
 */
.plan-kopfzeile > .plan-band-auswahl {
    position: absolute;
    left: var(--seiten-polster);
    right: var(--seiten-polster);
    bottom: 0;
    /* Über dem Plan – die markierten Blöcke liegen auf 5. */
    z-index: 6;
    margin: 0;
    min-height: var(--auswahlleiste);
    min-width: 0;
    flex-wrap: nowrap;
    gap: 0.4rem;
    /* Die Höhe trägt das Tippziel; ein eigenes Polster käme oben und unten
       dazu. Seitlich bleibt es, damit der Name nicht an der Auswahlkontur und
       der Ausstieg nicht an der rechten Kante klebt. */
    padding: 0 0.6rem;
    /* Sie liegt über dem Programm: Der Schatten trennt sie von dem, was
       darunter durchscrollt. `.plan-band` deckt mit `--flaeche-zart` bereits
       vollflächig – durchscheinen soll hier nichts. */
    box-shadow: 0 -2px 10px -6px rgba(0, 0, 0, 0.45);
}

/*
 * Name und Zusatz nebeneinander, und der Name ist der, der nachgibt.
 * `min-width: 0` hebt die Vorgabe auf, dass ein Flex-Element nicht unter seinen
 * Inhalt schrumpft – ohne sie schöbe ein langer Titel die Leiste über den Rand
 * hinaus, statt sich kürzen zu lassen.
 */
.plan-band-auswahl .plan-band-text {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: 0.3rem;
    min-width: 0;
}
.plan-band-auswahl [data-rgs-auswahl-titel] {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
/* „– in diesem Zeitraum nicht im Plan" ist der Satz, für den es das Band
   überhaupt gibt. Er wird nicht gekürzt, sondern rutscht neben einem langen
   Namen unter ihn – daher `flex-wrap` oben. Zwei kurze Zeilen sind in einer
   44 px hohen Leiste kostenlos; ein in vier Stücke gebrochener Halbsatz wäre es
   nicht. Der Name darüber bleibt dabei immer **eine** Zeile – er wird gekürzt,
   nicht umgebrochen. */
.plan-band-auswahl .plan-auswahl-leer { flex: 0 0 auto; max-width: 100%; }

/*
 * Der Platz für die Leiste – und zwar nur, solange es eine Auswahl gibt.
 *
 * Dauerhaft reserviert wäre es dieselbe verschenkte Zeile, die hier gerade
 * abgeschafft wird. `:has()` fragt den einen Zustand ab, der darüber
 * entscheidet, und zwar denselben, den Server und Skript ohnehin über das
 * `hidden` am Band führen – kein zweiter Schalter, der auseinanderlaufen
 * könnte.
 */
.sendeplan-arbeitsflaeche:has(.plan-band-auswahl:not([hidden])) {
    padding-bottom: calc(var(--auswahlleiste) + 0.5rem);
}

/* Der Zusatz für eine Woche, in der die Auswahl nicht vorkommt. Er tritt
   zurück – die Auswahl gilt weiter, hier läuft sie nur nicht. */
.plan-auswahl-leer { color: var(--text-leise); }

/* Das „×" ist die Telefonfassung von „Auswahl aufheben". Am Schreibtisch ist
   Platz für Worte; dort nimmt es keinen. */
.plan-aktion-kurz { display: none; }

/* Die Aktion im Programmeditor – abgesetzt vom Formular, weil sie keine
   Variante des Speicherns ist. */
/*
 * Die Sonderaktion zum Ausfallenlassen.
 *
 * Sie gehört zum Speicherbereich darüber, nicht in eine eigene Zone: knapper
 * Abstand, knappe Polsterung – und dieselben Außenkanten wie der Formularkasten
 * über ihr. Sie steht zwar außerhalb des `<form>` (Absagen ist keine Variante
 * des Speicherns), aber im selben Raster: gleiche linke, gleiche rechte Kante.
 *
 * Text links, die Schaltfläche rechts auf der Grundlinie der letzten Zeile.
 * Sie bleibt in Warnfarbe, aber ohne Nachdruck: Wer hier landet, hat den Modus
 * „Nur diesen Termin" schon bewusst gewählt.
 */
.einzelaktion {
    display: grid;
    grid-template-columns: 1fr auto;
    align-items: end;
    column-gap: 1rem;
    row-gap: 0.15rem;
    /*
     * Die Breite steht hier nicht mehr.
     *
     * Sie kam schon immer aus dem Formular daneben (Issue #66, Nacharbeit nach
     * Martins Sichtprüfung) – dazwischen stand hier eine eigene, engere
     * Höchstbreite von 660 px: so breit, wie der Inhalt der Kachel es
     * verlangt. Gemessen war das stimmig, gesehen nicht – die Kachel endete
     * auf dem Laptop 200 px vor dem Formularkasten und stand als losgelöste
     * Karte daneben statt im Raster. Beurteilt wird das Bild, nicht die
     * Messung.
     *
     * Seit Issue #76 ist das keine Sonderregel dieser einen Kachel mehr: Die
     * Kachel trägt im Markup `formularflucht` und bekommt ihre Außenkanten aus
     * derselben gemeinsamen Regel wie jeder andere Aktions-, Warn- und
     * Löschbereich der Anwendung. Hier steht nur noch, wie sie innen aussieht.
     */
    margin-top: 0.35rem;
    padding: 0.55rem 0.8rem;
    border: 1px solid var(--gefahr-rand-zart);
    border-radius: 6px;
    background: var(--gefahr-flaeche-zart);
}
.einzelaktion-titel {
    grid-column: 1;
    font-size: 0.88rem;
    font-weight: 600;
    color: var(--text-stark);
    margin: 0;
}
.einzelaktion-hinweis {
    grid-column: 1;
    font-size: 0.8rem;
    line-height: 1.4;
    color: var(--text-leise);
    margin: 0;
}
/* Über beide Textzeilen hinweg, unten bündig mit der letzten. */
.einzelaktion form {
    grid-column: 2;
    grid-row: 1 / span 2;
    align-self: end;
}
.einzelaktion .schaltflaeche {
    padding: 0.35rem 0.75rem;
    font-size: 0.83rem;
    white-space: nowrap;
}

/* Schmal: Der Text steht oben, die Schaltfläche darunter rechts. Gestapelt,
   aber weiterhin knapp. */
@media (max-width: 620px) {
    .einzelaktion {
        grid-template-columns: 1fr;
        row-gap: 0.5rem;
    }
    .einzelaktion form {
        grid-column: 1;
        grid-row: auto;
        justify-self: end;
    }
}

/* ── Freie Zeiten ──────────────────────────────────────────── */

/*
 * Eine zugeschaltete Zusatzebene, kein zweiter Plan. Sie muss deshalb leiser
 * sein als alles, was Programm ist: keine Fläche, die mit einem Block
 * verwechselt werden kann, keine Farbe, die neben den Typenfarben um
 * Aufmerksamkeit wirbt. Ein schraffierter, heller Untergrund – man sieht ihn,
 * wenn man ihn sucht, und übersieht ihn, wenn man das Programm liest.
 */
.frei-fenster {
    position: absolute;
    left: 0;
    right: 2px;
    z-index: 0;                     /* unter den Blöcken (z-index: 2) */
    border-radius: 3px;
    border: 1px dashed var(--rand-sanft);
    /* Schraffur statt Füllung: eine gefüllte Fläche läse sich als Eintrag. */
    background: repeating-linear-gradient(
        135deg, var(--frei-streifen-hell), var(--frei-streifen-hell) 6px,
        var(--frei-streifen-dunkel) 6px, var(--frei-streifen-dunkel) 12px);
    overflow: hidden;
    pointer-events: none;           /* nichts daran ist bedienbar */
}

/*
 * Beschriftet wird nur, wo der Text hinpasst – ohne Platz bleibt der Streifen
 * mit seinem Tooltip; ein abgeschnittenes „1:4…" hülfe niemandem.
 *
 * `--frei-text-oben` schiebt den Text unter dem Überstand des Blocks darüber
 * hervor. Ein Block wird mindestens 24 px hoch gezeichnet, auch wenn seine
 * Sendezeit nur eine Minute dauert – ohne dieses Ausweichen lag die
 * Beschriftung darunter und war unsichtbar, selbst bei einem 2:44 Stunden
 * langen Fenster. Der Server rechnet den Wert, der Regler führt ihn beim
 * Ziehen nach.
 */
.frei-text {
    display: block;
    margin-top: var(--frei-text-oben, 0px);
    padding: 2px 6px;
    font-size: 0.72rem;
    font-weight: 500;
    color: var(--frei-text);
    white-space: nowrap;
}

/* In der Tagesansicht ist es kein Untergrund, sondern eine eigene Zeile. */
.frei-zeile {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding: 0.45rem 0.9rem;
    margin-bottom: 0.35rem;
    border: 1px dashed var(--rand-sanft);
    border-radius: 4px;
    background: repeating-linear-gradient(
        135deg, var(--frei-streifen-hell), var(--frei-streifen-hell) 6px,
        var(--frei-streifen-dunkel) 6px, var(--frei-streifen-dunkel) 12px);
    color: var(--frei-text);
}
.frei-zeile .termin-zeit { color: var(--frei-text); }
.frei-zeile-text { font-weight: 500; }

.frei-schalter { margin-bottom: 0.9rem; }

.frei-hinweis {
    margin: 0.4rem 0 0;
    font-size: 0.8rem;
    color: var(--warnung-text);
}

/*
 * Ausgeschaltet verschwindet die Ebene, ihr Platz aber nicht.
 *
 * `visibility: hidden` und ausdrücklich nicht `display: none`: Nur so behält
 * die Bilanzzeile ihre Breite und der Hinweis seine Höhe. Sonst wuchs die Zeile
 * beim Einschalten um 185 px und wanderte – sie liegt mit `margin-left: auto`
 * rechts – um dieselbe Strecke nach links; in der umbrechenden Werkzeugleiste
 * verschob das bei 900 bis 1100 px die ganze Zeile.
 *
 * Vorgelesen wird ein `visibility: hidden`-Bereich ohnehin nicht; das
 * zusätzliche `aria-hidden` im Markup sagt es nur noch einmal ausdrücklich.
 */
.frei-statistik-still,
.frei-hinweis-still { visibility: hidden; }

/* ── Abgrenzung von Inhalt, Darstellung und Planung ────────── */

/*
 * Ein Hinweis, der einer Verwechslung vorbeugt, statt ein Feld zu erklären.
 * Er tritt deshalb nicht zurück wie die übrigen Hilfetexte, sondern bekommt
 * eine Kante – man soll ihn lesen, bevor man das Datum falsch versteht.
 */
.hinweis-abgrenzung {
    display: block;
    margin-top: 0.4rem;
    padding: 0.45rem 0.6rem;
    border-left: 3px solid var(--rand-akzent);
    background: var(--flaeche-zart);
    border-radius: 3px;
    color: var(--text-mittel);
}

/*
 * Der Planungsabschnitt trägt keine Felder – er sagt nur, wo die dritte Ebene
 * wohnt. Deshalb ohne weiße Fläche: Er ist ein Wegweiser, kein Formularteil.
 */
.abschnitt-verweis {
    background: var(--flaeche-zart);
    border-style: dashed;
}
.abschnitt-verweis legend { color: var(--text-leise); }

/* ── Personenkarten ────────────────────────────────────────── */

.personenkarten {
    display: grid;
    /* Wächst mit dem Fenster; auf schmalen rücken die Karten von selbst
       untereinander. Die Mindestbreite ist an der tatsächlich vorhandenen
       gedeckelt, sonst liefe die Seite bei großer Schrift über. */
    grid-template-columns: repeat(auto-fill, minmax(min(17rem, 100%), 1fr));
    gap: 1rem;
}

.personenkarte {
    display: flex;
    flex-direction: column;
    background: var(--flaeche-karte);
    border: 1px solid var(--rand);
    border-radius: 10px;
    overflow: hidden;
    box-shadow: var(--schatten-zart);
}
.personenkarte:hover { border-color: var(--rand-akzent); }

/*
 * Feste Bildfläche mit gleichbleibendem Seitenverhältnis. Ohne sie ruckelten
 * die Karten beim Laden, weil jedes Foto eine andere Höhe mitbrächte.
 *
 * Der Platzhalter liegt darunter: Fehlt das Bild oder lädt es nicht, tritt er
 * hervor, ohne dass sich an der Höhe etwas ändert.
 */
.personenkarte-bild {
    position: relative;
    /*
     * Quadratisch (Nacharbeit zu Issue #150). Dieselbe Fläche trägt in der
     * Personenübersicht ein Porträt und in der Teamkarten-Verwaltung das Bild
     * einer Kachel; draußen auf `radio-gude.de/team/` ist sie ebenfalls ein
     * Quadrat. Drei Flächen mit demselben Inhalt und zwei Verhältnissen
     * ergäben ein Bild, das je nach Ort anders beschnitten ist.
     *
     * Ein 4:3-Bestandsbild geht dabei nicht kaputt: `object-fit: cover`
     * darunter füllt das Quadrat und schneidet links und rechts ab, statt zu
     * verzerren. Quadratisch heißt 1:1 und nicht rund – der Rahmen bleibt ein
     * Rechteck mit weichen Ecken.
     */
    aspect-ratio: 1 / 1;
    background: var(--flaeche-bild);
    display: flex;
    align-items: center;
    justify-content: center;
}
.personenkarte-bild img {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    /* Füllt die Fläche, ohne zu verzerren – beschnitten statt gequetscht. */
    object-fit: cover;
}
/*
 * Der Platzhalter ist seit Martins Abnahme vom 18.09.2026 eine Silhouette und
 * keine Initialen mehr. Er füllt die Fläche ruhig aus – dieselbe Zeichnung wie
 * draußen im Team-Plugin, damit Backend und Teamseite an dieser Stelle nicht
 * zweierlei zeigen.
 */
.personenkarte-platzhalter {
    display: block;
    /* Unten in der Fläche wie ein Brustbild – und nicht in der Mitte
       schwebend, wo die Silhouette wie ein Fleck aussähe. */
    align-self: flex-end;
    width: 62%;
    line-height: 0;
    color: var(--platzhalter-text);
    user-select: none;
}

/* ── Der Avatar-Platzhalter (`app/templates/_avatar.html`) ──
 *
 * Eine Zeichnung, kein Bild: Ein Platzhalter, der selbst ein Bild ist, kann
 * fehlen – und dann stünde ausgerechnet dort ein kaputtes Bild, wo ein
 * fehlendes aufgefangen werden soll. `currentColor` nimmt die Farbe der
 * Fläche; für den dunklen Modus braucht es deshalb keine zweite Datei.
 */
.avatar-platzhalter {
    display: block;
    width: 100%;
    height: auto;
    fill: currentColor;
}

.personenkarte-inhalt {
    display: flex;
    flex-direction: column;
    gap: 0.3rem;
    padding: 0.85rem 1rem 0.6rem;
    /* Schiebt die Aktionen nach unten, auch bei ungleich langen Texten. */
    flex: 1 1 auto;
}
.personenkarte-name { font-size: 1rem; color: var(--text-stark); }
.personenkarte-oeffentlich { font-size: 0.82rem; color: var(--text-leise); }
.marke-ki { align-self: flex-start; background: var(--warnung-flaeche); color: var(--warnung-text); }

/*
 * Drei Zeilen Vorschau, dann Schluss. Ein langer Text machte sonst eine Karte
 * doppelt so hoch wie ihre Nachbarn – gelesen wird er im Formular.
 */
.personenkarte-vorstellung {
    font-size: 0.85rem;
    line-height: 1.4;
    color: var(--text-leise);
    display: -webkit-box;
    -webkit-line-clamp: 3;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

/*
 * Wo die Person noch zugeordnet ist (Issue #80). Ein kleiner Abstand nach oben
 * setzt die Angabe von der Kurzvorstellung ab: Das eine ist Text über den
 * Menschen, das andere eine Auskunft über den Bestand.
 */
.personenkarte-verwendung { margin-top: 0.25rem; }

.personenkarte-aktionen {
    display: flex;
    gap: 0.4rem;
    padding: 0.7rem 1rem;
    border-top: 1px solid var(--rand-zart);
}
.personenkarte-aktionen form { display: inline; }
/* Gesperrt und erkennbar gesperrt – der Grund steht im Titel des Knopfes. */
.personenkarte-aktionen button[disabled] {
    opacity: 0.4;
    cursor: not-allowed;
}

/*
 * Mehrere Aktionen rechts im Seitenkopf. Auf schmalen Geräten rutschen sie
 * gemeinsam unter die Überschrift, statt sich einzeln zu verteilen.
 */
.seite-kopf-aktionen {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    align-items: center;
}

/* ── Die öffentliche Teamseite (Issue #150) ─────────────────
 *
 * Die Verwaltung zeigt, was draußen steht: dieselben Kacheln in derselben
 * Reihenfolge. Sie benutzt deshalb die Maße der Personenkarten und nicht
 * eigene – zwei Kachelgrößen nebeneinander behaupteten einen Unterschied, den
 * es fachlich nicht gibt.
 */
.marke-team {
    align-self: flex-start;
    background: var(--flaeche-gewaehlt);
    color: var(--info-text);
    text-decoration: none;
}
/* Eine ausgeschaltete Karte ist kein Fehler – sie ist nur gerade nicht drin. */
.marke-team-inaktiv {
    background: var(--flaeche-sanft);
    color: var(--text-leise);
    border: 1px solid var(--rand);
}

.teamkarte-verwaltung { position: relative; }
/* Eine ausgeschaltete Karte bleibt vollständig lesbar und ist trotzdem auf
   einen Blick als „gerade nicht öffentlich" erkennbar. */
.teamkarte-verwaltung.teamkarte-aus { border-style: dashed; }
.teamkarte-verwaltung.teamkarte-aus .personenkarte-bild { opacity: 0.55; }

.teamkarte-personen {
    font-size: 0.82rem;
    color: var(--text-leise);
}

/* Woher Titel, Bild und Text stammen: gepflegt oder von der Person geerbt.
   Klein und ruhig – es ist eine Herkunftsangabe und keine Warnung. */
.teamkarte-herkunft {
    display: flex;
    flex-wrap: wrap;
    gap: 0.3rem;
    margin-top: 0.25rem;
}
.teamkarte-herkunft .marke {
    font-size: 0.7rem;
    font-weight: 500;
    background: var(--flaeche-sanft);
    color: var(--text-leise);
    border: 1px solid var(--rand-zart);
}

/* Die öffentliche Kontaktadresse einer Karte, sofern gepflegt. Ruhig und
   einzeilig – sie ist eine Auskunft und kein Knopf. */
.teamkarte-kontakt {
    font-size: 0.8rem;
    color: var(--text-leise);
    overflow-wrap: anywhere;
}

/*
 * Welche Bildquelle gerade gilt – im Formular, über den drei Auswahlknöpfen.
 * Die Miniatur daneben zeigt das Ergebnis der Wahl und nicht das Feld: Bei
 * „Bild von Person 2“ steht dort das Foto von Person 2.
 */
.bildquelle-jetzt {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    margin-bottom: 0.5rem;
    font-size: 0.85rem;
    color: var(--text-leise);
}
.bildquelle-vorschau {
    position: relative;
    flex: 0 0 auto;
    width: 3rem;
    aspect-ratio: 1 / 1;
    background: var(--flaeche-bild);
    border: 1px solid var(--rand);
    border-radius: 6px;
    overflow: hidden;
}
.bildquelle-vorschau img {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
}

/*
 * Die Verweislisten der Teamkartenmaske (Nacharbeit zu Issue #168).
 *
 * Je Kontaktart eine Liste statt eines Feldes: eine Zeile je Eintrag, ein
 * kleines × daneben, darunter „+ … hinzufügen". Die Knöpfe stehen mit `hidden`
 * in der Seite; sichtbar macht sie `static/teamverweise.js`, also genau dann,
 * wenn sie auch etwas tun.
 */
.verweisliste .verweis-eintraege {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
}
.verweis-eintrag {
    display: flex;
    align-items: center;
    gap: 0.4rem;
}
/* Das Feld nimmt den Platz, der Knopf nur seine eigene Breite. */
.verweis-eintrag input { flex: 1 1 auto; min-width: 0; }
.verweis-eintrag .verweis-weg {
    flex: 0 0 auto;
    /* Dieselbe Höhe wie das Feld daneben – ein Kreuz, das eine halbe Zeile
       höher sitzt, sieht aus wie ein Fehler. */
    line-height: 1;
    padding: 0.45rem 0.6rem;
}
.verweisliste .verweis-mehr { align-self: flex-start; margin-top: 0.45rem; }

/*
 * Weitere Suchnamen für die KI in der Personenmaske (Issue #375).
 *
 * Je Name ein Eintrag mit eigenem ×, darunter das Eingabefeld. Die Einträge
 * umbrechen auf dem Telefon von selbst; jedes × ist mindestens 2.25rem groß,
 * damit es sich mit dem Finger treffen lässt. Farben ausschließlich aus den
 * Tokens – hell und dunkel entstehen von selbst. `static/suchnamen.js`
 * blendet die Knöpfe ein.
 */
.suchnamen-liste {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
    list-style: none;
    margin: 0.45rem 0;
    padding: 0;
}
.suchname {
    display: inline-flex;
    align-items: center;
    gap: 0.15rem;
    max-width: 100%;
    padding: 0.1rem 0.15rem 0.1rem 0.65rem;
    border-radius: 999px;
    background: var(--flaeche-gewaehlt);
    color: var(--text-stark);
    border: 1px solid var(--rand);
    font-size: 0.88rem;
}
/* `display: flex` schlägt das eingebaute `[hidden]` – deshalb ausdrücklich. */
.suchnamen-liste[hidden],
.suchname-weg[hidden],
.suchname-eingabe [hidden],
.suchnamen-bestaetigung[hidden] { display: none; }
.suchname-text { overflow-wrap: anywhere; }
.suchname-weg {
    flex: 0 0 auto;
    min-width: 2.25rem;
    min-height: 2.25rem;
    border: 0;
    border-radius: 999px;
    background: transparent;
    color: var(--text-leise);
    font-size: 1.1rem;
    line-height: 1;
    cursor: pointer;
}
.suchname-weg:hover { color: var(--fehler-text); background: var(--fehler-flaeche-zart); }
.suchname-weg:focus-visible { outline: 2px solid var(--kontur); outline-offset: 1px; }
.suchname-eingabe {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
    align-items: center;
}
.suchname-eingabe input { flex: 1 1 12rem; min-width: 0; }
.suchnamen-status { min-height: 1.2em; margin: 0.3rem 0 0; font-size: 0.85rem; color: var(--text-mittel); }
.suchnamen-automatisch {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.3rem;
    margin: 0.3rem 0;
    font-size: 0.85rem;
    color: var(--text-leise);
}
/* Automatisch berücksichtigt – nicht entfernbar, deshalb gestrichelt und ohne ×. */
.suchname-auto {
    padding: 0.15rem 0.6rem;
    background: transparent;
    border-style: dashed;
    color: var(--text-mittel);
}
.suchnamen-fehler { margin: 0.3rem 0; font-size: 0.85rem; color: var(--fehler-text); }
.suchnamen-pruefung.laedt { opacity: 0.75; }
.suchnamen-konflikte {
    margin: 0.5rem 0;
    padding: 0.65rem 0.85rem;
    border-radius: 6px;
    background: var(--warnung-flaeche-blass);
    border: 1px solid var(--warnung-kraeftig);
    color: var(--warnung-text);
    font-size: 0.88rem;
}
.suchnamen-konflikte ul { margin: 0.35rem 0; padding-left: 1.2rem; }
.suchnamen-konflikte p { margin: 0.3rem 0 0; }
.suchnamen-bestaetigung { margin-top: 0.4rem; }

/*
 * „Namenserkennung testen“ in der Personenübersicht (Issue #375). Ein kleiner
 * Kasten über den Karten; das Ergebnis als Liste mit Grund je Treffer.
 */
.namenstest {
    margin: 0 0 1.2rem;
    padding: 0.9rem 1rem;
    border-radius: 8px;
    background: var(--flaeche-karte);
    border: 1px solid var(--rand);
}
.namenstest h3 { margin: 0 0 0.35rem; font-size: 1rem; color: var(--text-stark); }
.namenstest form {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    align-items: flex-end;
}
.namenstest form .formular-zeile { flex: 1 1 16rem; margin: 0; }
.namenstest-ergebnis { margin-top: 0.75rem; font-size: 0.9rem; }
.namenstest-ergebnis ul { margin: 0.3rem 0; padding-left: 1.2rem; }
.namenstest-ergebnis li { margin: 0.2rem 0; overflow-wrap: anywhere; }
.personenkarte-suchnamen { font-size: 0.82rem; color: var(--text-leise); overflow-wrap: anywhere; }

/* Die beiden Pfeile stehen links, die übrigen Aktionen rechts. */
.teamkarte-reihenfolge {
    display: flex;
    gap: 0.25rem;
    margin-right: auto;
}

/* ── Bildfeld: URL oder Hochladen, mit Zuschnitt (Issue #67) ─
 *
 * Ein Baustein für vier Masken. Alles darin richtet sich nach der Breite des
 * Behälters und nie nach der des Fensters: Das Feld steht am Schreibtisch in
 * einer breiten Spalte, auf dem Telefon in einer schmalen, und beide Male soll
 * es sich selbst ausmessen. Deshalb `flex-wrap` statt einer Bildschirmabfrage
 * und `aspect-ratio` statt fester Höhen.
 *
 * Das Seitenverhältnis von Bühne und Vorschau steht als `style` am Element und
 * kommt aus `app/bildtypen.py` – es ist je Bildtyp ein anderes und hat in
 * einem Stylesheet nichts verloren.
 */

.bildfeld { display: block; }

.bildfeld-quelle {
    border: 0;
    padding: 0;
    margin: 0 0 0.5rem;
}
.bildfeld-quelle legend {
    padding: 0;
    margin-bottom: 0.35rem;
    font-weight: 600;
    font-size: 0.9rem;
    color: var(--text-normal);
}
/* Die Umschalter nebeneinander – aber in einem eigenen Behälter. Eine
   `legend` in einem Flex-Container wird von Browsern unterschiedlich
   behandelt; das Feld muss überall gleich aussehen. */
.bildfeld-umschalter {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.3rem 1.2rem;
}
.bildfeld-umschalter .checkbox-label { margin: 0; }

/*
 * Eingabe und Vorschau nebeneinander – solange beide nebeneinander passen.
 * Unter rund 20 rem Feldbreite rückt die Vorschau von selbst darunter; das
 * geht ohne Bildschirmabfrage und richtet sich damit nach dem Platz, den das
 * Feld wirklich hat, und nicht nach der Breite des Geräts.
 */
.bildfeld-koerper {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    gap: 0.8rem;
}
.bildfeld-eingabe { flex: 1 1 12rem; min-width: 0; }
.bildfeld-eingabe input[type="url"],
.bildfeld-eingabe input[type="file"] { width: 100%; }

.bildfeld-unterschrift {
    display: block;
    font-size: 0.82rem;
    color: var(--text-mittel);
    margin-bottom: 0.2rem;
}

.bildfeld-vorschau {
    position: relative;
    flex: 0 0 auto;
    width: 7rem;
    background: var(--flaeche-bild);
    border: 1px solid var(--rand);
    border-radius: 6px;
    overflow: hidden;
    display: flex;
    align-items: center;
    justify-content: center;
}
.bildfeld-vorschau img {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
}
.bildfeld-vorschau-hinweis {
    font-size: 0.68rem;
    color: var(--text-still);
    text-align: center;
    padding: 0 0.3rem;
    /* Über der Silhouette und nicht daneben: Der Hinweis erscheint nur noch,
       wenn eine eingetragene Adresse sich nicht laden lässt. */
    position: relative;
    z-index: 1;
}
/* Die Silhouette in der Vorschaufläche – sichtbar, solange kein Bild da ist. */
.bildfeld-vorschau-avatar {
    position: absolute;
    bottom: 0;
    left: 20%;
    width: 60%;
    line-height: 0;
    color: var(--platzhalter-text);
}

/* ── „Bild entfernen“ (Martins Abnahme vom 18.09.2026) ──
 *
 * Ein Kästchen, das wie ein Knopf aussieht: Ohne JavaScript bliebe ein echter
 * Knopf wirkungslos, und das Löschen wäre genau die Funktion, die dann
 * fehlte. Markiert wird hier, entfernt wird beim Speichern – deshalb ist der
 * Zustand „wird entfernt“ sichtbar und nicht sofort vollzogen.
 */
.bildfeld-entfernen {
    margin-top: 0.6rem;
    display: flex;
    flex-direction: column;
    gap: 0.3rem;
    align-items: flex-start;
}
.bildfeld-entfernen-knopf {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.3rem 0.7rem;
    border: 1px solid var(--rand-stark);
    border-radius: 6px;
    background: var(--flaeche-sanft);
    color: var(--text-normal);
    font-size: 0.85rem;
    cursor: pointer;
}
.bildfeld-entfernen-knopf:hover {
    border-color: var(--fehler-rand);
    color: var(--fehler-text);
}
.bildfeld-entfernen-knopf:focus-within {
    outline: 2px solid var(--kontur);
    outline-offset: 2px;
}
/* Markiert: Der Knopf sagt, dass beim Speichern etwas verschwindet. */
.bildfeld-entfernen-aktiv {
    border-color: var(--fehler-rand);
    background: var(--gefahr-flaeche-zart);
    color: var(--fehler-text);
    font-weight: 600;
}
/* Und die Vorschau zeigt es – blass und durchgestrichen, aber noch da. */
.bildfeld-vorschau-entfernt img {
    opacity: 0.35;
    filter: grayscale(1);
}

/* Die Fläche, auf die eine Datei fallen gelassen werden darf. */
.bildfeld-ziel {
    outline: 2px dashed var(--akzent);
    outline-offset: 3px;
    border-radius: 6px;
}

.bildfeld-zuschnitt {
    margin-top: 0.8rem;
    padding: 0.8rem;
    border: 1px solid var(--rand);
    border-radius: 8px;
    background: var(--flaeche-zart);
}
.bildfeld-anleitung {
    margin: 0 0 0.6rem;
    font-size: 0.82rem;
    color: var(--text-mittel);
}

/*
 * Die Bühne. Sie ist so breit wie der Platz hergibt und so hoch, wie ihr
 * Seitenverhältnis es verlangt – auf dem Telefon wird sie damit schmaler und
 * flacher, aber nie unbedienbar und nie breiter als die Seite. Die Obergrenze
 * hält sie am Schreibtisch in einer Größe, die neben das Formular passt.
 */
.bildfeld-buehne {
    position: relative;
    width: 100%;
    max-width: 28rem;
    background: var(--bild-unterlage);
    border-radius: 6px;
    overflow: hidden;
}
.bildfeld-buehne canvas {
    display: block;
    width: 100%;
    height: 100%;
    /* Ohne das rollt das Telefon beim Schieben die Seite statt das Bild. */
    touch-action: none;
    cursor: move;
}

.bildfeld-regler {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    margin-top: 0.6rem;
    max-width: 28rem;
}
.bildfeld-regler label { font-size: 0.82rem; color: var(--text-mittel); }
.bildfeld-regler input[type="range"] { flex: 1 1 auto; min-width: 0; }

.bildfeld-messung {
    margin: 0.5rem 0 0;
    font-size: 0.82rem;
    color: var(--text-mittel);
}
.bildfeld-messung-warnung {
    color: var(--warnung-text);
    background: var(--warnung-flaeche-zart);
    border-left: 3px solid var(--warnung-kraeftig);
    padding: 0.4rem 0.6rem;
    border-radius: 0 4px 4px 0;
}

.bildfeld-aktionen {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
    margin-top: 0.7rem;
}

.bildfeld-meldung {
    margin: 0.6rem 0 0;
    padding: 0.5rem 0.7rem;
    border-radius: 4px;
    font-size: 0.85rem;
    background: var(--fehler-flaeche-zart);
    border: 1px solid var(--fehler-rand);
    color: var(--fehler-text);
}

/* ── Bildvorschau im Personenformular ─────────────────────── */

.foto-eingabe {
    display: flex;
    align-items: flex-start;
    gap: 0.8rem;
}
.foto-eingabe input { flex: 1 1 auto; min-width: 0; }
.foto-vorschau {
    position: relative;
    flex: 0 0 auto;
    width: 5.5rem;
    /* Dieselbe Form wie die Kachel, die dieses Bild später trägt (1:1). */
    aspect-ratio: 1 / 1;
    background: var(--flaeche-bild);
    border: 1px solid var(--rand);
    border-radius: 6px;
    overflow: hidden;
    display: flex;
    align-items: center;
    justify-content: center;
}
.foto-vorschau img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.foto-vorschau-hinweis {
    font-size: 0.68rem;
    color: var(--text-still);
    text-align: center;
    padding: 0 0.3rem;
}

/* ── Verwendung: wo ein Stammdatensatz noch hängt ───────────
 *
 * Erst für die Typen gebaut, seit Issue #80 auch auf den Personenkarten. Ein
 * zweiter Satz Klassen daneben hieße, denselben Gedanken zweimal zu pflegen –
 * die Auskunft ist an beiden Stellen dieselbe.
 */

.spalte-zusatz { font-weight: 400; font-size: 0.78rem; color: var(--fuellung-text-still); }

.verwendung { font-size: 0.86rem; }
.verwendung-zahl { display: block; font-weight: 600; color: var(--text-normal); }
.verwendung-namen { display: block; color: var(--text-leise); }
/* Ein unbenutzter Typ ist kein Fehler – nur eine Feststellung. */
.verwendung-frei { color: var(--text-still); font-style: italic; }

.verwendung-liste summary {
    cursor: pointer;
    color: var(--text-leise);
    font-size: 0.82rem;
}
.verwendung-liste ul {
    margin: 0.3rem 0 0 1rem;
    padding: 0;
    color: var(--text-leise);
}

/* Gesperrt und erkennbar gesperrt – der Grund steht im Titel des Knopfes. */
.aktionen button[disabled] {
    opacity: 0.4;
    cursor: not-allowed;
}

/* ── Rollen und Benutzerverwaltung ─────────────────────────── */

.benutzer-rolle {
    margin-left: 0.5rem;
    padding: 0.1rem 0.45rem;
    border: 1px solid rgba(255, 255, 255, 0.35);
    border-radius: 999px;
    font-size: 0.72rem;
    color: rgba(255, 255, 255, 0.85);
}

/*
 * Marken für Rolle und Status.
 *
 * Ruhige Flächen statt kräftiger Signalfarben: Eine Rolle ist eine Zuordnung
 * und keine Warnung. Nur der Admin bekommt einen wärmeren Ton, weil er der
 * einzige ist, bei dem die Zuordnung eine Folge hat.
 */
.marke {
    display: inline-block;
    padding: 0.12rem 0.55rem;
    border-radius: 999px;
    font-size: 0.75rem;
    font-weight: 600;
    line-height: 1.5;
    white-space: nowrap;
}
.marke-rolle { background: var(--flaeche-gewaehlt); color: var(--info-text); }
.marke-rolle-admin { background: var(--fehler-flaeche-zart); color: var(--fehler-text); }
.marke-rolle-moderator { background: var(--flaeche-sanft); color: var(--text-mittel); }
/* Ein technischer Zugang, kein Mensch – deshalb sichtbar anders als die drei
   Rollen darüber und bewusst ohne Alarmfarbe: Er darf ja nichts. */
.marke-rolle-qm { background: var(--erfolg-zart-flaeche); color: var(--erfolg-zart-text); border: 1px dashed var(--erfolg-zart-rand); }
/* Sachlich, nicht alarmistisch – ein deaktivierter Zugang ist kein Fehler. */
.marke-status { background: var(--flaeche-sanft); color: var(--text-leise); border: 1px solid var(--rand); }

/* Die Aktionsspalte einer Zeile, in der nur gelesen werden darf. */
.nur-lesen { font-size: 0.8rem; color: var(--text-still); font-style: italic; }

/* ── Benutzerkarten ────────────────────────────────────────── */

.seite-unterzeile { margin-top: 0.15rem; font-size: 0.88rem; color: var(--text-leise); }

.benutzerkarten {
    display: grid;
    /* Wächst mit dem Fenster und rückt auf schmalen von selbst untereinander –
       ohne eigenen Haltepunkt. `min(...)` deckelt die Mindestbreite an der
       tatsächlich vorhandenen, sonst liefe die Seite bei großer Schrift über. */
    grid-template-columns: repeat(auto-fill, minmax(min(22rem, 100%), 1fr));
    gap: 0.9rem;
    margin-bottom: 1.4rem;
}

.benutzerkarte {
    display: flex;
    flex-direction: column;
    gap: 0.7rem;
    padding: 1rem 1.1rem;
    background: var(--flaeche-karte);
    border: 1px solid var(--rand);
    border-radius: 10px;
    box-shadow: var(--schatten-zart);
}
.benutzerkarte:hover { border-color: var(--rand-akzent); }
/* Zurückgenommen, nicht ausgegraut: Die Angaben bleiben vollständig lesbar. */
.benutzerkarte-inaktiv { background: var(--flaeche-still); }
.benutzerkarte-inaktiv .benutzerkarte-name { color: var(--text-leise); }

.benutzerkarte-kopf {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 0.5rem;
}
/*
 * Das Profilbild in der Karte. Feste Fläche mit festem Seitenverhältnis, damit
 * die Karten nicht beim Nachladen der Bilder zucken und Karten mit und ohne
 * Bild gleich hoch bleiben.
 *
 * Die Initialen liegen darunter, nicht daneben: Fehlt das Bild oder lädt es
 * nicht, blendet `onerror` das `<img>` aus und der Platzhalter tritt hervor –
 * ohne zweite Fallunterscheidung im Markup.
 *
 * `flex: 0 0 auto`, damit ein langer Name die Bildfläche nicht zusammendrückt.
 */
.benutzerkarte-bild {
    position: relative;
    flex: 0 0 auto;
    width: 2.6rem;
    aspect-ratio: 1;
    border-radius: 50%;
    overflow: hidden;
    background: var(--info-flaeche-zart);
    align-self: center;
}
.benutzerkarte-bild img {
    position: relative;
    width: 100%;
    height: 100%;
    /* Füllt die Fläche, ohne zu verzerren – ein Hochformat wird beschnitten,
       nicht gestaucht. */
    object-fit: cover;
    display: block;
}
.benutzerkarte-platzhalter {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 0.9rem;
    font-weight: 600;
    color: var(--initialen-text);
    letter-spacing: 0.02em;
}
/* Der Name soll die Marken rechts halten, auch wenn links das Bild steht. */
.benutzerkarte-kopf .benutzerkarte-name { flex: 1 1 auto; min-width: 0; }
.benutzerkarte-inaktiv .benutzerkarte-bild { filter: grayscale(1); opacity: 0.7; }

.benutzerkarte-name { font-size: 1.02rem; color: var(--text-stark); }
.benutzerkarte-marken { display: flex; gap: 0.35rem; flex-wrap: wrap; }

/* Bezeichnung links, Wert rechts – zwei Zeilen, kein Fließtext. */
.benutzerkarte-angaben {
    display: grid;
    grid-template-columns: auto 1fr;
    column-gap: 0.9rem;
    row-gap: 0.2rem;
    margin: 0;
    font-size: 0.86rem;
}
.benutzerkarte-angaben dt { color: var(--text-still); }
.benutzerkarte-angaben dd { margin: 0; color: var(--text-normal); }
/* Eine lange Adresse bricht um, statt die Karte zu verbreitern. */
.benutzerkarte-mail { overflow-wrap: anywhere; }

.benutzerkarte-aktionen {
    display: flex;
    /* Drei Aktionen passen auf einer schmalen Karte nicht immer nebeneinander –
       bei großer Schrift rutscht die letzte dann in die nächste Zeile, statt
       die Karte zu sprengen. */
    flex-wrap: wrap;
    gap: 0.4rem;
    padding-top: 0.7rem;
    border-top: 1px solid var(--rand-zart);
    /* Die Aktionen sitzen unten, auch wenn eine Karte weniger Inhalt hat. */
    margin-top: auto;
}
.benutzerkarte-aktionen form { display: inline; }
.benutzerkarte-aktionen button[disabled] { opacity: 0.45; cursor: not-allowed; }
/* Löschen sitzt abgesetzt am rechten Rand – dieselbe Zeile, aber sichtbar nicht
   dieselbe Art von Aktion wie Bearbeiten und Deaktivieren. Bricht die Zeile auf
   einer schmalen Karte um, bleibt es auch dort rechts. */
.benutzerkarte-loeschen { margin-left: auto; }

.benutzer-fusshinweis { max-width: 46rem; }

/*
 * Der Löschbereich unter dem Benutzerformular.
 *
 * Klar abgesetzt und in gedämpftem Rot, aber ohne Warndreiecke und ohne Kasten
 * in Signalfarbe: Es ist eine seltene, endgültige Handlung – kein Alarm.
 */
.loeschbereich {
    margin-top: 1.6rem;
    padding: 1rem 1.1rem;
    background: var(--gefahr-flaeche-zart);
    border: 1px solid var(--gefahr-rand-zart);
    border-radius: 10px;
}
.loeschbereich h3 {
    margin: 0 0 0.4rem;
    font-size: 1rem;
    color: var(--fehler-text);
}
.loeschbereich p {
    margin: 0 0 0.8rem;
    font-size: 0.88rem;
    color: var(--text-leise);
    max-width: 44rem;
}
/* Steht ein Hindernis da, folgt keine Schaltfläche – dann trägt der Absatz
   allein und braucht keinen Abstand nach unten. */
.loeschbereich p:last-child { margin-bottom: 0; }

/* Die Rollen im Formular – untereinander statt als Textwand. */
.rollen-erklaerung {
    list-style: none;
    display: grid;
    gap: 0.3rem;
    margin: 0.4rem 0 0;
    padding: 0;
    font-size: 0.85rem;
    color: var(--text-leise);
}
.rollen-erklaerung li {
    display: flex;
    align-items: baseline;
    gap: 0.5rem;
    flex-wrap: wrap;
}

/* ── Umfang einer Bearbeitung: Serie oder einzelner Termin ─── */

/*
 * Die Frage steht ganz oben im Formular, weil sie alles darunter betrifft. Zwei
 * gleichwertige Karten nebeneinander, auf schmalen Fenstern untereinander – die
 * Auswahl soll gelesen und nicht überflogen werden.
 */
.umfang-wahl {
    border-color: var(--rand-akzent);
    background: var(--flaeche-zart);
}
.umfang-optionen {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(20rem, 100%), 1fr));
    gap: 0.6rem;
}
.umfang-option {
    display: grid;
    grid-template-columns: auto 1fr;
    grid-template-areas: "radio titel" ".     satz";
    column-gap: 0.55rem;
    row-gap: 0.15rem;
    align-items: baseline;
    padding: 0.7rem 0.85rem;
    border: 1px solid var(--rand-stark);
    border-radius: 6px;
    background: var(--flaeche-karte);
    cursor: pointer;
}
.umfang-option:hover { border-color: var(--rand-akzent); }
/* Die gewählte Karte hebt sich ab, ohne die andere zu verstecken. */
.umfang-option:has(input:checked) {
    border-color: var(--kontur);
    box-shadow: inset 0 0 0 1px var(--kontur);
}
.umfang-option input { grid-area: radio; margin: 0; }
.umfang-titel {
    grid-area: titel;
    font-weight: 600;
    color: var(--text-stark);
}
.umfang-satz {
    grid-area: satz;
    font-size: 0.85rem;
    line-height: 1.4;
    color: var(--text-leise);
}
.umfang-hinweis {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.5rem;
    margin: 0.7rem 0 0;
    padding: 0.55rem 0.7rem;
    border-left: 3px solid var(--akzent);
    background: var(--flaeche-karte);
    border-radius: 3px;
    font-size: 0.85rem;
    line-height: 1.45;
    color: var(--text-normal);
}

/*
 * Serienfelder im Einzelterminmodus: sichtbar, aber erkennbar außer Betrieb.
 * Ausgeblendet wären sie schlechter – man soll sehen, was zur Serie gehört und
 * warum es hier nicht zur Wahl steht.
 */
.serienbereich-gesperrt { opacity: 0.55; }
.serienbereich-gesperrt legend { color: var(--text-leise); }
.serienbereich-sperre {
    margin: 0 0 0.8rem;
    padding: 0.55rem 0.7rem;
    border-left: 3px solid var(--rand-still);
    background: var(--flaeche-sanft);
    border-radius: 3px;
    font-size: 0.85rem;
    line-height: 1.45;
    color: var(--text-normal);
    /* Der Hinweis bleibt lesbar, auch wenn der Abschnitt zurücktritt. */
    opacity: 1;
}

/* ── Wochenansicht: Stundenraster ─────────────────────────── */
/*
 * Der Arbeitsbereich des Sendeplans – für Tages- und Wochenansicht derselbe.
 *
 * Zuvor lag die Wochenansicht auf `seite-wrapper-voll` und die Tagesansicht auf
 * `seite-wrapper-breit` (1400 px, mittig). Auf einem 2560 px breiten Monitor
 * begann die eine bei 8 px und die andere bei 580 px: Beim Umschalten sprang
 * die gesamte Oberfläche einschließlich Kopfzeile und Schaltern um über einen
 * halben Bildschirm.
 *
 * Beide nutzen jetzt dieselbe äußere Geometrie. Kopfzeile, Navigation und
 * Schalter stehen dadurch in beiden Ansichten an derselben Stelle – das ist die
 * Bedingung dafür, dass der Wechsel ruhig wirkt.
 *
 * Der Klassenname sagt, worum es geht, statt eine Breite zu beschreiben: Dies
 * ist die Fläche, auf der geplant wird. Was darin wie breit sein darf,
 * entscheidet die jeweilige Ansicht für sich.
 */
.sendeplan-arbeitsflaeche {
    /* Die Lesebreite einer Inhaltsspalte in diesem Bereich. Sie steht hier,
       weil die Tagesansicht sie braucht und der Wert zur Fläche gehört. */
    --arbeitsflaeche-spalte: 1400px;
    /* Die Höhe der Auswahlleiste steht an genau einer Stelle: Sie ist zugleich
       das Tippziel nach der 44-px-Regel aus #43 und das Maß des Platzes, der
       unten frei gehalten wird. */
    --auswahlleiste: 44px;

    /* Der Bezugspunkt der Auswahlleiste: Sie liegt außerhalb des Flusses an der
       Unterkante dieser Fläche – siehe `.plan-kopfzeile > .plan-band-auswahl`. */
    position: relative;
    max-width: none;
    margin: 0;
    padding: 0 var(--seiten-polster);
}

/*
 * Die Terminliste behält genau ihre bisherige Breite – die Karten sehen aus wie
 * zuvor. Sie steht nur nicht mehr mittig, sondern beginnt am linken Rand des
 * Arbeitsbereichs, dort, wo auch die Wochenansicht beginnt.
 *
 * Der Platz rechts daneben bleibt bewusst frei. Er ist die Stelle, an der
 * später Planungsangaben stehen können; heute erfindet hier nichts eine zweite
 * Spalte, die es fachlich noch nicht gibt.
 */
.sendeplan-arbeitsflaeche .terminliste,
.sendeplan-arbeitsflaeche .leer-hinweis,
.sendeplan-arbeitsflaeche .musikmix-hinweis {
    max-width: min(var(--arbeitsflaeche-spalte, 1400px), 100%);
}

/* Bestandsklasse: außerhalb des Sendeplans unverändert in Gebrauch. */
.seite-wrapper-voll { max-width: none; padding: 0 0.5rem; }

/* ── Regler für die vertikale Zeitskala ───────────────────────────────────
 *
 * Gestreckt wird ausschließlich die Zeitachse des Wochenrasters. Der Regler
 * ändert nichts an Schriftgrößen, Innenabständen oder Breiten – weder an
 * seinen eigenen noch an denen der Programmblöcke.
 *
 * Er sitzt in derselben Zeile wie die übrigen Schalter und übernimmt deren
 * Rahmen, Radius und Schriftgröße: eine Bedienung, kein zweites Formular­gefühl.
 */
.skala-regler {
    display: inline-flex;
    align-items: center;
    /*
     * Auf schmalen Fenstern bricht die Gruppe um, statt über den Rand zu
     * laufen. Bei 390 px maß sie zuvor 417 px und war der einzige Grund, warum
     * die Wochenansicht dort waagerecht überlief – der Rasterkasten selbst
     * scrollt in sich und trägt nichts nach außen.
     */
    flex-wrap: wrap;
    max-width: 100%;
    gap: 0.45rem;
    padding: 0.15rem 0.5rem 0.15rem 0.65rem;
    border: 1px solid var(--rand-stark);
    border-radius: 6px;
    background: var(--flaeche-karte);
}
.skala-titel {
    font-size: 0.85rem;
    color: var(--text-normal);
    white-space: nowrap;
}
.skala-taste {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.5rem;
    height: 1.5rem;
    border: 1px solid var(--rand);
    border-radius: 4px;
    background: var(--flaeche-zart);
    color: var(--text-normal);
    font-size: 0.95rem;
    line-height: 1;
    text-decoration: none;
}
.skala-taste:hover { background: var(--flaeche-gewaehlt); border-color: var(--rand-akzent); }
/* Am Anschlag bleibt die Taste sichtbar, aber erkennbar wirkungslos. */
.skala-taste[aria-disabled="true"] { opacity: 0.4; pointer-events: none; }

.skala-schieber {
    /* Wachsen bis 7,5 rem, darunter nachgeben statt hinauszuragen. */
    flex: 0 1 7.5rem;
    width: 7.5rem;
    min-width: 4rem;
    margin: 0;
    accent-color: var(--kontur);
    vertical-align: middle;
}
.skala-wert {
    /*
     * Reservierter Platz für den Zahlenwert.
     *
     * `50 %` braucht weniger Raum als `200 %`. Ohne Reservierung änderte die
     * Anzeige mit jedem Schritt ihre Breite und schöbe die Gruppe daneben. Die
     * 3,2 rem decken den längsten Wert des Bereichs ab – nachgemessen belegt
     * jeder Wert von 50 bis 200 % dieselben 51,19 px. `tabular-nums` sorgt
     * zusätzlich dafür, dass gleich viele Ziffern immer gleich breit sind.
     */
    min-width: 3.2rem;
    font-size: 0.85rem;
    font-weight: 600;
    color: var(--text-stark);
    font-variant-numeric: tabular-nums;
    text-align: right;
}

/*
 * Die Vorschaumarke – und ihr ebenso reservierter Platz.
 *
 * Sie stand zuvor als `::after` im Zahlenfeld selbst. Damit hing ihre Breite am
 * Zahlenwert: Beim Ziehen maß das Feld 99,75 px bei „90 %" und 107,31 px bei
 * „100 %", die Gruppe wuchs entsprechend, und die Nachrichten-Schalter daneben
 * sprangen um 7,57 px – im Browser gemessen.
 *
 * Jetzt ist sie ein eigenes Element mit fester Breite. Der Platz ist in jedem
 * Zustand belegt; sichtbar wird die Marke nur während des Ziehens. Die Gruppe
 * hat dadurch bei jedem Wert und in jedem Zustand dieselben Außenmaße.
 *
 * `visibility` statt `display`: Nur so bleibt der Platz erhalten. Ausgeblendet
 * wäre er weg, und das Springen begänne von vorn.
 */
.skala-vorschau-marke {
    width: 4.2rem;
    flex: 0 0 auto;
    font-size: 0.78rem;
    line-height: 1;
    color: var(--text-leise);
    white-space: nowrap;
    visibility: hidden;
}
.skala-regler-vorschau .skala-vorschau-marke { visibility: visible; }
/*
 * Rückweg auf die Voreinstellung. Etwas breiter als die beiden Pfeiltasten,
 * weil eine Zahl darin steht, sonst dieselbe Formensprache.
 *
 * Auf der Voreinstellung ist die Schaltfläche gefüllt statt hell – dieselbe
 * Auszeichnung, die im Werkzeug auch sonst den aktuellen Zustand anzeigt
 * (`schalter-aktiv`). Sie ist dann nicht bedienbar, denn es gäbe nichts zu tun.
 */
.skala-standard {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 3rem;
    height: 1.5rem;
    padding: 0 0.45rem;
    margin-left: 0.15rem;
    border: 1px solid var(--rand);
    border-radius: 4px;
    background: var(--flaeche-zart);
    color: var(--text-normal);
    font-size: 0.8rem;
    line-height: 1;
    font-variant-numeric: tabular-nums;
    text-decoration: none;
    white-space: nowrap;
}
.skala-standard:hover { background: var(--flaeche-gewaehlt); border-color: var(--rand-akzent); }
.skala-standard-aktiv {
    background: var(--fuellung);
    border-color: var(--fuellung);
    color: var(--fuellung-text);
    font-weight: 600;
    pointer-events: none;
}

/*
 * Während gezogen wird, ist zu sehen, dass der Plan eine Vorschau zeigt: Der
 * Regler hebt sich ab, und der Prozentwert wird als vorläufig gekennzeichnet.
 * Beim Loslassen lädt die Ansicht neu, und die Kennzeichnung verschwindet mit
 * ihr – sie braucht kein eigenes Zurücksetzen.
 */
.skala-regler-vorschau {
    border-color: var(--kontur);
    box-shadow: inset 0 0 0 1px var(--kontur);
}

/* Ohne JavaScript die Auslöse-Schaltfläche, mit JavaScript ausgeblendet. */
.skala-los { border-right: none; border-radius: 4px; border: 1px solid var(--rand); }
.skala-los[hidden] { display: none; }

.raster {
    /* Luft über der ersten und unter der letzten Stundenlinie. Ohne sie ragt die
       auf ihre Linie zentrierte 06:00-Marke in die Kopfzeile und wird von der
       sticky Ecke verdeckt. */
    --rand-oben: 0.85rem;
    --rand-unten: 0.85rem;
    overflow: auto;                     /* trägt beide Scrollrichtungen */
    max-height: calc(100vh - 15rem);
    min-height: 22rem;
    border: 1px solid var(--rand);
    border-radius: 8px;
    background: var(--flaeche-karte);
}

.raster-gitter {
    display: grid;
    grid-template-columns: 4.5rem repeat(7, minmax(9.5rem, 1fr));
    grid-template-rows: auto 1fr;
    min-width: 72rem;                   /* darunter wird horizontal gescrollt */
}

/* Kopfzeile: bleibt beim vertikalen Scrollen stehen */
.raster-ecke, .tageskopf { position: sticky; top: 0; z-index: 3; }
.raster-ecke {
    left: 0;
    z-index: 5;                         /* Ecke über beiden Achsen */
    background: var(--flaeche-sanft);
    border-right: 1px solid var(--rand);
    border-bottom: 1px solid var(--rand-stark);
    font-size: 0.7rem;
    color: var(--text-still);
    text-transform: uppercase;
    letter-spacing: 0.05em;
    display: flex;
    align-items: flex-end;
    justify-content: center;
    padding-bottom: 0.4rem;
}
.tageskopf {
    background: var(--flaeche-zart);
    border-bottom: 1px solid var(--rand-stark);
    border-left: 1px solid var(--raster-spaltenrand);
    padding: 0.5rem 0.6rem;
    display: flex;
    flex-direction: column;
    gap: 0.05rem;
}
.tageskopf-heute { background: var(--flaeche-gewaehlt); box-shadow: inset 0 -2px 0 var(--kontur); }
.tageskopf-name { font-weight: 600; font-size: 0.92rem; color: var(--text-stark); text-decoration: none; }
.tageskopf-name:hover { color: var(--akzent-text); }
.tageskopf-datum { font-size: 0.78rem; color: var(--text-leise); }
.tageskopf-bilanz { font-size: 0.7rem; color: var(--text-still); }

/* Zeitachse: bleibt beim horizontalen Scrollen stehen */
.zeitachse {
    position: sticky;
    left: 0;
    z-index: 2;
    background: var(--flaeche-sanft);
    border-right: 1px solid var(--rand);
    /* Der Rand verschiebt die Achse als Ganzes, nicht ihre Beschriftungen:
       06:00 bleibt exakt auf der ersten Stundenlinie der Tagesspalten. */
    margin: var(--rand-oben) 0 var(--rand-unten);
    height: calc(var(--stunden-gesamt) * var(--stunde));
}
.stunde-marke {
    position: absolute;
    right: 0.5rem;
    transform: translateY(-0.5em);
    font-size: 0.72rem;
    color: var(--text-leise);
    font-variant-numeric: tabular-nums;
}

/*
 * Die Stundenbeschriftung, die der aktuellen Uhrzeit ausweicht (Issue #155).
 *
 * Um 18:01 stünde „18:00" genau dort, wo die Uhrzeit-Marke steht, und zwei
 * Zeilen lägen übereinander. Die aktuelle Uhrzeit hat Vorrang; die Stunde
 * weicht für diesen Moment. `visibility` und nicht `display`: Der Platz in der
 * Achse bleibt erhalten, und aus dem Vorlesefluss ist der Text ebenso heraus.
 */
.stunde-marke-still { visibility: hidden; }

/* ── Wo stehen wir gerade? Die Jetzt-Linie (Issue #155) ───────────────────────
 *
 * Eine waagerechte Linie quer über die Woche: Im Wochenraster läuft die Zeit
 * von oben nach unten, und eine Rasterzeile ist für alle sieben Tage dieselbe
 * Tageszeit. Gezeichnet wird sie als ein Abschnitt je Tagesspalte – dort liegt
 * dieselbe Zeitachse, auf der auch die Blöcke sitzen, und nur so kann der
 * heutige Tag anders aussehen als die übrigen sechs.
 *
 * `--jetzt-stunden` sind die Stunden ab Rasterbeginn. Die Pixel entstehen erst
 * hier, aus derselben `--stunde`, an der die Stundenlinien hängen: Verstellt der
 * Regler die Zeitskala, wandert die Linie deshalb von selbst mit.
 *
 * Ohne `data-rgs-jetzt="an"` gibt es sie nicht. Das ist die Bedingung des Auftrags:
 * Nur wenn die gezeigte Woche heute enthält und die Uhr im dargestellten Raster
 * steht. Das Markup steht trotzdem immer da – so kann ein lange offener Tab um
 * 06:00 Uhr eine Linie bekommen, ohne neu aufgebaut zu werden.
 */
.jetzt-linie {
    display: none;
    position: absolute;
    left: 0;
    right: 0;
    top: calc(var(--jetzt-stunden, 0) * var(--stunde));
    /*
     * Über allem in der Spalte – auch über einem angefahrenen Block, der sich
     * selbst auf 6 hebt. Eine Zeitlinie, die verschwindet, sobald der Zeiger
     * über einer Karte steht, beantwortete die Frage genau dann nicht mehr,
     * wenn man sie stellt.
     *
     * Trotzdem bleibt sie, wo sie hingehört: Die Tagesspalte ist ein eigener
     * Stapelkontext (`z-index: 1`), und darin gilt diese Zahl. Die klebenden
     * Tagesköpfe und die Topbar liegen darüber und können nicht überholt
     * werden.
     */
    z-index: 7;
    /*
     * Zwei Pixel und eine selbst gesetzte Strichelung (Nacharbeit zu #155).
     *
     * Der gestrichelte Rahmen des Browsers war die naheliegende Lösung, gibt
     * aber sein Muster nicht her: Strichlänge und Lücke rechnet jede Maschine
     * aus der Linienstärke selbst aus, und bei einem Pixel wurde daraus ein so
     * feines Muster, dass es im Wochenraster unterging. Der Verlauf zeichnet
     * genau 8 px Strich und 4 px Lücke – dicht genug, dass der Blick die Linie
     * als eine Linie liest, offen genug, dass die Karte darunter lesbar bleibt.
     *
     * Die zwei Pixel wachsen nach unten; die Oberkante bleibt exakt auf der
     * Uhrzeit, an der vorher der Rahmen saß. Die Lage ändert sich dadurch
     * nicht – die Linie ist nur zu sehen.
     */
    height: 2px;
    background-image: repeating-linear-gradient(to right,
        var(--jetzt-linie) 0, var(--jetzt-linie) 8px,
        transparent 8px, transparent 12px);
    /* Sie ist Orientierung und kein Bedienelement: kein Klick, kein Hover, kein
       Fokus bleibt an ihr hängen – auch nicht an der Stelle, wo sie über einer
       Karte oder einer Schaltfläche liegt. */
    pointer-events: none;
}
.raster[data-rgs-jetzt="an"] .jetzt-linie { display: block; }

/*
 * Der heutige Tag: derselbe Strich, in vollem Gelb und mit einem Hauch Schein.
 *
 * `drop-shadow` und nicht `box-shadow`: Der Schein folgt den gezeichneten
 * Strichen statt dem Kasten, der über die ganze Spalte reicht – sonst läge
 * hinter der gestrichelten Linie ein durchgehender Balken. Wenige Pixel,
 * gedämpft: Er hebt das Gelb von einer farbigen Karte ab, ohne zu leuchten.
 */
.jetzt-linie-heute {
    background-image: repeating-linear-gradient(to right,
        var(--jetzt-linie-heute) 0, var(--jetzt-linie-heute) 8px,
        transparent 8px, transparent 12px);
    filter: drop-shadow(0 0 2px var(--jetzt-glanz));
}

/*
 * Die aktuelle Uhrzeit – ausschließlich in der klebenden Zeitachse links.
 *
 * Im Raster selbst verdeckte sie einen Programmpunkt, und zwar ausgerechnet
 * den, der gerade läuft. Hier verdeckt sie höchstens eine Stundenbeschriftung,
 * und die weicht ihr (`.stunde-marke-still`).
 */
.jetzt-uhr {
    display: none;
    position: absolute;
    right: 0.35rem;
    top: calc(var(--jetzt-stunden, 0) * var(--stunde));
    transform: translateY(-50%);
    max-width: calc(100% - 0.7rem);
    padding: 0.08rem 0.34rem;
    border-radius: 4px;
    background: var(--jetzt-marke-flaeche);
    color: var(--jetzt-marke-text);
    /*
     * Etwas präsenter als eine Stundenbeschriftung (0,72 rem, mager) – sie ist
     * die Uhrzeit und nicht die nächste Marke auf der Achse. Derselbe Schein
     * wie am gelben Abschnitt hält beide als ein Zeichen zusammen.
     */
    font-size: 0.74rem;
    line-height: 1.35;
    font-weight: 700;
    box-shadow: 0 0 3px var(--jetzt-glanz);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}
.raster[data-rgs-jetzt="an"] .jetzt-uhr { display: block; }

/* Tagesspalten mit Stunden- und Halbstundenlinien */
.tagesspalte {
    position: relative;
    z-index: 1;             /* eigener Stapelkontext: kein Block über die Kopfzeile */
    margin: var(--rand-oben) 0 var(--rand-unten);
    height: calc(var(--stunden-gesamt) * var(--stunde));
    border-left: 1px solid var(--raster-spaltenrand);
    background-image:
        linear-gradient(to bottom, var(--raster-linie-stunde) 1px, transparent 1px),
        linear-gradient(to bottom, var(--raster-linie-halb) 1px, transparent 1px);
    background-size: 100% var(--stunde), 100% calc(var(--stunde) / 2);
}
.tagesspalte-heute { background-color: var(--raster-heute); }

.nachrichten-sammel {
    position: absolute;                 /* top/left/width kommen aus der Spurverteilung */
    z-index: 1;
    box-sizing: border-box;
    overflow: hidden;
    background: var(--info-flaeche-zart);
    border: 1px dashed var(--rand-akzent);
    border-radius: 5px;
    padding: 0.25rem 0.4rem;
    font-size: 0.7rem;
    line-height: 1.25;
    color: var(--text-mittel);
    margin: 0;
}
.nachrichten-titel, .nachrichten-zeit {
    display: block;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.nachrichten-titel { font-weight: 600; color: var(--info-text); }
.nachrichten-zeit { color: var(--text-leise); font-variant-numeric: tabular-nums; }
.nachrichten-sammel-schmal { font-size: 0.62rem; padding: 0.2rem 0.25rem; }

/* Programmblöcke – absolut über dem Raster, verschieben nichts.
   Überschneiden sich zwei gerenderte Rechtecke, liegen sie nebeneinander
   (siehe _visuelle_spuren_verteilen); verdeckt wird nie etwas. */
.block {
    position: absolute;
    z-index: 2;
    overflow: hidden;
    box-sizing: border-box;
    padding: 0.15rem 0.35rem 0.15rem 0.4rem;
    border: 1px solid var(--rand);
    border-left: 3px solid var(--rand-still);
    border-radius: 4px;
    background: var(--flaeche-karte);
    font-size: 0.76rem;
    line-height: 1.25;
    color: var(--text-stark);
}
.block:hover { z-index: 6; box-shadow: var(--schatten-block); }

/*
 * Reihenfolge im Block: Uhrzeit, Titel mit Wiederholungskennzeichen, dann
 * erst die Zusatzangaben.
 *
 * Der Block ist ein umbrechender Flexkasten. Das klingt aufwendiger als es ist
 * und löst genau eine Sache: Die Uhrzeit belegt eine ganze Zeile, Titel und
 * Wiederholungskennzeichen teilen sich die nächste. Das Kennzeichen kostet
 * dadurch keine eigene Zeile und keinen Millimeter Höhe – im knappsten Block
 * gibt es beides nicht.
 *
 * Die Reihenfolge im Markup bleibt dabei unverändert (Uhrzeit, Titel,
 * Kennzeichen). Es kommt kein Umschließer hinzu; die Zeile entsteht allein aus
 * dem Umbruchverhalten.
 */
.block {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    align-content: flex-start;
    column-gap: 0.25rem;
}
.block-zeit, .block-titel {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
/* Eigene Zeile: Die Uhrzeit ist die erste Angabe und teilt sie mit nichts. */
.block-zeit {
    flex: 0 0 100%;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}
/*
 * Der Titel gibt nach, das Kennzeichen nicht. `min-width: 0` hebt die Vorgabe
 * auf, dass ein Flexkind nie schmaler wird als sein Inhalt – ohne sie liefe
 * die Kürzung mit Auslassungspunkten ins Leere und der Titel schöbe das
 * Kennzeichen aus dem Kasten.
 */
/*
 * `flex-basis: 0` und nicht `auto` – das ist hier der entscheidende Wert.
 *
 * Mit `auto` ist die Ausgangsbreite die Inhaltsbreite. Ein sehr langer Titel
 * überschreitet damit die Zeile, und ein umbrechender Flexkasten bricht dann
 * um, statt zu schrumpfen: Das Kennzeichen landete auf einer dritten Zeile und
 * damit außerhalb des knappen Kastens. Mit `0` beansprucht der Titel zunächst
 * gar keine Breite, bekommt den Rest nach dem Kennzeichen und kürzt darin mit
 * Auslassungspunkten – genau die gewünschte Rangfolge.
 */
.block-titel { flex: 1 1 0; min-width: 0; color: var(--text-normal); }
.block-voll .block-titel { white-space: normal; }
/* Alles Weitere bekommt wieder eine eigene Zeile. */
.block-zusatz { flex: 0 0 100%; }
.block-moderator, .block-typ, .block-intern-marke {
    /* Sekundäre Angaben – je eine eigene Zeile, sichtbar nur, wo Platz ist. */
    flex: 0 0 100%;
    font-size: 0.68rem;
    color: var(--text-still);
}
.block-intern { background: var(--flaeche-still); }
.block-intern-marke { color: var(--text-still); }

/* Knappe Blöcke: kleinere Schrift, damit Uhrzeit UND Titelanfang in die
   Mindesthöhe von 24 px passen. */
.block-knapp {
    font-size: 0.6rem;
    line-height: 1.05;
    padding: 0.1rem 0.2rem 0.1rem 0.3rem;
}
.block-mittel { font-size: 0.72rem; line-height: 1.2; }

/* Geteilte Spuren sind KEIN Konflikt – nur weniger Platz. Deshalb neutral grau. */
.block-geteilt { border-color: var(--rand-stark); }
.block-spuren-viele { font-size: 0.56rem; line-height: 1.05; padding: 0.08rem 0.12rem 0.08rem 0.25rem; }
.block-spuren-viele .block-link { display: none; }

/* Rot bleibt ausschließlich fachlichen Konflikten vorbehalten. */
.block-konflikt { border-color: var(--akzent); background: var(--gefahr-flaeche-zart); }
.block-konflikt .block-titel { color: var(--fehler-text); }
.badge-konflikt { background: var(--fehler-flaeche); color: var(--fehler-text); margin-left: 0.3rem; font-size: 0.65rem; }

.block-link {
    position: absolute;
    top: 0.1rem;
    right: 0.2rem;
    font-size: 0.75rem;
    color: var(--block-link-farbe);
    text-decoration: none;
    opacity: 0;
    transition: opacity 0.15s;
}
.block:hover .block-link { opacity: 1; }
.block-link:hover { color: var(--akzent-text); }

@media (max-width: 900px) {
    .raster { max-height: calc(100vh - 12rem); }
    .raster-gitter { grid-template-columns: 3.6rem repeat(7, minmax(8.5rem, 1fr)); min-width: 64rem; }
}

/* Farbwahl im Elementformular: Farbwähler, Hex-Feld und Zurücksetzen in einer
   Zeile. Das Textfeld bleibt schmal – es trägt genau sieben Zeichen. */
.farbwahl {
	display: flex;
	align-items: center;
	gap: 0.5rem;
	flex-wrap: wrap;
}

.farbwahl input[type="color"] {
	width: 3rem;
	height: 2.2rem;
	padding: 0.1rem;
	cursor: pointer;
}

.farbwahl input[type="text"] {
	width: 9rem;
	font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}


/* ── Öffentliche Elementfarbe ─────────────────────────────────
 *
 * Der schmale Streifen links am Programmpunkt und die linke Kante des
 * Rasterblocks zeigen die öffentliche Elementfarbe, sofern eine gepflegt ist –
 * sonst weiterhin die Typenfarbe. Welche der beiden gilt, entscheidet das
 * Template; hier steht nur, dass eine gepflegte Farbe sichtbar abgesetzt wird.
 *
 * Es ist ausschließlich eine Darstellungsrangfolge. Die Typenfarbe bleibt
 * unverändert am Typ, die öffentliche Farbe am Element; keine schreibt die
 * andere fort, und keine wird aus der anderen hergeleitet.
 *
 * Eine Eckfahne gibt es in dieser Ansicht bewusst nicht: Der Streifen ist
 * ohnehin da und sagt dasselbe. Die Ecke bleibt der öffentlichen Webseite
 * vorbehalten, wo sie Beiträge von Sendungen unterscheidet.
 */
.termin-farbe-oeffentlich {
    /* Etwas breiter als der Typenstreifen – die redaktionelle Farbe ist die
       Aussage, die man auch öffentlich wiedererkennt. */
    width: 6px;
}

/* ── Schmale Geräte ───────────────────────────────────────────
 *
 * Zwei Dinge waren auf dem Telefon kaputt, beide im Browser nachgemessen.
 *
 * Erstens die Programmpunkte: `.termin-zeit` hatte eine feste Mindestbreite
 * von 6,5 rem und `.termin-aktion` konnte wegen `min-width: auto` nicht unter
 * die Breite der Schaltfläche schrumpfen. Bei 360 px Fensterbreite blieben
 * dem Titel dadurch 24,5 px – der Text lief aus seinem Kasten heraus, und das
 * Wdh.-Kennzeichen dahinter war praktisch nicht mehr zu sehen. Es war nie
 * ausgeblendet; es hatte nur keinen Platz mehr.
 *
 * Zweitens der Fokus-Zoom: Safari auf iOS vergrößert die ganze Seite, sobald
 * ein Formularfeld mit weniger als 16 px Schrift den Fokus bekommt. Sämtliche
 * Felder lagen darunter – das Datumsfeld der Sendeplan-Navigation bei 13,6 px.
 * Der saubere Weg ist die Schriftgröße selbst; `user-scalable=no` oder
 * `maximum-scale=1` kämen nicht in Frage, sie nähmen dem Benutzer das eigene
 * Zoomen.
 */
@media (max-width: 640px) {
    /*
     * Der Kopfbereich auf dem Telefon: eine Zeile.
     *
     * Nachgemessen im Browser, vorher: 203 px hoch bei 390 px Fensterbreite,
     * 256 px bei 375 px – die zehn Bereiche brauchten dort sechs bzw. acht
     * Zeilen. Der Seiteninhalt begann erst bei 235 bzw. 288 px. Wer auf dem
     * Handy nur nachsehen wollte, was der Worker gerade tut, musste vorher
     * scrollen. Jetzt sind es 58 px, der Inhalt beginnt bei 90 px.
     *
     * In der Zeile steht, was man ohne Umweg braucht: Logo, der Schalter für
     * die Bereiche und Abmelden. Die Bereiche selbst sind nicht weg, sie
     * klappen auf Antippen darunter auf – ohne Skript, ohne Hover, ohne eine
     * zweite Navigation. Am Schreibtisch ändert sich nichts: dort war der Kopf
     * mit 62 px immer eine Zeile, und er bleibt es.
     *
     * Klebend bliebe der Streifen auch schmal im Weg, deshalb scrollt er auf
     * schmalen Geräten weiterhin mit.
     */
    .hauptmenue {
        position: static;
        padding: 0.45rem 0.7rem;
        gap: 0.4rem;
        /* Die gemeinsame Schreibtischhöhe aus Issue #164 gilt hier nicht: Auf
           dem Telefon ist die Leiste mehrzeilig und soll so flach sein wie
           möglich. 72 px wären hier ein Fünftel des Bildschirms, bevor der
           Inhalt anfängt – und zu vergleichen gibt es nichts, weil die
           Zentrale daneben ihre eigene, ebenso flache Fassung hat. */
        min-height: 0;
    }
    .logo-bild { height: 30px; }
    .untertitel { margin-left: 0.4rem; }
    .menue-rechts { gap: 0.5rem; }

    /*
     * Sichtbar ausgeblendet, für die Tastatur erreichbar: Das Kästchen selbst
     * hat keine Fläche, lässt sich aber anspringen und mit der Leertaste
     * schalten. Es aus der Darstellung zu nehmen nähme es der Tastatur ganz.
     *
     * Genau das war bis hierher der Fall, und es ist im Zuge von #63
     * aufgefallen: Für den Schreibtisch steht weiter oben eine Regel, die
     * Kästchen und Beschriftung aus der Darstellung nimmt – und dieser Block
     * hat sie nie zurückgenommen. Für die Maus fiel das nicht auf, weil die
     * Beschriftung das Kästchen schaltet; für die Tastatur war der Schalter
     * unerreichbar, und die Fokusregel darunter lief ins Leere. Die erste
     * Zeile stellt das richtig.
     */
    .menue-umschalter {
        display: block;
        position: absolute;
        width: 1px;
        height: 1px;
        margin: -1px;
        padding: 0;
        border: 0;
        overflow: hidden;
        clip-path: inset(50%);
        white-space: nowrap;
    }
    .menue-schalter {
        display: inline-flex;
        align-items: center;
        gap: 0.45rem;
        /* Ein Tippziel, kein Textlink: 44 px sind das Maß, das Apple nennt. */
        min-height: 44px;
        padding: 0 0.7rem;
        border: 1px solid rgba(255, 255, 255, 0.35);
        border-radius: 6px;
        font-size: 0.85rem;
        color: var(--kopf-text-hell);
        cursor: pointer;
        /* Sonst markiert ein zweites Antippen die Beschriftung. */
        -webkit-user-select: none;
        user-select: none;
    }
    .menue-umschalter:focus-visible + .menue-schalter {
        outline: 2px solid var(--kopf-akzent);
        outline-offset: 2px;
    }
    /* Drei Striche aus einem einzigen Kasten. */
    .menue-schalter-strich,
    .menue-schalter-strich::before,
    .menue-schalter-strich::after {
        display: block;
        width: 16px;
        height: 2px;
        background: currentColor;
        border-radius: 2px;
    }
    .menue-schalter-strich { position: relative; }
    .menue-schalter-strich::before,
    .menue-schalter-strich::after {
        content: "";
        position: absolute;
        left: 0;
    }
    .menue-schalter-strich::before { top: -5px; }
    .menue-schalter-strich::after { top: 5px; }

    /* Zugeklappt ist der Ausgangszustand. */
    .menue-nav { display: none; }
    .menue-umschalter:checked ~ .menue-nav {
        display: flex;
        /* Eine eigene Zeile unter dem Kopf, linksbündig statt mittig. */
        flex-basis: 100%;
        justify-content: flex-start;
        gap: 0.2rem;
        padding-top: 0.2rem;
    }
    /*
     * „Mehr" gehört dem Schreibtisch.
     *
     * Auf dem Telefon steht schon der Hamburger; zwei Klappmenüs übereinander
     * wären eine Bedienfalle. Das Skript räumt die Bereiche dort ohnehin
     * zurück in die Leiste – diese Regel ist der Riegel davor, dass beim
     * Drehen des Geräts für einen Wimpernschlag doch etwas stehen bleibt.
     */
    .hauptmenue .menue-mehr,
    .hauptmenue .menue-mehr-liste { display: none; }

    /*
     * Und die Leiste bekommt ihre Zeilen zurück.
     *
     * Für den Schreibtisch schneidet sie ihren Überhang ab, damit `menue.js`
     * ihn einsammeln kann. Hier gibt es niemanden, der einsammelt – hier klappt
     * das Kästchen auf, und dann sollen alle Bereiche untereinander stehen.
     * Dieselbe Klasse, dieselbe Spezifität, nur später im Stylesheet: Es
     * braucht dafür keinen zweiten Breakpoint.
     *
     * Der Boden von 6 rem darf stehen bleiben: Aufgeklappt nimmt die Leiste
     * hier ohnehin die volle Breite (`flex-basis: 100%`), und zugeklappt hat
     * sie gar keinen Kasten. 96 px sind auf keinem Telefon eine Grenze.
     */
    html.js .hauptmenue { flex-wrap: wrap; }
    html.js .menue-nav {
        flex-wrap: wrap;
        overflow: visible;
    }
    html.js .menue-nav > * {
        flex: 0 1 auto;
        white-space: normal;
    }

    /* Aufgeklappt wird getippt, nicht gezeigt: jede Fläche mindestens 44 px. */
    .menue-link {
        display: inline-flex;
        align-items: center;
        min-height: 44px;
        padding: 0 0.7rem;
    }

    /*
     * Und ohne den Messabdruck aus Issue #163.
     *
     * Er gehört der Zeile am Schreibtisch: Dort liegen die Bereiche
     * nebeneinander, und eine Breite, die vom Zustand abhängt, schiebt die
     * Nachbarn zur Seite. Hier stehen sie untereinander – es gibt keinen
     * Nachbarn, den eine breitere Beschriftung verschieben könnte.
     *
     * Wegnehmen muss man ihn trotzdem, und zwar wegen der Zeile darüber: Der
     * Verweis ist hier ein `inline-flex`-Kasten, und darin stünde der Abdruck
     * **neben** dem Namen statt unter ihm. Jede Tippfläche wäre dann um ihre
     * eigene Beschriftung breiter.
     */
    .menue-link[data-titel]::before,
    .menue-mehr-wort[data-titel]::before { display: none; }

    /*
     * Abmelden bleibt immer sichtbar – es ist die einzige Bedienung im Kopf,
     * die man auch ohne den Umweg über das Menü braucht. Name und Rolle
     * dagegen kosten auf 390 px mehr Platz, als sie sagen; sie erscheinen mit
     * den Bereichen.
     */
    .abmelden {
        display: inline-flex;
        align-items: center;
        min-height: 44px;
        padding: 0 0.7rem;
    }
    .benutzer,
    .benutzer-rolle { display: none; }
    .menue-umschalter:checked ~ .menue-rechts .benutzer,
    .menue-umschalter:checked ~ .menue-rechts .benutzer-rolle { display: inline; }
    /*
     * Der Verweis nach „Mein Konto" liegt um den Namen herum (Issue #69) und
     * verschwindet mit ihm. Bliebe er stehen, wäre er ein leeres, unsichtbares
     * Tabulatorziel in der Kopfzeile.
     */
    .benutzer-konto { display: none; }
    .menue-umschalter:checked ~ .menue-rechts .benutzer-konto { display: inline; }
    /*
     * „Nachahmen" und „nachgeahmt von …" (Issue #238) folgen dem Namen: Sie
     * erscheinen mit dem aufgeklappten Menü. Dass eine Nachahmung läuft, sagt
     * auf dem Telefon das Band über dem Inhalt – es steht ohne Umweg da und
     * trägt „Nachahmung beenden".
     */
    .nachahmen-einstieg,
    .nachahmung-marke { display: none; }
    .menue-umschalter:checked ~ .menue-rechts .nachahmen-einstieg {
        display: inline-flex;
        align-items: center;
        min-height: 44px;
        padding: 0 0.7rem;
    }
    .menue-umschalter:checked ~ .menue-rechts .nachahmung-marke { display: inline; }
    .nachahmung-band { padding: 0.65rem 0.8rem; }
    .nachahmung-band-aktion,
    .nachahmung-band-aktion .schaltflaeche { width: 100%; }
    .nachahmung-band-aktion .schaltflaeche { min-height: 44px; }
    .nachahmen-eintrag .schaltflaeche { min-height: 44px; }

    /*
     * Die Maße dieser Regel stammen aus #44: weniger Polster, engere Zeilen.
     * Warum sie hier stehen und nicht im Abschnitt weiter unten – es gibt für
     * einen Selektor eine Regel, nicht zwei, von denen die zweite die erste
     * überschreibt. Die Begründung steht bei „Kompaktere Programmkarten".
     */
    .termin {
        flex-wrap: wrap;
        gap: 0.6rem;
        row-gap: 0.4rem;
        padding: 0.5rem 0.6rem;
    }
    /* Die Uhrzeit braucht keine 6,5 rem – „06:15“ passt in 4,5 rem. */
    .termin-zeit { min-width: 4.5rem; }
    .termin-inhalt { flex: 1 1 8rem; }
    /* Deutsche Zusammensetzungen wie „Landesgartenschau-Wissen“ sind breiter
       als die Spalte. Sie dürfen umbrechen, statt in das Polster zu laufen. */
    .termin-titel,
    .termin-moderator { overflow-wrap: break-word; }
    /* Die Schaltfläche bekommt eine eigene Zeile, statt dem Titel den Platz
       wegzunehmen. Sie verschwindet nicht – sie steht nur darunter. */
    .termin-aktion { flex: 1 1 100%; }

    /* Mindestens 16 px, sonst zoomt iOS beim Antippen die ganze Seite. */
    .formular-zeile input[type="text"],
    .formular-zeile input[type="url"],
    .formular-zeile input[type="number"],
    .formular-zeile input[type="date"],
    .formular-zeile input[type="time"],
    .formular-zeile select,
    .formular-zeile textarea,
    .datumswahl input[type="date"],
    .farbwahl input[type="text"],
    input,
    select,
    textarea {
        font-size: 16px;
    }

    /* ── Mobile Basis, Teil 1: Tippflächen ────────────────────────────────
     *
     * Der Mobile-Audit zu Issue #35 nennt das den Befund mit der größten
     * Reichweite, und er ist keine Sammlung von Einzelfällen, sondern die
     * Grundregel des Stylesheets: `.schaltflaeche` misst 30 px,
     * `.schaltflaeche-klein` 23 px, `.schalter` 29 px, ein Kontrollkästchen
     * 13 × 13 px. „Deaktivieren“ und „Löschen“ stehen in der
     * Benutzerverwaltung mit je 23 px Höhe direkt nebeneinander.
     *
     * 44 px ist das Maß, das Apple nennt, und dasselbe, das die Kopfzeile seit
     * Issue #26 einhält – deshalb steht es hier noch einmal und nicht in einer
     * neuen Zahl.
     *
     * Erreicht wird es über `min-height` und nicht über `padding`: Die
     * Beschriftung bleibt dadurch, wo sie ist, und eine Schaltfläche mit
     * umbrechendem Text wird höher statt gequetscht. `inline-flex` mit
     * `align-items: center` hält den Text dabei in der Mitte der neuen Höhe;
     * ohne das säße er oben.
     *
     * Die Breite bleibt unberührt. Sie ist auf dem Telefon der knappe Wert,
     * und ein Knopf, der über den Rand wächst, wäre kein besseres Tippziel.
     *
     * Warum an der Fensterbreite und nicht an `pointer: coarse`, wie es der
     * Audit vorschlägt: Issue #43 begrenzt dieses Paket ausdrücklich auf
     * schmale Smartphone-Ansichten und verlangt, dass Tablet und Querformat
     * unverändert bleiben. Ein Tablet ist genauso fingerbedient, aber breiter
     * als 640 px – es bleibt damit außen vor. Das ist eine bewusste
     * Entscheidung dieses Pakets, keine vergessene Zeile.
     */
    .schaltflaeche,
    .schalter {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        min-height: 44px;
    }
    /* Die Anmeldeschaltfläche ist bereits zeilenbreit – sie bleibt ein
       Blockelement, sonst entstünde unter ihr die Grundlinienlücke einer
       Textzeile. */
    .anmelde-button {
        display: flex;
        align-items: center;
        justify-content: center;
        min-height: 44px;
    }
    /* `.schaltflaeche-klein` ändert nur Polster und Schriftgröße – die
       Mindesthöhe kommt von `.schaltflaeche` und gilt damit auch hier. */

    /*
     * Die Zeitskala des Wochenrasters. Ihre drei Bedienelemente sind mit
     * 24 px die kleinsten Ziele der ganzen Anwendung; `height` ist dort fest
     * gesetzt und muss deshalb ausdrücklich zurückgenommen werden, bevor
     * `min-height` etwas bewirken kann.
     */
    .skala-taste,
    .skala-standard {
        height: auto;
        min-height: 44px;
    }
    .skala-taste { width: 44px; }
    .skala-schieber { min-height: 44px; }

    /*
     * Kontrollkästchen, Auswahlknöpfe und ihre Beschriftung als **eine**
     * Fläche.
     *
     * Das Kästchen allein bleibt ein 13-px-Ziel, egal wie groß die Zeile
     * daneben ist – erst die gemeinsame Fläche macht daraus ein Tippziel. Die
     * Beschriftungen sind bereits `<label>`-Elemente mit dem Feld darin, also
     * genügt hier die Höhe.
     *
     * Die Selektoren nennen dieselben Kombinationen wie die Regeln weiter
     * oben (`.checkbox-label input[type="checkbox"]` und Geschwister). Das ist
     * kein Zufall: Mit geringerer Spezifität bliebe das dortige `width: auto`
     * stehen und das Kästchen 13 px klein.
     */
    .checkbox-label,
    .wochentag-feld,
    .umfang-option {
        min-height: 44px;
    }
    .checkbox-label input[type="checkbox"],
    .wochentag-feld input[type="checkbox"],
    .auswahl-schalter input[type="radio"],
    .umfang-option input,
    input[type="checkbox"],
    input[type="radio"] {
        width: 22px;
        height: 22px;
        /* In einer Flex-Beschriftung gäbe das Kästchen sonst Breite ab, sobald
           der Text daneben eng wird – nachgemessen 13 von 22 px. */
        flex-shrink: 0;
    }
    /*
     * Auswahl- und Eingabefelder. Die Schriftgröße steht weiter oben schon auf
     * 16 px (Fokus-Zoom); was fehlt, ist die Höhe. Kontrollkästchen und
     * Auswahlknöpfe sind ausdrücklich ausgenommen – sie sind quadratisch und
     * würden sonst zu 44-px-Klötzen.
     */
    .formular-zeile input:not([type="checkbox"]):not([type="radio"]),
    .formular-zeile select,
    .formular-zeile textarea,
    .datumswahl input[type="date"],
    .farbwahl input[type="text"],
    .formfeld input,
    select {
        min-height: 44px;
    }

    /*
     * Das Augensymbol im Passwortfeld (Issue #69). Es liegt im Feld und ist
     * damit kein Geschwister, sondern ein Überzug – trotzdem gilt für es
     * dieselbe Zusage wie für jedes andere Bedienelement auf dem Telefon:
     * 44 px in beide Richtungen. Der rechte Innenabstand des Feldes wächst
     * mit, sonst liefe der eingegebene Text darunter.
     */
    .passwort-auge {
        width: 44px;
        height: 44px;
    }

    .passwortfeld .passwortfeld-eingabe,
    .formfeld .passwortfeld .passwortfeld-eingabe,
    .formular-zeile .passwortfeld .passwortfeld-eingabe { padding-right: 3.3rem; }

    /*
     * Kleine Bedienelemente, die keine Schaltfläche sind: der Aufklapper
     * „Namen anzeigen“ in der Typenliste.
     */
    summary {
        display: flex;
        align-items: center;
        min-height: 44px;
    }

    /*
     * Die Verweise neben einem Feld. Sie stehen inmitten eines Satzes – in der
     * Programmliste und im Programmformular jeweils als „… in der
     * Elemente-Verwaltung gepflegt.“. Als Blockelement zerrisse der Verweis
     * genau diesen Satz auf drei Zeilen; `inline-flex` bleibt im Textfluss und
     * trägt die Mindesthöhe trotzdem, wie schon bei `.karte-aktiv p a`.
     */
    .link-extern {
        display: inline-flex;
        align-items: center;
        min-height: 44px;
    }

    /*
     * Die Nebenaktionen der Dashboard-Kacheln (Issue #157). Bis dahin standen
     * hier die vier Verweise der Karte „Stammdaten“, durch „·“ getrennt in
     * einer Zeile – der senkrechte Abstand zwischen „Elemente“ und
     * „Programmpunkt-Typen“ war gemessene 0 px. Die vier sind heute eigene
     * Kacheln und damit riesige Tippziele; was übrig bleibt, sind die kleinen
     * Abkürzungen („Redaktion gesamt“, „+ Aufgabe“, „Teamseite verwalten“,
     * „Öffentliche Vorschau“). Sie sind 25 px hoch und bekommen hier dieselbe
     * Zusage wie jedes andere Bedienelement auf dem Telefon.
     *
     * Der Hauptweg braucht keine Regel: Er ist die ganze Kachel.
     */
    .kachel-aktion {
        min-height: 44px;
        padding: 0.2rem 0.8rem;
    }

    /*
     * Und das Raster steht auf dem Telefon einspaltig. `auto-fill` käme bei
     * 15 rem von selbst auf eine Spalte; ausgeschrieben steht es trotzdem hier,
     * weil damit auch der Abstand zwischen den Kacheln enger wird – vier
     * Kacheln untereinander mit je 0,9 rem Luft sind auf 390 px eine
     * Bildschirmhöhe, die aus Abstand besteht.
     */
    .kachelfeld {
        grid-template-columns: 1fr;
        gap: 0.7rem;
    }

    /*
     * Und dieselbe Zusage für die beiden Wege des Abschnitts „Uploads"
     * (Issue #203): 44 px hoch, wie jedes andere Bedienelement auf dem
     * Telefon. Der Überblick darüber braucht keine Regel – er ist ohnehin so
     * breit wie die Seite; nur die Zahlen rücken enger zusammen, damit
     * „Übergaben", „angekommen" und „nicht angekommen" auf 390 px in zwei
     * Zeilen stehen und nicht in drei.
     */
    .uploadblick-einstieg,
    .uploadblick-aktion {
        min-height: 44px;
        padding: 0.2rem 0.8rem;
    }

    .uploadblick-zahlen { gap: 0.3rem 1.1rem; }

    .dashboard-wrapper h2 { font-size: 1.25rem; }

    /* ── Mobile Basis, Teil 2: Ränder und Innenabstände ───────────────────
     *
     * Auf 390 px waren 24 px Rand je Seite aus `main` plus 8 px aus
     * `.seite-wrapper` zu vergeben – 16 % der Bildschirmbreite für nichts. Am
     * deutlichsten auf der Anmeldeseite: Dort kamen noch einmal 32 px dazu,
     * und die Karte war 276 px breit in einem 390-px-Fenster.
     *
     * Die Werte sind nicht auf null gesetzt: Ein Kasten, der die Bildschirm-
     * kante berührt, sieht nach Fehler aus, und auf gebogenen Displays liegt
     * er teilweise darunter.
     */
    main { padding: 1rem 0.75rem; }
    .hauptbereich-arbeit { padding: 0.6rem 0.5rem 0.7rem; }
    /* Als Variable, weil die Auswahlleiste weiter unten denselben Wert braucht:
       Sie steht außerhalb des Flusses und muss ihre linke und rechte Kante
       selbst setzen – aus dieser einen Angabe statt aus einer zweiten Zahl. */
    .seite-wrapper { --seiten-polster: 0.25rem; padding: 0 var(--seiten-polster); }
    .login-wrapper { padding: 1rem 0.75rem; }
    .login-box { padding: 1.6rem 1.2rem; }
    .karte,
    .formular-abschnitt { padding: 1rem 0.9rem; }

    /* ── Mobile Basis, Teil 3: der Kopf der Sendeplan-Ansichten ───────────
     *
     * Fünf der sechs gemessenen Überstände der Tagesansicht kommen aus dem
     * Kopf, keiner aus der Liste. Die Ursache ist in beiden Fällen dieselbe:
     * `white-space: nowrap`.
     *
     * `.plan-statistik` („32 Programmpunkte · davon 18 nicht im öffentlichen
     * Tagesplan“) wurde dadurch 607 px breit in einem 342 px breiten Kasten
     * und schob das ganze Dokument um 257 px zur Seite – Kopfzeile
     * inbegriffen. `.plan-zeitraum` machte `.plan-blaettern` 374 px breit, und
     * die „heute“-Marke stand rechts außerhalb des Bildschirms.
     *
     * Am Schreibtisch hat `nowrap` seinen Grund und bleibt: Dort passt die
     * Zeile, und ein Umbruch beim Blättern verschöbe alles darunter. Auf dem
     * Telefon passt sie nicht, und eine zweite Zeile ist das kleinere Übel
     * gegenüber einer Seite, die sich zur Seite schieben lässt.
     */
    .plan-zeitraum {
        white-space: normal;
        flex-wrap: wrap;
        min-width: 0;
        /*
         * Die Überschrift bekommt eine eigene Zeile – und zwar die erste.
         *
         * Umgebrochen stünde sie sonst zwischen „Vortag“ und „Heute“ und
         * zerrisse die drei Blätterschaltflächen auf zwei Zeilen; gemessen
         * 120 px für eine Zone, die 80 braucht. Oben stehend sagt sie
         * außerdem zuerst, welchen Tag man sieht, und die Bedienung steht
         * darunter beieinander.
         *
         * `order` verschiebt nur die Darstellung. Die Reihenfolge im Dokument
         * bleibt, was sie ist – für Vorlesesoftware und für die Tabtaste; die
         * Überschrift selbst ist ohnehin kein Sprungziel.
         */
        order: -1;
        flex-basis: 100%;
    }
    .plan-statistik {
        flex: 0 1 auto;
        min-width: 0;
        white-space: normal;
    }
    .plan-fussnote {
        flex-wrap: wrap;
        gap: 0 0.6rem;
    }
    .plan-notiz { flex-basis: 100%; }
    /*
     * Die Breitenplatzhalter der Kopfzeile dürfen auf dem Telefon nicht mehr
     * Platz reservieren.
     *
     * Am Schreibtisch halten sie die Zeile ruhig: „KW 2“ wird „KW 32“, 2026
     * wird 2027, „gerade“ wird „ungerade“ – jede dieser Änderungen verschöbe
     * sonst alles rechts davon. Auf 390 px bricht die Zeile ohnehin um; dort
     * reservieren sie nur noch die Breite der längsten Variante, die niemand
     * sieht. `visibility: hidden` hält den Platz, `display: none` gibt ihn
     * frei – mehr ist der Unterschied nicht.
     */
    .stabil-platz { display: none; }
    .stabil > * { white-space: normal; }

    /*
     * Die Werkzeugleiste darf wachsen.
     *
     * Der einzige echte Layoutfehler des Audits: Auf 390 px bricht die
     * Zeitskala um und wird 62 px hoch, aber `.plan-werkzeuge` ist ein
     * Flex-Element in der festen Spalte von `.seite-fest` und darf unter seine
     * Inhaltshöhe schrumpfen. Es blieb bei seiner `min-height: 2rem`, und die
     * zweite Zeile lief unten heraus – der dunkle „100 %“-Knopf stand mitten
     * im Text der Bilanzzeile darunter, gemessene 18 px Überdeckung.
     *
     * `flex-shrink: 0` an den drei Kopfzonen behebt das an der Ursache. Die
     * reservierte Mindesthöhe hat auf dem Telefon ohnehin nichts mehr zu tun:
     * Sie hielt den Rasterbeginn beim Wechsel zwischen Tag und Woche um einen
     * Pixel ruhig – ein Wert aus der Schreibtischansicht.
     */
    .plan-kopfzeile,
    .plan-werkzeuge,
    .plan-fussnote {
        flex-shrink: 0;
    }
    .plan-werkzeuge { min-height: 0; }
    /* Die beiden Umschaltergruppen zusammen sind rund 340 px breit und passen
       damit auf 390 px gerade eben nicht mehr neben den Rand. Sie dürfen
       umbrechen statt hinauszuragen. */
    .plan-ansichten { flex-wrap: wrap; }
    /*
     * Gestapelt gibt es kein „rechts“ mehr.
     *
     * `margin-left: auto` und die rechtsbündige Ausrichtung halten die Zone am
     * Schreibtisch beim Umbruch an ihrem Platz – der Grund steht oben bei
     * `.plan-kopfzeile`. Auf 390 px steht sie ohnehin allein in ihrer Zeile,
     * und das Datumsfeld hing dort ohne Not am rechten Rand, während links
     * daneben nichts stand. Untereinander an derselben Kante gelesen zu
     * werden, ist die Form, die eine gestapelte Ansicht braucht.
     */
    .plan-rechts {
        margin-left: 0;
        justify-content: flex-start;
    }
    /* Die Suche bekommt auf dem Telefon die ganze Zeile und die Tippfläche
       aus #43 – als schmales Feld neben dem Datum wäre ihr Text unlesbar. */
    .plan-suche { flex: 1 1 100%; }
    .plan-suche-eingabe { width: 100%; min-height: 44px; }
    .plan-suche-liste { width: 100%; max-width: none; }
    .plan-suche-vorschlag { min-height: 44px; }

    /* ── Die aktive Auswahl als Leiste am unteren Rand (Issue #85) ────────
     *
     * Gemessen bei 390 px: Das Band kostete als eigener Block über der Liste
     * 94 px plus Abstand, und die Programmliste begann dadurch 102 px weiter
     * unten. Der Grund war nicht der Text, sondern die beschriftete
     * Schaltfläche „Auswahl aufheben": 137 px breit, neben dem Namen nicht
     * unterzubringen und deshalb in einer zweiten, 44 px hohen Zeile.
     *
     * Der erste Anlauf machte daraus eine Pille im freien Platz der
     * Steuerzeile. Nachgemessen blieb davon nichts: In der Zeile mit
     * Datumsfeld und „Ansicht" sind bei 390 px 92 px frei und bei 360 px
     * 62 px – zu wenig für einen erkennbaren Namen neben einem 44-px-Tippziel.
     * Die Pille brach deshalb doch in eine eigene Zeile um, und die kostete
     * 54 px. Genau die waren der gemeldete Sprung: Beim Auswählen wuchs die
     * Kopfzeile, und der ganze Plan darunter rutschte 54 px nach unten; beim
     * Aufheben sprang er zurück. (Die Kontur am gewählten Programmpunkt hat
     * daran keinen Anteil – sie liegt seit jeher als innerer Schein im Kasten
     * und verschiebt nichts, siehe `[data-rgs-plan].hat-inhalt .block.ist-inhalt`.)
     *
     * Waagerecht ist der Platz also nicht da. Also senkrecht – aber von
     * **unten**. Wo die Leiste steht und wie ihr Platz frei gemacht wird,
     * steht seit der Nacharbeit zu #91 nicht mehr hier, sondern außerhalb
     * jeder Medienabfrage bei `.plan-kopfzeile > .plan-band-auswahl`: Der
     * Schreibtisch hatte denselben Sprung, also gilt dort dieselbe Antwort.
     *
     * Was hier bleibt, ist der Unterschied des Telefons – die kompakte
     * Beschriftung und das Tippziel. Nebenbei bekommt der Name damit die volle
     * Breite statt einer Restbreite, und das „×" liegt in der Daumenzone statt
     * am oberen Rand.
     */
    /*
     * Mit dem Zusatz „nicht im Plan" hat die Leiste zwei Zeilen zu tragen, und
     * auf 320 px bricht der Halbsatz selbst noch einmal um: gemessen 54,8 px
     * statt 46. Ohne diese Ausnahme deckte sie dort die letzten drei Pixel des
     * Scrollbereichs zu – überlagern statt umbrechen, und genau das soll es
     * nicht geben. Der Rest ist Luft für eine dritte Zeile. Am Schreibtisch
     * passt derselbe Halbsatz neben den Namen; dort reicht die Grundhöhe.
     */
    .sendeplan-arbeitsflaeche:has(.plan-band-auswahl:not([hidden]) .plan-auswahl-leer:not([hidden])) {
        padding-bottom: calc(var(--auswahlleiste) + 1.25rem);
    }
    /* Rechts läuft die Leiste bis an die Kante: Das Tippziel ist dort ihr
       Ende und kein Kasten mit Abstand darin. */
    .plan-kopfzeile > .plan-band-auswahl { padding-right: 0; }
    /* Das Wort steht weiter im Vorlesefluss – siehe `.nur-vorlesen`, dieselbe
       Technik. Sichtbar sagt die Kontur links dasselbe kürzer. */
    .plan-band-auswahl .plan-auswahl-wort {
        position: absolute;
        width: 1px;
        height: 1px;
        padding: 0;
        margin: -1px;
        overflow: hidden;
        clip-path: inset(50%);
        white-space: nowrap;
        border: 0;
    }
    /* Das Tippziel: 44 × 44 px nach der Regel aus #43, quadratisch statt
       breit, und ohne den Rahmen einer Schaltfläche – in einer Pille ist es
       ihr rechtes Ende und kein zweiter Kasten darin. */
    .plan-band-auswahl .plan-band-aktion {
        min-width: 44px;
        min-height: 44px;
        padding: 0;
        border: 0;
        background: none;
        font-size: 1.4rem;
        line-height: 1;
    }
    /* Die Rückmeldung beim Antippen bleibt: Sie ist nur dann eine Fläche,
       wenn sie etwas zu sagen hat. */
    .plan-band-auswahl .plan-band-aktion:hover,
    .plan-band-auswahl .plan-band-aktion:active {
        background: var(--flaeche-neutral);
    }
    .plan-aktion-lang { display: none; }
    .plan-aktion-kurz { display: inline; }

    /*
     * Der Kasten für die Typenliste – der einzige Ort, an dem er einer ist.
     * Warum nur hier und nicht am Schreibtisch: siehe `.tabelle-quer` oben.
     */
    .tabelle-quer { overflow-x: auto; }

    /* ══ Sendeplan im Hochformat (Issue #44) ══════════════════════════════
     *
     * Das zweite Paket aus dem Mobile-Audit #35. Die Tagesansicht ist auf dem
     * Telefon die Ansicht – dorthin schickt `sendeplan/woche.html` schmale
     * Geräte, wenn keine Ansicht ausdrücklich verlangt war. Hier steht, was
     * sie braucht, um dort allein zu tragen: ein Tageswähler statt des
     * Wochenrasters, kürzere Karten und eine Kopfzeile, die nicht die halbe
     * Anzeige belegt.
     *
     * Alles davon liegt in diesem Block und wirkt ausschließlich unter 640 px.
     */

    /* ── 1) Der Tagesstreifen ─────────────────────────────────────────────
     *
     * Sieben gleich breite Knöpfe. `1fr` statt einer Breitenangabe: Bei 390 px
     * bleiben je Tag rund 50 px, bei 360 px rund 46 – in beiden Fällen über
     * dem 44-px-Maß aus #43, und ohne dass hier eine Zahl stünde, die bei der
     * nächsten Randänderung wieder falsch wäre.
     *
     * `flex-shrink: 0` aus demselben Grund wie bei den drei Kopfzonen: Er
     * steht in der festen Spalte von `seite-fest` und dürfte sonst unter seine
     * Inhaltshöhe gedrückt werden.
     */
    .tagesstreifen {
        display: grid;
        grid-template-columns: repeat(7, 1fr);
        gap: 0.25rem;
        margin-bottom: 0.5rem;
        flex-shrink: 0;
    }
    .tagesstreifen-tag {
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        min-height: 44px;
        padding: 0.15rem 0;
        border: 1px solid var(--rand-stark);
        border-radius: 6px;
        background: var(--flaeche-karte);
        color: var(--text-normal);
        text-decoration: none;
        line-height: 1.15;
    }
    .tagesstreifen-kuerzel { font-size: 0.82rem; font-weight: 600; }
    .tagesstreifen-zahl {
        font-size: 0.68rem;
        color: var(--text-still);
        font-variant-numeric: tabular-nums;
    }
    /*
     * „Heute" und „gerade gewählt" sind zwei verschiedene Aussagen und
     * bekommen deshalb zwei verschiedene Mittel: Farbe für heute, Fläche für
     * die Auswahl. Beides zugleich ist der Normalfall und muss lesbar bleiben
     * – deshalb steht der aktive Zustand hinter dem heutigen und gewinnt.
     */
    .tagesstreifen-tag-heute {
        border-color: var(--info-marke);
        color: var(--info-marke);
    }
    .tagesstreifen-tag-heute .tagesstreifen-zahl { color: var(--info-marke); }
    .tagesstreifen-tag-aktiv {
        background: var(--fuellung);
        border-color: var(--fuellung);
        color: var(--fuellung-text);
    }
    .tagesstreifen-tag-aktiv .tagesstreifen-zahl { color: var(--fuellung-text-leise); }
    .tagesstreifen-tag:focus-visible {
        outline: 2px solid var(--kontur);
        outline-offset: 2px;
    }

    /* ── 2) Die Werkzeuge hinter „Ansicht" ────────────────────────────────
     *
     * Zugeklappt ist der Ausgangszustand. Die Zeitskala, „Nachrichten" und
     * „Freie Zeiten" sind Werkzeuge des Wochenrasters am Schreibtisch; auf dem
     * Telefon kosteten sie in der Tagesansicht eine Zeile, die man beim
     * Nachsehen nie braucht. Die Umschalter „Tag | Woche" und „Intern |
     * Öffentliche Vorschau" gehören zur selben Frage und klappen mit auf.
     *
     * Verloren geht dabei nichts: Jedes dieser Bedienelemente steht
     * unverändert im Dokument und ist einen Fingertipp entfernt.
     *
     * Dasselbe Mittel wie beim Hauptmenü – ein Kontrollkästchen und seine
     * Beschriftung, kein Skript. Das Kästchen ist sichtbar ausgeblendet, aber
     * für die Tastatur erreichbar; `display: none` nähme es ihr ganz.
     */
    .plan-werkzeug-schalter,
    .plan-nebensachen-schalter {
        position: absolute;
        width: 1px;
        height: 1px;
        margin: -1px;
        padding: 0;
        border: 0;
        overflow: hidden;
        clip-path: inset(50%);
        white-space: nowrap;
    }
    .plan-werkzeug-taste {
        display: inline-flex;
        align-items: center;
        gap: 0.35rem;
        cursor: pointer;
        /* Sonst markiert ein zweites Antippen die Beschriftung. */
        -webkit-user-select: none;
        user-select: none;
    }
    /*
     * Das Dreieck als Ränder eines leeren Kastens statt als Schriftzeichen:
     * „▸" fehlt in manchen Systemschriften und wäre dann ein Ersatzkästchen.
     */
    .plan-werkzeug-pfeil {
        width: 0;
        height: 0;
        border: 4px solid transparent;
        border-bottom: 0;
        border-top-color: currentColor;
    }
    .plan-werkzeug-schalter:checked ~ .plan-kopfzeile .plan-werkzeug-pfeil {
        border-top: 0;
        border-bottom: 4px solid currentColor;
    }
    .plan-werkzeug-schalter:focus-visible ~ .plan-kopfzeile .plan-werkzeug-taste,
    .plan-nebensachen-schalter:focus-visible ~ .plan-kopfzeile .plan-nebensache-taste {
        outline: 2px solid var(--kontur);
        outline-offset: 2px;
    }

    /*
     * Zugeklappt. Ausdrücklich auch für „Details": Es trägt die Klasse
     * `.schaltflaeche`, und die steht in diesem Block auf `inline-flex` –
     * ohne diese Zeile wäre es sichtbar, obwohl nichts aufgeklappt ist.
     */
    .plan-ansichten,
    .plan-werkzeuge,
    .plan-nebensache-taste { display: none; }
    .plan-werkzeug-schalter:checked ~ .plan-werkzeuge { display: flex; }
    .plan-werkzeug-schalter:checked ~ .plan-kopfzeile .plan-ansichten { display: flex; }
    /* „Details" ist eine Option von „Ansicht" und erscheint mit ihr. */
    .plan-werkzeug-schalter:checked ~ .plan-kopfzeile .plan-nebensache-taste {
        display: inline-flex;
        align-items: center;
        cursor: pointer;
        -webkit-user-select: none;
        user-select: none;
    }
    /* Eingeschaltet sieht er aus wie ein aktiver Schalter – dieselbe Fläche,
       dieselbe Farbe, dieselbe Aussage. */
    .plan-nebensachen-schalter:checked ~ .plan-kopfzeile .plan-nebensache-taste {
        background: var(--fuellung);
        border-color: var(--fuellung);
        color: var(--fuellung-text);
    }

    /* ── 3) Die Nebenangaben in den Karten ────────────────────────────────
     *
     * Toleranz, öffentlicher Zeittext und Sidebar-Modus. Der Audit führt sie
     * als das, was auf 390 px in fast jeder Karte steht, dort eine ganze Zeile
     * kostet und beim Nachsehen nie gebraucht wird.
     *
     * Ein Aufklapper je Karte wäre nach der 44-px-Regel selbst höher als die
     * Zeile, die er spart. Deshalb einer für die ganze Liste – er steht bei
     * den übrigen Ansichtsoptionen hinter „Ansicht".
     */
    .termin-nebensache { display: none; }
    .plan-nebensachen-schalter:checked ~ .arbeitsflaeche-scroll .termin-nebensache {
        display: inline;
    }

    /* ── 4) Kompaktere Programmkarten ─────────────────────────────────────
     *
     * Weniger Polster, engere Zeilen, aber **mehr** Abstand zwischen zwei
     * Karten: Der Audit misst dort heute 6 px, und eine kürzere Karte braucht
     * eine deutlichere Kante, sonst verschmilzt die Liste zu einer Fläche.
     *
     * Die Schriftgrößen bleiben, wie sie sind. Kompakter heißt hier weniger
     * Leerraum, nicht kleinere Schrift – lesbar muss die Karte bleiben.
     */
    .terminliste { gap: 0.55rem; }
    /* Polster und Zeilenabstände der Karte selbst stehen weiter oben bei den
       übrigen `.termin`-Regeln aus #43 – ein Selektor, eine Regel. */
    .termin-titel { line-height: 1.3; }
    .termin-merkmale { margin-top: 0.3rem; gap: 0.3rem; }
    /*
     * „Bearbeiten" behält seine eigene Zeile aus #43 – nachgemessen und
     * verworfen: Neben den Inhalt gestellt spart die Schaltfläche bei 390 px
     * rund 30 px Kartenhöhe, drückt den Titel dabei aber von 220 auf 151 px.
     * Ein langer Titel braucht dann drei Zeilen statt zwei, und die Karte ist
     * am Ende höher als vorher. Der Platz gehört dem Titel.
     */

    /* ══ Mobile Listen: Karten statt Tabellen (Issue #45) ═════════════════
     *
     * Das dritte Paket aus dem Mobile-Audit #35. Vier Listen, ein Muster.
     *
     * Der Befund war überall derselbe: eine Tabelle, viel breiter als das
     * Gerät. Planung 1345 px auf elf Spalten, Programmpunkt-Typen 1125 px,
     * Elemente 988 px, Sendungen 685 px – sichtbar davon jeweils 342 px. Wer
     * in der Planung „Bearbeiten“ suchte, wischte über einen Meter
     * Bildschirmbreite, und die feststehende Kopfzeile half dabei nicht: Sie
     * sagte, in welcher Spalte man ist, aber nicht mehr, zu welcher Zeile.
     *
     * Eine schmalere Tabelle ist keine Lösung. Elf Spalten in 342 px sind
     * 31 px je Spalte – das ist keine Tabelle mehr, sondern ein Rest davon.
     * Deshalb steht hier je Datensatz eine Karte: oben, was die Zeile
     * bezeichnet, darunter die Merkmale als Marken, dann die übrigen Spalten
     * als Paare aus Bezeichnung und Wert, unten die Aktionen.
     *
     * Weggelassen wird dabei nichts. Jede Spalte der Tabelle hat in der Karte
     * ihren Platz – auch die, die dort nur ein `title` war.
     */

    /*
     * Die Tabelle geht, die Karten kommen. Beides an einer Stelle, damit
     * niemals beide zugleich dastehen.
     *
     * `.datentabelle` trägt genau diese vier Tabellen und keine weitere.
     * Ausgeblendet statt entfernt: Der Schreibtisch braucht sie, und mehrere
     * Suiten prüfen, dass sie im Dokument steht.
     */
    .datentabelle { display: none; }
    .listenkarten {
        display: flex;
        flex-direction: column;
        /* Derselbe Abstand wie zwischen zwei Programmkarten aus #44 – eine
           kurze Karte braucht eine deutliche Kante, sonst verschmilzt die
           Liste zu einer Fläche. */
        gap: 0.55rem;
    }
    .listenkarte {
        background: var(--flaeche-karte);
        border: 1px solid var(--rand);
        border-radius: 8px;
        box-shadow: var(--schatten-liste);
        padding: 0.6rem 0.7rem;
    }

    /*
     * Der Kopf: das, woran man den Datensatz erkennt.
     *
     * In der Planung sind das zwei Angaben – die interne Zielzeit und der
     * Titel –, überall sonst nur der Titel. Die Zeit gibt keine Breite ab
     * (`flex-shrink: 0`), der Titel nimmt den Rest und darf umbrechen;
     * deutsche Zusammensetzungen wie „Landesgartenschau-Wissen“ sind länger
     * als 342 px.
     */
    .listenkarte-kopf {
        display: flex;
        align-items: baseline;
        gap: 0.5rem;
        flex-wrap: wrap;
    }
    .listenkarte-zeit {
        flex-shrink: 0;
        font-weight: 700;
        font-size: 1.05rem;
        color: var(--text-stark);
        font-variant-numeric: tabular-nums;
    }
    .listenkarte-titel {
        flex: 1 1 8rem;
        margin: 0;
        font-size: 1rem;
        font-weight: 600;
        color: var(--text-stark);
        line-height: 1.3;
        overflow-wrap: anywhere;
    }
    /* Die Anzahl der Unter-Elemente steht am Titel, nicht in einer eigenen
       Zeile – sie beschreibt ihn. */
    .listenkarte-zusatz { font-size: 0.8rem; font-weight: 400; color: var(--text-leise); }

    /* Die Marken direkt unter dem Titel: Typ, „Wdh.“, Kategorie. Sie sind das,
       was man beim Überfliegen sucht, und brauchen deshalb keine
       Bezeichnung. */
    .listenkarte-marken {
        display: flex;
        flex-wrap: wrap;
        gap: 0.3rem;
        margin-top: 0.4rem;
    }

    /*
     * Die übrigen Spalten als Paare aus Bezeichnung und Wert – dieselbe Form
     * wie in den Angaben einer Benutzerkarte und im Bestätigungsdialog.
     *
     * Zwei Spalten und nicht untereinander: Die Bezeichnungen sind kurz, und
     * nebeneinander gelesen bleibt die Karte halb so hoch.
     */
    .listenkarte-angaben {
        display: grid;
        grid-template-columns: auto 1fr;
        column-gap: 0.7rem;
        row-gap: 0.2rem;
        margin: 0.45rem 0 0;
        font-size: 0.85rem;
    }
    .listenkarte-angaben dt { color: var(--text-leise); }
    .listenkarte-angaben dd { margin: 0; color: var(--text-normal); overflow-wrap: anywhere; }

    /*
     * Die Aktionen als Fußzeile: abgesetzt, damit ein „Löschen“ nicht wie eine
     * weitere Angabe aussieht, und umbrechend, weil in der Elementliste bis zu
     * fünf davon nebeneinanderstehen.
     *
     * Die Höhe von 44 px bringen die Schaltflächen aus #43 selbst mit.
     */
    .listenkarte-aktionen {
        display: flex;
        flex-wrap: wrap;
        align-items: center;
        gap: 0.4rem;
        margin-top: 0.5rem;
        padding-top: 0.45rem;
        border-top: 1px solid var(--rand-zart);
    }
    .listenkarte-aktionen form { display: inline; }

    /*
     * Die Reihenfolge-Tasten der Elementliste. Sie sind die einzigen
     * Schaltflächen der Anwendung, die nacheinander mehrfach gedrückt werden,
     * und mit „↑“ als ganzem Inhalt die schmalsten – der Audit misst 30 × 23
     * px. Die Höhe kommt von `.schaltflaeche`, die Breite hier.
     */
    .listenkarte-rang { min-width: 44px; }

    /*
     * Unter-Elemente. In der Tabelle sind sie eine eingerückte Zeile mit „↳“;
     * hier eine eingerückte Karte mit derselben Kante. Eigene Karte und nicht
     * ein Abschnitt der Elternkarte: Sie haben eigene Angaben und eigene
     * Aktionen, und eine Karte, die zwei Datensätze bedient, ist keine mehr.
     */
    .listenkarte-kind {
        margin-left: 0.9rem;
        border-left: 3px solid var(--rand-akzent);
        background: var(--flaeche-still);
    }

    /* ══ Feinschliff: Personen, Benutzer, Formulare, Login ═══════════════
     *
     * Das vierte und letzte Paket aus dem Mobile-Audit #35 (Issue #46). Diese
     * Bereiche bewertet der Audit als „strukturell brauchbar“ – hier wird
     * deshalb nichts umgebaut, sondern verdichtet: weniger Leerraum, breitere
     * Felder und eine Karte, die nicht den ganzen Bildschirm belegt.
     *
     * Der fünfte Bereich des Pakets waren die Klärungen mit ihrem Archiv-
     * Aufklapper. Er ist mit Issue #156 entfallen – der Bereich ist
     * abgeschafft, und seine Regeln sind es mit ihm.
     *
     * Alles davon steht in diesem Block und wirkt ausschließlich unter 640 px.
     * Der Entwicklungsstatus bleibt unberührt: Der Audit nennt ihn das gute
     * Vorbild, und es gibt hier keine Regel, die ihn träfe.
     */

    /* ── 1) Die Anmeldeseite ──────────────────────────────────────────────
     *
     * Zwei Ränder übereinander. `main` gibt seit #43 auf dem Telefon 0,75 rem
     * je Seite, `.login-wrapper` noch einmal 0,75 rem – zusammen 48 px auf
     * 390 px Bildschirm für einen Kasten, der ohnehin allein auf der Seite
     * steht. Die Karte maß dadurch 342 px.
     *
     * Der Rand verschwindet nicht, er wird nur einmal statt zweimal vergeben:
     * `.login-wrapper` trägt ihn, `main` gibt ihn ab. Die Karte hat damit
     * 366 px und denselben Abstand zur Bildschirmkante wie der Inhalt jeder
     * anderen Seite.
     *
     * Der senkrechte Rand geht aus einem zweiten Grund mit: `.login-wrapper`
     * ist 100 vh hoch. In einem `main` mit 1 rem Polster oben und unten war
     * das Dokument dadurch 876 px hoch in einem 844-px-Fenster – 32 px
     * Scrollweg auf einer Seite, die nichts zu scrollen hat.
     *
     * Die Klasse steht am `main` der Anmeldeseite und sonst nirgends; die
     * allgemeine Regel für `main` bleibt, wie sie ist.
     */
    main.hauptbereich-anmeldung { padding: 0; }

    /* ── 2) Personen: Miniatur statt Kopfbild ─────────────────────────────
     *
     * Der Audit misst 16 Personen mit einem 6023 px hohen Dokument – gut
     * sieben Bildschirmlängen für eine Liste, in der man einen Namen sucht.
     * Der Grund ist das Bild: Es steht in voller Kartenbreite über dem Namen
     * und ist bei 390 px 356 × 267 px groß, also zwei Drittel einer 402 px
     * hohen Karte.
     *
     * Auf dem Telefon steht es deshalb links neben dem Namen statt darüber.
     * Weggelassen wird nichts: dieselbe Bildfläche, dasselbe Seitenverhältnis,
     * dieselben Angaben, dieselben Aktionen – nur anders angeordnet. Aus
     * „ein Porträt je Bildschirm“ werden vier Personen je Bildschirm.
     *
     * Ein Raster und keine Flex-Zeile: Die Aktionen sollen unter **beiden**
     * Spalten stehen und nicht neben dem Bild eingerückt beginnen.
     */
    .personenkarten { gap: 0.55rem; }
    .personenkarte {
        display: grid;
        grid-template-columns: auto 1fr;
        column-gap: 0.7rem;
        padding: 0.6rem 0.7rem;
    }
    /*
     * Die Miniatur. 3,6 rem Breite ergeben bei 1:1 rund 58 × 58 px – groß
     * genug, um ein Gesicht zu erkennen, klein genug, um die Karte nicht mehr
     * zu beherrschen. `align-self: start` hält sie an der Oberkante, damit sie
     * neben dem Namen steht und nicht neben der Mitte des Textes.
     */
    .personenkarte-bild {
        width: 3.6rem;
        align-self: start;
        border-radius: 6px;
    }
    /* Die Silhouette ist der Ersatz für das Bild und folgt seiner Größe – sie
       sitzt in derselben Fläche und misst sich an ihr, nicht an der Schrift. */
    .personenkarte-platzhalter { width: 66%; }
    .personenkarte-inhalt { padding: 0; gap: 0.2rem; }
    /*
     * Zwei Zeilen Vorschau statt drei. Am Schreibtisch stehen die Karten
     * nebeneinander und eine dritte Zeile kostet nichts; untereinander
     * gestapelt ist sie die dritte Zeile jeder einzelnen Karte.
     */
    .personenkarte-vorstellung { -webkit-line-clamp: 2; }
    .personenkarte-aktionen {
        grid-column: 1 / -1;
        margin-top: 0.5rem;
        padding: 0.45rem 0 0;
    }

    /* ── 4) Benutzerverwaltung: eine feste Anordnung ──────────────────────
     *
     * Die Tippflächen sind seit #43 in Ordnung; der Audit nennt hier sonst
     * nur die Struktur, und die ist es im Grundsatz auch. Was auf dem Telefon
     * fehlte, war Verlässlichkeit: Der Kopf ist eine Flex-Zeile mit
     * `space-between`, und dadurch stand die Rolle bei einem kurzen Namen
     * rechts neben ihm, bei einem langen dagegen umgebrochen an der linken
     * Kartenkante unter dem Bild. Zwei Karten untereinander sahen aus wie zwei
     * verschiedene Kartenarten.
     *
     * Als Raster steht sie immer an derselben Stelle: Bild links über beide
     * Zeilen, Name oben rechts, Marken darunter. Gemessen wird der Unterschied
     * an der zweiten Karte – sie war 237 px hoch und ist es nicht mehr, weil
     * der Name nicht mehr in eine eigene Zeile gedrängt wird.
     *
     * Dazu dieselbe Kartendichte wie in den Listen aus #45: weniger Polster,
     * engerer Abstand, deutliche Kante.
     */
    .benutzerkarten { gap: 0.55rem; }
    .benutzerkarte {
        padding: 0.7rem 0.8rem;
        gap: 0.55rem;
    }
    .benutzerkarte-kopf {
        display: grid;
        grid-template-columns: auto 1fr;
        align-items: center;
        column-gap: 0.6rem;
        row-gap: 0.25rem;
    }
    /* Das Bild steht neben beiden Zeilen, nicht neben einer von ihnen. */
    .benutzerkarte-bild { grid-row: span 2; }
    .benutzerkarte-name,
    .benutzerkarte-marken { grid-column: 2; }
    .benutzerkarte-aktionen { padding-top: 0.5rem; }

    /* ── 5) Formulare: ein Feld, eine Zeile ───────────────────────────────
     *
     * Die Masken sind einspaltig und bleiben es – das ist die Form, die auf
     * ein Telefon gehört, und der Audit bestätigt sie. Was nicht stimmte,
     * waren die Feldbreiten **innerhalb** einer Zeile: Überall dort, wo neben
     * einem Feld noch etwas steht – ein Verweis, eine Schaltfläche, eine
     * Bildvorschau –, teilten sich beide eine Zeile, die dafür zu schmal ist.
     *
     * Gemessen bei 390 px: die Typ-Auswahl der Elementmaske 116 px, das
     * Bild-Feld der Benutzermaske 162 px, die Foto-Adresse der Personenmaske
     * 193 px, die Moderatorenauswahl der Sendungsmaske 170 px, das Farbfeld
     * der Elementmaske 144 px. In eine 116 px breite Auswahl passt kein
     * Typname.
     *
     * Die Regel ist überall dieselbe: Das Feld nimmt die ganze Zeile, sein
     * Begleiter rückt darunter. Keines der Paare verliert dabei seinen
     * Zusammenhang – sie stehen weiterhin in derselben Zeile des Formulars,
     * nur untereinander statt nebeneinander.
     */
    .select-mit-neu,
    .foto-eingabe { flex-wrap: wrap; }
    .select-mit-neu select,
    .foto-eingabe input { flex-basis: 100%; }
    /* `.zeit-eingabe` bricht schon um; dem Zeitfeld fehlte nur die Breite. */
    .zeit-eingabe input[type="time"] { width: 100%; }
    /*
     * Das Farbfeld. Der Farbwähler daneben ist mit 48 × 44 px ein gültiges
     * Tippziel und behält seine Breite – ein Farbfleck wird nicht besser,
     * wenn er breiter ist. Das Hex-Feld bekommt den Rest der Zeile;
     * „Farbe entfernen“ rückt darunter.
     */
    .farbwahl input[type="text"] {
        flex: 1 1 8rem;
        width: auto;
    }
    /*
     * E-Mail und Passwort sind die einzigen beiden Felder der Anwendung, die
     * nie in die Breiten- und Rahmenregel der Formularzeile aufgenommen
     * wurden – sie standen deshalb als browsereigene Felder da, 204 px breit
     * neben 263 px breiten Nachbarn. Auf dem Telefon zählt jeder Millimeter
     * Zeilenbreite, und ein Formular, in dem zwei von sechs Feldern anders
     * aussehen als der Rest, ist keines.
     *
     * Am Schreibtisch bleiben sie unverändert: Issue #46 lässt ihn
     * ausdrücklich in Ruhe. Dass sie auch dort dazugehören sollten, ist ein
     * eigener Befund und gehört in ein eigenes Issue.
     */
    .formular-zeile input[type="email"],
    .formular-zeile input[type="password"] {
        width: 100%;
        padding: 0.55rem 0.8rem;
        border: 1px solid var(--rand-stark);
        border-radius: 6px;
        font-family: inherit;
        background: var(--flaeche-eingabe);
    }
    /*
     * Die Aktionsleiste darf umbrechen.
     *
     * Nachgemessen bei 360 px: „Programmeintrag anlegen“ und „Abbrechen“
     * ergeben zusammen 271 px in einer 264 px breiten Leiste – „Abbrechen“
     * stand 7 px außerhalb. Umbrechend steht es darunter, und beide behalten
     * ihre volle Beschriftung, statt sie auf zwei Zeilen zu quetschen.
     *
     * Eine am unteren Rand feststehende Aktionsleiste, die der Audit zur
     * Prüfung stellt, ist bewusst nicht daraus geworden: Sie nähme auf jeder
     * Maske dauerhaft 60 px vom ohnehin knappen Bildschirm und verdeckte beim
     * Tippen das Feld darunter. Die Masken sind lang, aber sie sind
     * einspaltig und laufen auf ihr Ende zu – das Ende ist der richtige Ort
     * für den Knopf, der sie abschließt.
     */
    .formular-aktionen { flex-wrap: wrap; }
}

/*
 * Der Fokus-Zoom hängt nicht an der Fensterbreite, sondern am Gerät: Er tritt
 * dort auf, wo mit dem Finger bedient wird. Ein Tablet mit 900 px fällt nicht
 * unter die Regel oben, ist aber genauso betroffen. Diese Abfrage trifft genau
 * die Geräte, um die es geht, und lässt maus-bediente Fenster in Ruhe.
 */
@media (pointer: coarse) {
    .formular-zeile input[type="text"],
    .formular-zeile input[type="url"],
    .formular-zeile input[type="number"],
    .formular-zeile input[type="date"],
    .formular-zeile input[type="time"],
    .formular-zeile select,
    .formular-zeile textarea,
    .datumswahl input[type="date"],
    .farbwahl input[type="text"],
    input,
    select,
    textarea {
        font-size: 16px;
    }
}

/* ── Bedienelemente: dieselbe Fläche wie ihr Formular (Issue #65) ─────────
 *
 * Die Mischzustände, die einen dunklen Modus kaputt aussehen lassen, entstehen
 * alle an derselben Stelle: bei den Feldern, die ihre Farbe bisher vom Browser
 * bekamen und nicht vom Stylesheet. Ein weißes Eingabefeld auf dunkler Karte,
 * ein heller Vorschlagstext, ein von der Autovervollständigung gelb gefärbtes
 * Feld, ein deaktiviertes Feld, das man nicht mehr lesen kann.
 *
 * Der Block steht bewusst **hinter** den Formularregeln: Er soll ergänzen, was
 * dort fehlt, und nicht überschreiben, was dort ausdrücklich steht. Deshalb
 * auch die niedrige Spezifität – `.formular-zeile input[readonly]` und
 * jedes Feld mit eigener Fläche behalten sie.
 *
 * Alles hier gilt in **beiden** Modi. Es gibt keine Regel, die nur im Dunkeln
 * existiert; nur Werte, die sich mit dem Modus ändern.
 */
input[type="text"],
input[type="url"],
input[type="number"],
input[type="email"],
input[type="password"],
input[type="search"],
input[type="date"],
input[type="time"],
select,
textarea {
    background: var(--flaeche-eingabe);
    color: var(--text-normal);
}

/*
 * Die Vorbelegung eines leeren Feldes. `opacity: 1` gehört dazu: Browser
 * dämpfen den Platzhalter zusätzlich, und aus einem ohnehin leisen Grau würde
 * im Dunkeln ein kaum sichtbares.
 */
::placeholder {
    color: var(--text-platzhalter);
    opacity: 1;
}

/*
 * Die Autovervollständigung des Browsers.
 *
 * Chrome und Safari übermalen ein ausgefülltes Feld mit einem eigenen hellen
 * Gelb – über jede `background`-Angabe hinweg; das ist eine Benutzeragenten-
 * Regel, die sich nicht schlicht überschreiben lässt. Der eingebürgerte Weg
 * ist ein innerer Schatten über die volle Feldfläche: Er liegt darüber, folgt
 * dem Eckenradius und nimmt die Farbe, die das Feld ohnehin haben soll. Die
 * Schriftfarbe braucht denselben Sonderweg (`-webkit-text-fill-color`).
 */
input:-webkit-autofill,
input:-webkit-autofill:hover,
input:-webkit-autofill:focus,
textarea:-webkit-autofill,
select:-webkit-autofill {
    -webkit-box-shadow: 0 0 0 40rem var(--flaeche-eingabe) inset;
    -webkit-text-fill-color: var(--text-normal);
    caret-color: var(--text-normal);
}

/*
 * Und derselbe Fall in Firefox (Issue #144).
 *
 * Firefox malt keine Fläche, sondern legt seit Fx 86 einen **Filter** über das
 * ausgefüllte Feld (`sepia`, `saturate`). Über einer hellen Fläche fällt der
 * kaum auf – über einer dunklen wird daraus ein oliv-grünes Feld. Der Block
 * darüber greift dort nicht: Er ist Webkits Sonderweg, und Firefox kennt seine
 * Pseudoklasse nicht.
 *
 * Hier steht deshalb nur `filter: none`, und das mit Absicht: Fläche und
 * Schrift hat das Feld aus den Regeln oben schon, und was `:autofill` darüber
 * hinaus anfasste, wäre eine Zusage für Felder, die niemand gemeldet hat. Ein
 * Feld, das der Browser gefüllt hat, sieht damit aus wie eines, das jemand
 * getippt hat – mehr will diese Regel nicht.
 *
 * Jede Schreibweise steht für sich: Eine unbekannte Pseudoklasse macht die
 * **ganze** Auswahl ungültig, `input:-webkit-autofill, input:-moz-autofill`
 * wirkte deshalb in keinem der beiden Browser.
 */
input:autofill { filter: none; }
input:-moz-autofill { filter: none; }
input:-moz-autofill-preview { filter: none; }

/*
 * Gesperrte Felder. Erkennbar gesperrt, aber lesbar: `opacity` würde den Text
 * gegen die Fläche ausbleichen – im Dunkeln bis zur Unlesbarkeit. Stattdessen
 * eine eigene, ruhigere Fläche und eine leisere, aber kontrastreiche Schrift.
 */
.formular-zeile input:disabled,
.formular-zeile select:disabled,
.formular-zeile textarea:disabled,
input:disabled,
select:disabled,
textarea:disabled {
    background: var(--flaeche-gesperrt);
    color: var(--text-leise);
    -webkit-text-fill-color: var(--text-leise);
    opacity: 1;
}

/*
 * Verweise im Fließtext.
 *
 * Sie hatten bisher gar keine Farbe und trugen damit das Browserblau
 * (#0000EE). Auf Weiß fiel das nicht auf; auf einer dunklen Fläche ist es
 * praktisch nicht mehr zu lesen. Betroffen sind die Verweise, die aus dem
 * Handbuchtext selbst kommen (`app/handbuch_html.py` setzt dort ein blankes
 * `<a>`) und der Hinweis auf die öffentliche Seite im Dashboard.
 */
.hb-inhalt a,
.hb-kasten a,
.hb-vorspann a,
.etappe-hinweis a,
.leer-hinweis a {
    color: var(--verweis);
}

/* ── Farbwert in Listen ───────────────────────────────────────
 *
 * Ein echtes Farbfeld neben dem Hex-Wert. Nur Anzeige – gepflegt wird die
 * Farbe ausschließlich im Elementformular.
 */
.farbwert {
    display: inline-flex;
    align-items: center;
    gap: 0.45rem;
}

.farbfeld {
    display: inline-block;
    width: 1rem;
    height: 1rem;
    border-radius: 3px;
    /* Die Kontur macht auch sehr helle Töne als Fläche erkennbar. */
    border: 1px solid var(--kontur-farbfeld);
    flex-shrink: 0;
}

.farbcode {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 0.8rem;
    color: var(--text-mittel);
}

.zelle-farbe { white-space: nowrap; }

/* ── Entwicklungsstatus ───────────────────────────────────────
 *
 * Die Seite `/entwicklungsstatus` – ausdrücklich von unten nach oben gebaut:
 * Was hier ohne Medienabfrage steht, gilt auf dem Handy. Erst weiter unten
 * kommt der Schreibtisch dazu.
 *
 * Drei Regeln, die das Layout auf schmalen Geräten zusammenhalten:
 *
 *   * keine feste Breite, nirgends – jedes Raster benutzt `minmax(0, 1fr)`,
 *     damit ein langer Issue-Titel die Spalte nicht aufblähen kann;
 *   * lange Zeichenketten (Titel, Commit-SHA) brechen `anywhere` um statt
 *     die Seite waagerecht scrollen zu lassen;
 *   * jedes antippbare Ziel ist mindestens 44 px hoch.
 */

.entw-seite {
    max-width: 1400px;
    margin: 0 auto;
    /* Nichts in dieser Seite darf breiter werden als ihr Kasten. */
    min-width: 0;
    /*
     * Die reservierte Höhe der oberen Kartenzeile – Statuskarte links,
     * Fortschritt rechts. Auf dem Handy gilt sie nicht: Dort stehen die
     * Karten untereinander, und abgeschnittener Text wäre schlimmer als eine
     * längere Seite. Am Schreibtisch dagegen entscheidet sie darüber, ob die
     * Seite bei den Zehn-Sekunden-Takten ruhig stehen bleibt: Ohne feste
     * Höhe schöbe jede längere Meldung alles darunter nach unten.
     *
     * Bemessen ist sie nach der vollsten regulären Statuskarte – Zustand über
     * zwei Zeilen, Meldung, Issue mit Titel, Laufzeit, Browser-QM,
     * Qualitätsprüfung und Handlungssatz. Was darüber hinausgeht, bekommt
     * innerhalb der Karte seinen Platz und schiebt die Zeile nicht auf.
     */
    --entw-kopfhoehe: 26rem;
}

/*
 * Ausgeblendet heißt ausgeblendet. Mehrere Bausteine dieser Seite bekommen
 * hier ein eigenes `display` – und das schlägt die eingebaute Regel des
 * Browsers für `hidden`. Ohne diese Zeile bliebe ein leerer Wert sichtbar,
 * den die Seite gerade nicht zeigen will.
 */
.entw-seite [hidden] { display: none !important; }

.entw-kopf h2 {
    margin: 0 0 0.25rem;
    font-size: 1.35rem;
    color: var(--text-stark);
}

.entw-kopf-hinweis {
    margin: 0 0 1rem;
    font-size: 0.88rem;
    color: var(--text-leise);
    line-height: 1.5;
}

.entw-takt { display: block; font-size: 0.8rem; }

/* ── Kacheln anordnen (Issue #89) ─────────────────────────────
 *
 * Ein eigener Modus und keine dauernd sichtbare Bedienung: Wer die Seite
 * liest, soll sie lesen. Erst `Kacheln anordnen` schaltet den Griff an jeder
 * Kachel ein – und weil er bis dahin in einem `display: none` steckt, ist er
 * auch für Tastatur und Vorlesehilfe nicht da.
 *
 * Nichts hier hat eine feste Farbe: Die Leiste benutzt dieselben
 * `.schaltflaeche`-Klassen wie der Rest der Anwendung, der Rest die
 * Farbtoken. Der dunkle Modus entsteht daraus von selbst.
 */
.entw-ordnen {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem;
    margin: 0 0 1rem;
}

.entw-ordnen-form {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
}

/*
 * Der Weg zurück auf den Standard – nur für die Ansicht, die man vor sich hat
 * (Issue #94).
 *
 * Der Server liefert beide Formulare aus; er kennt die Fensterbreite nicht.
 * Ausgewählt wird hier, an derselben 900-px-Grenze, an der auch das Raster von
 * einer Spalte auf zwei umbricht – und damit an derselben, an der das Skript
 * entscheidet, welche Ordnung es anordnet. Zwei Grenzen für dieselbe Frage
 * wären zwei Antworten.
 *
 * Eine Medienabfrage und kein Skript: Der Ausweg muss auch ohne JavaScript
 * gehen, und `display: none` steht schon beim ersten Zeichnen. Ein Knopf, den
 * ein Skript erst nachträglich wegnimmt, hinterließe genau den Sprung, den es
 * hier nicht geben soll. Weggenommen wird das ganze Formular und nicht nur
 * sein Knopf: Ein leeres Formular bliebe als Lücke in der Leiste stehen.
 *
 * Unterhalb der Grenze gilt das Telefon – das ist die Grundeinstellung, wie
 * überall in diesem Stylesheet.
 */
.entw-ordnen-standard[data-entw-ordnen-fuer="schreibtisch"] { display: none; }

/*
 * Diese Seite hält jedes antippbare Ziel bei 44 px – auch am Schreibtisch, wo
 * `.schaltflaeche` sonst 30 px misst. Das ist keine Sonderregel für die
 * Leiste, sondern die dritte Regel aus dem Kopf dieses Abschnitts; jeder
 * andere Verweis dieser Seite hält sie schon. Nachgemessen wird sie in einem
 * echten Browser von `tests/bildbeleg_entwicklungsstatus.py`.
 */
.entw-ordnen .schaltflaeche {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
}

.entw-ordnen-hinweis {
    /* Der Satz gehört unter die Knöpfe und nicht neben den letzten. */
    flex-basis: 100%;
    margin: 0;
    font-size: 0.85rem;
    color: var(--text-leise);
}

/*
 * Welche der beiden Ordnungen gerade bearbeitet wird – Schreibtisch oder
 * Telefon. Der Satz steht unter dem Bedienhinweis und trägt dessen Aussehen:
 * Beide beantworten dieselbe Frage „was tue ich hier gerade", und zwei
 * verschiedene Schriftgrößen dafür wären eine Behauptung über eine
 * Rangfolge, die es nicht gibt.
 */
.entw-ordnen-ansicht {
    flex-basis: 100%;
    margin: -0.25rem 0 0;
    font-size: 0.85rem;
    color: var(--text-leise);
}

.entw-ordnen-meldung {
    margin: -0.4rem 0 1rem;
    font-size: 0.85rem;
    color: var(--erfolg-text);
}

/* Der Griff über der Kachel – im Ruhezustand gibt es ihn nicht. */
.entw-kachel-ordnen { display: none; }

.entw-ordnen-aktiv .entw-kachel-ordnen {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.4rem;
    margin: -0.25rem 0 0.6rem;
    padding-bottom: 0.5rem;
    border-bottom: 1px dashed var(--rand-stark);
}

.entw-kachel-name {
    font-size: 0.85rem;
    font-weight: 600;
    color: var(--text-stark);
    margin-right: auto;
}

.entw-kachel-breite,
.entw-kachel-grund {
    font-size: 0.75rem;
    color: var(--text-still);
}

.entw-kachel-pfeil {
    /*
     * 44 px im Quadrat, damit die Taste auch auf dem Telefon zu treffen ist –
     * dort ist sie die einzige Bedienung, weil Ziehen auf Touchgeräten nicht
     * geht. Die Breite kommt aus dem Seitenverhältnis und nicht aus einer
     * `min-width` in Pixeln: Diese Seite darf nirgends eine feste
     * Mindestbreite bekommen, sonst entstünde auf einem 375 px breiten
     * Bildschirm eine waagerechte Scrollleiste.
     */
    min-height: 44px;
    aspect-ratio: 1;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: 1px solid var(--rand-stark);
    border-radius: 6px;
    background: var(--flaeche-sanft);
    color: var(--text-normal);
    font-size: 1rem;
    cursor: pointer;
}

.entw-kachel-pfeil:hover { background: var(--flaeche-neutral); }

/*
 * Im Bearbeitungsmodus sieht man, was verschiebbar ist – und was nicht.
 * Eine Kachel, die sich stumm weigert, wäre eine Panne; eine, die ihren
 * Grund danebenschreibt, ist eine Grenze.
 */
.entw-ordnen-aktiv .entw-karte {
    outline: 2px dashed var(--rand-akzent);
    outline-offset: 2px;
    cursor: grab;
}

.entw-ordnen-aktiv .entw-karte[data-entw-fest="1"] {
    outline-style: dotted;
    cursor: default;
}

/*
 * Solange angeordnet wird, führt kein Verweis in einer Kachel mehr weg. Ein
 * Klick auf „#88" mitten im Verschieben verließe die Seite – und mit ihr die
 * noch nicht gespeicherte Reihenfolge. Zugleich bekommt damit die Kachel den
 * Zeiger und nicht der Verweis darin: Ziehen beginnt auch dann, wenn man sie
 * an einer Stelle anfasst, an der zufällig ein Verweis liegt. Die Tasten des
 * Griffs sind ausgenommen – sie sind die Bedienung dieses Modus.
 */
.entw-ordnen-aktiv .entw-karte a { pointer-events: none; }
.entw-ordnen-aktiv .entw-kachel-ordnen { pointer-events: auto; }

.entw-kachel-zieht { opacity: 0.5; }

.entw-raster {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: 1rem;
}

.entw-karte {
    background: var(--flaeche-karte);
    border: 1px solid var(--rand);
    border-left: 4px solid var(--rand);
    border-radius: 10px;
    padding: 1rem;
    min-width: 0;
}

.entw-karte h3 {
    margin: 0 0 0.75rem;
    font-size: 0.95rem;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--text-leise);
}

/* Die Statuskarte: das Erste, was man sieht, und das Größte auf der Seite. */
.entw-status {
    padding: 1.1rem 1rem 1.25rem;
    display: flex;
    flex-direction: column;
}

/*
 * Der Textkörper der Statuskarte. Er trägt am Schreibtisch die reservierte
 * Höhe von innen: Wird der Inhalt einmal länger als der Platz – eine
 * ungewöhnlich lange Meldung, ein dreizeiliger Issue-Titel –, bekommt er ihn
 * hier und nicht dadurch, dass die Karte wächst.
 */
.entw-status-koerper { min-height: 0; }

.entw-zustand {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    margin: 0 0 0.6rem;
    font-size: 1.25rem;
    font-weight: 700;
    line-height: 1.3;
    color: var(--text-stark);
    overflow-wrap: anywhere;
}

.entw-symbol { font-size: 1.5rem; line-height: 1; flex-shrink: 0; }

.entw-zustand-ruhig   { border-left-color: var(--erfolg-kraeftig); }
.entw-zustand-arbeit  { border-left-color: var(--warnung-kraeftig); }
.entw-zustand-gut     { border-left-color: var(--erfolg-kraeftig); }
.entw-zustand-fehler  { border-left-color: var(--fehler-kraeftig); }
.entw-zustand-unbekannt { border-left-color: var(--rand-still); }

.entw-meldung,
.entw-warnung,
.entw-handeln {
    margin: 0 0 0.7rem;
    padding: 0.6rem 0.75rem;
    border-radius: 8px;
    font-size: 0.9rem;
    line-height: 1.5;
    overflow-wrap: anywhere;
}

.entw-meldung { background: var(--flaeche-sanft); color: var(--text-normal); }
.entw-warnung { background: var(--warnung-flaeche); color: var(--warnung-text-tief); }
.entw-handeln { background: var(--info-flaeche-zart); color: var(--info-text-tief); font-weight: 600; }

/*
 * Der Satz für den ruhigen Fall: keine Warnfarbe, kein Kasten – es ist ja
 * nichts passiert. Er trägt die Karte, wenn sonst nichts darauf steht.
 */
.entw-hinweis-karte {
    margin: 0 0 0.7rem;
    font-size: 0.95rem;
    line-height: 1.55;
    color: var(--text-normal);
    overflow-wrap: anywhere;
}

/*
 * Ohne Statusdatei bleibt genau diese eine Karte übrig. Damit sie auf dem
 * Schreibtisch nicht als leerer Balken über 1400 px steht, bekommt sie eine
 * eigene, begrenzte Breite – bewusst schmal, statt Platz zu füllen, den es
 * nicht zu füllen gibt. Links bündig zur Überschrift wie jede andere Karte
 * des Backends; eine mittig schwebende Karte sähe aus wie ein Dialog.
 */
.entw-modus-leer {
    max-width: 46rem;
    padding: 1.25rem 1rem 1.4rem;
}

/* Bricht die Überschrift auf dem Handy um, gehört das Symbol nach oben –
 * mittig neben drei Zeilen sieht es aus wie verrutscht. */
.entw-modus-leer .entw-zustand { align-items: flex-start; }

.entw-modus-leer .entw-meldung {
    font-size: 0.85rem;
    background: transparent;
    padding: 0;
    color: var(--text-leise);
}

.entw-auftrag { margin-bottom: 0.7rem; }

.entw-issue { margin: 0; }

/*
 * Die Zuordnung der laufenden Kachel (Issue #95): `#95 – Titel`, in einer
 * Zeile und als Verweis nach GitHub. Ist keine Adresse bekannt, steht
 * derselbe Satz ohne Verweis da – dieselbe Stelle, dieselbe Größe, nur ohne
 * Unterstreichung. Ein Sprung im Layout wäre der falsche Weg, „ohne Adresse"
 * zu sagen.
 */
.entw-issue-kopf {
    display: inline-block;
    padding: 0.55rem 0.1rem;
    font-size: 1rem;
    font-weight: 600;
    color: var(--text-normal);
    line-height: 1.45;
    overflow-wrap: anywhere;
}

/*
 * Antippbar mit dem Daumen: 44 px hoch, ohne dass der Text dadurch verrutscht.
 * Hover ist ausdrücklich nur Zugabe – auf dem iPhone gibt es ihn nicht.
 */
.entw-link {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
    padding: 0 0.1rem;
    color: var(--text-stark);
    font-weight: 600;
    text-decoration: underline;
    overflow-wrap: anywhere;
}

.entw-link:hover { color: var(--akzent-text); }

.entw-laufzeit {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: 0.5rem;
    margin: 0.4rem 0 0.7rem;
}

.entw-laufzeit-wert {
    font-size: 1.9rem;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    color: var(--text-stark);
}

.entw-laufzeit-text { font-size: 0.85rem; color: var(--text-leise); }

/*
 * Automatik: der Countdown ist die eine Zahl, auf die Martin schaut. Deshalb
 * steht er groß und mit fester Ziffernbreite (`tabular-nums`) – sonst
 * zappelte die Zeile bei jedem Sekundenwechsel.
 */
.entw-countdown {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: 0.5rem;
    margin: 0 0 0.8rem;
}

.entw-countdown-wert {
    font-size: 1.9rem;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    color: var(--text-stark);
}

.entw-countdown-text {
    font-size: 0.85rem;
    color: var(--text-leise);
    overflow-wrap: anywhere;
}

/*
 * Warteschlange: auf dem Handy je Auftrag eine Karte mit Beschriftungen, am
 * Schreibtisch eine scanbare Zeile – derselbe Gedanke wie bei der Historie.
 */
.entw-queue {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    gap: 0.5rem;
}

.entw-queue-eintrag {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: 0.1rem;
    padding: 0.6rem 0.75rem;
    border: 1px solid var(--rand);
    border-left: 4px solid var(--rand-still);
    border-radius: 8px;
    background: var(--flaeche-still);
    min-width: 0;
}

.entw-queue-eintrag span { overflow-wrap: anywhere; min-width: 0; }

.entw-queue-position {
    font-weight: 700;
    color: var(--text-leise);
    font-variant-numeric: tabular-nums;
}

.entw-queue-titel { color: var(--text-stark); font-size: 0.95rem; }

.entw-queue-seit { color: var(--text-leise); font-size: 0.82rem; }
.entw-queue-seit::before { content: "angelegt "; }

/*
 * Der ausdrücklich priorisierte Auftrag (Issue #169).
 *
 * Er steht in derselben Liste und auf Position 1 – hervorgehoben wird nur,
 * **warum** er dort steht: sonst sähe eine Priorisierung genauso aus wie eine
 * kleine Issue-Nummer. Farbe und Form sind dieselben wie bei der vorgemerkten
 * Wartung; es ist derselbe Gedanke „eingeplant, nicht kaputt".
 */
.entw-queue-bevorzugt { border-left-color: var(--info-kante); }

.entw-queue-vorrang { font-size: 0.82rem; }

/* Dieselbe Zusage wie in den beiden Listen weiter unten: `anywhere` ist für
 * einen Titel richtig und für eine Pille falsch – sie bräche mitten im Wort. */
.entw-queue-eintrag .entw-pille { overflow-wrap: break-word; }

.entw-queue-vorrangliste {
    list-style: none;
    margin: 0.5rem 0 0;
    padding: 0;
    display: grid;
    gap: 0.4rem;
}

.entw-queue-vorrangzeile {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: 0.1rem;
    padding: 0.5rem 0.75rem;
    border: 1px dashed var(--rand);
    border-radius: 8px;
    min-width: 0;
}

.entw-queue-vorrangzeile span { overflow-wrap: anywhere; min-width: 0; }

.entw-queue-vorranghinweis {
    color: var(--text-leise);
    font-size: 0.82rem;
    line-height: 1.45;
}

.entw-queue-leer {
    margin: 0;
    font-size: 0.95rem;
    line-height: 1.55;
    color: var(--text-normal);
    overflow-wrap: anywhere;
}

/*
 * Fortschritt: auf dem Handy untereinander, am Schreibtisch kompakt in zwei
 * Spalten rechts neben der Statuskarte – mit Haken für Erledigtes.
 */
.entw-fortschritt {
    display: flex;
    flex-direction: column;
}

/*
 * „Aktuell: Qualitätsprüfung" – der laufende Schritt im Klartext, bevor die
 * Liste ihn als Kreis zeigt. In einer zweispaltigen Liste ist ein einzelner
 * hervorgehobener Eintrag sonst nichts, was man im Vorbeigehen findet.
 */
.entw-jetzt {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.4rem;
    margin: 0 0 0.7rem;
    font-size: 1.05rem;
    line-height: 1.35;
    overflow-wrap: anywhere;
}

.entw-jetzt-marke { color: var(--text-leise); font-size: 0.85rem; }
.entw-jetzt-titel { font-weight: 700; color: var(--text-stark); }

.entw-jetzt-aktuell  .entw-jetzt-titel { color: var(--warnung-mittel); }
.entw-jetzt-fehler   .entw-jetzt-titel { color: var(--fehler-text); }
.entw-jetzt-erledigt .entw-jetzt-titel { color: var(--erfolg-text); }

/*
 * Der Nebenlauf (Issue #125): eine Nacharbeit, eine Reparatur, eine Sicherung.
 *
 * Sie stehen **neben** dem Hauptlauf und nicht an seiner Stelle. Bis zu diesem
 * Auftrag rechnete die Anzeige den ganzen Fortschritt aus der zuletzt
 * gemeldeten Phase – eine Nacharbeit meldet sich als zweiter Programmierlauf,
 * und aus „Station 9 von 11" wurde dadurch wieder „Station 2 von 11".
 *
 * Mobile-first: eine umbrechende Zeile ohne feste Breite und ohne eigene
 * Schwelle. Was auf 320 px stimmt, stimmt auf 1440 px auch.
 */
.entw-neben {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.4rem;
    margin: -0.3rem 0 0.7rem;
    padding: 0.4rem 0.6rem;
    border-left: 3px solid var(--warnung-mittel);
    border-radius: 0 4px 4px 0;
    background: var(--warnung-flaeche-blass);
    font-size: 0.9rem;
    line-height: 1.35;
    overflow-wrap: anywhere;
}

.entw-neben-marke { color: var(--warnung-mittel); font-weight: 700; }
.entw-neben-titel { font-weight: 700; color: var(--text-stark); }
.entw-neben-text { color: var(--text-still); }

/* Welcher Lauf das ist – leise, aber vorhanden. */
.entw-laufkennung { font-style: italic; }

.entw-schritte {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    gap: 0.1rem;
}

.entw-schritt {
    display: flex;
    align-items: center;
    gap: 0.65rem;
    min-height: 44px;
    padding: 0.25rem 0;
    font-size: 0.95rem;
    color: var(--text-still);
    overflow-wrap: anywhere;
}

.entw-schritt-marke {
    width: 1.4rem;
    height: 1.4rem;
    border-radius: 999px;
    border: 2px solid var(--rand-stark);
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 0.85rem;
    line-height: 1;
}

.entw-schritt-erledigt { color: var(--erfolg-text); }
.entw-schritt-erledigt .entw-schritt-marke {
    border-color: var(--erfolg-kraeftig);
    background: var(--erfolg-kraeftig);
    color: var(--fuellung-text);
}
.entw-schritt-erledigt .entw-schritt-marke::before { content: "✓"; }

.entw-schritt-aktuell { color: var(--text-stark); font-weight: 700; }
.entw-schritt-aktuell .entw-schritt-marke {
    border-color: var(--warnung-kraeftig);
    background: var(--warnung-flaeche);
}
.entw-schritt-aktuell .entw-schritt-marke::before { content: "●"; color: var(--warnung-kraeftig); }

.entw-schritt-fehler { color: var(--fehler-text); font-weight: 700; }
.entw-schritt-fehler .entw-schritt-marke {
    border-color: var(--fehler-kraeftig);
    background: var(--fehler-flaeche);
}
.entw-schritt-fehler .entw-schritt-marke::before { content: "!"; color: var(--fehler-text); }

/*
 * Die Station, an der ein Nebenlauf gerade noch einmal arbeitet (Issue #125).
 * Sie behält ihren Haken – sie war durch –, bekommt aber eine Kante, damit man
 * sieht, wohin die Nacharbeit zurückgegangen ist.
 */
.entw-schritt-neben {
    box-shadow: inset 3px 0 0 0 var(--warnung-mittel);
    padding-left: 0.5rem;
}

/* Angaben als Paarliste – auf dem Handy untereinander, sonst zweispaltig. */
.entw-angaben {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: 0.15rem 1rem;
    margin: 0;
    font-size: 0.92rem;
}

.entw-angaben dt {
    color: var(--text-leise);
    font-size: 0.8rem;
}

.entw-angaben dd {
    margin: 0 0 0.6rem;
    color: var(--text-stark);
    overflow-wrap: anywhere;
}

.entw-titel { display: block; color: var(--text-normal); font-size: 0.9rem; }

.entw-commit {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 0.85rem;
    overflow-wrap: anywhere;
}

.entw-hinweis,
.entw-orientierung {
    margin: 0.5rem 0 0;
    font-size: 0.88rem;
    line-height: 1.55;
    color: var(--text-normal);
}

.entw-orientierung {
    padding-top: 0.6rem;
    border-top: 1px solid var(--rand-zart);
}


/*
 * Aktueller Stand der Aufgaben (Issue #87) – die kompakte Zusammenfassung
 * über den Einzelläufen.
 *
 * Dieselbe Formensprache wie die Historie darunter, und das mit Absicht: Es
 * sind zwei Sichten auf denselben Bestand, keine zwei Bereiche. Der
 * Unterschied steckt im Inhalt – hier steht je Aufgabe ihr **heutiger**
 * Zustand, darunter je Lauf sein Ergebnis von damals.
 *
 * Auf dem Handy eine Karte je Aufgabe mit Beschriftungen davor, am
 * Schreibtisch eine scanbare Zeile. Keine Tabelle, keine feste Breite: Was
 * hier ohne Medienabfrage steht, gilt auf 375 px.
 */
.entw-aufgaben {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    gap: 0.6rem;
}

.entw-aufgabe {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: 0.2rem;
    padding: 0.75rem;
    border: 1px solid var(--rand);
    border-left: 4px solid var(--rand-still);
    border-radius: 8px;
    background: var(--flaeche-still);
    font-size: 0.9rem;
    min-width: 0;
}

.entw-aufgabe span { overflow-wrap: anywhere; min-width: 0; }

/*
 * Derselbe Farbstreifen wie in den Laufzeilen – nur speist ihn hier der
 * heutige Zustand der Aufgabe und nicht das Ergebnis eines Laufs. Grün heißt
 * damit „abgenommen und geschlossen", auch wenn der letzte Lauf darunter rot
 * ist; genau das war der Befund aus Issue #87.
 */
.entw-aufgabe.entw-abn-abnahme-offen { border-left-color: var(--warnung-kraeftig); }
.entw-aufgabe.entw-abn-abgenommen    { border-left-color: var(--erfolg-kraeftig); }
.entw-aufgabe.entw-abn-fehler        { border-left-color: var(--fehler-kraeftig); }
.entw-aufgabe.entw-abn-unklar        { border-left-color: var(--rand-still); }
/* Vorgemerkte Wartung: eingeplant, nicht kaputt (Nachtrag zu Issue #87). */
.entw-aufgabe.entw-abn-vorgemerkt    { border-left-color: var(--info-kante); }

.entw-aufgabe .entw-pille { overflow-wrap: break-word; }

.entw-aufgabe-titel { font-weight: 600; color: var(--text-stark); }

.entw-aufgabe-bilanz,
.entw-aufgabe-zeit { color: var(--text-mittel); font-size: 0.85rem; }

/* Auf schmalen Geräten sagt eine Beschriftung, was die Zeile bedeutet. */
.entw-aufgabe-zustand::before { content: "Heute: "; color: var(--text-still); }
.entw-aufgabe-bilanz::before  { content: "Läufe: "; color: var(--text-still); }
.entw-aufgabe-zeit::before    { content: "Zuletzt: "; color: var(--text-still); }

.entw-aufgaben-kopf { display: none; }

/*
 * Der Satz „Aufgabe inzwischen abgeschlossen" (Issue #87).
 *
 * Er steht in der Statuszelle unter der roten Pille und ist bewusst **keine**
 * zweite Pille: Zwei farbige Marken nebeneinander sähen aus wie zwei
 * widersprüchliche Aussagen über denselben Lauf. Der Lauf bleibt rot – der
 * Satz sagt nur, dass seine Aufgabe heute erledigt ist.
 *
 * `--erfolg-text` und nicht `--text-leise`: Es ist eine gute Nachricht, und
 * sie muss neben einer roten Pille auch als solche zu erkennen sein.
 */
.entw-erledigt,
.entw-lauf .entw-erledigt {
    display: block;
    margin-top: 0.2rem;
    font-size: 0.78rem;
    color: var(--erfolg-text);
    /* Kein `anywhere`: Die Zeilenregel `.entw-lauf span` ist spezifischer und
       hätte den Satz mitten im Wort getrennt – derselbe Fall wie bei der Pille
       eine Regel weiter oben. */
    overflow-wrap: break-word;
}

/*
 * Historie: auf dem Handy je Lauf eine Karte mit Beschriftungen davor, auf
 * dem Schreibtisch dieselben Felder als scanbare Zeile. Bewusst keine
 * Tabelle – die brächte auf 375 px eine waagerechte Scrollleiste mit.
 */
.entw-historie {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    gap: 0.6rem;
}

.entw-lauf {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: 0.2rem;
    padding: 0.75rem;
    border: 1px solid var(--rand);
    border-left: 4px solid var(--rand-still);
    border-radius: 8px;
    background: var(--flaeche-still);
    font-size: 0.9rem;
    min-width: 0;
}

.entw-lauf-fertig { border-left-color: var(--erfolg-kraeftig); }
.entw-lauf-fehler { border-left-color: var(--fehler-kraeftig); }

.entw-lauf span { overflow-wrap: anywhere; min-width: 0; }

.entw-lauf-titel { font-weight: 600; color: var(--text-stark); }
.entw-lauf-ergebnis,
.entw-lauf-abnahme,
.entw-lauf-dauer,
.entw-lauf-zeit,
.entw-lauf-tests,
.entw-lauf-deployment,
.entw-lauf-pruefung,
.entw-lauf-commit { color: var(--text-mittel); font-size: 0.85rem; }

/* Auf schmalen Geräten sagt eine Beschriftung, was die Zeile bedeutet. */
.entw-lauf-ergebnis::before   { content: "Ergebnis: "; color: var(--text-still); }
.entw-lauf-abnahme::before    { content: "Status: "; color: var(--text-still); }
.entw-lauf-dauer::before      { content: "Dauer: "; color: var(--text-still); }
.entw-lauf-zeit::before       { content: "Ende: "; color: var(--text-still); }
.entw-lauf-tests::before      { content: "Tests: "; color: var(--text-still); }
.entw-lauf-deployment::before { content: "Deployment: "; color: var(--text-still); }
.entw-lauf-pruefung::before   { content: "Prüfung: "; color: var(--text-still); }
.entw-lauf-commit::before     { content: "Commit: "; color: var(--text-still); }

/*
 * Das Ergebnis der unabhängigen Qualitätsprüfung in der Statuskarte. Es steht
 * bewusst über der Handlungsaufforderung: erst wissen, ob schon jemand
 * nachgesehen hat, dann lesen, was zu tun ist.
 */
/*
 * Der Browser-QM steht direkt darüber und sieht genauso aus: beides sind
 * Aussagen darüber, wer den Stand schon angesehen hat.
 */
.entw-pruefung,
.entw-browser {
    margin: 0.4rem 0 0;
    font-size: 0.9rem;
    color: var(--text-normal);
    overflow-wrap: anywhere;
}

.entw-pruefung-marke,
.entw-browser-marke { color: var(--text-leise); }

.entw-historie-kopf { display: none; }

.entw-verbindung-weg { color: var(--fehler-text); }


/*
 * Die vollständige Laufhistorie (Issue #72) – `/entwicklungsstatus/historie`.
 *
 * Dieselbe Formensprache wie die Historie auf der Statusseite, nur mit einer
 * Zeile mehr Aufgabe: Auf dem Telefon ist ein Lauf eine Karte mit
 * Beschriftungen davor, am Schreibtisch eine scanbare Zeile – und beides ist
 * aufklappbar. Das Aufklappen kann `<details>` selbst; das Dreieck des
 * Browsers weicht dabei einer eigenen Marke – es säße sonst im Rasterfluss
 * und verschöbe auf dem Telefon jede Zeile um seine Breite.
 *
 * Wieder keine Tabelle und wieder keine feste Breite: Was hier ohne
 * Medienabfrage steht, gilt auf 375 px.
 */
.entw-hist-bilanz {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.5rem 1rem;
}

.entw-hist-bilanz h3 { margin: 0 0 0.5rem; }

.entw-hist-bereich {
    margin: 0 0 0.5rem;
    font-size: 0.85rem;
    color: var(--text-leise);
    font-variant-numeric: tabular-nums;
}

.entw-hist-alle,
.entw-hist-zurueck { font-size: 0.9rem; }

/* Suche, Ergebnisfilter und Zeitraum: auf dem Telefon untereinander. */
.entw-hist-filter {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: 0 1rem;
}

.entw-hist-filter .formular-zeile { margin-bottom: 0.8rem; }

/*
 * Ein Datumsfeld und die Taste, die es wieder leert (Issue #84).
 *
 * Sie steht **neben** dem Feld und nicht darunter: Die Zeilenhöhe ändert sich
 * dadurch nicht, wenn ein Datum gesetzt oder entfernt wird – die Taste kommt
 * und geht, ohne dass der Filterblock darunter springt. `flex: 0 0 auto` hält
 * sie in ihrer Breite; das Feld daneben gibt nach, nicht sie.
 */
.entw-hist-datum {
    display: flex;
    flex-wrap: wrap;
    align-items: stretch;
    gap: 0.5rem;
    min-width: 0;
}

/* Das Feld gibt nach, bis es 8 rem breit ist; darunter rutscht lieber die
   Taste unter das Feld, als dass der Kalender des Browsers unlesbar würde.
   Genau dafür bekommt es **keine** `min-width: 0`: Die Angabe hätte gesagt,
   was hier gerade nicht gemeint ist – ein Flex-Element mit `min-width: 0`
   schrumpft unter seinen Inhalt, und `flex-shrink` hätte das Feld an einer
   engen Stelle bis unter „TT.MM.JJJJ" zusammengezogen, statt die Taste
   umbrechen zu lassen. Ohne die Angabe gilt die selbsttätige Mindestgröße,
   und die ist bei einem Datumsfeld sein Inhalt. Am Schreibtisch trifft das
   die zweite Filterzeile: Dort steht „Abgeschlossen bis" in einer der
   schmalen Spalten, und knapp über 900 px bliebe von seinem Datum sonst nur
   ein Ausschnitt. */
.entw-hist-datum input[type="date"] {
    flex: 1 1 8rem;
}

.entw-hist-leeren {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    white-space: nowrap;
}

/* Der Vorgabeknopf des Filterformulars steht vor allen anderen, damit Enter
   im Suchfeld „Anzeigen" auslöst und nicht die erste „Leeren"-Taste. Er
   verschwindet, ohne zu verschwinden: `display: none` nähmen manche Browser
   zum Anlass, ihn beim Absenden zu übergehen. Er nimmt keinen Platz, fängt
   keine Berührung ab und bleibt damit ohne Wirkung auf das Bild der Seite. */
.entw-hist-vorgabe {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    border: 0;
    overflow: hidden;
    clip-path: inset(50%);
    pointer-events: none;
}

.entw-hist-tasten {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.6rem;
}

.entw-hist-liste {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    gap: 0.6rem;
}

.entw-hist-eintrag { min-width: 0; }

.entw-hist-lauf {
    border: 1px solid var(--rand);
    border-left: 4px solid var(--rand-still);
    border-radius: 8px;
    background: var(--flaeche-still);
    min-width: 0;
}

/*
 * Der Farbstreifen sagt das Ergebnis, bevor man liest. „Zurückgerollt" ist
 * bewusst nicht rot wie ein Abbruch und nicht grün wie ein Erfolg: Der Lauf
 * war produktiv und ist wieder zurückgenommen worden – das ist ein dritter
 * Ausgang und kein Zwischenton.
 *
 * Alle drei ausdrücklich an `.entw-hist-lauf` gebunden: `.entw-lauf-fehler`
 * steht weiter oben und käme sonst **vor** der Kurzschreibweise `border-left`
 * dieser Karte – der rote Streifen wäre wieder grau.
 */
.entw-hist-lauf.entw-lauf-erfolgreich    { border-left-color: var(--erfolg-kraeftig); }
.entw-hist-lauf.entw-lauf-zurueckgerollt { border-left-color: var(--warnung-kraeftig); }
.entw-hist-lauf.entw-lauf-fehler         { border-left-color: var(--fehler-kraeftig); }

/*
 * Und darüber der **fachliche** Zustand (Issue #78). Er steht bewusst
 * dahinter und gewinnt damit den Streifen: Auf einen Blick soll die Zeile
 * nicht sagen „technisch durchgelaufen", sondern „muss ich hinsehen?".
 * Gelb heißt fertig, aber noch nicht abgenommen; grün heißt erledigt; rot
 * bleibt der technische Fehler; grau heißt, dass es keinen belastbaren
 * Abnahmestand gibt – geraten wird nicht.
 *
 * Beide Listen tragen dieselben Klassen: die lange Historie
 * (`.entw-hist-lauf`) und die kompakten letzten Läufe (`.entw-lauf`).
 */
.entw-hist-lauf.entw-abn-abnahme-offen,
.entw-lauf.entw-abn-abnahme-offen { border-left-color: var(--warnung-kraeftig); }
.entw-hist-lauf.entw-abn-abgenommen,
.entw-lauf.entw-abn-abgenommen    { border-left-color: var(--erfolg-kraeftig); }
.entw-hist-lauf.entw-abn-fehler,
.entw-lauf.entw-abn-fehler        { border-left-color: var(--fehler-kraeftig); }
.entw-hist-lauf.entw-abn-unklar,
.entw-lauf.entw-abn-unklar        { border-left-color: var(--rand-still); }

/*
 * Die Statuspille. Farbe **und** Wort, immer beide: Wer Farben nicht
 * unterscheiden kann, liest dasselbe wie alle anderen. Das Symbol davor ist
 * ausdrücklich `aria-hidden` – es wiederholt nur, was daneben steht, und
 * niemand möchte „gelber Kreis Abnahme offen" vorgelesen bekommen.
 */
.entw-pille {
    display: inline-flex;
    align-items: baseline;
    gap: 0.3rem;
    padding: 0.1rem 0.5rem;
    border: 1px solid var(--rand-still);
    border-radius: 999px;
    background: var(--flaeche-sanft);
    color: var(--text-leise);
    font-size: 0.8rem;
    line-height: 1.5;
    /* Kein `nowrap`: In einer schmalen Spalte liefe die Pille sonst über ihre
       Zelle hinaus – auf einem 1280er Laptop reicht der Platz für „Abnahme
       offen" nicht immer. Sie darf lieber zweizeilig werden.

       Aber auch kein `anywhere` mehr (Issue #84). Der Unterschied ist genau
       der, an dem die Pille auf dem Telefon zerbrochen ist: `anywhere` zählt
       die Trennstelle in die **Mindestbreite** ein, und die einer Pille war
       damit ein einziges Zeichen. In einer Flex-Zeile neben einem langen
       Erklärungstext schrumpfte sie auf diese Breite zusammen – aus „Gelb"
       wurde „Gel" über „b", und aus der Pille ein Kreis. `break-word` trennt
       ein zu langes Wort genauso, lässt die Mindestbreite aber beim längsten
       Wort: Die Pille bleibt eine Pille und bricht erst, wenn es wirklich
       eng wird. */
    overflow-wrap: break-word;
}

/*
 * Und dieselbe Zusage in den Zeilen beider Listen (Issue #84): Dort steht
 * `overflow-wrap: anywhere` an `span` – für den Titel und den Commit genau
 * richtig, für die Pille genau falsch. Die Regel ist mit ihrer Zelle
 * spezifischer als `.entw-pille` und hätte den Bruch mitten im Wort dort
 * wieder eingeführt, den die Pille eine Zeile höher gerade abgelegt hat.
 */
.entw-hist-zeile .entw-pille,
.entw-lauf .entw-pille { overflow-wrap: break-word; }

.entw-pille-symbol { font-size: 0.7em; }

.entw-pille-abnahme-offen {
    background: var(--warnung-flaeche);
    border-color: var(--warnung-kraeftig);
    color: var(--warnung-text);
}

.entw-pille-abgenommen {
    background: var(--erfolg-flaeche);
    border-color: var(--erfolg-kraeftig);
    color: var(--erfolg-text);
}

.entw-pille-fehler {
    background: var(--fehler-flaeche);
    border-color: var(--fehler-kraeftig);
    color: var(--fehler-text);
}

/*
 * Vorgemerkte Wartung (Nachtrag zu Issue #87) – blau und ausdrücklich nicht
 * rot.
 *
 * An so einer Aufgabe hängt oft ein Fehler, aber der ist bereits in eine
 * Aktion überführt: Sie wartet nicht auf eine Entscheidung, sondern auf den
 * nächsten Lauf. Rot wäre ein Alarm für etwas, das schon eingeplant ist – und
 * grün oder gelb wären eine der vier vorhandenen Aussagen, die sie nicht
 * meint. Die Info-Token gibt es in beiden Modi; eine feste Farbe stünde im
 * dunklen Modus als zweite Quelle daneben.
 */
.entw-pille-vorgemerkt {
    background: var(--info-flaeche);
    border-color: var(--info-kante);
    color: var(--info-text);
}

/* „Unklar" bleibt bewusst farblos: ein Zustand ohne Aussage sieht auch aus
   wie einer ohne Aussage. */
.entw-pille-unklar {
    background: var(--flaeche-sanft);
    border-color: var(--rand-still);
    color: var(--text-leise);
}

/*
 * Die Liste der vorgemerkten Wartungsaktionen in der Warteschlangenkarte.
 *
 * Auf dem Telefon wie am Schreibtisch dieselbe Form: je Aktion ein kleiner
 * Block mit Nummer, Titel, Zustand und zwei Sätzen darunter. Bewusst **keine**
 * Spaltenzeile wie die Warteschlange darüber – die beiden Sätze („was tut
 * das" und „wann") sind der Inhalt, und in einer Spalte wären sie unlesbar
 * schmal. Was hier ohne Medienabfrage steht, gilt auf 375 px.
 */
.entw-wartung-block { margin-top: 0.9rem; }

.entw-wartung-titel {
    margin: 0 0 0.5rem;
    font-size: 0.85rem;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    color: var(--text-still);
}

.entw-wartung {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    gap: 0.5rem;
}

.entw-wartung-eintrag {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: 0.25rem;
    padding: 0.6rem 0.75rem;
    border: 1px solid var(--rand);
    border-left: 4px solid var(--info-kante);
    border-radius: 8px;
    background: var(--flaeche-still);
    font-size: 0.9rem;
    min-width: 0;
}

/* Eine Marke, die nichts auslöst, sieht auch aus wie eine ohne Aussage. */
.entw-wartung-eintrag.entw-abn-unklar { border-left-color: var(--rand-still); }

.entw-wartung-eintrag span { overflow-wrap: anywhere; min-width: 0; }
.entw-wartung-eintrag .entw-pille { overflow-wrap: break-word; }

.entw-wartung-titelzeile { font-weight: 600; color: var(--text-stark); }

.entw-wartung-zustand { margin: 0.15rem 0; }

.entw-wartung-erklaerung,
.entw-wartung-wann {
    font-size: 0.82rem;
    color: var(--text-mittel);
}

/* „Wartet hinter dem laufenden Auftrag" ist die Antwort auf die Frage, wegen
   der dieser Block entstanden ist – sie darf nicht wie eine Fußnote wirken. */
.entw-wartung-wann { color: var(--info-text); }

/*
 * Die Legende über der Liste: dieselben vier Wörter wie in den Zeilen.
 *
 * Auf dem Telefon ist jeder Eintrag eine eigene Zeile – Farbe links, Erklärung
 * rechts daneben. Nebeneinander gesetzt bekämen vier Einträge dort je ein
 * Viertel der Breite, und jede Erklärung stünde als schmale Wortsäule da.
 * Nebeneinander stehen sie erst am Schreibtisch, wo der Platz dafür da ist
 * (siehe die Medienabfrage weiter unten).
 */
.entw-legende {
    list-style: none;
    margin: 0 0 0.8rem;
    padding: 0;
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: 0.4rem;
}

.entw-legende-eintrag {
    display: flex;
    align-items: baseline;
    gap: 0.4rem;
    min-width: 0;
}

/*
 * Die Farbmarke in der Legende bleibt eine Pille – auch dann, wenn der Text
 * daneben zweizeilig wird (Issue #84).
 *
 * Vier Angaben, und jede verhindert einen eigenen Bruch: `flex: 0 0 auto`
 * nimmt sie aus dem Zusammenschrumpfen der Zeile heraus, `nowrap` hält „Gelb"
 * und „Grün" in einem Stück, `overflow-wrap: normal` nimmt jede Trennstelle
 * innerhalb des Wortes zurück, und die Mindestbreite gibt allen vier dieselbe
 * Größe – „Rot" ist sonst deutlich schmaler als „Grau", und die Erklärungen
 * daneben begännen an vier verschiedenen Stellen. Der Text rechts darf
 * umbrechen, so oft er will; er ist der einzige Teil dieser Zeile, der Platz
 * abgibt.
 */
.entw-legende .entw-pille {
    flex: 0 0 auto;
    justify-content: center;
    min-width: 3.6rem;
    white-space: nowrap;
    overflow-wrap: normal;
}

.entw-legende-text {
    font-size: 0.85rem;
    color: var(--text-leise);
    overflow-wrap: break-word;
}

/* Der Issue-Fokus: „Historie für #73" mit seiner Bilanz. */
.entw-hist-fokus-titel {
    margin: 0 0 0.5rem;
    color: var(--text-stark);
    font-weight: 600;
    overflow-wrap: anywhere;
}

.entw-hist-fokus-bilanz {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.5rem 0.8rem;
    margin: 0;
}

.entw-hist-fokus-zahlen {
    font-size: 0.88rem;
    color: var(--text-leise);
}

/* Der Hinweis über der Aufgabensicht – ein Satz, keine Anleitung. */
.entw-hist-hinweis {
    margin: 0 0 0.8rem;
    font-size: 0.85rem;
    color: var(--text-leise);
}

/* Wie viele Läufe hinter einer Aufgabenzeile stehen.
 *
 * Bewusst `--text-leise` und nicht `--text-still`: Der Ton daneben wäre
 * hübscher, käme aber nur auf 2,5:1 gegen die Kartenfläche. Diese Angabe ist
 * eine Aussage über den Lauf und keine Verzierung – sie muss lesbar sein. */
.entw-lauf-weitere {
    display: inline-block;
    margin-left: 0.4rem;
    font-size: 0.78rem;
    font-weight: 400;
    color: var(--text-leise);
    white-space: nowrap;
}

/* Der kleine Pfeil zur Aufgabe auf GitHub, neben ihrer Nummer. */
.entw-lauf-extern {
    margin-left: 0.3rem;
    font-size: 0.85em;
    text-decoration: none;
}

.entw-hist-zeile {
    position: relative;
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: 0.2rem;
    padding: 0.75rem 2.25rem 0.75rem 0.75rem;
    font-size: 0.9rem;
    min-width: 0;
    cursor: pointer;
    /* Das eingebaute Dreieck weicht der eigenen Marke – in beiden Browsern. */
    list-style: none;
}

.entw-hist-zeile::-webkit-details-marker { display: none; }

.entw-hist-zeile span { overflow-wrap: anywhere; min-width: 0; }

.entw-hist-zeile:hover { background: var(--flaeche-sanft); }

.entw-hist-marke {
    position: absolute;
    top: 0.7rem;
    right: 0.75rem;
    color: var(--text-still);
    font-size: 0.9rem;
}

.entw-hist-marke::before { content: "▸"; }
.entw-hist-lauf[open] .entw-hist-marke::before { content: "▾"; }

.entw-hist-lauf[open] .entw-hist-zeile {
    border-bottom: 1px solid var(--rand-zart);
}

.entw-hist-details { padding: 0.75rem; }

.entw-hist-blaettern {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    margin-top: 0.9rem;
}

.entw-hist-seitenzahl {
    font-size: 0.88rem;
    color: var(--text-leise);
    font-variant-numeric: tabular-nums;
}

.entw-hist-fuss { margin: 0.5rem 0 0; }

.entw-hist-kopf { display: none; }

/*
 * Die Laufhistorie seit Issue #293.
 *
 * Erstens Luft zwischen den Karten: Fokus, „Suchen und filtern" und die Liste
 * standen ohne jeden Abstand aufeinander – die Seite hat kein Raster wie die
 * Statusseite, das ihn mitbrächte.
 *
 * Zweitens die beiden neuen Spalten: Herkunft (W1, W2, Integrator, Alt) und
 * der heutige Auftragsstand als Pille. Auf dem Telefon tragen sie wie alle
 * anderen ihre Beschriftung davor.
 *
 * Alles aus den vorhandenen Farbtoken – hell und dunkel aus einer Quelle.
 */
.entw-hist-seite > .entw-karte + .entw-karte { margin-top: 1.25rem; }

.entw-hist-zeile .entw-lauf-ergebnis::before { content: "Laufergebnis: "; }
.entw-lauf-herkunft,
.entw-lauf-auftrag { color: var(--text-mittel); font-size: 0.85rem; }
.entw-lauf-herkunft::before { content: "Herkunft: "; color: var(--text-still); }
.entw-lauf-auftrag::before  { content: "Auftrag heute: "; color: var(--text-still); }

.entw-lauf-herkunft {
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.entw-hist-fokus-genauer {
    margin: 0.5rem 0 0;
    font-size: 0.85rem;
    color: var(--text-leise);
    overflow-wrap: break-word;
}

/* Woraus die Seite besteht – zugeklappt, eine Lücke steht in der Zeile. */
.entw-hist-quellen {
    margin: 0 0 0.9rem;
    padding: 0.5rem 0.75rem;
    border: 1px solid var(--rand-zart);
    border-radius: 8px;
    background: var(--flaeche-still);
    font-size: 0.85rem;
    color: var(--text-leise);
}

.entw-hist-quellen summary { cursor: pointer; color: var(--text-mittel); }
.entw-hist-quellen ul { margin: 0.5rem 0 0; padding-left: 1.1rem; }
.entw-hist-quellen li { margin: 0.2rem 0; overflow-wrap: break-word; }
.entw-hist-quellen-luecke { color: var(--warnung-text); }
.entw-hist-quelle-luecke { color: var(--warnung-text); }

/* Laufende Versuche – getrennt vom Abschlussbestand. */
.entw-hist-laufend {
    margin: 0 0 0.9rem;
    padding: 0.6rem 0.75rem;
    border: 1px solid var(--info-kante);
    border-left-width: 4px;
    border-radius: 8px;
    background: var(--info-flaeche);
    color: var(--info-text);
    font-size: 0.88rem;
}

.entw-hist-laufend-titel { margin: 0 0 0.3rem; font-weight: 600; }
.entw-hist-laufend ul { margin: 0 0 0.4rem; padding-left: 1.1rem; }
.entw-hist-laufend li { margin: 0.15rem 0; overflow-wrap: anywhere; }
.entw-hist-laufend-art { color: var(--text-leise); }

/* Zwei Legenden, jede mit ihrer Überschrift. */
.entw-hist-legende-titel {
    margin: 0 0 0.35rem;
    font-size: 0.78rem;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    color: var(--text-still);
}

/* Der Farbstreifen der Zeile als Muster in der Legende. */
.entw-streifen {
    flex: 0 0 auto;
    align-self: stretch;
    width: 4px;
    min-height: 1rem;
    border-radius: 2px;
    background: var(--rand-still);
}

.entw-streifen-erfolgreich    { background: var(--erfolg-kraeftig); }
.entw-streifen-fehler         { background: var(--fehler-kraeftig); }
.entw-streifen-zurueckgerollt { background: var(--warnung-kraeftig); }

/*
 * Ab hier der Schreibtisch. Der vorhandene Platz wird verteilt, statt die
 * Handyansicht breitzuziehen: oben links der laufende Auftrag und rechts
 * daneben der Fortschritt, darunter Automatik und Warteschlange, dann Zeiten
 * und letzter Lauf über die ganze Breite, ganz unten die Historie als
 * Zeilenraster mit Kopfzeile.
 */
@media (min-width: 900px) {
    .entw-raster { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .entw-voll { grid-column: 1 / -1; }

    /* Hier gilt der Schreibtisch – und damit sein Weg zurück (Issue #94). */
    .entw-ordnen-standard[data-entw-ordnen-fuer="schreibtisch"] {
        display: flex;
    }

    .entw-ordnen-standard[data-entw-ordnen-fuer="mobil"] { display: none; }

    /*
     * Die obere Kartenzeile: links der laufende Auftrag, rechts der
     * Fortschritt – beide mit derselben reservierten Höhe. Damit bleiben sie
     * dauerhaft bündig, und vor allem: Die Seite springt bei den
     * Zehn-Sekunden-Takten nicht. Ein Phasenwechsel von „Gesamttests laufen"
     * auf „Browser-QM läuft – geschützte Seiten werden angemeldet geprüft"
     * verändert die Außenhöhe der Karte nicht mehr.
     *
     * Nur im vollen Modus: Ohne laufenden Auftrag steht die Statuskarte
     * allein in der Zeile und darf so hoch sein, wie ihr einer Satz es
     * verlangt – eine halbleere Karte über 26 rem wäre kein ruhiges Layout,
     * sondern ein Loch.
     */
    .entw-status.entw-modus-voll,
    .entw-fortschritt { height: var(--entw-kopfhoehe); }

    .entw-status.entw-modus-voll .entw-status-koerper {
        flex: 1 1 auto;
        overflow-y: auto;
    }

    .entw-kopf h2 { font-size: 1.6rem; }
    .entw-takt { display: inline; margin-left: 0.4rem; }

    .entw-zustand { font-size: 1.5rem; }
    .entw-symbol { font-size: 1.8rem; }
    .entw-laufzeit-wert { font-size: 2.2rem; }
    .entw-countdown-wert { font-size: 2.2rem; }

    /* Position, Nummer, Titel, Alter – in einer Zeile statt untereinander. */
    .entw-queue-eintrag {
        grid-template-columns:
            minmax(0, 2rem) minmax(0, 4.5rem) minmax(0, 1fr) minmax(0, 8rem);
        align-items: baseline;
        gap: 0.75rem;
    }

    /*
     * Die Priorisierung bekommt darunter die ganze Breite (Issue #169).
     * Als fünfte Spalte hätte sie die vier darüber zusammengedrückt – und auf
     * dem Telefon steht ohnehin alles untereinander.
     */
    .entw-queue-vorrang { grid-column: 1 / -1; }

    .entw-queue-vorrangzeile {
        grid-template-columns: minmax(0, 4.5rem) minmax(0, 1fr);
        align-items: baseline;
        gap: 0.75rem;
    }

    .entw-queue-vorranghinweis { grid-column: 2; }

    /* Steht die Infokarte allein, darf sie ruhig etwas Luft haben. */
    .entw-modus-leer { padding: 1.75rem 1.75rem 2rem; }

    /*
     * Elf Schritte untereinander sind eine halbe Bildschirmhöhe. In zwei
     * Spalten werden daraus sechs Zeilen. Gefüllt wird spaltenweise – erst
     * die linke von oben nach unten, dann die rechte –, damit die
     * Reihenfolge des Ablaufs beim Lesen erhalten bleibt; zeilenweise
     * gefüllt stünde Schritt 2 rechts neben Schritt 1.
     *
     * Die Zeilenzahl ist die Hälfte der Phasen aus
     * `app/entwicklungsstatus.py`, aufgerundet. Beides muss zusammenpassen,
     * sonst entstünde eine dritte Spalte – die Testsuite hält es zusammen.
     */
    .entw-fortschritt .entw-schritte {
        flex: 1 1 auto;
        min-height: 0;
        overflow-y: auto;
        grid-template-columns: repeat(2, minmax(0, 1fr));
        grid-template-rows: repeat(6, auto);
        grid-auto-flow: column;
        align-content: start;
        column-gap: 1.25rem;
    }

    .entw-angaben {
        grid-template-columns: minmax(0, 13rem) minmax(0, 1fr);
        align-items: baseline;
    }
    /*
     * In einer halben Kachel bleiben von 13 rem Beschriftung zu wenig für den
     * Wert daneben – seit Issue #89 stehen `Zeiten` und `Letzter
     * abgeschlossener Lauf` dort. Die Beschriftungen sind kurz genug für
     * 10 rem („Letzter Statuszeitpunkt" ist die längste), und der Wert
     * bekommt damit den Platz, der ihm vorher fehlte. Volle Kacheln behalten
     * ihre 13 rem.
     */
    .entw-karte:not(.entw-voll) .entw-angaben {
        grid-template-columns: minmax(0, 10rem) minmax(0, 1fr);
    }
    .entw-angaben dt { font-size: 0.9rem; }
    .entw-angaben dd { margin-bottom: 0.35rem; }

    /*
     * Zehn Spalten seit Issue #78: Die Statuspille steht neben dem Ergebnis
     * und nicht an seiner Stelle – „erfolgreich" und „abgenommen" sind zwei
     * Aussagen. Die Issue-Spalte ist etwas breiter geworden, weil dort neben
     * der Nummer der Pfeil zur Aufgabe auf GitHub steht.
     */
    .entw-lauf {
        grid-template-columns:
            minmax(0, 5.5rem) minmax(0, 2.6fr) minmax(0, 1.2fr)
            minmax(0, 2fr) minmax(0, 1fr) minmax(0, 1.4fr) minmax(0, 1.5fr)
            minmax(0, 1.5fr) minmax(0, 1.5fr) minmax(0, 1fr);
        align-items: baseline;
        gap: 0.75rem;
    }

    /* Am Schreibtisch verrät die Spaltenstellung, was gemeint ist. */
    .entw-lauf span::before { content: none !important; }

    /*
     * Der aktuelle Stand je Aufgabe (Issue #87) – fünf Spalten, dieselbe
     * Stellung für Kopfzeile und Zeile. Die Spaltenmaße folgen den ersten
     * Spalten der Laufliste darunter, damit beide Listen fluchten: Nummer,
     * Titel, Zustand. Was danach kommt, ist bewusst schmaler – „3 Läufe, 2
     * fehlgeschlagen" ist eine Bilanz und keine Tabelle.
     */
    .entw-aufgabe,
    .entw-aufgaben-kopf {
        grid-template-columns:
            minmax(0, 5.5rem) minmax(0, 3fr) minmax(0, 2fr)
            minmax(0, 1.8fr) minmax(0, 1.4fr);
        gap: 0.75rem;
    }

    .entw-aufgabe { align-items: baseline; }
    .entw-aufgabe span::before { content: none !important; }

    .entw-aufgaben-kopf {
        display: grid;
        /* Links um Rahmen und Farbstreifen der Zeilen versetzt, damit die
         * Überschrift genau über ihrer Spalte steht. */
        padding: 0 0.75rem 0.4rem calc(0.75rem + 5px);
        font-size: 0.78rem;
        text-transform: uppercase;
        letter-spacing: 0.03em;
        color: var(--text-still);
    }

    .entw-historie-kopf {
        display: grid;
        grid-template-columns:
            minmax(0, 5.5rem) minmax(0, 2.6fr) minmax(0, 1.2fr)
            minmax(0, 2fr) minmax(0, 1fr) minmax(0, 1.4fr) minmax(0, 1.5fr)
            minmax(0, 1.5fr) minmax(0, 1.5fr) minmax(0, 1fr);
        gap: 0.75rem;
        /* Links um Rahmen und Farbstreifen der Zeilen versetzt, damit die
         * Überschrift genau über ihrer Spalte steht. */
        padding: 0 0.75rem 0.4rem calc(0.75rem + 5px);
        font-size: 0.78rem;
        text-transform: uppercase;
        letter-spacing: 0.03em;
        color: var(--text-still);
    }

    /* ── Die vollständige Laufhistorie (Issue #72) ──────────────────────── */
    /*
     * Die Filterfelder nebeneinander statt untereinander: Sie gehören zu einer
     * Frage („welche Läufe?") und sollen auch wie eine aussehen. Die
     * Schaltflächen bekommen die ganze Breite darunter.
     *
     * Vier Spalten und – seit dem Abnahmefilter und der Ansicht aus Issue #78 –
     * sechs Felder: Oben stehen Suche, Ergebnis, Abnahme und Ansicht, darunter
     * die beiden Datumsfelder in den ersten zwei Spalten. „Abgeschlossen bis"
     * landet damit in einer der schmalen; dass sein Datum dort trotzdem ganz
     * dasteht, hängt an der selbsttätigen Mindestgröße des Feldes weiter oben.
     */
    .entw-hist-filter {
        grid-template-columns:
            minmax(0, 2fr) minmax(0, 1.3fr) minmax(0, 1.3fr) minmax(0, 1.3fr);
        align-items: end;
    }
    /* Seit Issue #293 sieben Felder in denselben vier Spalten: Suche,
     * Laufergebnis, Abnahme und Herkunft oben, Ansicht und Zeitraum darunter. */

    .entw-hist-tasten { grid-column: 1 / -1; }

    /*
     * Die Legende darf hier in eine Zeile: Vier Einträge nebeneinander sind
     * am Schreibtisch ein Streifen und keine Wortsäulen. Auf dem Telefon
     * bleibt es bei einem Eintrag je Zeile.
     *
     * Die gemeinsame Mindestbreite der vier Pillen fällt dabei weg: Sie richtet
     * die Erklärungen **untereinander** aus, und untereinander stehen sie hier
     * nicht mehr. Nebeneinander wäre sie nur zusätzliche Luft in „Rot".
     */
    .entw-legende {
        display: flex;
        flex-wrap: wrap;
        gap: 0.4rem 1rem;
    }

    .entw-legende .entw-pille { min-width: auto; }

    /* Acht Spalten – dieselbe Stellung für Kopfzeile und Zeile (Issue #293):
     * Auftrag, Titel, Herkunft, Laufergebnis (damals), Auftrag heute, Dauer,
     * Ende, Commit. Laufergebnis und Auftrag heute stehen nebeneinander, weil
     * erst beide zusammen die Lage ergeben – und getrennt, weil sie zwei
     * verschiedene Fragen beantworten. */
    .entw-hist-zeile,
    .entw-hist-kopf {
        grid-template-columns:
            minmax(0, 4.5rem) minmax(0, 3fr) minmax(0, 5.5rem)
            minmax(0, 1.6fr) minmax(0, 1.8fr) minmax(0, 0.9fr)
            minmax(0, 1.4fr) minmax(0, 0.9fr);
        gap: 0.75rem;
    }

    .entw-hist-zeile .entw-lauf-herkunft::before,
    .entw-hist-zeile .entw-lauf-auftrag::before { content: none !important; }

    .entw-hist-legenden {
        display: grid;
        grid-template-columns: max-content minmax(0, 1fr);
        align-items: baseline;
        gap: 0 1rem;
    }

    .entw-hist-zeile { align-items: baseline; }

    /*
     * Am Schreibtisch verrät die Spaltenstellung, was gemeint ist – die
     * Beschriftungen der Handyansicht fallen weg. Ausdrücklich aufgezählt und
     * nicht über `span::before`: Die Auf- und Zuklappmarke ist ebenfalls ein
     * `span` mit `::before`, und eine pauschale Regel löschte sie mit.
     */
    .entw-hist-zeile .entw-lauf-ergebnis::before,
    .entw-hist-zeile .entw-lauf-abnahme::before,
    .entw-hist-zeile .entw-lauf-dauer::before,
    .entw-hist-zeile .entw-lauf-zeit::before,
    .entw-hist-zeile .entw-lauf-commit::before { content: none !important; }

    .entw-hist-kopf {
        display: grid;
        /* Rechts um die Klappmarke versetzt, links um Rahmen und Farbstreifen –
         * dann steht jede Überschrift über ihrer Spalte. */
        padding: 0 2.25rem 0.4rem calc(0.75rem + 5px);
        font-size: 0.78rem;
        text-transform: uppercase;
        letter-spacing: 0.03em;
        color: var(--text-still);
    }
}


/* ── Handbuch (Issue #56) ─────────────────────────────────────
 *
 * Ein Lesebereich, kein Arbeitswerkzeug. Deshalb gelten hier andere Maße als
 * im Sendeplan: eine begrenzte Zeilenlänge, mehr Zeilenabstand, deutliche
 * Abstände zwischen den Abschnitten. Alles, was hier steht, hängt an einer
 * `hb-`-Klasse und kann damit keine bestehende Seite verändern.
 *
 * Kein neuer Breakpoint: Die mobile Fassung steht unten im vorhandenen Block
 * `@media (max-width: 640px)`, dem Telefon-Breakpoint dieses Projekts.
 */

.hb-seite {
    /*
     * Die Zeilenlänge ist die wichtigste Einstellung eines Lesetexts. Bei
     * 1600 px Fensterbreite liefe eine Zeile sonst über den ganzen Schirm,
     * und das Auge fände den Anfang der nächsten nicht mehr.
     */
    max-width: 62rem;
    margin: 0 auto;
}

.hb-kopf { margin-bottom: 1.5rem; }

.hb-augenbraue {
    font-size: 0.8rem;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--text-leise);
    margin-bottom: 0.3rem;
}

.hb-kopf h2 {
    font-size: 1.9rem;
    line-height: 1.2;
    margin-bottom: 0.5rem;
}

.hb-vorspann {
    font-size: 1.05rem;
    line-height: 1.6;
    color: var(--text-normal);
    max-width: 46rem;
}

.hb-abschnitt {
    margin: 2.5rem 0 1rem;
    font-size: 1.15rem;
    padding-bottom: 0.4rem;
    border-bottom: 1px solid var(--rand);
}

/* ── Brotkrumen ──────────────────────────────────────────── */

.hb-brotkrumen { margin-bottom: 1rem; }

.hb-brotkrumen ol {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.35rem;
    list-style: none;
    font-size: 0.85rem;
    color: var(--text-leise);
}

/* Der Trenner ist Dekoration und gehört deshalb nicht in den Text, den ein
   Vorleseprogramm ausgibt – `::before` steht dort nicht. */
.hb-brotkrumen li + li::before { content: "›"; margin-right: 0.35rem; }
.hb-brotkrumen a { color: var(--text-mittel); text-decoration: none; }
.hb-brotkrumen a:hover { text-decoration: underline; }
.hb-brotkrumen [aria-current="page"] { color: var(--text-stark); font-weight: 600; }

/* ── Suche ───────────────────────────────────────────────── */

.hb-suche {
    position: relative;
    background: var(--flaeche-karte);
    border: 1px solid var(--rand);
    border-radius: 10px;
    padding: 1rem 1.1rem;
    margin-bottom: 1.5rem;
}

.hb-suche-beschriftung {
    display: block;
    font-weight: 600;
    margin-bottom: 0.45rem;
}

.hb-suche-zeile { display: flex; gap: 0.6rem; align-items: stretch; }

.hb-suche input[type="search"] {
    flex: 1 1 auto;
    min-width: 0;
    padding: 0.55rem 0.75rem;
    border: 1px solid var(--rand-stark);
    border-radius: 8px;
    font-size: 1rem;
    font-family: inherit;
}

.hb-suche input[type="search"]:focus {
    outline: 2px solid var(--kontur);
    outline-offset: 1px;
}

.hb-suche .hinweis { display: block; margin-top: 0.5rem; }

/*
 * Die Trefferliste beim Tippen. Sie liegt über dem Inhalt, damit die Seite
 * beim Tippen nicht wächst und wieder schrumpft – ein springendes Layout
 * unter dem Finger ist schlimmer als kein Vorschlag.
 */
.hb-vorschlaege {
    position: absolute;
    left: 1.1rem;
    right: 1.1rem;
    top: calc(100% - 0.5rem);
    z-index: 50;
    background: var(--flaeche-karte);
    border: 1px solid var(--rand-stark);
    border-radius: 10px;
    box-shadow: var(--schatten-vorschlaege);
    padding: 0.6rem;
    max-height: 24rem;
    overflow-y: auto;
}

.hb-vorschlaege[hidden] { display: none; }

.hb-vorschlag-kopf {
    font-size: 0.78rem;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--text-leise);
    padding: 0.2rem 0.4rem 0.5rem;
}

.hb-vorschlagsliste { list-style: none; }

.hb-vorschlag a {
    display: block;
    padding: 0.5rem 0.55rem;
    border-radius: 8px;
    text-decoration: none;
    color: inherit;
}

.hb-vorschlag a:hover, .hb-vorschlag a:focus { background: var(--flaeche-sanft); }

.hb-vorschlag-titel { display: block; font-weight: 600; color: var(--text-stark); }

.hb-vorschlag-bereich {
    display: block;
    font-size: 0.75rem;
    color: var(--text-leise);
    margin-bottom: 0.15rem;
}

.hb-vorschlag-ausschnitt {
    display: block;
    font-size: 0.85rem;
    line-height: 1.45;
    color: var(--text-mittel);
}

.hb-vorschlag-alle {
    display: block;
    padding: 0.55rem;
    text-align: center;
    font-size: 0.9rem;
    font-weight: 600;
}

.hb-vorschlaege mark, .hb-treffer-ausschnitt mark {
    background: var(--treffer-flaeche);
    color: inherit;
    border-radius: 3px;
    padding: 0 0.1em;
}

/* ── Kapitelbaum ─────────────────────────────────────────── */

.hb-baum-bereich { margin-bottom: 1.75rem; }

.hb-baum-bereich h3 {
    font-size: 0.82rem;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--text-leise);
    margin-bottom: 0.6rem;
}

.hb-baum-liste { list-style: none; }

.hb-baum-eintrag {
    padding: 0.6rem 0.8rem;
    border-left: 3px solid var(--rand);
    margin-bottom: 0.35rem;
    background: var(--flaeche-karte);
    border-radius: 0 8px 8px 0;
}

.hb-baum-eintrag > a {
    font-weight: 600;
    color: var(--text-stark);
    text-decoration: none;
    display: block;
    /* Ein Kapitelverweis ist ein Tippziel wie jedes andere. */
    min-height: 24px;
}

.hb-baum-eintrag > a:hover { text-decoration: underline; }
.hb-baum-aktiv { border-left-color: var(--kontur); background: var(--flaeche-sanft); }

.hb-baum-kurz {
    display: block;
    font-size: 0.87rem;
    color: var(--text-leise);
    line-height: 1.45;
}

.hb-rollenmarke {
    display: inline-block;
    margin-top: 0.3rem;
    font-size: 0.75rem;
    padding: 0.1rem 0.45rem;
    border-radius: 999px;
    background: var(--info-flaeche-zart);
    color: var(--info-text);
}

.hb-rollenmarke-gross { font-size: 0.85rem; padding: 0.25rem 0.7rem; }

/* ── Kapitelinhalt ───────────────────────────────────────── */

.hb-gliederung {
    /* Bezugspunkt für das Kästchen des Aufklappers, das auf dem Telefon
       absolut positioniert und unsichtbar, aber fokussierbar ist. */
    position: relative;
    background: var(--flaeche-karte);
    border: 1px solid var(--rand);
    border-radius: 10px;
    padding: 0.85rem 1.1rem;
    margin-bottom: 1.75rem;
}

/*
 * Am Schreibtisch gibt es keinen Schalter: Der Kasten steht offen da, und
 * Kästchen und Beschriftung nehmen keinen Platz ein. Erst der Telefonblock
 * unten dreht das um.
 */
.hb-gliederung-schalter,
.hb-gliederung-schalter-marke { display: none; }

.hb-gliederung-titel { font-weight: 600; }

.hb-gliederung ol { list-style: none; margin-top: 0.6rem; }
.hb-gliederung li { padding: 0.15rem 0; }
.hb-gliederung-stufe3 { padding-left: 1.1rem; font-size: 0.9rem; }
.hb-gliederung-stufe4 { padding-left: 2.2rem; font-size: 0.85rem; }
.hb-gliederung a { color: var(--text-normal); }

.hb-inhalt {
    font-size: 1rem;
    line-height: 1.7;
    color: var(--text-lesetext);
}

.hb-inhalt p { margin-bottom: 1rem; max-width: 46rem; }

.hb-ueberschrift {
    /*
     * `scroll-margin-top` ist hier nicht Kosmetik: Der Kopfbereich klebt oben
     * (`position: sticky`). Ohne diesen Abstand landete eine angesprungene
     * Überschrift genau dahinter und wäre unsichtbar – gerade beim Sprung aus
     * der Trefferliste, dem häufigsten Weg in ein Kapitel.
     */
    scroll-margin-top: 5rem;
    margin: 2rem 0 0.8rem;
    line-height: 1.3;
    position: relative;
}

h3.hb-ueberschrift { font-size: 1.35rem; }
h4.hb-ueberschrift { font-size: 1.1rem; margin-top: 1.5rem; }
h5.hb-ueberschrift { font-size: 1rem; margin-top: 1.25rem; }

/* Der Verweis auf den eigenen Abschnitt. Er erscheint erst beim Überfahren
   oder bei Tastaturfokus – sichtbar wäre er ein Zeichensalat vor jeder
   Überschrift, und auf dem Telefon gäbe es kein Überfahren. */
.hb-ankerlink {
    position: absolute;
    left: -1.1rem;
    color: var(--text-still);
    text-decoration: none;
    opacity: 0;
}

.hb-ueberschrift:hover .hb-ankerlink, .hb-ankerlink:focus { opacity: 1; }

.hb-liste { margin: 0 0 1.2rem 1.3rem; max-width: 46rem; }
.hb-liste li { margin-bottom: 0.4rem; }

/*
 * Schrittfolgen als abgesetzte Karten mit großer Nummer.
 *
 * Der Auftrag verlangt ausdrücklich, dass Schrittfolgen „visuell gut erfassbar"
 * sind und keine Textwüste. Eine gewöhnliche `ol` mit kleinen Ziffern am Rand
 * erfüllt das nicht: Wer eine Anleitung mit dem Gerät in der Hand abarbeitet,
 * muss die Stelle nach jedem Blick wiederfinden.
 */
.hb-schritte {
    list-style: none;
    counter-reset: hb-schritt;
    margin: 0 0 1.5rem;
    max-width: 46rem;
}

.hb-schritte li {
    counter-increment: hb-schritt;
    position: relative;
    padding: 0.7rem 0.9rem 0.7rem 3rem;
    margin-bottom: 0.5rem;
    background: var(--flaeche-karte);
    border: 1px solid var(--rand);
    border-radius: 10px;
}

.hb-schritte li::before {
    content: counter(hb-schritt);
    position: absolute;
    left: 0.75rem;
    top: 0.6rem;
    width: 1.7rem;
    height: 1.7rem;
    border-radius: 50%;
    background: var(--fuellung);
    color: var(--fuellung-text);
    font-weight: 700;
    font-size: 0.9rem;
    display: flex;
    align-items: center;
    justify-content: center;
}

/* ── Hinweiskästen ───────────────────────────────────────── */

/*
 * Fünf Sorten plus der Rollenhinweis, jede mit eigener Farbe **und** eigener
 * Überschrift. Die Farbe allein wäre zu wenig: Wer sie nicht unterscheiden
 * kann, liest die Überschrift.
 */
.hb-kasten {
    border-left: 4px solid var(--rand-still);
    background: var(--flaeche-zart);
    border-radius: 0 10px 10px 0;
    padding: 0.85rem 1.1rem;
    margin: 0 0 1.25rem;
    max-width: 46rem;
}

.hb-kasten p { margin-bottom: 0; }

.hb-kasten-titel {
    font-size: 0.78rem;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    font-weight: 700;
    margin-bottom: 0.3rem !important;
}

.hb-kasten-hinweis { border-left-color: var(--hinweis-kante); background: var(--hinweis-flaeche); }
.hb-kasten-hinweis .hb-kasten-titel { color: var(--hinweis-text); }

.hb-kasten-voraussetzung { border-left-color: var(--voraussetzung-kante); background: var(--voraussetzung-flaeche); }
.hb-kasten-voraussetzung .hb-kasten-titel { color: var(--voraussetzung-text); }

.hb-kasten-warnung { border-left-color: var(--fehler-kraeftig); background: var(--gefahr-flaeche-zart); }
.hb-kasten-warnung .hb-kasten-titel { color: var(--fehler-text); }

.hb-kasten-ergebnis { border-left-color: var(--erfolg-zart-kante); background: var(--erfolg-zart-flaeche); }
.hb-kasten-ergebnis .hb-kasten-titel { color: var(--erfolg-zart-text); }

.hb-kasten-fehler { border-left-color: var(--warnung-kraeftig); background: var(--warnung-flaeche-blass); }
.hb-kasten-fehler .hb-kasten-titel { color: var(--warnung-mittel); }

.hb-kasten-rolle { border-left-color: var(--info-kante); background: var(--info-flaeche-zart); }
.hb-kasten-rolle .hb-kasten-titel { color: var(--info-text); }

/* ── Unterlisten, Tabellen und Codeblöcke (Issue #577) ──────
   Der Bestand benutzte sie längst; bis dahin standen ihre Markdownzeichen
   im Text. Sie bekommen nur, was zum Lesen nötig ist – Einzug, Linien und
   die Flächen- und Randfarben, die das Handbuch ohnehin trägt. */
.hb-unterliste { margin: 0.4rem 0 0.2rem 1.3rem; }
ul.hb-unterliste { list-style: disc; }
ol.hb-unterliste { list-style: decimal; }
/* Eine Unterliste in einer Schrittkarte ist keine zweite Karte und zählt
   die Schritte nicht mit. */
.hb-unterliste li {
    counter-increment: none;
    position: static;
    padding: 0;
    margin: 0 0 0.25rem;
    background: none;
    border: 0;
}
.hb-unterliste li::before { content: none; }

ol.hb-kastenliste { list-style: decimal; }
.hb-kasten > :not(.hb-kasten-titel) + :not(.hb-kasten-titel) { margin-top: 0.6rem; }
.hb-kasten > .hb-liste:last-child,
.hb-kasten > .hb-tabelle-rahmen:last-child,
.hb-kasten > .hb-code:last-child { margin-bottom: 0; }

.hb-tabelle-rahmen {
    max-width: 46rem;
    margin: 0 0 1.25rem;
    /* Eine breite Tabelle rollt auf dem Telefon in ihrem Rahmen, statt die
       Seite zu verbreitern. */
    overflow-x: auto;
}
.hb-tabelle { border-collapse: collapse; width: 100%; font-size: 0.92rem; line-height: 1.5; }
.hb-tabelle th,
.hb-tabelle td {
    text-align: left;
    vertical-align: top;
    padding: 0.45rem 0.6rem;
    border-bottom: 1px solid var(--rand);
}
.hb-tabelle th { background: var(--flaeche-karte); font-weight: 600; }

.hb-code {
    max-width: 46rem;
    margin: 0 0 1.25rem;
    padding: 0.7rem 0.9rem;
    background: var(--flaeche-karte);
    border: 1px solid var(--rand);
    border-radius: 8px;
    overflow-x: auto;
    font-size: 0.85rem;
    line-height: 1.45;
}

.hb-start-hinweise { margin-bottom: 1rem; }

/* ── Die Handbuchauswahl (Issue #127) ────────────────────────
   Drei Karten nebeneinander am Schreibtisch, untereinander auf dem Telefon –
   und zwar über `auto-fit` statt über eine feste Spaltenzahl: Käme ein viertes
   Handbuch dazu, müsste hier sonst jemand mitzählen. Ein eigener
   Telefon-Breakpoint ist dadurch überflüssig; unter 16 rem Kartenbreite bricht
   das Raster von selbst auf eine Spalte um.

   Die Aktionen sind gewöhnliche `.schaltflaeche`n. Dadurch gilt die
   44-px-Tippregel aus Issue #43 auch hier, ohne eine zweite Fassung davon. */

.hb-werkwahl { margin-bottom: 1.5rem; }

.hb-werkwahl-vorspann {
    font-size: 0.9rem;
    line-height: 1.5;
    color: var(--text-mittel);
    margin-bottom: 1rem;
}

.hb-werkwahl-liste {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
    gap: 0.9rem;
    list-style: none;
    margin: 0;
    padding: 0;
}

.hb-werkwahl-karte {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
    background: var(--flaeche-zart);
    border: 1px solid var(--rand);
    border-radius: 10px;
    padding: 1rem;
}

/* Das gerade geöffnete Handbuch ist nicht weniger wert, es ist nur schon da.
   Ein kräftiger linker Rand sagt das, ohne die Karte aus der Reihe zu nehmen –
   dieselbe Kante, die auch sonst im Produkt „hier stehst du" bedeutet. */
.hb-werkwahl-hier { border-left: 3px solid var(--akzent); }

.hb-werkwahl-fuer {
    font-size: 0.75rem;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--text-leise);
    margin-bottom: 0;
}

.hb-werkwahl-name {
    font-size: 1.05rem;
    color: var(--text-stark);
    margin: 0;
}

/* Dieselbe Pille wie die Rollenmarke, nur in der Farbe der Marke: Das
   Designhandbuch ist die verbindliche Referenz, und das gehört an die Karte
   und nicht erst in ein Kapitel. */
.hb-werkwahl-kennzeichen {
    align-self: flex-start;
    font-size: 0.75rem;
    font-weight: 600;
    padding: 0.1rem 0.55rem;
    border-radius: 999px;
    background: var(--gefahr-flaeche-zart);
    color: var(--akzent-text);
    margin-bottom: 0;
}

/* `flex: 1` hält die Aktionen aller Karten auf einer Höhe, auch wenn der eine
   Satz länger ist als der andere. */
.hb-werkwahl-kurz {
    flex: 1 1 auto;
    font-size: 0.9rem;
    line-height: 1.5;
    color: var(--text-mittel);
    margin-bottom: 0;
}

.hb-werkwahl-aktionen {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    margin-top: 0.35rem;
}

/* Der Stand einer Beigabe, die sich ändern kann – die freigegebene Fassung des
   Pluginpakets (Issue #136). Sie steht klein unter der Schaltfläche: Wer die
   Datei holen will, liest die Beschriftung; wer wissen muss, welchen Stand er
   weitergibt, findet ihn eine Zeile tiefer, ohne die Karte zu überladen. */
.hb-werkwahl-stand {
    font-size: 0.8rem;
    line-height: 1.5;
    color: var(--text-leise);
    margin: 0.35rem 0 0;
}

/* Die Tokendateien: auffindbar, aber keine Schaltfläche. Der Auftrag zu Issue
   #127 will sie „ohne dass diese die Hauptansicht überladen". */
.hb-werkwahl-leise {
    font-size: 0.8rem;
    line-height: 1.5;
    color: var(--text-leise);
    margin: 0.15rem 0 0;
}

.hb-werkwahl-leise a { color: var(--text-mittel); }

/* ── PDF-Ausgabe ─────────────────────────────────────────────
   Die Aktion steht auf der Startseite offen da und nicht in einem Menü: Wer
   das Handbuch weitergeben oder ausdrucken will, soll nicht danach suchen.
   Die Schaltfläche selbst ist die gewöhnliche `.schaltflaeche` – dadurch gilt
   auch auf dem Telefon die 44-px-Regel aus Issue #43, ohne eine zweite. */

.hb-pdf {
    display: flex;
    align-items: center;
    gap: 0.9rem;
    flex-wrap: wrap;
    background: var(--flaeche-zart);
    border: 1px solid var(--rand);
    border-radius: 10px;
    padding: 0.9rem 1.1rem;
    margin-bottom: 1.25rem;
}

.hb-pdf-text { flex: 1 1 18rem; font-size: 0.9rem; color: var(--text-mittel); line-height: 1.5; }

.hb-pdf-text strong { display: block; color: var(--text-stark); }

/* ── Bilder ──────────────────────────────────────────────── */

.hb-bild { margin: 0 0 1.5rem; max-width: 46rem; }

/* Der Zeiger sagt vorher, was der Klick tut: vergrößern. */
.hb-bild-verweis { display: block; cursor: zoom-in; }

.hb-bild img {
    display: block;
    width: 100%;
    height: auto;
    border: 1px solid var(--rand);
    border-radius: 10px;
    background: var(--bild-unterlage);
}

.hb-bild figcaption {
    font-size: 0.85rem;
    color: var(--text-leise);
    line-height: 1.5;
    margin-top: 0.45rem;
}

/* ── Die vergrößerte Bildansicht (Issue #61) ─────────────────
   Sie entsteht erst durch `static/handbuch-lupe.js`; ohne Skript gibt es
   keinen `.hb-lupe`-Knoten und diese Regeln greifen an nichts.

   Die Ansicht ist das ganze Fenster und kein Kasten in der Mitte: Ein
   Screenshot des Schreibtischs braucht jeden Pixel Breite, den ein Telefon
   hergibt. Aufgeteilt ist sie in zwei Teile, und diese Trennung ist der Kern
   der Sache:

     .hb-lupe-flaeche     scrollt – hier liegt das Bild, das höher sein darf
                          als das Fenster
     .hb-lupe-schliessen  scrollt nicht – das X liegt daneben in der Ansicht
                          selbst und steht deshalb immer an derselben Stelle

   Waagerecht wird nirgends gescrollt: Das Bild ist nie breiter als die
   Fläche. */

.hb-lupe {
    position: fixed;
    inset: 0;
    width: 100%;
    height: 100%;
    max-width: none;
    max-height: none;
    margin: 0;
    padding: 0;
    border: 0;
    overflow: hidden;
    background: rgba(15, 23, 42, 0.94);
}

.hb-lupe::backdrop { background: rgba(15, 23, 42, 0.94); }

.hb-lupe-flaeche {
    position: absolute;
    inset: 0;
    overflow-y: auto;
    overflow-x: hidden;
    -webkit-overflow-scrolling: touch;
    /* Oben bleibt Platz für das X: Beim Öffnen liegt es neben dem Bild und
       nicht darauf. Wird ein hohes Bild gescrollt, schiebt sich Bildinhalt
       darunter – dafür hat das X seinen Ring und seinen Schatten. */
    padding: 4.75rem 1.25rem 2.5rem;
    /* Der abgedunkelte Rand ist selbst ein Bedienelement: ein Klick darauf
       schließt. */
    cursor: zoom-out;
}

.hb-lupe-figur { margin: 0; }

.hb-lupe-bild {
    display: block;
    margin: 0 auto;
    max-width: 100%;
    height: auto;
    border-radius: 6px;
    background: var(--bild-unterlage);
    cursor: default;
}

.hb-lupe-unterschrift {
    max-width: 46rem;
    margin: 0.85rem auto 0;
    color: #e5e7eb;
    font-size: 0.9rem;
    line-height: 1.5;
    text-align: center;
    cursor: default;
}

.hb-lupe-schliessen {
    position: absolute;
    top: 0.9rem;
    right: 0.9rem;
    z-index: 2;
    display: flex;
    align-items: center;
    justify-content: center;
    /* Deutlich über der 44-px-Untergrenze dieses Projekts: Das X ist auf dem
       Telefon das einzige Bedienelement der Ansicht. */
    min-width: 48px;
    min-height: 48px;
    padding: 0;
    border-radius: 50%;
    /* Gegen jeden Bildinhalt: dunkle Fläche, weißer Ring, weißes Zeichen.
       Ein heller Screenshot dahinter lässt den Ring stehen, ein dunkler die
       Fläche – verschmelzen kann das X mit keinem von beiden. */
    border: 2px solid #ffffff;
    background: #111827;
    color: #ffffff;
    box-shadow: 0 2px 12px rgba(0, 0, 0, 0.55);
    cursor: pointer;
}

.hb-lupe-schliessen:hover { background: #374151; }

.hb-lupe-schliessen:focus-visible {
    outline: 3px solid #93c5fd;
    outline-offset: 2px;
}

.hb-lupe-kreuz { font-size: 1.75rem; line-height: 1; }

/* Solange die Ansicht offen ist, scrollt die Seite darunter nicht mit. Die
   Stelle im Kapitel bleibt damit stehen, wo sie war; das Skript setzt sie
   beim Schließen zusätzlich ausdrücklich zurück. */
.hb-lupe-offen, .hb-lupe-offen body { overflow: hidden; }

/* ── Blättern ────────────────────────────────────────────── */

.hb-blaettern {
    display: flex;
    gap: 1rem;
    margin: 2.5rem 0 1.5rem;
    padding-top: 1.25rem;
    border-top: 1px solid var(--rand);
}

.hb-blaettern-seite { flex: 1 1 0; min-width: 0; }
.hb-blaettern-rechts { text-align: right; }

.hb-blaettern a {
    display: block;
    padding: 0.7rem 0.9rem;
    background: var(--flaeche-karte);
    border: 1px solid var(--rand);
    border-radius: 10px;
    text-decoration: none;
    color: inherit;
    /* Vor und Zurück sind Tippziele – hier gilt dieselbe Untergrenze wie
       überall sonst im Backend. */
    min-height: 44px;
}

.hb-blaettern a:hover { border-color: var(--rand-still); }

.hb-blaettern-marke {
    display: block;
    font-size: 0.75rem;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--text-leise);
}

.hb-blaettern-titel { display: block; font-weight: 600; color: var(--text-stark); }

.hb-fussbereich { margin-top: 2rem; }

/* ── Trefferliste ────────────────────────────────────────── */

.hb-trefferbilanz {
    font-size: 0.9rem;
    color: var(--text-leise);
    margin-bottom: 0.75rem;
}

.hb-treffer { list-style: none; }

.hb-treffer-eintrag {
    background: var(--flaeche-karte);
    border: 1px solid var(--rand);
    border-radius: 10px;
    padding: 0.85rem 1.1rem;
    margin-bottom: 0.6rem;
}

.hb-treffer-titel {
    font-weight: 600;
    font-size: 1.05rem;
    color: var(--text-stark);
    text-decoration: none;
}

.hb-treffer-titel:hover { text-decoration: underline; }

.hb-treffer-bereich {
    display: block;
    font-size: 0.75rem;
    color: var(--text-leise);
    margin-bottom: 0.3rem;
}

.hb-treffer-ausschnitt {
    font-size: 0.92rem;
    line-height: 1.55;
    color: var(--text-mittel);
}

.hb-leer {
    background: var(--flaeche-karte);
    border: 1px solid var(--rand);
    border-radius: 10px;
    padding: 1.1rem;
}

.hb-leer p { margin-bottom: 0.6rem; }


/*
 * Die mobile Fassung des Handbuchs – im vorhandenen Telefon-Breakpoint.
 *
 * Der Auftrag nennt die Hochkantansicht ausdrücklich. Drei Dinge ändern sich:
 * die Zeilenlängenbegrenzung fällt weg (bei 390 px gibt es nichts zu
 * begrenzen), die Gliederung eines Kapitels steht zugeklappt statt offen, und
 * Vor/Zurück stehen untereinander statt nebeneinander – zwei Kacheln in einer
 * 390-px-Zeile wären zwei Wortsalate.
 */
@media (max-width: 640px) {
    .hb-seite { max-width: none; }
    .hb-kopf h2 { font-size: 1.5rem; }
    .hb-vorspann { font-size: 1rem; max-width: none; }
    .hb-inhalt p, .hb-liste, .hb-schritte, .hb-kasten, .hb-bild { max-width: none; }

    .hb-suche { padding: 0.85rem; }
    .hb-suche-zeile { flex-direction: column; }
    .hb-suche input[type="search"] { min-height: 44px; }
    .hb-vorschlaege { left: 0.5rem; right: 0.5rem; max-height: 60vh; }
    .hb-vorschlag a { min-height: 44px; }

    /*
     * Zugeklappt: Der Weg zur ersten Zeile Text ist auf dem Telefon kostbar,
     * und eine Gliederung über sieben Zeilen wäre genau der Umweg, den der
     * Auftrag „nicht als Textwüste" meint.
     *
     * Die Liste ist da, sie steht nur nicht offen – ein Fingertipp auf die
     * Beschriftung holt sie hervor. Aus dem Dokument verschwindet sie nicht:
     * Ein Vorleseprogramm findet sie, und ein Sprungziel bleibt gültig.
     */
    .hb-gliederung-titel { display: none; }
    .hb-gliederung > ol { display: none; }
    .hb-gliederung-schalter:checked ~ ol { display: block; }

    .hb-gliederung-schalter-marke {
        display: flex;
        align-items: center;
        min-height: 44px;
        font-weight: 600;
        cursor: pointer;
    }

    .hb-gliederung-schalter-marke::before {
        content: "▸";
        margin-right: 0.5rem;
        color: var(--text-leise);
    }

    .hb-gliederung-schalter:checked ~ .hb-gliederung-schalter-marke::before {
        content: "▾";
    }

    /* Für die Tastatur sichtbar, für das Auge nicht: Das Kästchen selbst
       bleibt fokussierbar, ohne neben der Beschriftung zu stehen. */
    .hb-gliederung-schalter {
        display: block;
        position: absolute;
        opacity: 0;
        width: 1px;
        height: 1px;
    }

    .hb-gliederung-schalter:focus-visible ~ .hb-gliederung-schalter-marke {
        outline: 2px solid var(--kontur);
        outline-offset: 2px;
    }

    /* Ein umbrechender Eintrag soll zwei enge Zeilen ergeben und nicht zwei
       44 px hohe – deshalb ein Mindestmaß und keine feste Zeilenhöhe. */
    .hb-gliederung a {
        display: flex;
        align-items: center;
        min-height: 44px;
        line-height: 1.35;
    }

    .hb-baum-eintrag { padding: 0.55rem 0.7rem; }
    .hb-baum-eintrag > a { min-height: 44px; display: flex; align-items: center; }

    .hb-blaettern { flex-direction: column; }
    .hb-blaettern-rechts { text-align: left; }

    .hb-treffer-titel { display: flex; align-items: center; min-height: 44px; }

    /* Der Ankerlink hätte auf dem Telefon kein Überfahren, unter dem er
       erscheinen könnte – dort steht er gar nicht erst. */
    .hb-ankerlink { display: none; }

    /*
     * Die vergrößerte Bildansicht auf dem Telefon.
     *
     * Der seitliche Rand schrumpft – bei 390 px zählt jeder Pixel Bildbreite –,
     * der Platz über dem Bild bleibt: Er ist es, der das X vom Bildinhalt
     * trennt. Das X selbst wird nicht kleiner, sondern rückt näher an die
     * Ecke; 48 px sind auch hier die Tippfläche, und zum Rand bleiben
     * sichtbare 10 px, damit es nicht am Displayrand klebt.
     */
    .hb-lupe-flaeche { padding: 4.5rem 0.5rem 1.5rem; }
    .hb-lupe-schliessen {
        top: 0.625rem;
        right: 0.625rem;
        min-width: 48px;
        min-height: 48px;
    }
    .hb-lupe-unterschrift { max-width: none; font-size: 0.85rem; }
}

/* ── Rollen & Rechte (Issue #68) ────────────────────────────── */

/*
 * Die Rollenkarten – dieselbe Bauform wie die Benutzerkarten daneben, nur
 * breiter: Auf einer Rollenkarte stehen zwei Listen (wer sie hat, was sie
 * darf), und die brauchen mehr Zeile als Kennung und E-Mail-Adresse.
 */
.rollenkarten {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(26rem, 100%), 1fr));
    gap: 0.9rem;
    margin-bottom: 1.4rem;
}

.rollenkarte {
    display: flex;
    flex-direction: column;
    gap: 0.7rem;
    padding: 1rem 1.1rem;
    background: var(--flaeche-karte);
    border: 1px solid var(--rand);
    border-radius: 10px;
    box-shadow: var(--schatten-zart);
}
.rollenkarte:hover { border-color: var(--rand-akzent); }

.rollenkarte-kopf {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 0.5rem;
}
.rollenkarte-name { margin: 0; font-size: 1rem; }
.rollenkarte-beschreibung {
    margin: 0;
    font-size: 0.88rem;
    color: var(--text-mittel);
}

.rollenkarte-abschnitt h4 {
    margin: 0 0 0.3rem;
    font-size: 0.78rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--text-still);
}
/* Die Zahl gehört zur Überschrift und nicht daneben – sie beantwortet die
   Frage „wie viele" ohne eine zweite Zeile. */
.rollenkarte-zahl {
    display: inline-block;
    min-width: 1.4rem;
    padding: 0 0.35rem;
    border-radius: 999px;
    background: var(--flaeche-sanft);
    color: var(--text-mittel);
    font-size: 0.78rem;
    text-align: center;
    letter-spacing: 0;
}

.rollenkarte-benutzer {
    margin: 0;
    padding-left: 1.1rem;
    font-size: 0.86rem;
    color: var(--text-normal);
}
.rollenkarte-benutzer li { margin-bottom: 0.15rem; }

.rollenkarte-rechte {
    margin: 0;
    padding: 0;
    list-style: none;
    font-size: 0.84rem;
}
.rollenkarte-rechte li {
    display: flex;
    flex-wrap: wrap;
    gap: 0.2rem 0.6rem;
    padding: 0.2rem 0;
    border-bottom: 1px solid var(--rand-zart);
}
.rollenkarte-rechte li:last-child { border-bottom: none; }
.rollenkarte-bereich { flex: 0 0 9rem; color: var(--text-mittel); }
.rollenkarte-verben { flex: 1 1 10rem; color: var(--text-leise); }
/*
 * Der Vermerk „nur Studioansicht" an einer eingeschränkten Zeile (Issue #154).
 * Er steht nur da, wenn eine Oberfläche fehlt – und dann soll er auffallen,
 * ohne wie ein Fehler auszusehen: dieselbe Marke wie anderswo, in Grundfarben
 * des Themas und damit im dunklen Modus ohne eigene Regel.
 */
.rollenkarte-zugang {
    flex: 0 0 auto;
    padding: 0.05rem 0.45rem;
    border-radius: 999px;
    background: var(--flaeche-gewaehlt);
    font-size: 0.75rem;
    font-weight: 600;
    color: var(--text-stark);
}

/* Die Bot-Rechte einer Rolle (Issue #367) – dieselbe Pille wie der
 * Oberflächenvermerk, aber umbrechend: „Gude Bot – Assistenz · Gude Bot –
 * Autopilot" passt auf einem Telefon nicht in eine Zeile. */
.rollenkarte-handlungen {
    flex: 0 1 auto;
    min-width: 0;
    padding: 0.05rem 0.45rem;
    border-radius: 999px;
    background: var(--flaeche-gewaehlt);
    font-size: 0.75rem;
    font-weight: 600;
    color: var(--text-stark);
    overflow-wrap: anywhere;
}

/*
 * Der Aufklapper der Rollenkarte gehört dem Telefon.
 *
 * Am Schreibtisch steht die Rechteliste offen – dort ist sie eine Spalte neben
 * anderen und kein Weg, den man scrollt. Kästchen, Beschriftung und die
 * Kurzfassung sind hier deshalb `display: none`; die Karte sieht aus wie vorher.
 */
.rollen-klapp-schalter,
.rollen-klapp-taste,
.rollenkarte-rechte-summe { display: none; }

.rollenkarte-aktionen {
    display: flex;
    gap: 0.4rem;
    flex-wrap: wrap;
    align-items: center;
    margin-top: auto;
    padding-top: 0.6rem;
    border-top: 1px solid var(--rand-zart);
}

.rollen-fusshinweis { max-width: 62rem; }
.rollen-zuordnung { margin-top: 1.6rem; }
.rollen-zuordnung h3 { font-size: 1rem; margin-bottom: 0.4rem; }

/*
 * Der Kopierbereich unter dem Rollenformular (Issue #204).
 *
 * Derselbe Zuschnitt wie der Löschbereich darunter – und ausdrücklich nicht
 * dessen Farbe: Kopieren nimmt nichts weg, es legt etwas an. Ein roter Kasten
 * wäre hier eine Warnung vor einer harmlosen Handlung, und die Warnfarbe
 * verlöre an der Stelle ihre Bedeutung, an der sie sie braucht.
 */
.rollen-kopierbereich {
    margin-top: 1.6rem;
    padding: 1rem 1.1rem;
    background: var(--flaeche-zart);
    border: 1px solid var(--rand-sanft);
    border-radius: 10px;
}
.rollen-kopierbereich h3 { margin: 0 0 0.4rem; font-size: 1rem; }
.rollen-kopierbereich p {
    margin: 0 0 0.8rem;
    font-size: 0.88rem;
    color: var(--text-leise);
    max-width: 44rem;
}

/*
 * Das Rollenformular ist breiter als die anderen.
 *
 * Wie viel breiter, steht seit Issue #76 nicht mehr hier, sondern einmal je
 * Seite bei `--flucht`: Der Wrapper liest die Klasse dieses Formulars und
 * setzt daraus die Flucht, an der sich auch die Zuordnungsliste und der
 * Löschbereich darunter ausrichten. Vorher stand die Zahl nur an dieser
 * Regel – und die beiden Kacheln darunter waren so breit wie der Wrapper.
 *
 * `width: 100%` sorgt dafür, dass daraus auf schmalen Fenstern nichts wird:
 * `max-width` ist eine Obergrenze, keine Vorgabe.
 */
.formular-rollen { width: 100%; }

/*
 * Die Rechte-Matrix.
 *
 * Ein Raster und keine Tabelle: Auf dem Schreibtisch stehen die vier Aktionen
 * als Spalten, auf dem Telefon rutschen sie unter den Bereichsnamen und werden
 * zu einer umbrechenden Reihe. Dafür genügt es, `grid-template-columns` zu
 * ändern – dasselbe HTML trägt beide Formen, und es gibt keine zweite Matrix
 * für schmale Fenster, die man mitpflegen müsste.
 *
 * `display: contents` an `.rechte-zellen` löst den Zwischenkasten auf dem
 * Schreibtisch auf, damit die Zellen direkt im Raster der Zeile liegen. Auf dem
 * Telefon wird derselbe Kasten wieder sichtbar und zur umbrechenden Reihe.
 *
 * Kein `overflow: hidden` mehr (Issue #204). Es rundete die Ecken, hätte aber
 * den Spaltenkopf am Feststehen gehindert: Ein Vorfahre mit `overflow: hidden`
 * ist ein Scrollbereich, und `position: sticky` bezieht sich auf ihn statt auf
 * das Fenster – der Kopf bewegte sich damit nie. Dieselbe Abwägung und
 * dieselbe Lösung wie bei `.datentabelle`: Die Rundung übernehmen die
 * Randkinder selbst.
 */
.rechte-matrix {
    border: 1px solid var(--rand);
    border-radius: 8px;
}

/*
 * Der Spaltenkopf bleibt beim Scrollen stehen (Issue #204).
 *
 * Die Matrix ist bei vierzehn Bereichen länger als jeder Bildschirm. Ohne
 * diese vier Zeilen verschwindet „Bereich | Sehen | Anlegen | Bearbeiten |
 * Löschen“ nach dem zweiten Bereich, und weiter unten ist an einem Kästchen
 * nicht mehr abzulesen, welche Spalte es trägt. Genau das nennt Martins
 * Ergänzung vom 26.09.2026.
 *
 * `top: var(--kopf-hoehe)` und nicht `top: 0`: Auf dieser Seite scrollt das
 * **Dokument** – es gibt hier keinen `.arbeitsflaeche-scroll` wie auf den
 * Arbeitsansichten. Die Bezugskante ist damit der obere Fensterrand, und dort
 * klebt schon das Hauptmenü. Es ist dieselbe Zahl, die auch das Menü selbst
 * als `min-height` trägt; eine zweite wäre bei der nächsten Kopfänderung
 * falsch. Derselbe Weg wie bei der Wissensliste (`.ki-wissenstabelle thead th`).
 *
 * Über- oder untereinander geraten kann nichts: Das Menü liegt auf
 * `z-index: 100`, dieser Kopf auf 2 – die Zeilen verschwinden unter dem Kopf,
 * der Kopf unter dem Menü. Und weil sein umschließender Block die Matrix ist,
 * hört das Kleben an deren Unterkante von selbst auf: Wer über die Rechte
 * hinausscrollt, hat den Kopf nicht mehr im Bild.
 *
 * Die beiden Rundungen sind die, die vorher das abgeschnittene Überlaufen
 * besorgte – ein Bildpunkt weniger als außen, weil eine Innenrundung ab der
 * Innenkante des Rahmens misst.
 */
.rechte-kopf {
    position: sticky;
    top: var(--kopf-hoehe);
    z-index: 2;
    border-top-left-radius: 7px;
    border-top-right-radius: 7px;
}
.rechte-zeile:last-child {
    border-bottom-left-radius: 7px;
    border-bottom-right-radius: 7px;
}

.rechte-kopf,
.rechte-zeile {
    display: grid;
    grid-template-columns: minmax(0, 1fr) repeat(4, 6.5rem);
    align-items: center;
    gap: 0.4rem;
    padding: 0.55rem 0.8rem;
}

.rechte-kopf {
    background: var(--fuellung);
    color: var(--fuellung-text);
    font-size: 0.78rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
}
.rechte-kopf .rechte-spalte { text-align: center; }
.rechte-kopf .rechte-bereich { color: var(--fuellung-text-leise); }

.rechte-zeile {
    border-top: 1px solid var(--rand-zart);
    /*
     * Der Platz, den der feststehende Spaltenkopf braucht (Issue #204).
     *
     * Nicht Kosmetik, sondern die Tastaturbedienung: Wer sich mit Tab durch
     * die Matrix arbeitet, lässt den Browser zur nächsten Zeile scrollen – und
     * der schiebt sie ohne diesen Abstand genau unter den Kopf, sodass das
     * angesprungene Kästchen verdeckt wäre. Die Zahl ist die Kopfhöhe des
     * Menüs plus eine Zeile für den Spaltenkopf darunter; dasselbe Mittel wie
     * bei `.hb-ueberschrift`.
     */
    scroll-margin-top: calc(var(--kopf-hoehe) + 3rem);
}
.rechte-zeile:nth-child(odd) { background: var(--flaeche-still); }

.rechte-bereich { display: flex; flex-direction: column; gap: 0.1rem; min-width: 0; }
.rechte-bereich-titel { font-weight: 600; color: var(--text-stark); font-size: 0.92rem; }
.rechte-bereich-kurz { font-size: 0.78rem; color: var(--text-leise); }

/*
 * Der Hinweis eines Bereichs mit einer Bedingung (`app/rechte.Bereich.hinweis`).
 *
 * Er steht **unter** der ganzen Zeile und nicht in der ersten Spalte: Was er
 * sagt, gilt für alle vier Häkchen daneben. Seit Martins Abnahme vom
 * 13.09.2026 trägt ihn genau ein Bereich – Guder Draht –, und zwar mit einer
 * gelben Signalkante links, damit er nicht wie eine weitere Kurzbeschreibung
 * aussieht.
 */
.rechte-bereich-hinweis {
    grid-column: 1 / -1;
    margin: 0.5rem 0 0.1rem;
    padding: 0.5rem 0.7rem;
    border-left: 3px solid var(--warnung-text);
    border-radius: 0 6px 6px 0;
    background: var(--flaeche-still);
    font-size: 0.8rem;
    line-height: 1.5;
    color: var(--text-normal);
}

.rechte-bereich-hinweis strong { color: var(--text-stark); }

/*
 * Die Oberflächen eines Bereichs (Issue #154): über welche Seite die Rolle an
 * den Bereich darankommt.
 *
 * Sie stehen wie der Hinweis über die ganze Zeile (`grid-column: 1 / -1`) und
 * nicht in der ersten Spalte – die Frage gehört zu allen vier Häkchen daneben
 * und zu keinem einzelnen. Kein Signalrand wie beim Hinweis: Das hier ist
 * Bedienung und keine Warnung.
 *
 * Nur Tokens, keine festen Farben – damit der dunkle Modus ohne eine zweite
 * Regel gilt.
 */
.rechte-oberflaechen {
    grid-column: 1 / -1;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.2rem 1rem;
    margin: 0.45rem 0 0.1rem;
    padding: 0.45rem 0.7rem;
    border-radius: 6px;
    background: var(--flaeche-zart);
}

/* Über beide Häkchen und nicht neben das erste: Sonst stünde „Zugang über"
   auf einer Höhe mit der Studioansicht und die Redaktionskonsole allein in der
   Zeile darunter – eine Überschrift, die aussieht wie eine Beschriftung. */
.rechte-oberflaechen-titel {
    flex: 0 0 100%;
    font-size: 0.78rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--text-leise);
}

/* Wieder 44 px Tippfläche – dasselbe Maß wie an einer Rechtezelle. */
.rechte-oberflaeche {
    display: flex;
    align-items: center;
    flex: 1 1 20rem;
    gap: 0.45rem;
    min-height: 44px;
    padding: 0 0.4rem;
    border-radius: 6px;
    cursor: pointer;
}
.rechte-oberflaeche:hover { background: var(--flaeche-still); }
.rechte-oberflaeche input[type="checkbox"] {
    width: 1.05rem;
    height: 1.05rem;
    cursor: pointer;
}
.rechte-oberflaeche strong {
    display: block;
    font-size: 0.85rem;
    font-weight: 600;
    color: var(--text-stark);
}
.rechte-oberflaeche small {
    display: block;
    font-size: 0.75rem;
    color: var(--text-leise);
}

.rechte-bereich-hinweis a { white-space: nowrap; font-weight: 600; }

.rechte-zellen { display: contents; }

/*
 * Eine Zelle ist die Tippfläche – nicht das Kästchen darin. 44 px sind das
 * Maß, das dieses Projekt überall für den Finger ansetzt.
 */
.rechte-zelle {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.35rem;
    min-height: 44px;
    border-radius: 6px;
    cursor: pointer;
}
.rechte-zelle:hover { background: var(--flaeche-zart); }
.rechte-zelle input[type="checkbox"] { width: 1.05rem; height: 1.05rem; cursor: pointer; }

/* Auf dem Schreibtisch sagt die Spaltenüberschrift, welches Verb gemeint ist –
   der Text an der Zelle bleibt für Vorlesewerkzeuge da und nur für sie. */
.rechte-verb {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
}

/* Eine Aktion, die es in diesem Bereich nicht gibt: ein Strich, kein Kästchen. */
.rechte-zelle-leer { color: var(--text-still); cursor: default; }
.rechte-zelle-leer:hover { background: none; }

/* Ein festgesetztes Häkchen der Systemrolle. Sichtbar anders, damit niemand
   vergeblich daran klickt – und mit dem Grund im `title`. */
.rechte-zelle-fest { cursor: not-allowed; background: var(--flaeche-gesperrt); }
.rechte-zelle-fest input[type="checkbox"] { cursor: not-allowed; }

.rechte-alles {
    display: flex;
    align-items: center;
    gap: 0.35rem;
    margin-top: 0.2rem;
    font-size: 0.78rem;
    color: var(--text-leise);
    cursor: pointer;
}
.rechte-alles input[type="checkbox"] { width: 0.95rem; height: 0.95rem; }
/*
 * `hidden` an einem Element, dem das Stylesheet ein `display` gibt, wirkt
 * nicht: Eine Autorenregel schlägt die des Browsers, gleich welcher
 * Spezifität. Ohne diese Zeile stünde die Sammelaktion auch ohne JavaScript
 * da – als Kästchen, das niemand einsammelt.
 */
.rechte-alles[hidden] { display: none; }

/*
 * Der Aufklapper eines Rechtebereichs gehört ebenfalls dem Telefon. Am
 * Schreibtisch trägt die Zeile ihre vier Spalten und ist damit fertig.
 */
.rechte-klapp-schalter,
.rechte-klapp-taste { display: none; }

/*
 * Der Laptop. Vier Spalten zu 6,5 rem plus Bereichsname passen ab etwa
 * 900 px bequem; darunter werden die Spalten schmaler, bevor sie ganz
 * umbrechen.
 */
@media (max-width: 900px) {
    .rechte-kopf,
    .rechte-zeile { grid-template-columns: minmax(0, 1fr) repeat(4, 4.6rem); }
    .rechte-bereich-kurz { display: none; }
}

/*
 * Das Telefon. Die Spalten verschwinden, die Aktionen rücken unter den
 * Bereichsnamen und tragen ihre Beschriftung wieder sichtbar – ohne
 * Spaltenkopf wüsste sonst niemand, welches Kästchen welches ist.
 */
@media (max-width: 640px) {
    .rechte-kopf { display: none; }
    .rechte-zeile { grid-template-columns: minmax(0, 1fr); gap: 0.3rem; }
    .rechte-bereich-kurz { display: block; }

    .rechte-zellen {
        display: flex;
        flex-wrap: wrap;
        gap: 0.3rem 0.5rem;
    }
    /*
     * Die Grundbreite ist eine Untergrenze, keine Vorgabe: Die Kästchen füllen
     * die Zeile und brechen um, sobald der Platz nicht mehr reicht. Auf einem
     * 390 px breiten Telefon bleibt der Zellenreihe so wenig Raum, dass jedes
     * für sich steht – das ist dort das richtige Ergebnis, denn ein 44 px
     * hoher Streifen mit lesbarer Beschriftung trifft sich mit dem Daumen. Auf
     * einem breiteren Gerät rücken sie von selbst nebeneinander.
     */
    .rechte-zelle {
        justify-content: flex-start;
        flex: 1 1 7rem;
        padding: 0 0.5rem;
        border: 1px solid var(--rand-zart);
        background: var(--flaeche-karte);
    }
    /*
     * Auf dem Telefon fällt weg, was es nicht gibt.
     *
     * Auf dem Schreibtisch steht in der Spalte ein Strich – dort trägt die
     * Spaltenüberschrift die Bedeutung, und die Zeile bliebe ohne Platzhalter
     * lückenhaft. Hier gibt es keine Spalten: Ein Kästchen mit dem Text
     * „Anlegen: gibt es hier nicht" wäre eine volle Zeile Bildschirm für eine
     * Auskunft, die niemand gesucht hat. Vier Bereiche kennen nur „Sehen“ –
     * ohne diese Regel wären das zwölf Zeilen zusätzliches Scrollen.
     */
    .rechte-zelle-leer { display: none; }
    .rechte-verb {
        position: static;
        width: auto;
        height: auto;
        margin: 0;
        overflow: visible;
        clip-path: none;
        font-size: 0.85rem;
        color: var(--text-normal);
    }
    .rechte-zelle-leer .rechte-verb { color: var(--text-still); }

    .rollenkarte-bereich { flex: 1 1 100%; }
}

/* ── Rollen & Rechte auf dem Telefon: verdichtet ─────────────────────────────
 *
 * Die Nacharbeit zur Abnahme von Issue #68. Was vorher stand, war responsiv:
 * nichts lief über den Rand, jede Tippfläche traf ihre 44 px. Was fehlte, war
 * Verdichtung – dieselbe Unterscheidung, die schon der Mobile-Audit (#35) für
 * Personen und Klärungen getroffen hat.
 *
 * Gemessen bei 375 px am Bestand mit sechs Rollen:
 *
 *   * **Die Rollenübersicht.** Jede Karte schrieb alle Bereiche der Rolle mit
 *     ihren Verben aus. Die Systemrolle allein sind zwölf Zeilen, und
 *     „Bearbeiten“ und „Löschen“ stehen darunter: Wer eine Rolle ändern will,
 *     scrollt zuerst an ihren Rechten vorbei. Genau der Befund, den der Audit
 *     für das Klärungsarchiv beschreibt.
 *   * **Der Rechte-Editor.** Zwölf Bereiche, je bis zu vier Kästchen in voller
 *     Zeilenbreite und darüber „alles in diesem Bereich“ – eine Maske, durch
 *     die man nur noch fährt.
 *   * **Der Kopf der Maske.** `.seite-kopf` ist `space-between`: Bei „Rolle
 *     bearbeiten: <langer Name>“ rutschte der Rückweg unter die Überschrift,
 *     bei „Neue Rolle“ blieb er daneben stehen. Dieselbe Maske, zwei Bilder.
 *
 * Das Mittel ist überall dasselbe und in diesem Projekt eingeführt: ein
 * Kontrollkästchen mit Beschriftung, kein Skript – wie beim Hauptmenü (#26),
 * bei „Ansicht“ im Sendeplan (#44) und beim Klärungsarchiv (#46, mit dem
 * Bereich entfallen). Zugeklappt
 * ist der Ausgangszustand; was zugeklappt ist, bleibt beziffert. An der
 * Rechte-Logik ändert sich nichts: Ein zugeklappter Bereich ist unsichtbar,
 * und ein unsichtbares Kontrollkästchen wird mit abgeschickt.
 *
 * Der Schreibtisch bleibt außen vor – alle Regeln stehen in dieser
 * Medienabfrage, und die Kästchen selbst sind außerhalb `display: none`.
 */
@media (max-width: 640px) {

    /* ── 1) Die Rollenübersicht ────────────────────────────────────────── */

    /* Weniger Polster, dieselbe Karte – die Dichte der Listen aus #45. */
    .rollenkarte {
        gap: 0.55rem;
        padding: 0.8rem 0.75rem;
    }

    .rollenkarte-rechteblock { position: relative; }

    /*
     * Der Rollenname ist freier Text und kann so lang sein, wie ein Admin ihn
     * tippt – „Programmredaktion mit Sendungspflege“ ragte bei 320 px zwei
     * Pixel über den Rand hinaus, und die Seite bekam eine waagerechte
     * Bildlaufleiste für zwei Pixel. Eine Marke ist sonst kurz und darf
     * deshalb `white-space: nowrap` tragen; diese eine trägt einen Namen und
     * bricht auf dem Telefon um.
     */
    .rollenkarte-name .marke {
        white-space: normal;
        overflow-wrap: anywhere;
    }

    /*
     * Überschrift und Schalter in einer Zeile: Links steht, wie viele Rechte
     * die Rolle hat, rechts kommt man an sie heran.
     */
    .rollenkarte-rechte-kopf {
        display: flex;
        align-items: center;
        justify-content: space-between;
        flex-wrap: wrap;
        gap: 0.3rem 0.6rem;
    }
    .rollenkarte-rechte-kopf h4 { margin: 0; }

    /*
     * Sichtbar ausgeblendet, für die Tastatur erreichbar – `display: block`
     * steht hier ausdrücklich, weil das Kästchen außerhalb dieser Abfrage
     * `display: none` ist und damit auch der Tastatur fehlte.
     */
    .rollen-klapp-schalter {
        display: block;
        position: absolute;
        width: 1px;
        height: 1px;
        margin: -1px;
        padding: 0;
        border: 0;
        overflow: hidden;
        clip-path: inset(50%);
        white-space: nowrap;
    }
    .rollen-klapp-taste {
        display: inline-flex;
        align-items: center;
        gap: 0.4rem;
        /* Ein Tippziel, kein Textlink – dasselbe Maß wie überall seit #43. */
        min-height: 44px;
        padding: 0 0.7rem;
        border: 1px solid var(--rand-stark);
        border-radius: 6px;
        background: var(--flaeche-zart);
        font-size: 0.85rem;
        font-weight: 600;
        color: var(--text-normal);
        cursor: pointer;
        /* Sonst markiert ein zweites Antippen die Beschriftung. */
        -webkit-user-select: none;
        user-select: none;
    }
    .rollen-klapp-schalter:focus-visible ~ .rollenkarte-rechte-kopf .rollen-klapp-taste {
        outline: 2px solid var(--kontur);
        outline-offset: 2px;
    }
    /*
     * Das Dreieck als Ränder eines leeren Kastens statt als Schriftzeichen –
     * derselbe Grund wie bei „Ansicht“: „▸“ fehlt in manchen Systemschriften
     * und wäre dort ein Ersatzkästchen.
     */
    .rollen-klapp-pfeil {
        width: 0;
        height: 0;
        border: 4px solid transparent;
        border-bottom: 0;
        border-top-color: currentColor;
    }
    .rollen-klapp-schalter:checked ~ .rollenkarte-rechte-kopf .rollen-klapp-pfeil {
        border-top: 0;
        border-bottom: 4px solid currentColor;
    }

    /* Die Beschriftung sagt, was der nächste Tipp tut. */
    .rollen-klapp-zu { display: none; }
    .rollen-klapp-schalter:checked ~ .rollenkarte-rechte-kopf .rollen-klapp-auf { display: none; }
    .rollen-klapp-schalter:checked ~ .rollenkarte-rechte-kopf .rollen-klapp-zu { display: inline; }

    /*
     * Zugeklappt ist der Ausgangszustand. Statt der ausgeschriebenen Liste
     * steht dort die Kurzfassung: wie viele Rechte, in welchen Bereichen. Das
     * ist die Auskunft, für die man eine Übersicht öffnet – die Verben je
     * Bereich sind die Frage danach.
     */
    .rollenkarte-rechte-summe {
        display: block;
        margin: 0;
        font-size: 0.84rem;
        line-height: 1.4;
        color: var(--text-leise);
    }
    .rollenkarte-rechte-voll { display: none; }
    .rollen-klapp-schalter:checked ~ .rollenkarte-rechte-summe { display: none; }
    .rollen-klapp-schalter:checked ~ .rollenkarte-rechte-voll { display: block; }

    /*
     * „Bearbeiten“ und „Löschen“ teilen sich die Zeile. Beide tragen ihre
     * Beschriftung vollständig, beide sind 44 px hoch, und mit zugeklappten
     * Rechten stehen sie wieder in Daumenreichweite unter dem Kartenkopf.
     * `.rollenkarte-aktionen form` ist der Kasten um „Löschen“ – ohne diese
     * Zeile wüchse der Knopf nicht mit.
     */
    .rollenkarte-aktionen { gap: 0.5rem; }
    .rollenkarte-aktionen form { flex: 1 1 8rem; display: flex; }
    .rollenkarte-aktionen > .schaltflaeche,
    .rollenkarte-aktionen form > .schaltflaeche {
        flex: 1 1 8rem;
        min-height: 44px;
        white-space: nowrap;
    }

    /* ── 2) Der Rechte-Editor ──────────────────────────────────────────── */

    /*
     * Bezugspunkt für das ausgeblendete Kästchen der Zeile. Ohne ihn hinge es
     * am nächsten positionierten Vorfahren und könnte die Seite verbreitern.
     */
    .rechte-zeile { position: relative; }

    .rechte-klapp-schalter {
        display: block;
        position: absolute;
        width: 1px;
        height: 1px;
        margin: -1px;
        padding: 0;
        border: 0;
        overflow: hidden;
        clip-path: inset(50%);
        white-space: nowrap;
    }
    .rechte-klapp-taste {
        display: inline-flex;
        align-items: center;
        align-self: flex-start;
        gap: 0.4rem;
        min-height: 44px;
        margin-top: 0.15rem;
        padding: 0 0.7rem;
        border: 1px solid var(--rand-stark);
        border-radius: 6px;
        background: var(--flaeche-zart);
        font-size: 0.85rem;
        font-weight: 600;
        color: var(--text-normal);
        cursor: pointer;
        -webkit-user-select: none;
        user-select: none;
    }
    .rechte-klapp-schalter:focus-visible ~ .rechte-bereich .rechte-klapp-taste {
        outline: 2px solid var(--kontur);
        outline-offset: 2px;
    }
    .rechte-klapp-pfeil {
        width: 0;
        height: 0;
        border: 4px solid transparent;
        border-bottom: 0;
        border-top-color: currentColor;
    }
    .rechte-klapp-schalter:checked ~ .rechte-bereich .rechte-klapp-pfeil {
        border-top: 0;
        border-bottom: 4px solid currentColor;
    }

    /*
     * Der Stand des zugeklappten Bereichs: „2 von 4“. Er kommt gezählt vom
     * Server und wird vom Skript fortgeschrieben. Dass überhaupt etwas gesetzt
     * ist, sagt zusätzlich die Farbe – und die hängt an `:has()` und damit am
     * Kästchen selbst, nicht am Skript: Ohne JavaScript stimmt sie trotzdem.
     */
    .rechte-klapp-stand {
        padding: 0.05rem 0.4rem;
        border-radius: 999px;
        background: var(--flaeche-sanft);
        font-size: 0.78rem;
        font-weight: 600;
        color: var(--text-leise);
    }
    .rechte-zeile:has(input[name="recht"]:checked) .rechte-klapp-stand {
        background: var(--flaeche-gewaehlt);
        color: var(--text-stark);
    }

    /*
     * Zugeklappt ist der Ausgangszustand: Sichtbar sind Bereichsname, Kurztext
     * und der Stand. „alles in diesem Bereich“ und die einzelnen Kästchen
     * kommen auf Antippen – die Sammelaktion gehört zu den Rechten, die sie
     * setzt, und stünde darüber allein in der Luft.
     */
    .rechte-klapp-schalter:not(:checked) ~ .rechte-zellen { display: none; }
    .rechte-klapp-schalter:not(:checked) ~ .rechte-bereich .rechte-alles { display: none; }
    /*
     * Die Oberflächen gehören zu den Häkchen darüber und klappen mit ihnen
     * weg (Issue #154). Zugeklappt ist auch hier nur **unsichtbar**: Ein
     * verborgenes Kontrollkästchen wird mitgeschickt, an der Klappe hängt
     * keine Freigabe.
     */
    .rechte-klapp-schalter:not(:checked) ~ .rechte-oberflaechen { display: none; }
    /* Aufgeklappt untereinander – nebeneinander bliebe vom Satz nichts übrig. */
    .rechte-oberflaechen { flex-direction: column; align-items: stretch; gap: 0.1rem; }
    /* Aufgeklappt ist die Sammelaktion selbst ein Tippziel. */
    .rechte-alles {
        min-height: 44px;
        font-size: 0.85rem;
    }

    /* ── 3) Der Kopf der Maske und ihre Abstände ───────────────────────── */

    /*
     * Überschrift oben, Rückweg darunter – unabhängig davon, wie lang der
     * Rollenname ist. Der Rückweg bleibt so breit wie seine Beschriftung: Eine
     * Schaltfläche über die volle Zeile läse sich als das Wichtigste der Seite,
     * und das ist sie nicht.
     */
    .seite-kopf-formular {
        flex-direction: column;
        align-items: stretch;
        gap: 0.5rem;
        margin-bottom: 1rem;
    }
    .seite-kopf-formular h2 { font-size: 1.2rem; }
    .seite-kopf-rueckweg {
        align-self: flex-start;
        padding: 0 0.7rem;
        font-size: 0.85rem;
    }

    /*
     * Mehr Nutzfläche im Rollenformular: Der Rahmen des Abschnitts darf
     * schmaler polstern als anderswo, weil die Matrix darin ihre eigenen
     * Kästchen mit eigenem Rand trägt – zwei Rahmen mit je 0,9 rem sind auf
     * 320 px zusammen fast ein Fünftel der Breite.
     *
     * `min-width: 0` steht aus einem anderen Grund dabei: Ein `fieldset` ist
     * in der Voreinstellung des Browsers mindestens so breit wie sein längstes
     * unteilbares Wort. In „Rolle bearbeiten“ ist dieses Wort der technische
     * Schlüssel im Hinweis unter dem Namensfeld, und bei einem langen
     * Rollennamen schob er die Maske bei 320 px um 27 px nach rechts. Zusammen
     * mit der Zeile darunter heißt das: Der Rahmen darf schmaler werden als
     * sein Inhalt, und der Schlüssel bricht um, statt zu schieben.
     */
    .formular-rollen .formular-abschnitt {
        padding: 0.8rem 0.6rem;
        min-width: 0;
    }
    .formular-rollen code { overflow-wrap: anywhere; }
    .formular-rollen .rechte-kopf,
    .formular-rollen .rechte-zeile { padding: 0.5rem 0.55rem; }
}


/* ══ Redaktionsaufgaben (Issue #71) ══════════════════════════════════════
 *
 * Ein Bereich, der täglich benutzt wird – und zwar von unterwegs genauso wie
 * am Schreibtisch. Deshalb ist hier alles eine **Karte** und keine Tabelle:
 * Was der Mobile-Audit #35 für die übrigen Listen nachträglich umgebaut hat,
 * fängt hier gar nicht erst als Tabelle an.
 *
 * Kein einziger fester Farbwert. Jede Farbe kommt aus den Tokens ganz oben und
 * hat dadurch im dunklen Modus ihre Entsprechung, ohne dass hier eine zweite
 * Regel stünde. Die Status- und Prioritätsmarken benutzen dieselben Paare wie
 * die vorhandenen Marken der Anwendung – ein neues Farbschema für ein neues
 * Modul wäre genau die Zersplitterung, die Issue #65 beendet hat.
 */

/* Zwei bis drei Knöpfe im Seitenkopf. `flex-wrap`, weil auf einem Telefon
   sonst der zweite aus dem Bild liefe. */
.kopf-aktionen {
    display: flex;
    gap: 0.5rem;
    flex-wrap: wrap;
    align-items: center;
}

/* Der Wechsel zwischen „Redaktion gesamt" und „Meine Aufgaben" (Issue #626).
   Zwei gewöhnliche Schaltflächen, die gewählte gefüllt und mit
   `aria-current` – dieselbe Stelle in beiden Ansichten, damit beim Umschalten
   nichts springt. Die Auskunft trägt der Text, nicht die Farbe allein. */
.aufgaben-ansichten {
    display: inline-flex;
    gap: 0.35rem;
    flex-wrap: wrap;
}

/* Der Satz zu einer Auswahl, die nichts finden kann (Issue #626) – eine
   Erklärung unter der Trefferzahl, kein Fehler. */
.treffer-widerspruch { margin-top: 0.4rem; color: var(--text-normal); }

/* ── Die drei Zahlen über der Liste ─────────────────────────────────────── */
.aufgaben-bilanz {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
    gap: 0.75rem;
    margin-bottom: 1.2rem;
}

.bilanz-kachel {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
    padding: 0.8rem 1rem;
    background: var(--flaeche-karte);
    border: 1px solid var(--rand);
    border-radius: 8px;
    text-decoration: none;
    box-shadow: var(--schatten-zart);
}
.bilanz-kachel:hover { border-color: var(--rand-stark); }
.bilanz-zahl { font-size: 1.5rem; font-weight: 700; color: var(--text-stark); }
.bilanz-text { font-size: 0.82rem; color: var(--text-leise); }
.bilanz-warnung .bilanz-zahl { color: var(--fehler-text); }
.bilanz-wartend .bilanz-zahl { color: var(--violett-text); }

/* Die Aufteilung des Gesamtbestands, eine Zeile unter seiner Beschriftung. Sie
   beantwortet in der Kachel selbst, wo die Aufgaben stecken, die die gefilterte
   Liste nicht zeigt (Issue #189). Dieselbe Farbe wie die Beschriftung darüber –
   eine dritte Abstufung wäre im dunklen Modus die erste, die durchfällt. */
.bilanz-aufteilung {
    font-size: 0.78rem;
    line-height: 1.45;
    color: var(--text-leise);
}

/* ── Gesamtbestand und Trefferzahl auseinanderhalten (Issue #189) ────────── */
/* Überschrift und erklärender Satz gehören zusammen und tragen den Abstand
   nach unten gemeinsam – deshalb steht er an der Gruppe und nicht an der
   Überschrift, die ihn sonst zwischen sich und den Satz legte. */
.treffer-kopf { margin-bottom: 1.2rem; }
.treffer-kopf .block-ueberschrift { margin-bottom: 0.6rem; }
.treffer-hinweis {
    margin: 0;
    font-size: 0.85rem;
    line-height: 1.5;
    color: var(--text-leise);
}
.treffer-hinweis a { color: var(--text-stark); }

/* ── Schnelleingabe: eine Aufgabe in drei Feldern ───────────────────────── */
.schnelleingabe {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: 0.75rem;
    padding: 1rem 1.2rem;
    margin-bottom: 1.2rem;
    background: var(--flaeche-karte);
    border: 1px solid var(--rand);
    border-radius: 8px;
    box-shadow: var(--schatten-zart);
}

.schnelleingabe-klein { margin-top: 1rem; margin-bottom: 0; }

.schnell-feld { display: flex; flex-direction: column; gap: 0.3rem; min-width: 0; }
.schnell-titel { flex: 1 1 16rem; }

.schnell-feld label {
    font-size: 0.8rem;
    font-weight: 600;
    color: var(--text-normal);
}

.schnell-feld input,
.schnell-feld select {
    padding: 0.5rem 0.7rem;
    border: 1px solid var(--rand-stark);
    border-radius: 6px;
    font-size: 0.92rem;
    font-family: inherit;
    background: var(--flaeche-eingabe);
    color: var(--text-normal);
}
.schnell-feld input:focus,
.schnell-feld select:focus { outline: none; border-color: var(--kontur); }

/* Der Knopf steht auf der Grundlinie der Felder und nicht unter einer leeren
   Beschriftung – deshalb bekommt er den Platz der Beschriftung als Polster. */
.schnell-knopf { justify-content: flex-end; }

/* ── Die Filterleiste ───────────────────────────────────────────────────── */
.aufgaben-filter {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr));
    gap: 0.75rem;
    padding: 1rem 1.2rem;
    margin-bottom: 1.2rem;
    background: var(--flaeche-still);
    border: 1px solid var(--rand);
    border-radius: 8px;
}

.filter-feld { display: flex; flex-direction: column; gap: 0.3rem; min-width: 0; }
.filter-suche { grid-column: 1 / -1; }

.filter-feld label {
    font-size: 0.78rem;
    font-weight: 600;
    color: var(--text-leise);
    text-transform: uppercase;
    letter-spacing: 0.03em;
}

.filter-feld input,
.filter-feld select {
    padding: 0.45rem 0.6rem;
    border: 1px solid var(--rand-stark);
    border-radius: 6px;
    font-size: 0.9rem;
    font-family: inherit;
    background: var(--flaeche-eingabe);
    color: var(--text-normal);
    min-width: 0;
}
.filter-feld input:focus,
.filter-feld select:focus { outline: none; border-color: var(--kontur); }

.filter-knoepfe {
    flex-direction: row;
    align-items: flex-end;
    gap: 0.5rem;
    flex-wrap: wrap;
}

/*
 * „Filtern" gibt es nur ohne JavaScript (Issue #207).
 *
 * Seitdem wirkt jede Auswahl sofort; der Knopf hätte mit Skript nichts mehr zu
 * tun. Ausgeblendet wird er über die Klasse am `<html>` und damit **vor** dem
 * ersten Zeichnen – dieselbe Bauart wie `.datum-los` und `.skala-los` im
 * Sendeplan, und aus demselben Grund: Ein Skript am Seitenende zeigte ihn erst
 * und nähme ihn dann weg, und in diesem Moment rutschte die Zeile.
 *
 * Eine eigene Regel und kein dritter Selektor an der dortigen: Der Knopf gehört
 * zu den Aufgaben und steht deshalb bei ihnen.
 */
.js .filter-los { display: none; }

/*
 * „Sortieren nach" und „Reihenfolge" (Issue #601).
 *
 * In der Gesamtübersicht sind es zwei Felder der Filterleiste und sehen aus wie
 * ihre Nachbarn. „Meine Aufgaben" und die Checkliste einer Aufgabe haben keine
 * Filterleiste; dort tragen die Felder eine eigene, ruhigere Leiste in denselben
 * Maßen. Der Stand darunter sagt in Worten, was gilt – Farbe trägt hier nichts.
 */
.aufgaben-sortierung {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
    gap: 0.75rem;
    align-items: end;
    padding: 0.8rem 1.2rem;
    margin-bottom: 1rem;
    background: var(--flaeche-still);
    border: 1px solid var(--rand);
    border-radius: 8px;
}
.aufgaben-sortierung .filter-knoepfe { align-self: end; }
.sortier-stand {
    margin: 0.3rem 0 0;
    font-size: 0.85rem;
    line-height: 1.5;
    color: var(--text-leise);
}
.sortier-stand strong { color: var(--text-stark); font-weight: 600; }
.aufgaben-sortierung .sortier-stand { grid-column: 1 / -1; margin: 0; }

/* ── Die Aufgabenkarte ──────────────────────────────────────────────────── */
.aufgaben-liste { display: flex; flex-direction: column; gap: 0.75rem; }
.aufgaben-liste-kinder { margin-bottom: 0.5rem; }

/*
 * Die Liste arbeitet gerade (Issue #207).
 *
 * Seit die Filter sofort wirken, wird die Liste geholt, während die Seite steht.
 * Zwischen Klick und neuer Liste liegt eine Netzstrecke, und ohne ein Zeichen
 * dafür sieht eine Liste, die noch die alte Auswahl zeigt, wie eine aus, die
 * nicht reagiert hat.
 *
 * Nur die Deckkraft und ausdrücklich kein Balken, kein Ring und kein Übergang:
 * Was hier gezeigt wird, dauert Millisekunden, und eine Bewegung an einer Liste,
 * die sich gleich austauscht, wäre Unruhe statt Auskunft. Gehängt wird es an
 * `aria-busy` und nicht an eine eigene Klasse – dasselbe Attribut, das ein
 * Vorleseprogramm ohnehin auswertet.
 */
.aufgaben-liste[aria-busy="true"] { opacity: 0.55; }

/*
 * Die Meldung zu einer Statusaktion – an der Karte, nicht am Seitenanfang
 * (Issue #207).
 *
 * Sie steht dort, weil die Seite stehen bleibt: Wer weit unten in einer langen
 * Liste arbeitet, sähe eine Meldung oben nie. Gezeichnet wird sie von `.meldung`
 * wie jede andere – hier steht nur, dass sie in einer Karte keinen zweiten
 * Abstand nach unten braucht; den trägt dort die Karte selbst.
 */
.aufgabe-meldung { margin-bottom: 0; }

.aufgabe-karte {
    background: var(--flaeche-karte);
    border: 1px solid var(--rand);
    border-left: 4px solid var(--rand-stark);
    border-radius: 8px;
    padding: 0.9rem 1.1rem;
    box-shadow: var(--schatten-zart);
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}

/*
 * Die farbige Kante links ist die eigentliche Auskunft der Liste: Sie sagt
 * ohne Lesen, ob etwas wartet, brennt oder erledigt ist. Deshalb hängt sie am
 * Status und nicht an der Priorität – eine dringende Aufgabe, die längst
 * erledigt ist, muss nicht mehr leuchten.
 */
.aufgabe-status-offen       { border-left-color: var(--warnung-kraeftig); }
.aufgabe-status-in_arbeit   { border-left-color: var(--hinweis-kante); }
.aufgabe-status-rueckfrage  { border-left-color: var(--voraussetzung-kante); }
.aufgabe-status-wartet      { border-left-color: var(--info-kante); }
.aufgabe-status-erledigt    { border-left-color: var(--erfolg-kraeftig); }
.aufgabe-status-abgebrochen { border-left-color: var(--rand-still); }
.aufgabe-ueberfaellig       { border-left-color: var(--fehler-kraeftig); }

.aufgabe-status-erledigt .aufgabe-titel a,
.aufgabe-status-abgebrochen .aufgabe-titel a { color: var(--text-leise); }

.aufgabe-kopf {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem;
}

.aufgabe-titel {
    font-size: 1rem;
    font-weight: 600;
    margin: 0;
    flex: 1 1 12rem;
    min-width: 0;
}
.aufgabe-titel a { color: var(--text-stark); text-decoration: none; }
.aufgabe-titel a:hover { text-decoration: underline; }

/* Die Statusmarke. Dieselben Farbpaare wie bei den vorhandenen Marken –
   ein eigenes Schema für dieses Modul gäbe es keinen Grund. */
.aufgabe-marke-offen       { background: var(--warnung-flaeche); color: var(--warnung-text); }
.aufgabe-marke-in_arbeit   { background: var(--blau-flaeche); color: var(--blau-text); }
.aufgabe-marke-rueckfrage  { background: var(--violett-flaeche); color: var(--violett-text); }
.aufgabe-marke-wartet      { background: var(--info-flaeche); color: var(--info-text); }
.aufgabe-marke-erledigt    { background: var(--erfolg-flaeche); color: var(--erfolg-text); }
.aufgabe-marke-abgebrochen { background: var(--flaeche-sanft); color: var(--text-leise); }

.aufgabe-prio-dringend { background: var(--fehler-flaeche); color: var(--fehler-text); }
.aufgabe-prio-hoch     { background: var(--warnung-flaeche-zart); color: var(--warnung-text-tief); }
.aufgabe-prio-normal   { background: var(--flaeche-sanft); color: var(--text-leise); }
.aufgabe-prio-niedrig  { background: var(--flaeche-sanft); color: var(--text-leise); }

.aufgabe-daten {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem 1rem;
    font-size: 0.85rem;
    color: var(--text-mittel);
}

.aufgabe-frist { font-variant-numeric: tabular-nums; }

/* Die kleine Beschriftung vor einem Wert auf der Karte – „Frist", „Zuständig".
   Aus demselben Grund ein Wort und kein Symbol wie in der Historie. */
/* `--text-leise` und nicht `--text-still`: Der stillere Ton kommt im hellen
   Modus nur auf 2,5:1 und liegt damit unter AA. Für eine Beschriftung, die
   sagt, was der Wert daneben bedeutet, ist das zu wenig. */
.daten-marke {
    color: var(--text-leise);
    font-size: 0.72rem;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    margin-right: 0.15rem;
}
.aufgabe-frist-spaet .daten-marke,
.aufgabe-frist-heute .daten-marke { color: inherit; }
.aufgabe-frist-spaet { color: var(--fehler-text); font-weight: 600; }
.aufgabe-frist-heute { color: var(--warnung-mittel); font-weight: 600; }

.aufgabe-einordnung {
    display: flex;
    flex-wrap: wrap;
    gap: 0.35rem 0.5rem;
    font-size: 0.8rem;
}

/* ── Die Karte in zwei Bahnen (Issue #211) ──────────────────────────────────
 *
 * Links die Arbeitssteuerung, rechts der fachliche Kontext – dieselbe Bauart
 * wie der Kopf einer geöffneten Aufgabe (Issue #235), und aus denselben
 * Gründen. Martins Befund war, dass die Karte am Schreibtisch rechts eine halbe
 * Breite leer stehen ließ, während die Beschreibung erst nach dem Öffnen zu
 * sehen war.
 *
 * Flexbox mit `wrap` und ausdrücklich keine Schablone mit zwei festen Spalten:
 *
 *   * **Fehlt die rechte Bahn, gibt es sie nicht.** Die Vorlage erzeugt sie
 *     ohne Beschreibung und ohne Programmbezug gar nicht, und die linke nimmt
 *     die ganze Breite. Eine Rasterspalte stünde leer da und die Karte wäre
 *     breiter beschriftet als sie Inhalt hat.
 *   * **Der Umbruch hängt am Platz und nicht an einer Gerätebreite.** Reichen
 *     die 20 rem je Bahn samt Abstand nicht mehr, legen sich die Bahnen
 *     untereinander – auf dem Tablet wie auf dem Telefon. Das kostet **keine**
 *     weitere Medienabfrage, und es gibt keine Breite, bei der zwei Bahnen zu
 *     je 10 rem nebeneinander stehen und beide unlesbar sind.
 *   * **20 rem und nicht die 26 rem der Detailseite.** Die Karte steht in einer
 *     Liste und trägt links nur Zeilen, keine Beschreibungsliste; mit 26 rem
 *     wären die Bahnen schon auf dem Tablet wieder eins, und genau dort soll
 *     die Beschreibung laut Auftrag „weiterhin sichtbar, aber kompakt" neben
 *     den Daten stehen.
 *   * **Die Höhe folgt dem Inhalt.** `align-items: flex-start` lässt eine kurze
 *     Beschreibung kurz bleiben, statt sie auf die Höhe der linken Bahn zu
 *     ziehen – „keine übermäßig hohe Karte nur wegen kurzer Texte".
 *
 * Die Knopfzeile liegt außerhalb dieses Behälters und läuft unter beiden Bahnen
 * quer. Das ist die Zusage „oben der Stand, unten die Aktionen" – und zugleich
 * die Reihenfolge, die der Auftrag für das Telefon verlangt: Daten,
 * Beschreibung, Aktionen.
 */
.aufgabe-spalten {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    gap: 0.5rem 1.6rem;
}
.aufgabe-bahn,
.aufgabe-kontext {
    flex: 1 1 20rem;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}
.aufgabe-kontext { gap: 0.4rem; }
.aufgabe-kontext-bezug { margin: 0; font-size: 0.8rem; }

/* Die Vorschau bleibt kompakt, der vollständige Text ist darin scrollbar.
 * Vier Textzeilen plus Innenabstand, mobil zwei. Kein Line-Clamp: Er würde
 * den restlichen Text auch innerhalb eines Scrollbereichs abschneiden.
 * Der normale Scrollübergang zur Seite bleibt erhalten; kein touch-action
 * oder overscroll-behavior hält die Touch-Geste am Rand der Vorschau fest.
 * Lange Wörter umbrechen, damit ausschließlich vertikal gescrollt wird.
 */
.aufgabe-vorschau {
    margin: 0;
    font-size: 0.85rem;
    line-height: 1.5;
    color: var(--text-lesetext);
    background: var(--flaeche-still);
    border-radius: 6px;
    padding: 0.5rem 0.7rem;
    overflow-wrap: anywhere;
    max-height: calc(4 * 1.5 * 0.85rem + 1rem);
    overflow-y: auto;
    overflow-x: hidden;
}

.aufgabe-vorschau:focus-visible {
    outline: 2px solid var(--kontur);
    outline-offset: 2px;
}

/* ── Die Metazeile der Karte (Martins Ergänzung vom 26.09.2026) ─────────────
 *
 * „Ruhige, kompakte Metazeile … keine Badge-Flut" steht im Auftrag, und das ist
 * hier wörtlich genommen: keine Fläche, kein Rahmen, kein Radius, nur Text in
 * `--text-leise` – derselbe Ton wie `.anhang-herkunft`, wo dieselbe Sorte
 * Auskunft steht („von Mona am 26.09.2026").
 *
 * **Kein Trennzeichen zwischen den beiden Teilen**, und das ist eine Korrektur:
 * Ein `·` als `::before` am zweiten Teil sah am Schreibtisch gut aus und stand
 * auf 320 px als führendes Zeichen am Anfang der zweiten Zeile – ein
 * Aufzählungspunkt, der nichts aufzählt. Getrennt wird deshalb wie in
 * `.aufgabe-daten` eine Zeile darüber: über den Abstand. Der ist beim Umbruch
 * einfach weg.
 *
 * Die Zahlen bekommen `tabular-nums` aus demselben Grund wie die Frist darüber:
 * In einer Liste von zwanzig Karten stehen die Uhrzeiten dann untereinander.
 */
.aufgabe-meta {
    margin: 0.15rem 0 0;
    display: flex;
    flex-wrap: wrap;
    gap: 0.1rem 1rem;
    font-size: 0.78rem;
    color: var(--text-leise);
    font-variant-numeric: tabular-nums;
}

.aufgabe-bezug,
.aufgabe-kategorie,
.aufgabe-tag {
    display: inline-block;
    padding: 0.1rem 0.5rem;
    border-radius: 999px;
    background: var(--flaeche-sanft);
    color: var(--text-mittel);
}
.aufgabe-bezug { background: var(--info-flaeche-zart); color: var(--info-text-tief); }
.aufgabe-bezug a { color: var(--info-text-tief); }
.aufgabe-bezug-fehlt { background: var(--gefahr-flaeche-zart); color: var(--fehler-text); }
.aufgabe-tag { background: var(--flaeche-gewaehlt); color: var(--text-mittel); }

/*
 * Kategorie und Schlagwort auseinanderhalten (Issue #211).
 *
 * Beide waren graue Pillen in zwei Grautönen – technisch unterscheidbar, für
 * Martin nicht. Die **Kategorie** bekommt deshalb einen kleinen Punkt im Rot
 * des Hauses (`--akzent`), das **Schlagwort** bleibt, was es war.
 *
 * Ein Punkt und keine rote Fläche, kein roter Rand und keine rote Schrift, und
 * das ist der Kern der Vorgabe „keine Farbe verwenden, die wie
 * Fehler/Überfälligkeit wirkt": Eine überfällige Frist auf derselben Karte
 * färbt **Text** rot (`.aufgabe-frist-spaet`), der gelöschte Knopf eine Kante.
 * Ein 6-px-Punkt vor einem Wort kann mit keinem von beiden verwechselt werden
 * und trägt die Marke trotzdem sichtbar.
 *
 * `--akzent` und nicht `--fehler-*`: Das ist das Rot der Marke, es ist in
 * beiden Modi derselbe Wert, und es hebt sich auf dem hellen wie auf dem
 * dunklen Kartengrund ab. Die Schrift bleibt in `--text-normal`, also eine
 * Spur kräftiger als die `--text-mittel` des Schlagworts – zwei Signale, beide
 * leise.
 *
 * `::before` und nicht ein Zeichen im HTML: Ein Punkt im Text stünde auch in
 * jeder Vorleseausgabe, in jedem Suchtreffer und in jedem Export.
 */
.aufgabe-kategorie { color: var(--text-normal); }
.aufgabe-kategorie::before {
    content: "";
    display: inline-block;
    width: 0.375rem;
    height: 0.375rem;
    border-radius: 50%;
    background: var(--akzent);
    margin-right: 0.35rem;
    /* Hebt den Punkt auf die Mitte der Kleinbuchstaben – auf der Grundlinie
       säße er unter dem Wort statt daneben. */
    vertical-align: 0.1em;
}

.aufgabe-aktionen {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
    align-items: center;
}
.aufgabe-aktionen form { display: inline; }
.aufgabe-aktionen .schaltflaeche:focus-visible {
    outline: 2px solid var(--kontur);
    outline-offset: 2px;
}

/*
 * „Löschen" auf der Karte (Issue #208).
 *
 * Der Auftrag verlangt beides auf einmal: „visuell eindeutig als destruktive
 * Aktion erkennbar, ohne die Karten mit Warnoptik zu überladen". Zwanzig
 * Karten mit je einem ausgefüllten roten Knopf wären eine Liste, die wie eine
 * Fehlermeldung aussieht – und in der man den einen Knopf, der wirklich
 * gefährlich ist, gerade nicht mehr sieht.
 *
 * Deshalb trägt hier die **Schrift** die Farbe und nicht die Fläche: rot auf
 * dem Kartengrund, mit einer zarten Kante, die ihn als Knopf ausweist. Erst
 * bei Berührung und Tastaturfokus wird daraus die volle Gefahrfläche – also
 * genau in dem Augenblick, in dem er angefasst wird. `--fehler-text` und
 * `--fehler-rand` sind dieselben Marken, aus denen auch die roten Kästen des
 * Hauses entstehen; im dunklen Modus wechseln sie mit.
 *
 * `margin-left: auto` schiebt ihn ans Ende der Zeile, weg von „Erledigt" und
 * „Öffnen". Auf 320 px hat die Zeile keinen Platz mehr übrig – dort wirkt der
 * Abstand nicht, und der Knopf rutscht in die nächste Zeile. Auch das ist der
 * gewünschte Abstand zu den gewöhnlichen Aktionen.
 */
.aufgabe-aktionen .aufgabe-loeschform { margin-left: auto; }
.aufgabe-loeschen {
    background: transparent;
    color: var(--fehler-text);
    border: 1px solid var(--fehler-rand);
    /* Die Kante kostet oben und unten je ein Pixel. Ohne diesen Ausgleich
       stünde der Knopf zwei Pixel höher als „Erledigt" daneben – auf einer
       Karte mit drei Knöpfen nebeneinander fällt genau das auf. */
    padding: calc(0.25rem - 1px) calc(0.6rem - 1px);
}
.aufgabe-loeschen:hover,
.aufgabe-loeschen:focus-visible {
    background: var(--fehler-flaeche);
    border-color: var(--fehler-flaeche-stark);
}

/* ── „Meine Aufgaben": die Sichten ──────────────────────────────────────── */
.sicht-wahl {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
    margin-bottom: 0.8rem;
}

.sicht-knopf {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.4rem 0.8rem;
    border: 1px solid var(--rand-stark);
    border-radius: 999px;
    background: var(--flaeche-karte);
    color: var(--text-normal);
    font-size: 0.86rem;
    text-decoration: none;
}
.sicht-knopf:hover { background: var(--flaeche-zart); }
.sicht-aktiv {
    background: var(--fuellung);
    border-color: var(--fuellung);
    color: var(--fuellung-text);
}
.sicht-aktiv .sicht-zahl { color: var(--fuellung-text-leise); }
.sicht-zahl { font-weight: 700; color: var(--text-leise); }
.sicht-warnung .sicht-zahl { color: var(--fehler-text); }
.sicht-aktiv.sicht-warnung .sicht-zahl { color: var(--fuellung-text); }

.sicht-erklaerung {
    font-size: 0.85rem;
    color: var(--text-leise);
    margin-bottom: 1rem;
}

/* ── Die Detailseite ────────────────────────────────────────────────────── */
.aufgabe-kopfdaten {
    background: var(--flaeche-karte);
    border: 1px solid var(--rand);
    border-left: 4px solid var(--rand-stark);
    border-radius: 8px;
    padding: 1.2rem 1.4rem;
    margin-bottom: 1.2rem;
    box-shadow: var(--schatten-karte);
}

.kopfdaten-zeile {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem;
    margin-bottom: 1rem;
}

/* ── Der Aufgabenkopf in zwei Bahnen (Issue #235) ─────────────────────────
 *
 * Links die Kopfdaten, rechts die Beschreibung. Bis zu diesem Auftrag stand die
 * Beschreibung als eigene vollbreite Zeile **unter** den Metadaten: Auf dem
 * Schreibtisch blieb damit rechts neben Status, Frist und Zuständigkeit die
 * halbe Karte leer, und die Karte war höher als nötig, ohne mehr zu zeigen.
 *
 * Flexbox mit `wrap` und nicht `grid` mit zwei festen Spalten – und das ist der
 * Auftrag selbst, nicht der Geschmack des Umsetzenden:
 *
 *   * **Kein reservierter Leerraum.** Fehlt die Beschreibung, gibt es die
 *     zweite Bahn gar nicht (die Vorlage erzeugt sie nicht), und die erste
 *     nimmt die ganze Breite. Eine Schablone mit zwei Spalten ließe die rechte
 *     leer stehen.
 *   * **Der Umbruch hängt am Platz, nicht an einer Zahl.** Reichen die 26 rem
 *     je Bahn zusammen mit dem Abstand nicht mehr, wandert die Beschreibung von
 *     selbst unter die Metadaten – auf dem Tablet wie auf dem Telefon. Das
 *     kostet keine weitere Medienabfrage, und es gibt keine Breite, bei der
 *     zwei Bahnen zu je 13 rem nebeneinander stehen und beide unlesbar sind.
 *   * **Die Höhe folgt dem Inhalt.** `align-items: flex-start` lässt eine kurze
 *     Beschreibung kurz bleiben, statt sie auf die Höhe der Metadaten zu
 *     ziehen; eine lange wächst, ohne gekürzt zu werden – die geöffnete Aufgabe
 *     zeigt den vollen Text, die Vorschau gehört auf die Karte in der Übersicht.
 *
 * `min-width: 0` an beiden Bahnen und `overflow-wrap: anywhere` am Text: Ein
 * langer Dateiname oder eine lange Adresse in der Beschreibung bricht damit
 * um, statt die Karte breiter zu machen als die Seite.
 */
.kopfdaten-spalten {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    gap: 1rem 1.6rem;
    margin-bottom: 1rem;
}
.kopfdaten-meta { flex: 1 1 26rem; min-width: 0; }
/* Der letzte Posten der linken Bahn hört an der Bahn auf: Den Abstand zur
   Beschreibung darunter setzt nach dem Umbruch die Zeilenlücke, und zweimal
   Abstand wären auf dem Telefon eine Lücke zu viel. */
.kopfdaten-meta > :last-child { margin-bottom: 0; }

.kopfdaten-liste {
    display: grid;
    grid-template-columns: 10rem minmax(0, 1fr);
    gap: 0.4rem 1rem;
    font-size: 0.9rem;
    margin: 0 0 1rem;
}
.kopfdaten-liste dt { font-weight: 600; color: var(--text-leise); }
.kopfdaten-liste dd {
    margin: 0;
    color: var(--text-normal);
    overflow-wrap: anywhere;
}

.aufgabe-beschreibung {
    flex: 1 1 26rem;
    min-width: 0;
    white-space: pre-wrap;
    overflow-wrap: anywhere;
    line-height: 1.55;
    color: var(--text-lesetext);
    background: var(--flaeche-still);
    border-radius: 6px;
    padding: 0.8rem 1rem;
    margin: 0;
}

.status-wahl {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.4rem;
}
.status-wahl form { display: inline; }
.status-wahl-titel {
    font-size: 0.82rem;
    font-weight: 600;
    color: var(--text-leise);
    margin-right: 0.2rem;
}

/* Oben derselbe Abstand wie unten (Issue #196). Zwischen zwei Blöcken ändert
   das nichts – benachbarte Außenabstände fallen zusammen, und 1,6 rem bleiben
   1,6 rem. Gebraucht wird er dort, wo ein Block **nicht** auf einen anderen
   folgt: Auf dem Übergabeschritt steht darüber ein Absatz ohne eigenen
   Abstand nach unten, und die Überschrift legte sich sonst auf seine letzte
   Zeile. */
.aufgabe-block { margin: 1.6rem 0; }

/* ── Anhänge (Issue #194) ───────────────────────────────────────────────────
 *
 * Ein Anhang sieht aus wie ein Beitrag und ist doch keiner: Er trägt eine Datei
 * und nicht einen Satz. Deshalb dieselbe Karte wie `.beitrag` – gleicher Grund,
 * gleicher Rand, gleicher Innenabstand –, und darin drei Dinge, die es beim
 * Beitrag nicht gibt: den Namen mit Typ und Größe, die Wiedergabe und die
 * früheren Fassungen.
 *
 * Alle Farben kommen aus den Tokens: Der Abschnitt ist in hell und dunkel
 * dieselbe Datei, und eine eigene Zahl darin wäre die Stelle, an der der dunkle
 * Modus nachzieht (`tests/test_dunkelmodus.py`).
 */
.anhang-liste {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 0.6rem;
}
.anhang-eintrag {
    background: var(--flaeche-karte);
    border: 1px solid var(--rand);
    border-radius: 8px;
    padding: 0.7rem 1rem;
}
.anhang-kopf {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem;
    margin: 0 0 0.25rem;
}
/* Der Dateiname darf lang sein und soll trotzdem nicht aus der Karte laufen –
   auf 320 px ist „Jingle-Gude-Zeit-final-final.mp3" breiter als die Spalte. */
.anhang-name {
    font-weight: 600;
    color: var(--text-stark);
    overflow-wrap: anywhere;
}
.anhang-marke {
    background: var(--flaeche-still);
    color: var(--text-mittel);
    border: 1px solid var(--rand);
}
.anhang-fassung {
    background: var(--hinweis-flaeche);
    color: var(--hinweis-text);
}
.anhang-groesse,
.anhang-herkunft {
    font-size: 0.82rem;
    color: var(--text-leise);
}
.anhang-herkunft { margin: 0 0 0.5rem; }

/* Der Abspieler nimmt die ganze Breite: Auf dem Telefon sind die Knöpfe darin
   sonst schmaler als eine Fingerkuppe. Eine eigene Gestaltung bekommt er nicht –
   die des Betriebssystems ist die, die der Hörer kennt. */
.anhang-spieler {
    width: 100%;
    margin: 0.2rem 0 0.6rem;
}

/* Die Bildvorschau ist das Original in einer Höhenbegrenzung und keine zweite,
   kleinere Datei. `max-width: 100%` hält es in der Karte, `max-height` hält die
   Karte lesbar – ein Plakat im Hochformat füllte sonst den halben Bildschirm. */
.anhang-vorschau {
    display: inline-block;
    margin: 0.2rem 0 0.6rem;
    border: 1px solid var(--rand);
    border-radius: 6px;
    overflow: hidden;
    line-height: 0;
}
.anhang-vorschau img {
    display: block;
    max-width: 100%;
    max-height: 18rem;
    height: auto;
}
/* Eine Zeile mit zwei bis drei Knöpfen (seit Issue #339 an einer MP3 in den
   Details: „Herunterladen“, „Einzeln zum Upload“, „Entfernen“). Sie umbricht, statt zu
   überlaufen – auf 320 px steht der dritte Knopf unter den ersten beiden –,
   und das `form` darin ist `contents`: Ein Formularrahmen um genau einen Knopf
   soll im Fluss nichts bedeuten, sonst stünde er in einer eigenen Zeile. */
.anhang-aktionen {
    margin: 0;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.4rem;
}
.anhang-behalten-form { display: contents; }

/* ── Der Übergabestatus (Issue #196) ────────────────────────────────────────
 *
 * „Eindeutiger, aber nicht störender Übergabestatus“ steht im Auftrag, und das
 * ist hier wörtlich genommen: eine kleine Marke in der Zeile, keine Farbfläche
 * über der Karte. Rot gibt es dafür nicht – eine noch nicht übergebene Datei
 * ist kein Fehler, sondern eine offene Entscheidung.
 */
.anhang-sendesystem {
    margin: 0 0 0.5rem;
    font-size: 0.82rem;
    color: var(--text-leise);
}
.anhang-marke-offen {
    background: var(--hinweis-flaeche);
    color: var(--hinweis-text);
}
.anhang-marke-uebergeben {
    background: var(--erfolg-flaeche);
    color: var(--erfolg-text);
    border: 1px solid var(--erfolg-rand);
}
.anhang-marke-behalten {
    background: var(--flaeche-still);
    color: var(--text-mittel);
    border: 1px solid var(--rand);
}
/* Der dauerhafte Hinweis über der Liste. Er steht im Abschnitt und nicht über
   der Seite: Er gehört zu den Dateien, von denen er spricht. */
.anhang-offenhinweis { margin-bottom: 0.9rem; }

/* ── Der Audio-Abschlussstatus (Issue #229) ─────────────────────────────────
 *
 * „Audio: 1 von 2 noch ungeklärt" – eine Zeile über der Liste, die sagt, ob
 * sich diese Aufgabe erledigen lässt. Keine Meldungsfläche: Sie steht
 * dauerhaft da und wäre als Kasten ein Alarm, der nie aufhört. Die Farbe
 * trägt deshalb nur der Punkt davor und nicht der Hintergrund.
 */
.anhang-audiostand {
    margin: 0 0 0.8rem;
    font-size: 0.86rem;
    font-weight: 600;
    display: flex;
    align-items: center;
    gap: 0.45rem;
}
.anhang-audiostand::before {
    content: "";
    width: 0.6rem;
    height: 0.6rem;
    border-radius: 50%;
    flex: none;
}
.anhang-audiostand-offen { color: var(--hinweis-text); }
.anhang-audiostand-offen::before { background: var(--hinweis-text); }
.anhang-audiostand-fertig { color: var(--erfolg-text); }
.anhang-audiostand-fertig::before { background: var(--erfolg-text); }

/* Die Datei, auf die „Zur offenen MP3" gesprungen ist. `:target` und kein
 * Skript: Der Sprung ist ein Verweis auf eine Sprungmarke, und gefunden wird
 * das Ziel von derselben Mechanik, die auch scrollt. `scroll-margin-top` hält
 * die Zeile unter der feststehenden Kopfleiste; ohne sie läge die Überschrift
 * der Datei darunter.
 *
 * ── Kein Rahmen beim Ankommen (Issue #377) ──────────────────────────────────
 *
 * Vom 01.10.2026 bis Issue #377 stand hier ein orangener Ring (vier
 * Sekunden stehen, zwei Sekunden verblassen).
 * Martins Befund dazu: Er wirkt wie ein Fehler. Davor war es eine blaue
 * Dauerkante, die wie eine Auswahl wirkte. Deshalb jetzt gar keine farbige
 * Einfassung mehr – weder dauerhaft noch blinkend oder ausblendend. Der Sprung
 * selbst bleibt: Der Browser scrollt die Datei unter die Kopfleiste, setzt den
 * Tastaturfokus auf den Kasten (`tabindex="-1"` in der Vorlage), und eine
 * Vorlesehilfe sagt die Datei an. Was an ihr offen ist, sagt ihre eigene
 * Statuspille („Noch offen“, Issue #339) und der Zähler über der Liste. */
.anhang-eintrag { scroll-margin-top: 5rem; }
/* Der Fokusring bleibt dabei aus, und zwar weiter – „Fokus-/Tastaturführung
   und `scroll-margin` aus #229 erhalten" steht in derselben Abnahme, die den
   Dauerrahmen beanstandet. Die Begründung ist dieselbe wie bei
   `main[tabindex="-1"]:focus`: Diese Zeile steht nicht auf dem Tabulatorweg,
   sie wird nur beim Sprung angesteuert, und ein Ring um den ganzen Kasten
   gehört an ein Bedienelement, nicht um eine Datei mit vier Knöpfen darin. Ein
   Ring um den Kasten wäre außerdem genau die Einfassung, die Issue #377
   abschafft. Die Knöpfe darin behalten ihre sichtbare Fokusanzeige. */
.anhang-eintrag:focus { outline: none; }

/* ── Die MP3-Entscheidung (Issue #339) ──────────────────────────────────────
 *
 * Die Anhangliste ist an einer Aufgabe mit MP3s zugleich die Liste, in der
 * über sie entschieden wird. Darüber steht der Stand (Zähler aus #229 und drei
 * Pillen), darunter eine Leiste mit Auswahl und beiden Sammelaktionen, und
 * jede MP3 ist eine Zeile: Häkchen, Name, kleines Vorhören, Statuspille.
 *
 * Alles aus den Tokens des Hauses – dieselben Flächen wie die Marken aus #196,
 * dieselben Schaltflächen wie überall, und damit hell und dunkel aus einer
 * Regel (`tests/test_dunkelmodus.py`).
 */
.mp3-entscheidung { scroll-margin-top: 5rem; }
.mp3-entscheidung:focus { outline: none; }
.mp3-stand {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.4rem 0.8rem;
    margin: 0 0 0.6rem;
}
.mp3-stand .anhang-audiostand { margin: 0; }
.mp3-bilanz {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-wrap: wrap;
    gap: 0.3rem;
}

/* Die Statuspillen – vier Zustände, vier Flächen. Rot gibt es nur für den
   gescheiterten Versuch; eine offene Datei ist eine offene Frage, kein Fehler. */
.mp3-pille { white-space: nowrap; border: 1px solid transparent; }
.mp3-pille-offen {
    background: var(--hinweis-flaeche);
    color: var(--hinweis-text);
    border-color: var(--hinweis-kante);
}
.mp3-pille-uebergeben {
    background: var(--erfolg-flaeche);
    color: var(--erfolg-text);
    border-color: var(--erfolg-rand);
}
.mp3-pille-behalten {
    background: var(--flaeche-sanft);
    color: var(--text-mittel);
    border-color: var(--rand-stark);
}
.mp3-pille-fehlversuch {
    background: var(--fehler-flaeche);
    color: var(--fehler-text);
    border-color: var(--fehler-rand);
}

.mp3-einleitung {
    margin: 0 0 0.5rem;
    font-size: 0.88rem;
    color: var(--text-mittel);
}

/* Die Leiste. Eine Fläche wie eine Karte, damit sie sich von den Zeilen
   darunter abhebt; links die Auswahl, rechts die beiden Sammelaktionen –
   gleich groß, nebeneinander, keine versteckt. */
.mp3-leiste {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem 1rem;
    margin: 0 0 0.6rem;
    padding: 0.55rem 0.75rem;
    background: var(--flaeche-zart);
    border: 1px solid var(--rand-sanft);
    border-radius: 8px;
}
.mp3-leiste-auswahl,
.mp3-leiste-aktionen {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.4rem;
}
.mp3-zahl {
    font-size: 0.86rem;
    font-weight: 600;
    color: var(--text-normal);
    font-variant-numeric: tabular-nums;
}
.mp3-grund,
.mp3-rueckfrage {
    flex-basis: 100%;
    margin: 0;
    font-size: 0.84rem;
    color: var(--text-leise);
}
.mp3-rueckfrage {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.4rem 0.8rem;
    padding: 0.5rem 0.65rem;
    background: var(--flaeche-karte);
    border: 1px solid var(--rand-stark);
    border-left: 4px solid var(--warnung-kraeftig);
    border-radius: 6px;
    color: var(--text-normal);
}
.mp3-rueckfrage[hidden],
.mp3-grund[hidden] { display: none; }
.mp3-rueckfrage p { margin: 0; flex: 1 1 16rem; }
.mp3-rueckfrage-knoepfe { display: flex; gap: 0.4rem; flex-wrap: wrap; }

/* Alles geklärt: der nächste Schritt, ruhig in Grün. */
.mp3-fertig {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem 1rem;
    margin: 0 0 0.6rem;
    padding: 0.55rem 0.75rem;
    background: var(--erfolg-zart-flaeche);
    border: 1px solid var(--erfolg-zart-rand);
    border-radius: 8px;
}
.mp3-fertig p { margin: 0; color: var(--erfolg-zart-text); font-weight: 600; }

/* Eine MP3 – eine Zeile. Das Raster hält Häkchen, Name und den rechten Block
   auseinander; ein langer Name bricht um, statt die Pille aus der Karte zu
   schieben. */
.mp3-zeile { padding: 0.45rem 0.75rem; }
.mp3-zeile-kopf {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) auto;
    align-items: center;
    gap: 0.25rem 0.6rem;
}
.mp3-zeile-kopf > .mp3-titel:first-child { grid-column: 1 / 3; }
.mp3-haken {
    width: 1.25rem;
    height: 1.25rem;
    margin: 0;
    accent-color: var(--hinweis-kante);
    cursor: pointer;
}
.mp3-titel {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.2rem 0.45rem;
    min-width: 0;
    min-height: 2.25rem;
}
label.mp3-titel { cursor: pointer; }
.mp3-rechts {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 0.5rem;
}
.mp3-meta { margin: 0.1rem 0 0; }
.mp3-zeile-waehlbar .mp3-meta { padding-left: 1.85rem; }
.mp3-zeile-gewaehlt {
    background: var(--flaeche-gewaehlt);
    border-color: var(--hinweis-kante);
}
.mp3-details { margin: 0.2rem 0 0; }
.mp3-zeile-waehlbar .mp3-details { padding-left: 1.85rem; }
.mp3-fehler {
    margin: 0.3rem 0 0;
    font-size: 0.84rem;
    color: var(--fehler-text);
}
.mp3-fehler[hidden] { display: none; }
/* Der Hinweis unter einem Abspieler, den dieser Browser nicht bedienen kann
   (Issue #451). `.hinweis` setzt `display: block` und schlüge sonst das
   eingebaute `[hidden]` des Browsers. */
.anhang-tonhinweis[hidden] { display: none; }
.mp3-zeile-tonfehler .mp3-knopf[data-mp3-spielen] { border-color: var(--fehler-rand); }

/* Das kompakte Vorhören: zwei runde Knöpfe und die Zeit. Ohne Skript steht
   stattdessen der Abspieler des Browsers da, und dann in seiner gewohnten,
   begrenzten Breite. */
.mp3-spieler {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
}
.mp3-spieler[hidden],
.mp3-zeile .anhang-spieler[hidden] { display: none; }
.mp3-zeile .anhang-spieler { width: 100%; max-width: 18rem; margin: 0; }
.mp3-knopf {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.25rem;
    height: 2.25rem;
    padding: 0;
    border-radius: 50%;
    border: 1px solid var(--rand-stark);
    background: var(--flaeche-karte);
    color: var(--text-stark);
    cursor: pointer;
}
.mp3-knopf:hover:not(:disabled) { background: var(--flaeche-zart); }
.mp3-knopf:focus-visible {
    outline: 2px solid var(--hinweis-kante);
    outline-offset: 2px;
}
.mp3-knopf:disabled { opacity: 0.45; cursor: default; }
.mp3-knopf[aria-busy="true"] { border-color: var(--hinweis-kante); }
.mp3-symbol { width: 0.95rem; height: 0.95rem; fill: currentColor; }
.mp3-knopf .mp3-symbol-pause,
.mp3-knopf[data-laeuft="1"] .mp3-symbol-spielen { display: none; }
.mp3-knopf[data-laeuft="1"] .mp3-symbol-pause { display: block; }
.mp3-knopf[data-laeuft="1"] {
    background: var(--hinweis-flaeche);
    color: var(--hinweis-text);
    border-color: var(--hinweis-kante);
}
.mp3-zeit {
    min-width: 4.6rem;
    font-size: 0.78rem;
    color: var(--text-leise);
    font-variant-numeric: tabular-nums;
}
.mp3-zeit:empty { min-width: 0; }
/* Die Zeitleiste (Issue #579): schmal, aber mit voller Knopfhöhe als
   Tippfläche. Sie nimmt den Platz neben den Knöpfen und bricht bei wenig
   Breite in die nächste Zeile um, statt die Seite zu verbreitern. */
.mp3-spieler { flex-wrap: wrap; max-width: 100%; }
.mp3-zeitleiste {
    flex: 1 1 8rem;
    min-width: 6rem;
    max-width: 18rem;
    height: 2.25rem;
    margin: 0;
    padding: 0;
    accent-color: var(--hinweis-kante);
    cursor: pointer;
    touch-action: pan-y;
}
.mp3-zeitleiste:focus-visible {
    outline: 2px solid var(--hinweis-kante);
    outline-offset: 2px;
}
.mp3-zeitleiste:disabled { opacity: 0.45; cursor: default; }

/* Der Herkunftskasten auf der Upload-Seite: welche MP3s von welcher Aufgabe
   mitgekommen sind, und der Weg zurück. Dieselbe Fläche wie die Leiste an der
   Aufgabe – es ist derselbe Vorgang auf der anderen Seite. */
.upl-aufgabenquelle {
    margin: 0 0 1rem;
    padding: 0.65rem 0.85rem;
    background: var(--flaeche-zart);
    border: 1px solid var(--rand-sanft);
    border-left: 4px solid var(--hinweis-kante);
    border-radius: 8px;
}
.upl-aufgabenquelle-kopf { margin: 0 0 0.4rem; color: var(--text-normal); }
.upl-aufgabenquelle-liste {
    list-style: none;
    margin: 0 0 0.5rem;
    padding: 0;
    display: flex;
    flex-wrap: wrap;
    gap: 0.3rem;
    max-height: 9rem;
    overflow-y: auto;
}
.upl-aufgabenquelle-liste li {
    padding: 0.1rem 0.5rem;
    border-radius: 999px;
    background: var(--flaeche-karte);
    border: 1px solid var(--rand);
    font-size: 0.8rem;
    color: var(--text-mittel);
    overflow-wrap: anywhere;
}
.upl-aufgabenquelle-fuss {
    margin: 0;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.4rem 0.8rem;
}

/* Der Verweis im Kopf der Aufgabe. */
.aufgabe-mp3stand { margin: 0.5rem 0 0; font-size: 0.88rem; }
.aufgabe-mp3sprung { font-weight: 600; color: var(--text-mittel); }
.aufgabe-mp3sprung-offen { color: var(--hinweis-text); }

/* Am Schreibtisch und auf dem Tablet klebt die Leiste beim Scrollen durch die
   Liste unter dem Hauptmenü. Auf dem Telefon nicht: Dort ist das Menü
   mehrzeilig, und eine klebende Leiste darunter nähme die halbe Höhe. Das
   Zurücknehmen steht im vorhandenen Telefon-Breakpoint – kein eigener
   Breakpoint ab 641 px. */
.mp3-leiste {
    position: sticky;
    top: calc(var(--kopf-hoehe) + 0.25rem);
    z-index: 3;
    box-shadow: 0 2px 6px -4px var(--rand-still);
}
@media (max-width: 640px) {
    .mp3-leiste { position: static; box-shadow: none; padding: 0.5rem; }
    .mp3-leiste-aktionen { width: 100%; }
    .mp3-leiste-aktionen .schaltflaeche { flex: 1 1 9rem; justify-content: center; }
    /* Auf dem Telefon rutschen Vorhören und Pille in eine zweite Zeile unter
       den Namen – der Name behält die ganze Breite. */
    .mp3-zeile-kopf { grid-template-columns: auto minmax(0, 1fr); }
    .mp3-rechts { grid-column: 1 / -1; justify-content: space-between; }
    .mp3-zeile-waehlbar .mp3-rechts { padding-left: 1.85rem; }
    .mp3-zeile-waehlbar .mp3-meta,
    .mp3-zeile-waehlbar .mp3-details { padding-left: 0; }
    /* Telefonbreite heißt hier auch Fingerbedienung – 44 px, auch wenn der
       Browser `pointer: coarse` nicht meldet. */
    .mp3-knopf { width: 2.75rem; height: 2.75rem; }
    .mp3-zeitleiste { height: 2.75rem; }
    .mp3-titel { min-height: 2.75rem; }
    .mp3-haken { width: 1.5rem; height: 1.5rem; }
}
/* Fingerkuppen: 44 px Tippfläche, wo mit dem Finger bedient wird. */
@media (pointer: coarse) {
    .mp3-knopf { width: 2.75rem; height: 2.75rem; }
    .mp3-zeitleiste { height: 2.75rem; }
    .mp3-titel { min-height: 2.75rem; }
    .mp3-haken { width: 1.5rem; height: 1.5rem; }
}

/* ── Der Riegel am Erledigen (Issue #229) ──────────────────────────────────
 *
 * Martins Abnahme vom 01.10.2026: „Die heutige Meldung nach einem blockierten
 * Erledigt-Versuch ist funktional korrekt, aber visuell zu zurückhaltend für
 * eine Aktion, die den Aufgabenabschluss tatsächlich verhindert." Vorher war
 * das eine `.meldung-hinweis` – zarte Fläche, sanfte Kante, gewöhnliche
 * Schriftfarbe –, und sie stand neben grünen Bestätigungen, die lauter waren
 * als sie.
 *
 * Die drei Mittel, mit denen dieser Kasten vorgeht, und alle drei aus dem
 * Bestand: die Warnfläche (dieselben Tokens wie `.badge-offen`, also hell und
 * dunkel geprüft), eine **tragende** Kante links statt eines Rahmens ringsum,
 * und eine fette Kopfzeile über dem gezählten Satz. Kein Modal und keine neue
 * Farbe – der Auftrag schließt das Fenster ausdrücklich aus, und eine siebte
 * Meldungsfarbe wäre eine Sorte zu viel.
 *
 * `--warnung-kraeftig` und nicht `--warnung-text` an der Kante: Die Kante ist
 * das, was man vor dem Lesen sieht, und sie soll sich auch im dunklen Modus
 * von der Fläche abheben. */
.meldung-riegel {
    background: var(--warnung-flaeche);
    border: 1px solid var(--warnung-kraeftig);
    border-left: 5px solid var(--warnung-kraeftig);
    color: var(--warnung-text);
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}
.riegel-kopf {
    margin: 0;
    font-size: 1rem;
    font-weight: 700;
}
.riegel-satz { margin: 0; }
/* Eine eigene Zeile für die Aktion: Im Satz stünde sie bei drei offenen
   Dateien hinter einem Nebensatz, und auf 320 px wäre sie das Wort, das
   umbricht. `flex-wrap`, weil hier eine zweite Aktion dazukommen kann, ohne
   dass diese Regel es merken muss. */
.riegel-wege {
    margin: 0;
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
}

/* Der Weg aus einer Meldung heraus (Issue #229). Seit Martins Abnahme vom
 * 01.10.2026 ist er im Riegel eine sichtbare Schaltfläche und kein Wort im
 * Satz mehr: Die alte Begründung („eine Meldung mit einem Knopf darin sähe aus
 * wie ein Dialog") galt einer Einordnung – dieser Kasten ist die Auskunft über
 * eine **verhinderte** Aktion und braucht die Aktion, die sie möglich macht.
 * Kein `white-space: nowrap` mehr: „Zu den offenen MP3-Dateien" ist der
 * längere der beiden Texte und darf auf dem Telefon umbrechen. */
.meldung-aktion { font-weight: 600; }

.anhang-fruehere { margin-top: 0.6rem; font-size: 0.85rem; }
.anhang-fruehere summary {
    cursor: pointer;
    color: var(--text-leise);
    /* Eine Tippfläche und keine Textzeile: Der Mobile-Audit verlangt 44 px, und
       ein `summary` ist von Haus aus so hoch wie seine Schrift. */
    padding: 0.5rem 0;
    min-height: 44px;
    display: flex;
    align-items: center;
}
.anhang-fassungen {
    list-style: none;
    margin: 0;
    padding: 0 0 0 0.2rem;
}
.anhang-fassungen li {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.5rem;
    padding: 0.3rem 0;
    border-top: 1px solid var(--rand-zart);
}

/* Das Dateifeld bekommt seine Breite ausdrücklich: Ein `<input type="file">`
   hat eine eigene, vom Browser gesetzte Größe und läuft auf 320 px sonst aus
   der Karte – dieselbe Zeile wie bei `.bildfeld-eingabe`.
   Über eine eigene Klasse und nicht über `.formular-zeile input[type="file"]`:
   Das Dateifeld der Upload-Zentrale steht in derselben Zeilenart, und dieser
   Auftrag ändert an ihrer Maske nichts. */
.anhang-feld { width: 100%; max-width: 100%; }

.anhang-form {
    margin-top: 1rem;
    background: var(--flaeche-karte);
    border: 1px solid var(--rand);
    border-radius: 8px;
    padding: 1rem 1.2rem;
}
.anhang-knopfzeile {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.75rem;
}
/* ── Das PDF im Fenster (Martins Ergänzung vom 26.09.2026) ──────────────────
 *
 * Zugeklappt, und der Rahmen entsteht erst beim Aufklappen (`loading="lazy"` in
 * der Vorlage). Die Höhe ist eine Bildschirmhöhe minus eine Handbreit: Ein PDF
 * in 18 rem wie die Bildvorschau wäre eine Briefmarke, in voller Seitenhöhe
 * verlöre man den Weg zurück zur Aufgabe.
 *
 * `summary` bekommt dieselbe Tippfläche wie bei den früheren Fassungen – 44 px
 * verlangt der Mobile-Audit, und ein `summary` ist von Haus aus so hoch wie
 * seine Schrift. */
.anhang-pdf { margin: 0.2rem 0 0.6rem; }
.anhang-pdf summary {
    cursor: pointer;
    color: var(--text-leise);
    font-size: 0.85rem;
    padding: 0.5rem 0;
    min-height: 44px;
    display: flex;
    align-items: center;
}
.anhang-pdf-rahmen {
    width: 100%;
    height: min(70vh, 40rem);
    border: 1px solid var(--rand);
    border-radius: 6px;
    background: var(--flaeche-sanft);
}

/* ── Die Vorschau der lokal gewählten Datei ─────────────────────────────────
 *
 * Sie sieht **anders** aus als ein gespeicherter Anhang, und das ist Absicht:
 * gestrichelter Rand statt durchgezogenem. Der Auftrag verlangt „Mehrere Anhänge
 * müssen unterscheidbar bleiben", und die wichtigste Unterscheidung ist hier die
 * zwischen „liegt an der Aufgabe" und „liegt noch auf meinem Schreibtisch". Ein
 * Kasten, der aussieht wie die Karten darüber, behauptete das Gegenteil.
 *
 * Verborgen ist er über `hidden` in der Vorlage; `display` steht hier deshalb
 * ausdrücklich nicht – eine Regel mit `display: block` überschriebe `hidden` und
 * der leere Kasten stünde immer da. */
.anhang-lokal {
    margin: 0.5rem 0 0;
    padding: 0.6rem 0.8rem;
    border: 1px dashed var(--rand);
    border-radius: 8px;
    background: var(--flaeche-sanft);
}
.anhang-lokal-kopf {
    margin: 0 0 0.4rem;
    font-size: 0.82rem;
    color: var(--text-mittel);
    overflow-wrap: anywhere;
}
.anhang-lokal-bild {
    display: block;
    max-width: 100%;
    max-height: 18rem;
    height: auto;
    border: 1px solid var(--rand);
    border-radius: 6px;
}
.anhang-lokal-pdf {
    width: 100%;
    height: min(60vh, 32rem);
    margin-top: 0.4rem;
    border: 1px solid var(--rand);
    border-radius: 6px;
    background: var(--flaeche-karte);
}
/* Der Verweis, der die gewählte PDF in einem neuen Tab öffnet – eine Zeile und
   keine Schaltfläche: Er steht neben dem Feld und nicht in einer Knopfzeile. */
.anhang-lokal-oeffnen {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
    font-size: 0.85rem;
}

/* Eine Karte je gewählter Datei (Martins Abnahme vom 26.09.2026).
 *
 * Bis dahin stand im Kasten **eine** Vorschau. Mehrere Dateien verlangt der
 * Auftrag ausdrücklich, und „mehrere Anhänge müssen unterscheidbar bleiben"
 * heißt: je Datei ein eigener Rahmen mit eigenem Namen, eigenem Fortschritt und
 * eigenem `Entfernen`. Ein gemeinsamer Kasten mit vier Abspielern darin wäre
 * eine Liste, an der man nicht ablesen kann, welcher Knopf welche Datei
 * wegnimmt.
 *
 * Der Abstand nach unten und nicht nach oben: Die erste Karte schließt damit an
 * die Feldzeile darüber an, und der Kasten endet nicht mit einer leeren Zeile. */
.anhang-lokal-karte {
    padding: 0.5rem 0 0.6rem;
    border-bottom: 1px dashed var(--rand);
}
.anhang-lokal-karte:last-child {
    border-bottom: none;
    padding-bottom: 0;
}
/* Der Zustand der Karte in drei Farben: `wird übertragen …` ruhig,
   `übertragen` grün, ein Satz über eine zu große Datei rot. Die Farben kommen
   aus den Tokens des Hauses und nicht als fester Wert – sonst blieben sie im
   dunklen Modus, wie sie im hellen waren. */
.anhang-lokal-stand { color: var(--text-mittel); }
.anhang-lokal-gut { color: var(--erfolg-text); font-weight: 600; }
.anhang-lokal-fehler { color: var(--fehler-text); font-weight: 600; }

/* Der Fortschritt je Datei. Über die ganze Breite und nicht neben dem Knopf:
   Hier steht kein Knopf daneben, und bei vier Dateien untereinander liest sich
   eine Reihe gleich langer Balken besser als vier verschieden breite. */
.anhang-lokal-fortschritt {
    display: block;
    width: 100%;
    height: 0.5rem;
    margin: 0 0 0.4rem;
}

/* `Entfernen` an der Karte – die Antwort auf „einzelne versehentlich
   ausgewählte Dateien wieder entfernen können, ohne das ganze Formular zu
   verlieren". 44 px Tippfläche wie jede andere Schaltfläche des Hauses. */
.anhang-lokal-weg { margin-top: 0.4rem; }

/* ── Gleichnamige Dateien (Issue #377) ──────────────────────────────────────
 *
 * Der Stand an der Karte: entschieden leise, offen in Warnfarbe mit dem Knopf
 * „Entscheiden“ daneben. Die Knöpfe stehen in einer eigenen Zeile, damit
 * „Entfernen“ seinen Platz behält. */
.anhang-lokal-knoepfe {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
    margin-top: 0.4rem;
}
.anhang-lokal-knoepfe > .schaltflaeche { margin-top: 0; }
.anhang-lokal-konflikt {
    margin: 0.3rem 0 0;
    font-size: 0.82rem;
    color: var(--text-mittel);
    overflow-wrap: anywhere;
}
.anhang-lokal-konflikt-offen { color: var(--warnung-text); font-weight: 600; }
/* `display` von `.schaltflaeche` und der Auswahlgruppe schlägt das eingebaute
   `[hidden] { display: none }` – ohne diese Zeile stünde „Entscheiden“ an jeder
   Karte und die drei Entscheidungen blieben neben dem Namensfeld stehen. */
.anhang-lokal-entscheiden[hidden],
.anhang-konflikt-wahl[hidden] { display: none; }
.anhang-konflikt-meldung {
    margin: 0.4rem 0 0;
    font-size: 0.85rem;
    color: var(--warnung-text);
    font-weight: 600;
}

/* Der Dialog selbst: in der Mitte, in der Kartenfarbe, auf dem Telefon mit
 * einem Rand von je 1 rem und in sich rollend, wenn die Höhe nicht reicht –
 * dieselbe Bauart wie `.schnell-dialog`, nur breiter, weil zwei Dateien
 * nebeneinander verglichen werden. */
.anhang-konflikt {
    position: fixed;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    margin: 0;
    border: 1px solid var(--rand-stark);
    border-radius: 10px;
    padding: 1.25rem;
    width: 100%;
    max-width: min(34rem, calc(100vw - 2rem));
    max-height: calc(100dvh - 2rem);
    overflow-y: auto;
    box-shadow: var(--schatten-dialog);
    background: var(--flaeche-karte);
    color: var(--text-normal);
}
.anhang-konflikt::backdrop { background: var(--dialog-schleier); }
.anhang-konflikt-fortschritt {
    margin: 0 0 0.3rem;
    font-size: 0.8rem;
    color: var(--text-leise);
}
.anhang-konflikt-titel {
    margin: 0 0 0.4rem;
    font-size: 1.15rem;
    color: var(--text-stark);
}
.anhang-konflikt-hinweis { margin: 0 0 0.8rem; font-size: 0.9rem; }
.anhang-konflikt-vergleich {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0.6rem;
    margin: 0 0 1rem;
}
.anhang-konflikt-datei {
    padding: 0.55rem 0.7rem;
    border-radius: 6px;
    background: var(--flaeche-zart);
    min-width: 0;
}
.anhang-konflikt-datei dt {
    font-size: 0.78rem;
    color: var(--text-leise);
    margin-bottom: 0.15rem;
}
.anhang-konflikt-datei dd { margin: 0; }
.anhang-konflikt-name {
    display: block;
    font-weight: 600;
    color: var(--text-stark);
    overflow-wrap: anywhere;
}
.anhang-konflikt-angaben {
    display: block;
    font-size: 0.8rem;
    color: var(--text-mittel);
}
/* Drei gleichwertige Entscheidungen untereinander – keine hervorgehoben, keine
   vorbelegt. Jede sagt in einem Satz, was sie tut. */
.anhang-konflikt-wahl { display: grid; gap: 0.5rem; }
.anhang-konflikt-knopf {
    display: grid;
    gap: 0.15rem;
    width: 100%;
    padding: 0.6rem 0.8rem;
    border: 1px solid var(--rand-stark);
    border-radius: 8px;
    background: var(--flaeche-karte);
    color: var(--text-normal);
    font: inherit;
    text-align: left;
    cursor: pointer;
}
.anhang-konflikt-knopf strong { color: var(--text-stark); font-size: 0.95rem; }
.anhang-konflikt-knopf span { font-size: 0.82rem; color: var(--text-mittel); }
.anhang-konflikt-knopf:hover { background: var(--flaeche-zart); }
.anhang-konflikt-knopf:focus-visible {
    outline: 3px solid var(--hinweis-kante);
    outline-offset: 2px;
}
.anhang-konflikt-titel:focus { outline: none; }
.anhang-konflikt-titel:focus-visible {
    outline: 3px solid var(--hinweis-kante);
    outline-offset: 2px;
}
.anhang-konflikt-umbenennen label {
    display: block;
    font-weight: 600;
    margin-bottom: 0.3rem;
}
.anhang-konflikt-namenszeile {
    display: flex;
    align-items: center;
    gap: 0.3rem;
}
.anhang-konflikt-namenszeile input { flex: 1 1 auto; min-width: 0; }
.anhang-konflikt-endung { font-weight: 600; color: var(--text-stark); }
.anhang-konflikt-fehler {
    margin: 0.35rem 0 0;
    font-size: 0.85rem;
    color: var(--fehler-text);
    font-weight: 600;
}
.anhang-konflikt-zeile,
.anhang-konflikt-fuss {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    margin-top: 0.8rem;
}
.anhang-konflikt-fuss { justify-content: flex-end; }
@media (max-width: 640px) {
    .anhang-konflikt { padding: 1rem; }
    .anhang-konflikt-vergleich { grid-template-columns: 1fr; }
    /* Tippflächen von mindestens 44 px – dasselbe Maß wie in der Kopfleiste. */
    .anhang-konflikt .schaltflaeche,
    .anhang-konflikt-knopf,
    .anhang-konflikt-namenszeile input { min-height: 44px; }
    .anhang-konflikt-zeile .schaltflaeche,
    .anhang-konflikt-fuss .schaltflaeche { flex: 1 1 100%; }
}

/* Der Balken nimmt den Platz, der neben dem Knopf übrig ist, und verschwindet
   ohne das Skript daneben vollständig (`hidden` in der Vorlage). */
.anhang-fortschritt {
    flex: 1 1 8rem;
    height: 0.75rem;
}

/* Das Formular hinter `Entfernen` an einem **gespeicherten** Anhang. Es steht in
   einer Reihe mit „Im Browser öffnen" und „Herunterladen" und darf deshalb keine
   eigene Zeile aufmachen – `display: contents` gäbe es dem Flex-Container
   darüber, ist aber in älteren Browsern mit Schaltflächen darin unzuverlässig.
   `inline` reicht: Das Formular trägt nur den Knopf. */
.anhang-entfernen { display: inline; margin: 0; }
/* In der Liste der früheren Fassungen steht es hinter der Herkunftszeile. Dort
   ist der Knopf kleiner als die Zeile hoch – ein kleiner Abstand links trennt
   ihn von der Zeit, ohne eine eigene Spalte aufzumachen. */
.anhang-fassungen .anhang-entfernen { margin-left: 0.4rem; }

/* ── Beiträge und Historie ──────────────────────────────────────────────── */
.beitraege { display: flex; flex-direction: column; gap: 0.6rem; }

.beitrag {
    background: var(--flaeche-karte);
    border: 1px solid var(--rand);
    border-radius: 8px;
    padding: 0.7rem 1rem;
}
.beitrag-kopf {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    font-size: 0.85rem;
    margin-bottom: 0.3rem;
    color: var(--text-normal);
}
.beitrag-datum { color: var(--text-leise); }
.beitrag-text { white-space: pre-wrap; color: var(--text-lesetext); line-height: 1.5; }

.beitrag-form {
    margin-top: 1rem;
    background: var(--flaeche-karte);
    border: 1px solid var(--rand);
    border-radius: 8px;
    padding: 1rem 1.2rem;
}
.beitrag-form textarea {
    width: 100%;
    padding: 0.55rem 0.8rem;
    border: 1px solid var(--rand-stark);
    border-radius: 6px;
    font-size: 0.92rem;
    font-family: inherit;
    background: var(--flaeche-eingabe);
    color: var(--text-normal);
}
.beitrag-form textarea:focus { outline: none; border-color: var(--kontur); }
.beitrag-aktionen {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
}

/* ── Der Kommunikationsbereich (Issue #209) ──────────────────────────────────
 *
 * Alles hier folgt einem Satz aus dem Auftrag: „@Erwähnungen sichtbar, aber
 * nicht grell." Eine Erwähnung ist deshalb keine Signalfarbe, sondern die
 * ruhige Infofläche, die diese Oberfläche für Auskünfte ohnehin benutzt – sie
 * hebt den Namen aus dem Satz, ohne ihn anzuschreien.
 */
.erwaehnung {
    background: var(--info-flaeche-zart);
    color: var(--info-text);
    border-radius: 4px;
    padding: 0 0.2rem;
    font-weight: 600;
}

/* Ein Beitrag, auf dessen Antwort noch gewartet wird. Eine Kante links und
   keine Fläche: Der Text soll gleich gut lesbar bleiben wie jeder andere –
   markiert ist der Beitrag, nicht das Gelesene. */
.beitrag-wartet { border-left: 3px solid var(--violett-text); }

.beitrag-antwortmarke {
    background: var(--violett-flaeche);
    color: var(--violett-text);
}
/* Die beantwortete Rückfrage bleibt stehen und wird still: Sie erzählt, was
   einmal offen war, und ruft nicht mehr danach. */
.beitrag-antwortmarke-erledigt {
    background: var(--flaeche-sanft);
    color: var(--text-leise);
}
.beitrag-fussnote { font-size: 0.8rem; margin: 0.35rem 0 0; }

/*
 * Der eigene Beitrag: ändern und zurücknehmen (Issue #230).
 *
 * Bewusst ohne eigene Abschnittslinie: Das sind zwei Wörter und ein Feld im
 * **Kommunikationsbereich** und kein zweites Bauteil daneben – und dadurch
 * gelten die Zusagen dieses Abschnitts auch für sie, Farbwert für Farbwert.
 *
 * „Dezent direkt am eigenen Kommentar, nicht als dominante Buttons an jedem
 * Beitrag." Deshalb sind es keine Schaltflächen, sondern zwei Wörter in der
 * Schriftgröße einer Fußnote – dieselbe Sprache wie die Klappe „Ansehen und
 * bearbeiten" an einer Datei.
 */
.beitrag-werkzeuge {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0 0.9rem;
    margin: 0.3rem 0 0;
}
.beitrag-wegform { margin: 0; }
.beitrag-werkzeug {
    /* Ein Knopf, der aussieht wie ein Wort: kein Rahmen, keine Fläche, aber
       die Fläche eines Knopfes, sobald jemand mit dem Finger danach greift
       (siehe den Telefonabschnitt dieses Moduls). */
    appearance: none;
    background: none;
    border: 0;
    padding: 0.15rem 0;
    font: inherit;
    font-size: 0.82rem;
    color: var(--text-leise);
    cursor: pointer;
    text-decoration: underline;
    text-underline-offset: 2px;
}
.beitrag-werkzeug:hover { color: var(--text-normal); }
.beitrag-werkzeug-gefahr:hover { color: var(--fehler-text); }
/* Das Dreieck der Klappe weg: Hier steht ein Wort und kein Baum. */
.beitrag-aendern > summary { list-style: none; }
.beitrag-aendern > summary::-webkit-details-marker { display: none; }

/* Offen nimmt die Klappe die ganze Zeile – das Feld soll so breit sein wie
   der Beitrag und nicht so breit wie das Wort darüber. */
.beitrag-aendern[open] { flex: 1 1 100%; }
.beitrag-aendern[open] > summary { margin-bottom: 0.4rem; }
.beitrag-aenderform textarea { width: 100%; }
.beitrag-aenderform .beitrag-aktionen {
    display: flex;
    flex-wrap: wrap;
    /* Beieinander und nicht auseinander: Die Zeile unter dem großen Feld
       trägt `space-between` (ein Knopf, der rechts steht). Hier sind es zwei,
       die zusammengehören – `Speichern` und `Abbrechen` an den beiden Enden
       eines Telefons wären zwei Entscheidungen statt einer. */
    justify-content: flex-start;
    gap: 0.5rem;
    margin-top: 0.5rem;
}

/*
 * Und der eigentliche Zustandswechsel – ohne eine Zeile JavaScript und ohne
 * eine zweite Klasse, die jemand setzen müsste: Solange die Klappe offen ist,
 * tritt der angezeigte Text zurück und `Löschen` verschwindet. Das ist der
 * „Bearbeitungszustand an Ort und Stelle" aus dem Auftrag.
 *
 * Ein Browser ohne `:has()` zeigt Text und Feld untereinander. Das ist kein
 * Defekt, sondern dieselbe Bedienung mit einer Zeile mehr – dieselbe Haltung
 * wie bei `.seite-wrapper:has(…)` weiter oben.
 */
.beitrag:has(.beitrag-aendern[open]) > .beitrag-text { display: none; }
.beitrag-werkzeuge:has(.beitrag-aendern[open]) .beitrag-wegform { display: none; }

/* Der Grabstein: Was zurückgenommen wurde, steht leise da und ruft nicht nach
   dem, was einmal darin stand. */
.beitrag-grabstein { color: var(--text-leise); font-style: italic; }
.beitrag-geaendert { font-size: 0.78rem; color: var(--text-leise); }

/* Die Auswahl hinter `@`. Sie liegt über dem Feld und nicht darunter im Fluss:
   Ein Kasten, der die Seite beim Tippen wachsen lässt, verschiebt den Text,
   den man gerade schreibt.

   Seit der Nacharbeit zu Issue #230 gilt das für **beide** Felder, in denen
   `@` getippt werden kann: das Schreibformular und die Korrektur am eigenen
   Kommentar. Zwei Auswahlausdrücke in derselben Regel und kein zweiter Block –
   es ist dieselbe Liste an einer zweiten Stelle. */
.beitrag-form,
.beitrag-aenderform { position: relative; }
.beitrag-form .formular-zeile,
.beitrag-aenderform .formular-zeile { position: relative; }

.erwaehnung-auswahl {
    position: absolute;
    z-index: 30;
    left: 0;
    right: 0;
    max-height: 13rem;
    overflow-y: auto;
    margin: 0.15rem 0 0;
    padding: 0.25rem;
    list-style: none;
    background: var(--flaeche-karte);
    border: 1px solid var(--rand-stark);
    border-radius: 6px;
    /* Derselbe Schatten wie an jeder anderen Vorschlagsliste dieser
       Oberfläche – sie soll nicht wie ein zweites Bauteil aussehen. */
    box-shadow: var(--schatten-vorschlaege);
}
.erwaehnung-auswahl li {
    display: flex;
    justify-content: space-between;
    gap: 0.6rem;
    align-items: baseline;
    padding: 0.4rem 0.55rem;
    border-radius: 4px;
    font-size: 0.9rem;
    color: var(--text-normal);
    /* Ein Tippziel und nicht nur ein Mausziel: Auf dem Telefon wird dieselbe
       Liste angetippt, und 2,25 rem sind die Höhe, die der mobile Feinschliff
       für Tippflächen dieser Art vorsieht. */
    min-height: 2.25rem;
    cursor: pointer;
}
.erwaehnung-auswahl li[aria-selected="true"],
.erwaehnung-auswahl li:hover { background: var(--flaeche-gewaehlt); }
.erwaehnung-auswahl .erwaehnung-rolle {
    font-size: 0.78rem;
    color: var(--text-leise);
    white-space: nowrap;
}
.beitrag-antwortzeile select { max-width: 32rem; }

/*
 * Die Historie ist eine Liste und kein Logdump: ein Zeichen, eine Zeit, ein
 * Name, ein Satz. Was sie ausdrücklich nicht zeigt, sind technische Felder –
 * der Verlaufstext entsteht schon im Router als deutscher Satzteil.
 */
.historie { list-style: none; margin: 0; padding: 0; }

.historie-zeile {
    display: grid;
    grid-template-columns: 6rem 9.5rem 8rem 1fr;
    gap: 0.5rem;
    align-items: baseline;
    padding: 0.35rem 0;
    border-top: 1px solid var(--rand-zart);
    font-size: 0.85rem;
}
.historie-zeile:first-child { border-top: none; }
/* Die Marke sagt in einem Wort, worum es in der Zeile ging. Sie ist bewusst
   kein Emoji: Was in der Bildpipeline des Handbuchs als leeres Kästchen
   ankommt, erklärt im Handbuch nichts. */
.historie-marke {
    color: var(--text-leise);
    font-size: 0.75rem;
    text-transform: uppercase;
    letter-spacing: 0.03em;
}
.historie-zeit { color: var(--text-leise); font-variant-numeric: tabular-nums; }
.historie-akteur { color: var(--text-leise); font-weight: 600; }
.historie-text { color: var(--text-normal); overflow-wrap: anywhere; }

/* Die Zuständigen im Formular: Kästchen nebeneinander statt untereinander. */
.zustaendig-wahl {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem 1.2rem;
    padding: 0.5rem 0.7rem;
    border: 1px solid var(--rand);
    border-radius: 6px;
    background: var(--flaeche-still);
}

/* Issue #559: Wer schon eingetragen ist, aber für neue Zuweisungen
 * ausgeschaltet wurde, trägt den Hinweis unter seinem Namen – als eigene
 * Zeile im Kästchen, damit er auf dem Telefon umbricht statt zu drängeln. */
.zustaendig-wahl .checkbox-label { flex-wrap: wrap; }
.zustaendig-ausgeschlossen {
    flex-basis: 100%;
    margin: 0;
    padding-left: 1.6rem;
}

/* ── Stufe B: Blockaden, Takte und Vorlagen ─────────────────────────────── */

/* Die Blockade. Sie liest sich wie eine Statusmarke, ist aber keine: Ein
   blockierter Stand wäre ein siebter Status, den niemand setzt – die Blockade
   ergibt sich aus einer anderen Aufgabe und verschwindet mit ihr. */
.aufgabe-marke-blockiert { background: var(--fehler-flaeche); color: var(--fehler-text); }

.aufgabe-blockade {
    margin: 0.5rem 0 0;
    font-size: 0.85rem;
    color: var(--text-mittel);
}
.aufgabe-blockade a { color: var(--text-stark); }

/* Der Serienzustand im Aufgabenkopf (Issue #237).
 *
 * Mehrere Zeilen in **einem** Beschreibungsfeld: Rhythmus, nächster Termin, die
 * Wege zu den Nachbarvorkommen und die beiden Serienaktionen. Die Zeilen
 * brauchen deshalb Luft untereinander, die ein `<br>` allein nicht gibt – und
 * eine eigene Zeile für die Aktionen wäre eine zweite Liste neben dieser.
 *
 * Kein eigener Kasten und keine eigene Farbe: Die Serie ist eine Kopfangabe wie
 * „Zuständig" und „Programmbezug" und steht in derselben Liste. Hervorgehoben
 * wird allein ihr erster Satz, und zwar mit `<strong>` – das ist, was er ist. */
.aufgabe-serie { line-height: 1.75; }
.aufgabe-serie .hinweis { display: inline; }

.abhaengig-spalten {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(18rem, 1fr));
    gap: 1.2rem;
}
/* Die Reihenfolge einer Abhängigkeitsliste (Issue #601) – über ihrer Liste. */
.abhaengig-spalte .aufgaben-sortierung { margin-bottom: 0.6rem; }
.abhaengig-spalte h4 {
    margin: 0 0 0.5rem;
    font-size: 0.9rem;
    color: var(--text-mittel);
}
.abhaengig-liste { list-style: none; margin: 0; padding: 0; }
.abhaengig-zeile {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem;
    padding: 0.4rem 0;
    border-bottom: 1px solid var(--rand-zart);
    font-size: 0.9rem;
}
/* Was noch offen ist, bremst wirklich – und steht deshalb kräftiger da als
   das, was längst erledigt ist und nur noch zeigt, worauf gewartet wurde. */
.abhaengig-zeile a { color: var(--text-leise); text-decoration: none; }
.abhaengig-offen a { color: var(--text-stark); font-weight: 600; }
.abhaengig-zeile a:hover { text-decoration: underline; }

.vorlagen-liste {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(24rem, 1fr));
    gap: 1rem;
}
.vorlage-karte {
    border: 1px solid var(--rand);
    border-radius: 8px;
    background: var(--flaeche-karte);
    padding: 1rem 1.1rem;
}
/* Eine abgeschaltete Vorlage bleibt lesbar und tritt zurück – sie ist nicht
   kaputt, sondern gerade nicht in Gebrauch. */
.vorlage-inaktiv { background: var(--flaeche-still); }
.vorlage-kopf {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem;
    margin-bottom: 0.5rem;
}
.vorlage-kopf h3 { margin: 0; font-size: 1rem; flex: 1 1 10rem; }
.vorlage-beschreibung { margin: 0 0 0.7rem; font-size: 0.9rem; color: var(--text-mittel); }
.vorlage-schritte {
    margin: 0;
    padding-left: 1.3rem;
    font-size: 0.88rem;
    color: var(--text-mittel);
}
.vorlage-schritte li {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.4rem 0.8rem;
    padding: 0.2rem 0;
}
.vorlage-schritt-titel { color: var(--text-stark); }
.vorlage-schritt-frist,
.vorlage-schritt-person { color: var(--text-leise); font-size: 0.82rem; }
.vorlage-aktionen {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    margin-top: 0.8rem;
}

/* Die Schritte in der Maske: vier Felder in einer Zeile, und der Titel nimmt
   den Platz, der übrig bleibt. */
.vorlage-posten-zeile {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem 0.8rem;
    align-items: flex-end;
    padding: 0.4rem 0;
    border-bottom: 1px solid var(--rand-zart);
}
.vorlage-posten-zeile .formular-zeile { margin: 0; }
.posten-titel { flex: 3 1 14rem; }
.posten-versatz { flex: 0 0 5.5rem; }
.posten-prio { flex: 1 1 8rem; }
.posten-person { flex: 1 1 9rem; }

/* ── Blöcke, die in sich rollen (Issue #219) ────────────────────────────────
 *
 * Drei Blöcke der Detailseite wachsen unbegrenzt: die Kommunikation, die
 * Historie und die Anhänge. **Ob** einer von ihnen diese Klasse trägt,
 * entscheidet der Server aus dem Bestand (`app/aufgabenabschnitte.py`); hier
 * steht nur, wie hoch er dann wird.
 *
 * `min(60vh, 34rem)` und keine feste Zahl: Auf einem Telefon im Querformat
 * sind 34 rem der ganze Bildschirm, auf einem großen Schreibtisch sind 60 vh
 * mehr Fläche, als ein Verlauf braucht. Die kleinere der beiden Zahlen ist
 * immer die richtige – und keine davon ist winzig: Der Auftrag verlangt
 * „mehrere Beiträge müssen gleichzeitig lesbar bleiben".
 *
 * `overflow-x: hidden` ist die zweite Zusage des Auftrags („Keine horizontale
 * Scrollfläche"). Ein Kasten mit `overflow-y: auto` bekommt sonst von selbst
 * auch waagerecht einen Balken, sobald ein einziger langer Dateiname breiter
 * ist als die Spalte.
 *
 * `scrollbar-gutter: stable` hält den Platz des Balkens frei: Ohne ihn
 * sprängen die Zeilen um seine Breite, sobald ein Beitrag dazukommt.
 */
.block-rollbar {
    max-height: min(60vh, 34rem);
    overflow-y: auto;
    overflow-x: hidden;
    scrollbar-gutter: stable;
    /* Eine zarte Kante sagt, dass hier etwas in sich rollt – ohne sie sieht
       eine abgeschnittene letzte Zeile nach einem Fehler aus. */
    border: 1px solid var(--rand-zart);
    border-radius: 8px;
    padding: 0.5rem;
}
/* Der Tastaturhalt an einer rollenden Fläche bekommt denselben Ring wie jedes
   andere Bedienelement des Hauses. */
.block-rollbar:focus-visible { outline: 2px solid var(--kontur); outline-offset: 2px; }

/* Ein langer Dateiname oder eine URL ohne Leerzeichen ist der einzige Inhalt,
   der einen Beitrag breiter machen kann als seine Spalte. */
.beitrag-text { overflow-wrap: anywhere; }

/* Die Auskunft in der Überschrift, dass dieser Block gerade kompakt steht. Sie
   ist keine Schaltfläche – die Darstellung folgt aus der Menge und nicht aus
   einer Wahl. */
.block-modus {
    font-size: 0.78rem;
    font-weight: 400;
    color: var(--text-leise);
}

/* ── Die kompakte Anhangliste (Issue #219) ──────────────────────────────────
 *
 * Martins Anlass ist eine Aufgabe „30 Jingles produzieren": Dreißig
 * ausführliche Karten sind keine Liste mehr. Ab der Schwelle in
 * `app/aufgabenabschnitte.py` bleibt je Datei eine Zeile stehen; alles zum
 * Ansehen und Bearbeiten liegt in der Klappe darin.
 *
 * Die Karte wird dafür flacher und nicht anders: derselbe Grund, derselbe
 * Rand, dieselben Farben. Nur Polster und Abstände schrumpfen – eine zweite
 * Kartenart daneben wäre im dunklen Modus die Stelle, die nachzieht.
 */
.anhang-liste-kompakt { gap: 0.3rem; }
.anhang-liste-kompakt .anhang-eintrag { padding: 0.45rem 0.7rem; }
.anhang-liste-kompakt .anhang-kopf { margin: 0; }
.anhang-liste-kompakt .anhang-herkunft { margin: 0; }
/* Der Abspieler bleibt in der Zeile – „Anhören muss weiterhin direkt möglich
   sein" –, nimmt dort aber nicht mehr die ganze Breite: Neben dem Namen ist er
   eine Bedienleiste und kein Block. */
.anhang-liste-kompakt .anhang-spieler {
    max-width: 22rem;
    margin: 0.35rem 0 0;
}
/* Die Klappe mit allem Übrigen. Sie sieht aus wie ein Verweis und nicht wie
   eine Schaltfläche: Sie öffnet etwas, sie tut nichts. */
.anhang-klapp { margin: 0.3rem 0 0; }
.anhang-klapp-knopf {
    cursor: pointer;
    font-size: 0.82rem;
    color: var(--text-leise);
    /* 44 px Tippziel bekommt sie auf dem Telefon im Block darunter. */
    padding: 0.15rem 0;
}
.anhang-klapp-knopf:hover { color: var(--text-normal); }
.anhang-klapp[open] .anhang-klapp-knopf { margin-bottom: 0.4rem; }

/* ── Die Aufgabenverwaltung (Issue #219) ────────────────────────────────────
 *
 * Die Liste, mit der die Reihenfolge der Blöcke eingestellt wird. Sie ist
 * derselbe Zeilensatz wie die Warteschlange der Entwicklungszentrale: Platz,
 * Text, Griffe – und dieselbe Bedienung mit Ziehen **und** Pfeilen.
 */
.abschnitt-liste {
    list-style: none;
    margin: 0.8rem 0 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
}
.abschnitt-zeile {
    display: flex;
    align-items: center;
    gap: 0.7rem;
    padding: 0.6rem 0.8rem;
    background: var(--flaeche-karte);
    border: 1px solid var(--rand);
    border-radius: 8px;
}
/* Der Kopf steht fest und sagt es auch: still, ohne Griffe, ohne Nummer. */
.abschnitt-fest {
    background: var(--flaeche-still);
    border-style: dashed;
}
.abschnitt-platz {
    flex: 0 0 2.2rem;
    text-align: center;
    font-variant-numeric: tabular-nums;
    font-weight: 600;
    color: var(--text-leise);
    font-size: 0.85rem;
}
.abschnitt-text {
    flex: 1 1 auto;
    display: flex;
    flex-direction: column;
    gap: 0.1rem;
    min-width: 0;
}
.abschnitt-name { font-weight: 600; color: var(--text-stark); }
.abschnitt-erklaerung,
.abschnitt-grund { font-size: 0.82rem; color: var(--text-leise); }
.abschnitt-griffe {
    display: flex;
    align-items: center;
    gap: 0.25rem;
    flex: 0 0 auto;
}
.abschnitt-griff,
.abschnitt-pfeil {
    background: var(--flaeche-still);
    color: var(--text-mittel);
    border: 1px solid var(--rand);
    border-radius: 6px;
    min-width: 2rem;
    min-height: 2rem;
    font-size: 0.95rem;
    line-height: 1;
    cursor: pointer;
}
.abschnitt-griff { cursor: grab; }
.abschnitt-griff:active { cursor: grabbing; }
.abschnitt-griff:hover,
.abschnitt-pfeil:hover { background: var(--flaeche-gewaehlt); color: var(--text-stark); }
/* Die gezogene Zeile wird blass, die Zielzeile bekommt eine Kante: Ohne beides
   ist Ziehen ein Ratespiel. */
.abschnitt-zieht { opacity: 0.5; }
.abschnitt-ziel { border-color: var(--kontur); }

.abschnitt-regeln {
    margin: 0.4rem 0 0;
    padding-left: 1.2rem;
    color: var(--text-normal);
    font-size: 0.9rem;
    line-height: 1.6;
}

/*
 * Das Telefon. Nichts wird versteckt – der Auftrag verlangt ausdrücklich, dass
 * die schnellen Aktionen auf dem Handy erreichbar bleiben. Sie werden nur
 * größer und rutschen in eine eigene Zeile.
 */
@media (max-width: 640px) {
    .aufgaben-filter,
    .aufgaben-sortierung,
    .schnelleingabe { padding: 0.8rem; }

    /* Vier Felder nebeneinander sind auf 390 px vier Wortfetzen. Ein Schritt
       einer Vorlage steht auf dem Telefon deshalb untereinander. */
    .vorlage-posten-zeile .formular-zeile { flex: 1 1 100%; }
    .vorlage-posten-zeile { padding: 0.6rem 0; }
    .vorlage-posten-zeile input,
    .vorlage-posten-zeile select { min-height: 44px; }
    .vorlage-aktionen .schaltflaeche,
    .abhaengig-zeile .schaltflaeche { min-height: 44px; display: inline-flex; align-items: center; }

    .schnell-feld,
    .schnell-titel { flex: 1 1 100%; }
    .schnell-knopf .schaltflaeche,
    .filter-knoepfe .schaltflaeche { width: 100%; text-align: center; }
    .filter-knoepfe { flex-direction: column; align-items: stretch; }

    /* 44 px sind das Maß, das der Mobile-Audit #35 für jedes Tippziel gesetzt
       hat. Es gilt hier für die schnellen Aktionen genauso wie überall. */
    .schnell-feld input,
    .schnell-feld select,
    .filter-feld input,
    .filter-feld select { min-height: 44px; }

    .aufgabe-aktionen .schaltflaeche,
    .status-wahl .schaltflaeche,
    .sicht-knopf { min-height: 44px; display: inline-flex; align-items: center; }

    .aufgabe-karte { padding: 0.8rem 0.9rem; }
    .aufgabe-titel { flex: 1 1 100%; }

    /* Die Vorschau der Beschreibung stärker begrenzen (Issue #211): „damit eine
       lange Aufgabe nicht den ganzen Bildschirm belegt". Die Bahnen liegen hier
       ohnehin untereinander – das regelt der Umbruch und keine Regel in diesem
       Block. */
    .aufgabe-vorschau { max-height: calc(2 * 1.5 * 0.85rem + 1rem); }

    .kopfdaten-liste { grid-template-columns: 1fr; gap: 0.1rem 0; }
    .kopfdaten-liste dd { margin-bottom: 0.5rem; }

    /* Die Historie bricht auf zwei Zeilen um: oben Zeichen, Zeit und Name,
       darunter der Satz über die volle Breite. Vier Spalten auf 390 px
       ließen von jeder Spalte zwei Wörter übrig. */
    .historie-zeile {
        grid-template-columns: auto auto 1fr;
        row-gap: 0.15rem;
    }
    .historie-text { grid-column: 1 / -1; }

    /* ── Issue #219 auf dem Telefon ──────────────────────────────────────── */

    /* Eine rollende Fläche darf auf 390 px nicht den ganzen Bildschirm nehmen –
       sonst sieht man den Block darunter nie und hält die Seite für zu Ende.
       50 vh lassen Überschrift und Anfang des nächsten Blocks stehen. */
    .block-rollbar { max-height: 50vh; }

    /* Die Klappe an einer kompakten Anhangzeile ist auf dem Telefon ein
       Tippziel wie jedes andere: 44 px, das Maß aus dem Mobile-Audit #35. */
    .anhang-klapp-knopf { min-height: 44px; display: flex; align-items: center; }

    /* Und genauso die beiden Wörter am eigenen Beitrag (Issue #230): Am
       Schreibtisch sind sie eine Fußnote, auf dem Telefon sind sie ein
       Tippziel. Nebeneinander bleiben sie trotzdem – 1,4 rem Abstand reichen,
       damit zwischen „Bearbeiten" und „Löschen" kein Daumen rutscht, und zwei
       Zeilen für zwei Wörter wären auf 390 px verschenkter Platz. */
    .beitrag-werkzeuge { gap: 0 1.4rem; align-items: stretch; }
    .beitrag-werkzeug { min-height: 44px; display: inline-flex; align-items: center; }
    .beitrag-aenderform .schaltflaeche { min-height: 44px; }

    /* Die Zeilen der Aufgabenverwaltung brechen um: oben Nummer und Name,
       darunter die Griffe über die volle Breite. Drei Knöpfe von 44 px neben
       einem Namen ließen vom Namen zwei Wörter übrig. */
    .abschnitt-zeile { flex-wrap: wrap; }
    .abschnitt-text { flex: 1 1 60%; }
    .abschnitt-griffe { flex: 1 1 100%; justify-content: flex-end; }
    .abschnitt-griff,
    .abschnitt-pfeil { min-width: 44px; min-height: 44px; }
}

/* ── Entwicklungszentrale (Issue #105) ───────────────────────────────────────
 *
 * Die eigene Oberfläche unter `entwicklung.martinheuser.de`. Sie benutzt
 * dieselben Farbtoken wie alles Übrige und deshalb von selbst denselben
 * hellen und dunklen Modus – eigene Farbwerte hier wären eine zweite Quelle,
 * die im Dunkeln stehen bliebe.
 *
 * Mobil zuerst, und zwar wörtlich: Was hier ohne `@media` steht, ist die
 * Darstellung auf dem Telefon. Die Regeln am Ende des Blocks stellen am
 * Schreibtisch um – nicht umgekehrt. Auf einem 390 px breiten Bildschirm gibt
 * es damit keine verkleinerte Schreibtischansicht, sondern eine Spalte
 * gestapelter Karten.
 *
 * Der Kopf ist bewusst ein anderer als der der Redaktion: kein Signet, kein
 * Radio-GUDE-Rot, keine Bereichsleiste. Die Entwicklungszentrale ist eine
 * Plattform für mehrere Projekte, und Radio GUDE ist eines davon.
 */

.zen-koerper {
    margin: 0;
    background: var(--flaeche-seite);
    color: var(--text-normal);
}

/* ── Der Kopf ───────────────────────────────────────────────────────────── */

.zen-kopf {
    position: sticky;
    top: 0;
    z-index: 20;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem 1rem;
    padding: 0.6rem 1rem;
    background: var(--kopf-flaeche);
    color: var(--kopf-text);
}

.zen-marke {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    text-decoration: none;
    color: inherit;
}

/*
 * Das Signet ist Martins Portrait – siehe `zentrale_rahmen.html`.
 *
 * Die feste Kantenlänge steht hier **und** als `width`/`height` am Element:
 * Sie hält den Platz frei, bevor das Bild da ist, und die Kopfzeile rutscht
 * beim Nachladen nicht einmal zur Seite (derselbe Gedanke wie am Logo der
 * Redaktion, Issue #82).
 *
 * `object-fit: cover` gehört dazu, weil ein Portrait nicht zwingend quadratisch
 * ankommt: Ohne die Zeile zöge der Browser ein hochformatiges Bild in die
 * Quadratfläche und machte aus einem Gesicht ein gestauchtes.
 *
 * Es ist zweimal gewachsen und einmal wieder ein Stück zurückgegangen. Zuerst
 * von 34 auf 44/56 px (Martins zweite Abnahme zu #105): 34 px waren zu
 * zurückhaltend – „sichtbar präsenter, aber nicht dominierend" war der
 * Auftrag. Mit Issue #108 kam derselbe Wunsch noch einmal, jetzt für Zentrale
 * und Monitor gemeinsam: 52 px auf dem Telefon, 68 px am Schreibtisch. Seit
 * Issue #164 sind es am Schreibtisch wieder 56 px – siehe die Regel im
 * 900-px-Block.
 *
 * 52 px liegt weiter über der Tippfläche dieses Bestands (44 px, Issue #35),
 * und die Marke ist ein Verweis. Der Kopf bleibt einzeilig: Die beiden
 * Textzeilen daneben sind zusammen rund 38 px hoch, das Bild gibt die Höhe
 * auf dem Telefon vor – 52 px zuzüglich der 0,6 rem Innenabstand oben und
 * unten sind knapp 72 px, und damit steht die Kopfzeile immer noch auf einer
 * Zeile.
 *
 * **Auf dem Telefon** gelten dieselben 52 px im Monitor
 * (`monitor/static/style.css`, `.mon-signet`). Am Schreibtisch tun sie es
 * seit #164 nicht mehr: Dort misst das Portrait hier 56 px und im Monitor
 * weiterhin 68. Die Kopplung aus #108 war eine Zusage an zwei Köpfe, die
 * nebeneinander gleich aussehen sollten; verglichen wird seitdem aber nicht
 * mehr Zentrale gegen Monitor, sondern Zentrale gegen Redaktion (#164). Der
 * Monitor bleibt deshalb ausdrücklich unangetastet – er ist ein eigener
 * Dienst mit eigenem Deployment und steht in keinem dieser Vergleiche.
 */
.zen-signet {
    width: 52px;
    height: 52px;
    flex: 0 0 52px;
    border-radius: 13px;
    object-fit: cover;
    background: var(--flaeche-sanft);
}

.zen-marke-text { display: flex; flex-direction: column; line-height: 1.15; }
.zen-name { color: var(--kopf-text-hell); font-weight: 600; font-size: 1rem; }
.zen-untertitel { font-size: 0.72rem; color: var(--kopf-text); }

/*
 * Auf dem Telefon teilt sich der Kopf zwei Zeilen: oben die Marke und der
 * angemeldete Zugang, darunter die beiden Bereiche. Gesteuert wird das über
 * `order` und nicht über eine zweite Kopfzeile – dasselbe Dokument, nur eine
 * andere Reihenfolge. Drei Zeilen übereinander hätten auf einem 390 px
 * breiten Bildschirm ein Fünftel der Höhe belegt, bevor der Inhalt anfängt.
 */
.zen-nav {
    display: flex;
    gap: 0.4rem;
    flex-wrap: wrap;
    order: 3;
}

/*
 * Mindestens 44 px hoch: Das ist die Tippfläche, die der mobile Feinschliff
 * für jedes Bedienelement dieser Anwendung verlangt (Issue #35). Ein Verweis,
 * den man auf dem Telefon dreimal trifft, ist kein Verweis.
 */
/*
 * Hervorgehoben wird hier über Schrift und Kante und nicht über eine Fläche.
 * Der Grund ist kein Geschmack: Der Kopf ist in beiden Modi dunkel, und die
 * Flächentoken dieses Stylesheets kippen zwischen hell und dunkel. Eine
 * aufgehellte Fläche bräuchte deshalb einen festen Farbwert – und ein fester
 * Farbwert wäre genau die zweite Quelle, die dieses Stylesheet nirgends hat.
 * Unterstrich und Kante kommen mit den vorhandenen Kopftoken aus.
 */
.zen-nav-link,
.zen-abmelden {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
    padding: 0 0.7rem;
    border-bottom: 2px solid transparent;
    color: var(--kopf-text);
    text-decoration: none;
    font-size: 0.9rem;
}

.zen-nav-link:hover,
.zen-abmelden:hover { color: var(--kopf-text-hell); text-decoration: underline; }

.zen-nav-link.aktiv {
    color: var(--kopf-text-hell);
    border-bottom-color: var(--kopf-akzent);
    font-weight: 600;
}

/* ── Die Seitenbeschriftung wächst um ihre Mitte (Issue #342) ─────────────
 *
 * Martins Befund vom 15.10.2026 in Firefox: Wird ein Seitenpunkt aktiv, wird
 * sein Wort fett und damit breiter – und es wuchs nach rechts. Der Verweis war
 * ein Flex-Kasten, der so breit ist wie sein Text; mit der Fettschrift wurde
 * er breiter, und alles dahinter in der Zeile rückte mit.
 *
 * Gelöst wird es mit dem Prinzip aus Issue #163 (`.menue-link[data-titel]`
 * in der Redaktion), aber nicht mit derselben Regel: Dort ist der Verweis ein
 * Inline-Kasten, hier ein Flex-Kasten, und darin stünde der Messabdruck neben
 * dem Wort statt darüber. Der Verweis wird deshalb zu einem Raster mit einer
 * Spalte:
 *
 *   * **Zeile 1** ist der Messabdruck – dieselbe Beschriftung aus
 *     `data-titel` in `600`, 0 px hoch, unsichtbar. Er macht die Spalte von
 *     vornherein so breit wie das fette Wort, ganz gleich, ob der Punkt gerade
 *     markiert ist. Damit stehen Fläche und Nachbarn in beiden Zuständen an
 *     derselben Stelle.
 *   * **Zeile 2** ist das sichtbare Wort, **mittig** in dieser Spalte. Es
 *     wächst beim Aktivwerden zu gleichen Teilen nach links und nach rechts
 *     – um denselben Mittelpunkt.
 *
 * Anders als in der Redaktion fehlt dem Abdruck hier `overflow: hidden`, und
 * das ist Absicht: Ein Rasterelement mit abgeschnittenem Überlauf darf bis auf
 * 0 px schrumpfen, und eine knappe Zeile nähme ihm dann genau die reservierte
 * Breite wieder weg. So bleibt das fette Wort die Untergrenze der Spalte.
 * Gezeichnet wird er trotzdem nicht – `visibility: hidden`.
 *
 * `align-content: center` ersetzt das `align-items: center` des Flex-Kastens:
 * Die beiden Zeilen sind zusammen genau eine Textzeile hoch und stehen damit
 * auf derselben Grundlinie wie vorher. Höhe, Schriftgrad, Kante und Farbe
 * kommen unverändert aus `.zen-nav-link`, das Polster in seiner Summe auch
 * (siehe den letzten Absatz) – diese Regel steht daneben und
 * nicht darin, weil Projektumschalter, `Ansicht` und `Abmelden` dieselbe
 * Klasse tragen und kein Messabdruck sie verformen soll, und weil der Monitor
 * die Regel von `.zen-nav-link` Zeichen für Zeichen teilt
 * (`tests/test_monitor.py`, L3 bis L5).
 *
 * Für Vorlesehilfen bleibt der Verweis ein Wort: `visibility: hidden` nimmt
 * den Abdruck aus dem zugänglichen Namen, und die zweite `content`-Zeile sagt
 * es ausdrücklich mit leerem Alternativtext – ein Browser, der diese
 * Schreibweise nicht kennt, verwirft sie und behält die erste.
 *
 * **Der Platz für das fette Wort kommt aus dem Polster, nicht dazu.** Die
 * erste Fassung reservierte ihn zusätzlich, und jeder Punkt wurde um seinen
 * Fettzuwachs breiter als vorher – auf der Übersicht bei 1152 px genug, dass
 * der Kopf in eine zweite Zeile brach (Issue #164, 72 px), auf dem Telefon
 * genug, dass ein Punkt in die andere Reihe rutschte. Deshalb trägt der
 * Verweis nur noch einen schmalen Mindestrand (`--zen-seite-rand`), und eine
 * dritte Zeile – **Zeile 3**, derselbe Abdruck in `400`, ebenfalls 0 px hoch –
 * bringt den Rest des gewohnten Polsters (`--zen-seite-polster`, dieselbe Zahl
 * wie das `padding` von `.zen-nav-link`) als eigenes Polster mit. Die Spalte
 * ist damit so breit wie das größere von beiden: das fette Wort oder das
 * normale samt Polster. Solange der Fettzuwachs in das Polster passt – bei
 * 0,5 rem je Seite am Schreibtisch und 0,3 rem auf dem Telefon jede Schrift
 * dieses Bestands –, ist jeder Punkt in jedem Zustand genau so breit wie
 * früher ein ruhender, und die Leiste braucht keinen Bildpunkt mehr als vorher.
 * Wäre er doch einmal größer, wird die Spalte so breit wie das fette Wort –
 * fest und mittig bleibt es trotzdem.
 */
.zen-nav-seite {
    --zen-seite-polster: 0.7rem;
    --zen-seite-rand: 0.2rem;
    display: inline-grid;
    align-content: center;
    justify-items: center;
    padding-inline: var(--zen-seite-rand);
    text-align: center;
}

.zen-nav-seite[data-titel]::before {
    content: attr(data-titel);
    content: attr(data-titel) / "";
    height: 0;
    visibility: hidden;
    white-space: nowrap;
    font-weight: 600;
}

.zen-nav-seite[data-titel]::after {
    content: attr(data-titel);
    content: attr(data-titel) / "";
    height: 0;
    padding-inline: calc(var(--zen-seite-polster) - var(--zen-seite-rand));
    visibility: hidden;
    white-space: nowrap;
    font-weight: 400;
}

.zen-rechts {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin-left: auto;
    order: 2;
    font-size: 0.85rem;
}

.zen-benutzer {
    color: var(--kopf-text-hell);
    max-width: 12rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Dieselbe Überlegung wie eine Regel höher: eine Kante statt einer Fläche,
   damit hier kein fester Farbwert nötig wird. */
.zen-rolle {
    padding: 0.1rem 0.5rem;
    border: 1px solid var(--kopf-text);
    border-radius: 999px;
    font-size: 0.75rem;
}

/* ── Der Projektumschalter in der Kopfzeile (Issue #232) ─────────────────
 *
 * Er steht hier bei den Regeln des Kopfes und nicht bei denen der Seite: Die
 * Projektwahl war bis Issue #232 eine Kachel im Inhalt, und ihre Regeln
 * standen entsprechend dort. Sie ist jetzt ein Bedienelement der Leiste – mit
 * denselben Maßen wie die Einträge daneben, denselben `--kopf-`-Token und
 * demselben Verhalten wie das `Ansicht`-Menü am anderen Ende: auf dem Telefon
 * ein Blatt am unteren Rand, am Schreibtisch ein Klappmenü unter dem
 * Auslöser.
 *
 * **Mobil zuerst**, und hier heißt das wirklich etwas anderes: Was ohne
 * Breitenabfrage steht, ist die Bedienung für Telefon und Tablet. Am
 * Schreibtisch stellt der 900-px-Block darunter um.
 *
 * Kein fester Farbwert kommt vor: Der Auslöser nimmt die Kopftoken, das Blatt
 * steht auf der Kartenfläche des Bestands mit dessen Kante und Schatten.
 */

/* Die Hülle. Am Schreibtisch wird sie zum Bezugspunkt des Klappmenüs (siehe
   dort); auf dem Telefon bleibt sie unpositioniert, denn das Blatt hängt dort
   am Fenster und nicht am Auslöser. */
.zen-projektwahl { display: inline-flex; }

/*
 * Der Auslöser trägt zusätzlich `.zen-nav-link` und damit Höhe, Polster,
 * Schriftgrad und Farbe der Einträge daneben. Hier steht nur, was ein
 * `<summary>` darüber hinaus braucht: kein Dreieck – weder das des Browsers
 * noch das von Safari –, ein Zeiger, der Abstand zwischen Beschriftung und
 * Winkel und die Zusage, dass die Leiste an ihm nicht auseinanderfällt.
 *
 * `max-width` mit `min()` ist die Vorkehrung gegen die waagerechte
 * Scrollleiste, die der Auftrag ausdrücklich ausschließt: Ein langer
 * Projektname darf die Kopfzeile nicht breiter machen als das Fenster. Er
 * bricht dann nicht um – er endet mit Auslassungspunkten, und der ganze Name
 * steht im Menü darunter.
 */
.zen-projektwahl-taste {
    gap: 0.35rem;
    max-width: min(20rem, calc(100vw - 2rem));
    cursor: pointer;
    list-style: none;
    user-select: none;
}

.zen-projektwahl-taste::-webkit-details-marker { display: none; }

.zen-projektwahl-taste:focus-visible {
    outline: 2px solid var(--kopf-text-hell);
    outline-offset: -2px;
}

/*
 * Beschriftung und Name stehen **übereinander** – dieselbe Bauart wie die
 * Marke links daneben.
 *
 * Das ist keine Kosmetik, sondern gemessener Platz: In einer Zeile ist der
 * Auslöser 183 px breit, und bei 1152 px bricht die Kopfzeile dann in eine
 * zweite Zeile um – gegen die vereinbarten 72 px aus Issue #164. Übereinander
 * ist er 120 px breit, denn die Breite ist die des längeren der beiden Wörter,
 * und die Höhe bleibt unter den 34 px des Eintrags. Die Beschriftung bleibt
 * damit auf jedem Gerät sichtbar, statt am Schreibtisch zu verschwinden.
 */
.zen-projektwahl-text {
    display: flex;
    min-width: 0;
    flex-direction: column;
    line-height: 1.15;
}

/* Das Wort `Projekt` ist die Beschriftung und nicht die Auskunft – es bleibt
   deshalb kleiner und leiser als der Name darunter. */
.zen-projektwahl-wort {
    font-size: 0.62rem;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    opacity: 0.85;
}

/* Der Name ist die Auskunft und darf als Einziger schrumpfen. */
.zen-projektwahl-name {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    color: var(--kopf-text-hell);
    font-weight: 600;
}

/* Das Winkelzeichen – in Pixeln und nicht in `em`: Der Eintrag ist auf dem
   Telefon kleiner gesetzt als am Schreibtisch, das Zeichen soll es nicht
   sein. Dieselbe Überlegung wie am Zahnrad der Ansicht. */
.zen-projektwahl-pfeil {
    width: 14px;
    height: 14px;
    flex: 0 0 14px;
    display: block;
}

/* Geöffnet dreht sich das Zeichen – die einzige Rückmeldung, die das Menü
   braucht, und sie kostet keinen zweiten Zustand im Dokument. */
.zen-projektwahl[open] > .zen-projektwahl-taste .zen-projektwahl-pfeil {
    transform: rotate(180deg);
}

/*
 * Das Blatt am unteren Rand – die Fassung für Telefon und Tablet, aus
 * demselben Grund wie beim `Ansicht`-Menü: Die Navigation ist dort die zweite
 * Kopfzeile, und ein Menü, das an ihr hinge, stünde je nach Breite einmal
 * links und einmal rechts an ihrem Rand.
 */
.zen-projektwahl-menue {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 40;
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
    padding: 0.4rem 0.4rem max(0.4rem, env(safe-area-inset-bottom));
    background: var(--flaeche-karte);
    border-top: 1px solid var(--rand);
    border-radius: 14px 14px 0 0;
    box-shadow: var(--schatten-dialog);
    /* Ein Blatt, das länger wird als der Bildschirm, scrollt in sich selbst.
       Bei einem Projekt tut es das nie – bei zwanzig irgendwann schon. */
    max-height: 70vh;
    overflow-y: auto;
}

/*
 * Ein Projekt ist eine ganze Zeile: Auf dem Telefon ist das die Tippfläche,
 * die der mobile Feinschliff verlangt (44 px, Issue #35), und auf jedem Gerät
 * die Form, in der zwei verschieden lange Namen gleich aussehen.
 *
 * Die Kante links trägt die Projektfarbe (siehe `.zen-farbe-*` darunter) – sie
 * ist das, was von der alten Projektkachel übrig bleibt und hier mehr nützt
 * als dort: Zwei Projekte in einer Liste unterscheidet man an ihr auf einen
 * Blick.
 */
.zen-projektwahl-punkt {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    width: 100%;
    min-height: 44px;
    padding: 0.4rem 0.6rem;
    border: 1px solid transparent;
    border-left: 4px solid var(--rand-stark);
    border-radius: 8px;
    color: var(--text-normal);
    text-decoration: none;
    font-size: 0.95rem;
}

.zen-projektwahl-punkt:hover { background: var(--flaeche-zart); }
.zen-projektwahl-punkt:focus-visible {
    outline: 2px solid var(--kontur);
    outline-offset: -2px;
}

/*
 * Das aktive Projekt ist auf drei Wegen erkennbar: am Haken, an der Schrift
 * und an der Fläche. Farbe allein wäre keiner davon.
 *
 * Drei Kanten einzeln und nicht `border-color` in einem: Die linke gehört der
 * Projektfarbe (`.zen-farbe-*` darunter), und eine Sammelangabe hier
 * überschriebe sie – sie ist spezifischer, egal in welcher Reihenfolge die
 * beiden Regeln stehen. Ausgerechnet das aktive Projekt verlöre dann seine
 * Kennzeichnung.
 */
.zen-projektwahl-punkt.aktiv {
    border-top-color: var(--kontur);
    border-right-color: var(--kontur);
    border-bottom-color: var(--kontur);
    background: var(--flaeche-gewaehlt);
}

.zen-projektwahl-punkt.aktiv .zen-projektwahl-punkt-name {
    font-weight: 700;
}

/*
 * Die Projektfarbe kommt als Wort aus `app/projekte.py` und wird hier zu einer
 * Kante. Ein freier Farbwert aus der Konfiguration stünde im dunklen Modus als
 * zweite Quelle daneben; ein Wort trifft in beiden Modi dasselbe Token. Für
 * ein neues Projekt kommt hier eine Zeile dazu – und sonst nichts.
 */
.zen-farbe-rot { border-left-color: var(--akzent); }
.zen-farbe-blau { border-left-color: var(--hinweis-kante); }
.zen-farbe-gruen { border-left-color: var(--erfolg-kraeftig); }
.zen-farbe-violett { border-left-color: var(--violett-text); }

/*
 * Der Haken. Sein Platz ist **immer** da, auch beim nicht gewählten Projekt:
 * Ohne die feste Breite rückten die Namen der übrigen Einträge um seine Breite
 * nach links, und eine Liste, deren Zeilen nicht untereinander anfangen, liest
 * sich schlechter als eine mit einer kleinen Leerstelle darin.
 */
.zen-projektwahl-haken {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 1.1rem;
    width: 1.1rem;
    color: var(--kontur);
}

.zen-projektwahl-haken svg { width: 1.1rem; height: 1.1rem; display: block; }

.zen-projektwahl-punkt-text {
    display: flex;
    min-width: 0;
    flex-direction: column;
    gap: 0.1rem;
}

.zen-projektwahl-punkt-name { color: var(--text-stark); font-weight: 600; }

/* Das Repository – die Angabe, die vorher unter dem Projektknopf stand. Sie
   ist hier klein und leise: Sie beantwortet eine Frage, die man selten stellt,
   und soll den Namen darüber nicht überstimmen. */
.zen-projektwahl-repo {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: 0.75rem;
    color: var(--text-leise);
}

/* Und die Beschreibung des aktiven Projekts, am Fuß des geöffneten Menüs. Sie
   ist der Ersatz für den Text hinter dem früheren Info-Zeichen – und steht
   damit weiterhin da, ohne auf der Seite Platz zu belegen. */
.zen-projektwahl-kurz {
    margin: 0.3rem 0.2rem 0.1rem;
    padding-top: 0.4rem;
    border-top: 1px solid var(--rand);
    color: var(--text-leise);
    font-size: 0.8rem;
}

/* ── Die Seite ──────────────────────────────────────────────────────────── */

.zen-haupt { padding: 1rem; }

/*
 * Die Seite nimmt die Breite, die da ist (Issue #110).
 *
 * Bis dahin stand hier `max-width: 1100px; margin: 0 auto`. Auf einem
 * 1440-px-Bildschirm blieben links und rechts je 170 px leer, während die
 * Seite nach unten immer länger wurde – und auf einem breiteren Bildschirm
 * wurde der leere Rand größer statt der Inhalt. Das war der Befund des
 * Auftrags.
 *
 * Was den Rand jetzt macht, ist der Innenabstand von `.zen-haupt` – 1 rem auf
 * dem Telefon, 1,5 rem am Schreibtisch. Eine Obergrenze für die Breite gibt
 * es bewusst nicht mehr: Die Kacheln stehen am Schreibtisch zu zweit
 * nebeneinander, und was breiter wird, ist die Spalte und nicht die Zeile.
 */
.zen-seite {
    max-width: none;
    display: flex;
    flex-direction: column;
    gap: 0.55rem;
    /* Nichts in dieser Seite darf breiter werden als ihr Kasten – dieselbe
       Zeile und derselbe Grund wie auf dem Entwicklungsstatus. */
    min-width: 0;
}

.zen-titel { margin: 0; font-size: 1.5rem; color: var(--text-stark); }
.zen-kurz { margin: 0; color: var(--text-leise); font-size: 0.9rem; }
.zen-leise { color: var(--text-leise); font-size: 0.85rem; }

.zen-abschnitt-titel {
    margin: 0 0 0.5rem;
    font-size: 0.8rem;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--text-leise);
}

.zen-karte {
    background: var(--flaeche-karte);
    border: 1px solid var(--rand);
    border-radius: 10px;
    padding: 1rem;
}

.zen-hinweis {
    margin: 0.4rem 0;
    padding: 0.6rem 0.8rem;
    border-left: 3px solid var(--hinweis-kante);
    background: var(--hinweis-flaeche);
    color: var(--text-normal);
    font-size: 0.9rem;
}

.zen-hinweis-leise { margin: 0.4rem 0 0; color: var(--text-leise); font-size: 0.82rem; }

.zen-warnung {
    margin: 0.4rem 0;
    padding: 0.6rem 0.8rem;
    border-left: 3px solid var(--warnung-kraeftig);
    background: var(--warnung-flaeche-blass);
    color: var(--warnung-text);
    font-size: 0.9rem;
}

/* Issue #406: Speicherlage je Dateisystem; lange Pfade brechen statt zu überlaufen. */
.zen-speicherlage p,
.zen-runtimelage p { margin: 0.2rem 0; }
/* Issue #404: Dateiliste der Runtime-Installation nutzt dieselbe umbrechende Liste. */
.zen-speicher-liste {
    margin: 0.3rem 0;
    padding-left: 1.1rem;
    overflow-wrap: anywhere;
}
/* Issue #412: Stillstand als Fehler, laufender Worker als Warnung; Details aufklappbar. */
.zen-speicherlage-gesperrt {
    border-left-color: var(--fehler-kraeftig);
    background: var(--fehler-flaeche-zart);
    color: var(--fehler-text);
}
.zen-speicher-details { margin-top: 0.3rem; overflow-wrap: anywhere; }
.zen-speicher-details summary { cursor: pointer; padding: 0.7rem 0; }

.zen-handeln {
    margin: 0.4rem 0 0;
    padding: 0.6rem 0.8rem;
    border-left: 3px solid var(--erfolg-kraeftig);
    background: var(--erfolg-zart-flaeche);
    color: var(--erfolg-zart-text);
    font-size: 0.9rem;
}

/* ── Die Kennzahlen ─────────────────────────────────────────────────────── */

.zen-zahlen { display: flex; flex-wrap: wrap; gap: 0.6rem; }

.zen-zahl {
    display: flex;
    flex: 1 1 8rem;
    flex-direction: column;
    gap: 0.1rem;
    min-height: 44px;
    padding: 0.6rem 0.9rem;
    border: 1px solid var(--rand);
    border-radius: 10px;
    background: var(--flaeche-karte);
    text-decoration: none;
    color: var(--text-normal);
}

.zen-zahl-wert { font-size: 1.6rem; font-weight: 700; color: var(--text-stark); line-height: 1; }
.zen-zahl-titel { font-size: 0.78rem; color: var(--text-leise); }
.zen-zahl-gesamt { background: var(--flaeche-sanft); }

/* ── Die Statuskarte ────────────────────────────────────────────────────── */

.zen-jetzt-zustand { margin: 0; font-size: 1.05rem; color: var(--text-stark); }
.zen-jetzt-auftrag { margin: 0.3rem 0 0; font-size: 0.95rem; }
.zen-jetzt-zeit { margin: 0.3rem 0 0; font-size: 0.95rem; color: var(--text-stark); }

/*
 * Die beiden mitzählenden Uhren – Laufzeit und Countdown der Automatik.
 *
 * `tabular-nums` ist hier keine Feinheit, sondern der Unterschied zwischen
 * einer Uhr und einem Zappeln: Ohne sie ist jede Ziffer verschieden breit, und
 * die Zahl wackelt im Sekundentakt hin und her. Der Text daneben rutschte
 * dabei mit.
 */
.zen-laufzeit { font-variant-numeric: tabular-nums; }

/* Der Zustand färbt die linke Kante und nicht die Fläche: Eine ganze rote
   Karte liest sich als Alarm über die Seite und nicht über den Lauf. */
.zen-jetzt { border-left: 4px solid var(--rand-stark); }
.zen-lage-arbeit { border-left-color: var(--warnung-kraeftig); }
.zen-lage-gut { border-left-color: var(--erfolg-kraeftig); }
.zen-lage-ruhig { border-left-color: var(--erfolg-kraeftig); }
.zen-lage-fehler { border-left-color: var(--fehler-kraeftig); }
.zen-lage-unbekannt { border-left-color: var(--rand-still); }

/* ── Die Fortschrittskachel (zweite Nacharbeit zu Issue #105) ───────────── */

/*
 * Die Karte trägt die Farbe des Fortschritts an der linken Kante – dieselbe
 * Sprache wie die Statuskarte darüber: Kante statt Fläche, damit ein Rot über
 * den Lauf spricht und nicht über die Seite.
 */
.zen-fortschritt { border-left: 4px solid var(--rand-stark); }
.zen-fort-aktuell { border-left-color: var(--warnung-kraeftig); }
.zen-fort-erledigt { border-left-color: var(--erfolg-kraeftig); }
.zen-fort-fehler { border-left-color: var(--fehler-kraeftig); }
.zen-fort-offen { border-left-color: var(--rand-still); }

/*
 * Die laufende Station als Satz. Sie ist die eine Zeile, die Martin auf einen
 * Blick sucht, und deshalb die größte Schrift dieser Karte – ein hervorgehobener
 * Punkt in einer elfzeiligen Liste findet man im Vorbeigehen nicht.
 */
.zen-fort-jetzt {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.4rem;
    margin: 0 0 0.7rem;
}

.zen-fort-marke {
    font-size: 0.78rem;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--text-leise);
}

.zen-fort-titel {
    font-size: 1.15rem;
    font-weight: 600;
    color: var(--text-stark);
    overflow-wrap: anywhere;
}

/*
 * Der Nebenlauf (Issue #125).
 *
 * Er steht **unter** der laufenden Station und nicht an ihrer Stelle: Der
 * Hauptlauf steht, wo er steht, und eine Nacharbeit ist etwas, das daneben
 * passiert. Bis zu diesem Auftrag hat sie ihn überschrieben – aus „Station 9
 * von 11" wurde „Station 2 von 11", und das war schlicht falsch.
 *
 * Mobile-first wie alles hier: eine Zeile, die umbricht, ohne feste Breite,
 * ohne eigene Schwelle. Auf dem Telefon steht der Kreispfeil über dem Satz,
 * ab dem Tablet daneben – dafür genügt `flex-wrap`, und die Karte behält in
 * jeder Breite dieselbe Regel.
 *
 * Das Zeichen ist ein Kreispfeil und keine Farbe allein: dieselbe Zusage wie
 * bei den Stationen darunter.
 */
.zen-fort-neben {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.4rem;
    margin: -0.4rem 0 0.7rem;
    padding: 0.4rem 0.6rem;
    border-left: 3px solid var(--warnung-kraeftig);
    border-radius: 0 4px 4px 0;
    background: var(--warnung-flaeche-blass);
    font-size: 0.88rem;
    color: var(--text-normal);
}

.zen-fort-neben-marke {
    color: var(--warnung-kraeftig);
    font-weight: 700;
}

.zen-fort-neben-text { overflow-wrap: anywhere; }

/* Welcher Lauf das ist – leise, aber vorhanden. */
.zen-fort-lauf { font-style: italic; }

/*
 * Der Balken. Er misst Stationen und keine Zeit – das steht als Satz an der
 * Karte, und `aria-valuetext` sagt es auch dem, der sie hört.
 *
 * Kein `transition`: Ein Balken, der nach dem Laden noch zwei Sekunden lang
 * wächst, sähe aus wie ein Fortschritt, der gerade passiert. Er ist aber schon
 * passiert.
 */
.zen-fort-balken {
    height: 10px;
    border-radius: 999px;
    background: var(--flaeche-sanft);
    border: 1px solid var(--rand);
    overflow: hidden;
}

.zen-fort-fuellung {
    display: block;
    height: 100%;
    background: var(--erfolg-kraeftig);
}

.zen-fort-fehler .zen-fort-fuellung { background: var(--fehler-kraeftig); }

.zen-fort-zahlen {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.5rem;
    margin: 0.4rem 0 0;
}

.zen-fort-stand {
    font-weight: 600;
    color: var(--text-stark);
    font-variant-numeric: tabular-nums;
}

.zen-fort-naechster {
    margin: 0.5rem 0 0;
    font-size: 0.9rem;
    color: var(--text-normal);
}

/* ── Die beiden mitlaufenden Protokollfenster ───────────────────────────── */

.zen-protokoll-kopf {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.5rem;
    margin-top: 0.9rem;
}

.zen-protokoll-titel {
    margin: 0;
    font-size: 0.8rem;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--text-leise);
}

/*
 * Der Knopf, mit dem sich das automatische Folgen anhalten und wieder
 * einschalten lässt. Er steht im Bauplan versteckt und wird erst vom Skript
 * gezeigt: Ohne Skript folgt nichts, und ein Knopf, der dann nichts täte, wäre
 * ein Versprechen.
 */
.zen-folgen-taste {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    min-height: 44px;
    padding: 0.3rem 0.7rem;
    border: 1px solid var(--rand);
    border-radius: 999px;
    background: var(--flaeche-karte);
    color: var(--text-leise);
    font: inherit;
    font-size: 0.8rem;
    cursor: pointer;
}

.zen-folgen-taste[hidden] { display: none; }

.zen-folgen-taste::before {
    /* Form vor Farbe: ein gefüllter Kreis, wenn gefolgt wird, ein leerer
       sonst. Wer Grün und Grau nicht auseinanderhält, liest denselben
       Unterschied – und daneben steht ohnehin das Wort. */
    content: "○";
    line-height: 1;
}

.zen-folgen-taste.aktiv {
    border-color: var(--erfolg-kraeftig);
    color: var(--erfolg-zart-text);
}

.zen-folgen-taste.aktiv::before { content: "●"; }

/*
 * Der Rahmen beider Listen – und seit Issue #200 nur noch der.
 *
 * Bis dahin standen hier zusätzlich `max-height: 14rem` und `overflow-y: auto`,
 * und damit galt dieselbe feste Höhe für beide Listen. Für das Laufprotokoll
 * ist genau das der Punkt (siehe `.zen-laufliste` darunter); für die Stationen
 * des laufenden Laufs war es Martins Befund vom 25.09.2026: Von den elf
 * Stationen eines Laufs war nur ein Teil zu sehen, obwohl die Karte auf der
 * Seite ohne Weiteres nach unten wachsen kann.
 */
.zen-protokoll {
    margin: 0.5rem 0 0;
    padding: 0;
    list-style: none;
    border: 1px solid var(--rand);
    border-radius: 8px;
    background: var(--flaeche-sanft);
}

/*
 * Für `.zen-stationen` steht hier deshalb **keine** Regel: Die Liste ist mit
 * dem Lauf fest – es sind die elf Stationen aus `app/entwicklungsstatus.PHASEN`,
 * und beim Lesen kommt keine zwölfte dazu. Eine Höhengrenze wäre eine zweite
 * Scrollfläche in einer Seite, die ohnehin scrollt. Damit entfällt dort auch
 * der Tabulatorhalt: Die Liste steht ohne `tabindex` im Bauplan, weil es nichts
 * zu scrollen gibt.
 *
 * Das Laufprotokoll behält sein Fenster. Eine feste Höhe ist dort der Punkt:
 * Ohne sie gäbe es nichts, dem etwas nachlaufen könnte – die Liste der letzten
 * Läufe wächst, und zwar nach unten.
 *
 * `scroll-behavior: auto` steht ausdrücklich da: Bei einem sanften Bildlauf
 * käme jeder neue Eintrag mit einer halben Sekunde Animation, und wer währenddessen
 * scrollt, kämpfte gegen sie an.
 */
.zen-laufliste {
    max-height: 14rem;
    overflow-y: auto;
    scroll-behavior: auto;
}

.zen-abnahmen { overflow-anchor: none; overscroll-behavior-y: contain; }
.zen-abnahmen .zen-liste { margin: 0; }
.zen-abnahmen-aelter { display: block; padding: 0.75rem; }

/* Die Tastaturmarke: Das Fenster ist mit Tab erreichbar, weil es scrollt –
   ein scrollbarer Bereich ohne Fokus ist mit der Tastatur nicht zu bedienen. */
.zen-laufliste:focus-visible { outline: 2px solid var(--kontur); outline-offset: 2px; }

.zen-station,
.zen-lauf {
    display: grid;
    align-items: baseline;
    gap: 0.15rem 0.5rem;
    padding: 0.45rem 0.7rem;
    border-bottom: 1px solid var(--rand-still);
    font-size: 0.9rem;
}

.zen-station:last-child,
.zen-lauf:last-child { border-bottom: none; }

/* Die Stationen: Marke, Nummer, Name. */
.zen-station { grid-template-columns: 1.2rem 1.6rem minmax(0, 1fr); }

.zen-station-nummer {
    color: var(--text-leise);
    font-variant-numeric: tabular-nums;
    font-size: 0.8rem;
}

.zen-station-titel { color: var(--text-normal); overflow-wrap: anywhere; }

/*
 * Form vor Farbe, dieselbe Zusage wie überall in diesem Bestand: Haken,
 * halbvoller Kreis, Kreuz, leerer Kreis – vier verschiedene Zeichen, nicht
 * vier Farben desselben Punktes.
 */
.zen-station-marke::before { content: "○"; color: var(--text-leise); }
.zen-station-erledigt .zen-station-marke::before { content: "✓"; color: var(--erfolg-kraeftig); }
.zen-station-aktuell .zen-station-marke::before { content: "◐"; color: var(--warnung-kraeftig); }
.zen-station-fehler .zen-station-marke::before { content: "✗"; color: var(--fehler-kraeftig); }

.zen-station-erledigt .zen-station-titel { color: var(--text-leise); }

.zen-station-aktuell {
    background: var(--warnung-flaeche-blass);
}

.zen-station-aktuell .zen-station-titel {
    font-weight: 600;
    color: var(--text-stark);
}

.zen-station-fehler { background: var(--fehler-flaeche-zart); }

/*
 * Die Station, an der ein Nebenlauf arbeitet (Issue #125).
 *
 * Sie bleibt erledigt – sie **war** durch, und ein zurückgenommener Haken wäre
 * genau die Lüge, wegen der es diesen Auftrag gibt. Dass gerade noch einmal an
 * ihr gearbeitet wird, steht als Wort in der Zeile und als Kante links davor;
 * die Reihenfolge und der Haken bleiben unberührt.
 *
 * Die Zeile hat dafür eine vierte Spalte statt einer festen Breite – damit
 * bricht das Wort auf dem Telefon unter den Namen statt ihn zu quetschen.
 */
.zen-station-neben {
    grid-template-columns: 1.2rem 1.6rem minmax(0, 1fr) auto;
    box-shadow: inset 3px 0 0 0 var(--warnung-kraeftig);
}

.zen-station-neben-wort {
    justify-self: end;
    padding: 0 0.35rem;
    border-radius: 999px;
    background: var(--warnung-flaeche-blass);
    /*
     * Die Schriftfarbe ist die Textstufe und nicht die kräftige Signalfarbe:
     * `--warnung-kraeftig` erreicht auf dieser sehr blassen Fläche nur rund
     * 3,1 : 1 und verfehlt damit die 4,5 : 1, die das Designhandbuch für
     * gewöhnliche Schrift zusagt. Die Kante links an `.zen-station-neben`
     * darf kräftig bleiben – sie ist Fläche und kein Text.
     */
    color: var(--warnung-text);
    font-size: 0.75rem;
    font-weight: 600;
    white-space: nowrap;
}

/*
 * ── Der Unterfortschritt einer Station (Issue #222) ────────────────────────
 *
 * Er liegt **in** der Zeile seiner Station und beginnt in derselben Spalte wie
 * ihr Name: `grid-column: 3 / -1`. Damit steht er unter dem Namen und nicht
 * unter dem Haken, und er braucht dafür keine eigene Breitenregel und keine
 * zweite Medienabfrage – die Spalte ist `minmax(0, 1fr)` und schrumpft auf dem
 * Telefon mit. Das `min-width: 0` darunter ist die Zusage, die auf 320 px
 * zählt: Ein Rasterfeld mit Inhalt wächst sonst über seine Spalte hinaus, und
 * die ganze Seite bekäme einen waagerechten Bildlauf.
 *
 * Ohne belastbaren Nenner steht hier gar nichts – die Vorlage zeichnet den
 * Block dann nicht. Ein leerer Balken behauptete einen Umfang, den niemand
 * kennt.
 */
.zen-station-fort {
    grid-column: 3 / -1;
    min-width: 0;
    margin-top: 0.35rem;
}

/*
 * Derselbe Balken wie oben in der Karte, nur schmaler: Dieselbe Aussage soll
 * auf einer Seite nicht zweimal anders aussehen. Kein `transition` – aus
 * demselben Grund wie beim großen: Ein Balken, der nach dem Laden noch wächst,
 * sähe aus wie ein Fortschritt, der gerade passiert. Er ist schon passiert.
 */
.zen-station-balken {
    height: 6px;
    border-radius: 999px;
    background: var(--flaeche-sanft);
    border: 1px solid var(--rand);
    overflow: hidden;
}

.zen-station-fuellung {
    display: block;
    height: 100%;
    background: var(--erfolg-kraeftig);
}

/*
 * Prozent und Zähler in einer Zeile, und sie bricht: `9.427 / 14.071
 * Prüfungen` neben `67 %` braucht am Schreibtisch eine Zeile und auf dem
 * Telefon zwei. `tabular-nums` hält die Ziffern an ihrem Platz, damit ein
 * weiterzählender Wert die Zeile nicht bei jedem Takt neu umbricht.
 */
.zen-station-zahlen {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.15rem 0.5rem;
    margin: 0.3rem 0 0;
    font-size: 0.8rem;
    font-variant-numeric: tabular-nums;
}

.zen-station-prozent {
    font-weight: 600;
    color: var(--text-stark);
}

.zen-station-zaehler {
    color: var(--text-normal);
    overflow-wrap: anywhere;
}

/*
 * Die leise zweite Zeile: Durchlauf, Zweitzähler, Ergebnis, vorheriger Lauf.
 * Beim ersten Durchlauf steht dort nur der Zweitzähler; die Vorlage zeichnet
 * leere Angaben gar nicht, weil in einer Flex-Zeile auch ein leerer Kasten
 * seinen Abstand bekäme. Deshalb reicht hier `gap` und es braucht kein
 * Trennzeichen im Stylesheet – ein `·` vor einer fehlenden Angabe wäre ein
 * Trenner ohne zweite Seite.
 */
.zen-station-fort-leise {
    display: flex;
    flex-wrap: wrap;
    gap: 0.15rem 0.6rem;
    margin: 0.2rem 0 0;
    font-size: 0.75rem;
    color: var(--text-leise);
    font-variant-numeric: tabular-nums;
}

/*
 * Der Durchlauf und das Urteil heben sich ab, weil sie die beiden Angaben
 * sind, nach denen man sucht, wenn die Zahlen ein zweites Mal von vorn zählen.
 * Farbe allein trägt dabei nichts: `bestanden` und `fehlgeschlagen` stehen als
 * **Wort** da, und die Farbe ist die Zugabe.
 */
.zen-station-runde { font-weight: 600; color: var(--text-normal); }
.zen-station-fort-bestanden { color: var(--erfolg-text); font-weight: 600; }
.zen-station-fort-fehlgeschlagen { color: var(--fehler-text); font-weight: 600; }

/* Die Läufe: auf dem Telefon gestapelt, am Schreibtisch in Spalten. */
.zen-lauf { grid-template-columns: 1.2rem minmax(0, 1fr); }

/*
 * Die vier Ergebnisse aus `app/entwicklungsstatus.ERGEBNISSE` – und jedes
 * bekommt sein eigenes Zeichen, nicht seine eigene Farbe. „Zurückgerollt" ist
 * dabei ausdrücklich nicht dasselbe wie „Fehler": Der Lauf ist gescheitert,
 * aber der letzte funktionierende Stand ist zurückgekommen.
 */
.zen-lauf-marke::before { content: "•"; color: var(--text-leise); }
.zen-lauf-erfolgreich .zen-lauf-marke::before { content: "✓"; color: var(--erfolg-kraeftig); }
.zen-lauf-zurueckgerollt .zen-lauf-marke::before { content: "↩"; color: var(--warnung-kraeftig); }
.zen-lauf-fehler .zen-lauf-marke::before { content: "✗"; color: var(--fehler-kraeftig); }
.zen-lauf-unbekannt .zen-lauf-marke::before { content: "?"; color: var(--text-leise); }

.zen-lauf .zen-eintrag-nummer,
.zen-lauf .zen-eintrag-titel,
.zen-lauf-ergebnis,
.zen-lauf-zeit { grid-column: 2; }

.zen-lauf-ergebnis { color: var(--text-leise); font-size: 0.82rem; }
.zen-lauf-zeit { color: var(--text-leise); font-size: 0.82rem; font-variant-numeric: tabular-nums; }

/*
 * Der Kasten der Betriebslage stand hier bis Issue #233 – `Der Betrieb läuft`
 * über der Warteschlange, mit seiner Farbe an der linken Kante. Er ist mit dem
 * Kasten gegangen: Was er sagte, steht sichtbarer in den Kennzahlen und in
 * `Was läuft gerade`, und die Kachel darunter ist jetzt die Warteschlange und
 * sonst nichts. Gemessen wird die Lage weiterhin
 * (`app/entwicklungszentrale.betriebslage()`) – sie trägt den Handlungssatz
 * der Statuskarte und steht vollständig auf `/entwicklungsstatus`.
 */

/* ── Die Gruppen ────────────────────────────────────────────────────────── */

.zen-aufgaben { display: flex; flex-direction: column; gap: 0.8rem; }

.zen-aufgaben-kopf {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.3rem;
}

.zen-stand { margin: 0; font-size: 0.8rem; color: var(--text-leise); }
.zen-stand-alt { color: var(--warnung-mittel); }

.zen-gruppe {
    background: var(--flaeche-karte);
    border: 1px solid var(--rand);
    border-left: 4px solid var(--rand-stark);
    border-radius: 10px;
    padding: 0.8rem 1rem;
    /* Der Sprung aus einer Kennzahl soll die Überschrift nicht unter den
       feststehenden Kopf schieben. */
    scroll-margin-top: 5rem;
}

/*
 * Farbe **und** Form: Die Kante trägt die Farbe, das Zeichen in der
 * Überschrift die Form. Wer Grün und Rot nicht auseinanderhält, unterscheidet
 * die Gruppen weiterhin am Zeichen und am Wort daneben – dieselbe Zusage wie
 * bei den Statuspillen der Laufhistorie.
 */
.zen-gruppe-arbeit { border-left-color: var(--warnung-kraeftig); }
.zen-gruppe-abnahme-offen { border-left-color: var(--warnung-kraeftig); }
.zen-gruppe-entscheidung { border-left-color: var(--warnung-kraeftig); }
.zen-gruppe-fehler { border-left-color: var(--fehler-kraeftig); }
.zen-gruppe-vorgemerkt { border-left-color: var(--info-kante); }
.zen-gruppe-warteschlange { border-left-color: var(--hinweis-kante); }
/* Eingeplant und noch nicht freigegeben tragen dieselbe stille Kante: Beides
   ist Bestand und keine Aufforderung. Auseinander hält sie das Zeichen und
   der Satz an der Zeile – „wartet auf #107 – …" gegen gar nichts. */
.zen-gruppe-geplant { border-left-color: var(--rand-still); }
.zen-gruppe-nicht-freigegeben { border-left-color: var(--rand-still); }
.zen-gruppe-abgenommen { border-left-color: var(--erfolg-kraeftig); }

.zen-gruppe-titel {
    display: flex;
    align-items: center;
    gap: 0.45rem;
    margin: 0;
    font-size: 1rem;
    color: var(--text-stark);
}

.zen-gruppe-symbol { flex-shrink: 0; font-size: 0.85em; }

/*
 * Der Titel ist das einzige Element der Überschrift, das nachgeben darf. Ohne
 * `min-width: 0` bleibt ein Flex-Element so breit wie sein längstes Wort – bei
 * einem langen Kompositum auf einem 320-px-Telefon schob das die Zahl 16 px
 * über den Rand. Getrennt wird zuerst an der Silbe und nur im Notfall mitten
 * im Wort.
 */
.zen-gruppe-name {
    min-width: 0;
    overflow-wrap: anywhere;
    hyphens: auto;
}

/*
 * Die Zahl an einer Gruppe. Sie ist in der Übersicht ein Verweis in die
 * gefilterte Gesamtansicht und auf der Aufgabenseite eine reine Zahl – beide
 * sehen gleich aus, weil es dieselbe Angabe ist. Die Unterstreichung kommt
 * erst beim Überfahren: Eine dauerhaft unterstrichene Zahl in einer
 * Überschrift läse sich wie ein Tippfehler.
 */
.zen-gruppe-anzahl {
    flex-shrink: 0;
    margin-left: auto;
    padding: 0.05rem 0.55rem;
    border-radius: 999px;
    background: var(--flaeche-sanft);
    color: var(--text-leise);
    font-size: 0.85rem;
    font-variant-numeric: tabular-nums;
    text-decoration: none;
}

a.zen-gruppe-anzahl:hover,
a.zen-gruppe-anzahl:focus-visible { text-decoration: underline; }

.zen-gruppe-erklaerung { margin: 0.25rem 0 0; font-size: 0.82rem; color: var(--text-leise); }
.zen-gruppe-leer { margin: 0.5rem 0 0; font-size: 0.9rem; color: var(--text-leise); }

/* ── Die Aufgabenzeilen ─────────────────────────────────────────────────── */

.zen-liste { list-style: none; margin: 0.6rem 0 0; padding: 0; }

/*
 * Auf dem Telefon eine Spalte: Nummer, Titel, Randangaben untereinander. Die
 * Zeile darf dabei umbrechen und wird nicht zusammengequetscht – eine
 * waagerechte Scrollleiste ist auf einem Telefon keine Lösung, sondern ein
 * Befund.
 */
.zen-eintrag {
    display: grid;
    grid-template-columns: auto 1fr;
    gap: 0.15rem 0.5rem;
    padding: 0.55rem 0;
    border-top: 1px solid var(--rand-zart);
}

.zen-eintrag:first-child { border-top: none; }

.zen-eintrag-nummer { font-variant-numeric: tabular-nums; color: var(--text-leise); }
.zen-eintrag-nummer a { color: var(--verweis); text-decoration: none; }
.zen-eintrag-titel { color: var(--text-stark); overflow-wrap: anywhere; }

.zen-eintrag-rand {
    grid-column: 1 / -1;
    display: flex;
    flex-wrap: wrap;
    gap: 0.2rem 0.8rem;
    font-size: 0.78rem;
    color: var(--text-leise);
}

.zen-eintrag-bilanz { color: var(--text-leise); }
.zen-eintrag-datum { color: var(--text-still); }

/* Die feinere Auskunft an einer Zeile – heute die vorgemerkte Wartungsaktion.
   Sie steht in der Farbe ihrer Gruppe, damit man sie der Kante zuordnet, und
   nicht in einer eigenen: Es ist dieselbe Aussage, nur genauer. */
.zen-eintrag-genauer { color: var(--info-text); }

/*
 * Der Zusatzsatz an einer Aufgabe – der Widerspruch, der nicht in einen
 * Zustand hineingerechnet wird (siehe `app/entwicklungszentrale.hinweise()`).
 * Er steht über die volle Breite und in der Warnfarbe: Er ist kein Zustand,
 * aber etwas, das jemand ansehen sollte.
 */
.zen-eintrag-hinweis {
    grid-column: 1 / -1;
    font-size: 0.78rem;
    color: var(--warnung-text);
}

/* ── Die Warteschlange ──────────────────────────────────────────────────── */

.zen-queue { list-style: none; margin: 0.4rem 0 0; padding: 0; }

.zen-queue-eintrag {
    display: grid;
    grid-template-columns: auto auto 1fr;
    gap: 0.2rem 0.5rem;
    padding: 0.45rem 0;
    border-top: 1px solid var(--rand-zart);
}

.zen-queue-eintrag:first-child { border-top: none; }

/* Issue #507: Grund, Fortschritt und nächster Schritt eines Rückkehrers unter seiner Zeile. */
.zen-queue-rueckkehr {
    grid-column: 1 / -1;
    flex-basis: 100%;
    margin: 0;
    overflow-wrap: anywhere;
}

.zen-queue-position {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 1.5rem;
    height: 1.5rem;
    border-radius: 999px;
    background: var(--flaeche-sanft);
    font-size: 0.78rem;
    color: var(--text-leise);
}

/*
 * Luft über der zweiten Überschrift der Kachel (Issue #233).
 *
 * Es gibt sie nur in einem Fall: Steht ein arbeitsfähiger Planpunkt da, folgt
 * `Weitere freigegebene Aufträge` auf die Liste des Arbeitsplans und braucht
 * den Abstand. Die **erste** Überschrift bekommt ihn ausdrücklich nicht –
 * vorher hing die Regel an `:not(:first-child)` und griff damit auch oben,
 * sobald der Griff zum Anordnen davorstand. Dort beginnt die Kachel wie jede
 * andere: mit ihrer Überschrift und ohne Vorlauf.
 */
.zen-betrieb .zen-queue ~ .zen-abschnitt-titel { margin-top: 1rem; }

/* ── Die Warteschlange von Hand sortieren (Issue #214) ───────────────────── */

/*
 * Der Erklärsatz über der Liste – und seit Issue #226 nur noch das.
 *
 * Bis dahin trug er `aria-live` und wurde nach jedem Zug durch die Rückmeldung
 * ersetzt: Erklärung im Ruhezustand, Ansage danach. Martins Befund vom
 * 30.09.2026: Die beiden Sätze sind unterschiedlich hoch, und die ganze
 * Warteschlange sprang bei jedem Zug um diesen Unterschied nach oben oder
 * unten. Er steht jetzt dauerhaft, und die Rückmeldung hat ihren eigenen
 * Platz unter der Liste.
 */
.zen-queue-hinweis {
    margin: 0.4rem 0 0;
    font-size: 0.78rem;
    color: var(--text-leise);
}

/*
 * Und ihr Platz: die Hülle unter der Liste (Issue #226).
 *
 * Sie steht immer da und ist leer 0 px hoch – ein Kasten ohne Inhalt hat keine
 * Höhe, die Ränder sitzen an der Meldung darin. Sie ist damit der Anker, den
 * `static/zentrale-warteschlange.js` beim ersten Zug wiederfindet, und kostet
 * im Ruhezustand nichts.
 *
 * Die Meldung selbst ist die **zentrale** des Werkzeugs (`.meldung`) und wird
 * hier nicht neu gezeichnet – nur ihre Ränder sitzen enger: `1.2 rem` unter
 * einer Meldung sind für den Seitenanfang gedacht, wo der Inhalt darunter
 * beginnt. Hier steht sie in einer halb breiten Kachel zwischen zwei Sätzen.
 */
.zen-queue-meldung .meldung { margin: 0.5rem 0 0; }

/*
 * Die sortierbare Zeile: Bedienung links, Inhalt daneben (Martins Abnahme vom
 * 28.09.2026).
 *
 * Bis dahin standen die drei Tasten in einer **eigenen Zeile** über Nummer und
 * Titel. Der Grund war gemessen: Drei Tippflächen von 44 px sind zusammen rund
 * 140 px, und diese Kachel ist auch am Schreibtisch nur halb breit – als starre
 * vierte Rasterspalte blieben dem Titel bei 900 px noch 138 px und bei 320 px
 * ganze 16. Der Preis dafür war der Befund: Rechts neben den Tasten blieb eine
 * ganze Zeilenbreite ungenutzt, und jeder wartende Auftrag kostete dadurch
 * gut 80 px Höhe. Mehr als eine Handvoll passte nicht ins Bild.
 *
 * Aus dem Raster wird deshalb ein **umbrechender Flusssatz**, und der löst
 * beides zugleich:
 *
 *   * Ist Platz, stehen Tasten, Positionsnummer, `#Nummer` und Titel in
 *     derselben Zeile – genau in der Fläche, die vorher leer blieb. Die Zeile
 *     ist damit so hoch wie ihr Inhalt: bei einzeiligem Titel rund 48 px statt
 *     rund 80, also etwa doppelt so viele wartende Aufträge im Bild.
 *   * Ist keiner, bricht der Titel als Ganzes in die nächste Zeile und hat
 *     dort die volle Breite – dasselbe Bild wie vorher, ohne eine zweite
 *     Medienabfrage. Die Schwelle steht als `flex-basis` am Titel, und sie ist
 *     gemessen: Bei 1024 px bleiben neben den Tasten 230 px für den Titel, bei
 *     900 px nur 168 – und ein Titel über hundert Zeichen braucht in 168 px
 *     sieben Zeilen und über die volle Breite zwei.
 *
 * Die Zeilenhöhe folgt damit dem Inhalt: Ein langer Titel darf zweizeilig
 * werden, und nichts reserviert Platz für eine Textzeile, die es nicht gibt.
 *
 * Gemessen wird das weiterhin auf allen neun Breiten von
 * `tests/browser_zentrale.py` – auch, dass dem Titel überall sein Anteil an der
 * Zeile bleibt (`TITELGRENZEN`).
 */
.zen-queue-sortierbar .zen-queue-eintrag {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    column-gap: 0.5rem;
    row-gap: 0.15rem;
}

/*
 * Der Titel nimmt den Rest der Zeile und bringt die Schwelle mit, unter der er
 * sich eine eigene holt. Keine Breite in Pixeln und kein `min-width`: Ein
 * `flex-basis` gibt nach, eine Mindestbreite nicht – und eine Mindestbreite in
 * dieser Zeile ließe die ganze Seite auf einem 320-px-Telefon seitlich
 * scrollen.
 */
.zen-queue-sortierbar .zen-eintrag-titel { flex: 1 1 11rem; }

/*
 * Die Marke `als Nächstes` – ein Kästchen **im** Titel (Issue #226).
 *
 * Bis dahin war sie ein eigenes Element der Zeile mit `flex-basis: 100%` und
 * damit eine eigene, 20 px hohe Zeile zwischen Position 1 und Position 2.
 * Martins Befund vom 30.09.2026: Sie unterbricht die Liste optisch und
 * erschwert genau das, wofür die Liste da ist.
 *
 * Neben dem Titel als eigenes Flex-Element hätte sie nichts gespart – das ist
 * gemessen: In der halb breiten Kachel bleiben dem Titel bei 1024 px 230 px,
 * und das Kästchen braucht rund 90 davon. Der Titel fiele damit unter seine
 * Umbruchschwelle (`flex: 1 1 11rem`) und nähme sich die nächste Zeile: wieder
 * eine Zeile mehr, nur an anderer Stelle.
 *
 * Als Inline-Inhalt des Titels teilt sie dagegen dessen Zeile, und der Titel
 * behält seinen Kasten – also seinen Anteil an der Zeile, den
 * `tests/browser_zentrale.mjs` mit `T` vermisst. Sie steht vor dem ersten Wort
 * und wandert mit, wenn der Titel sich auf einem schmalen Bildschirm die
 * nächste Zeile holt.
 *
 * Gezeichnet ist sie als ruhiges Kästchen: dieselbe leise Schrift wie vorher,
 * dazu eine stille Kante und die sanfte Fläche – so ist sie im Textfluss als
 * Marke zu erkennen und drängt sich trotzdem nicht vor den Titel. Kein
 * `white-space: nowrap`: Zeichen und Wort hält schon der Flex-Kasten
 * zusammen, und der lange Fall `Wiederaufnahme – als Nächstes` muss in einer
 * 239 px schmalen Zeile umbrechen dürfen.
 *
 * Der Abstand zum Titel steht **hier** und nicht als Leerzeichen in der
 * Vorlage: Ein Leerzeichen wäre eine Umbruchstelle, und dort könnte das
 * Kästchen auf einem schmalen Telefon allein auf einer Zeile landen – derselbe
 * Befund noch einmal, nur eine Ebene tiefer. So hängt es am ersten Wort und
 * wandert mit ihm.
 */
.zen-eintrag-titel > .zen-plan-marke {
    border: 1px solid var(--rand-still);
    border-radius: 999px;
    padding: 0 0.4rem;
    margin-right: 0.35rem;
    background: var(--flaeche-sanft);
    vertical-align: middle;
}

/*
 * Die drei Tasten einer Zeile stehen zusammen und brechen nicht auseinander:
 * Sie sind eine Bedienung und nicht drei.
 *
 * `justify-content: start` und keine Breite in Pixeln: Diese Seite darf
 * nirgends eine feste Mindestbreite bekommen, sonst entstünde auf einem
 * schmalen Bildschirm eine waagerechte Scrollleiste.
 */
.zen-queue-griffe {
    display: flex;
    justify-content: start;
    align-items: center;
    gap: 0.15rem;
}

/*
 * Griff und Pfeile sind dieselbe Taste in drei Rollen – gemeinsame Maße an
 * einer Stelle. 44 px hoch, damit sie auf dem Telefon zu treffen sind; die
 * Breite kommt aus dem Seitenverhältnis und nicht aus einer `min-width` in
 * Pixeln, damit hier keine feste Mindestbreite entsteht. Dieselbe Entscheidung
 * und derselbe Grund wie bei `.zen-kachel-pfeil`.
 */
.zen-queue-griff,
.zen-queue-pfeil {
    min-height: 44px;
    aspect-ratio: 1;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    border: 1px solid var(--rand-stark);
    border-radius: 6px;
    background: var(--flaeche-sanft);
    color: var(--text-normal);
    font-size: 0.9rem;
    line-height: 1;
    cursor: pointer;
}

.zen-queue-griff:hover,
.zen-queue-pfeil:hover { background: var(--flaeche-neutral); }

/* Der Griff ist die Ziehfläche und sagt das mit dem Zeiger. Er ist bewusst
   flacher gezeichnet als die Pfeile: Er tut ohne Maus nichts, und die Pfeile
   sind die Bedienung, die überall geht. */
.zen-queue-griff {
    cursor: grab;
    color: var(--text-leise);
    letter-spacing: -0.1em;
}

.zen-queue-griff:active { cursor: grabbing; }

/* Die gezogene Zeile bleibt sichtbar und tritt zurück – man soll wissen,
   welche man in der Hand hat. */
.zen-queue-zieht { opacity: 0.5; }

/*
 * Und die Zeile, auf der sie landen würde. Eine Kante und keine Fläche: Die
 * Zeilen tragen schon einen Trenner, und eine zweite Hintergrundfarbe darin
 * wäre in Dunkel kaum und in Hell zu viel. Die Kante liegt links, weil dort
 * auch die Zustandsfarben des Arbeitsplans liegen – dieselbe Stelle, dieselbe
 * Bedeutung: „hier passiert etwas".
 */
.zen-queue-ziel {
    box-shadow: inset 3px 0 0 0 var(--rand-akzent);
    background: var(--flaeche-sanft);
}

/* Das Formular der Pfeile trägt nur versteckte Felder – es soll keinen Platz
   kosten und trotzdem im Dokument stehen: Die Knöpfe finden es über ihre
   `form`-Angabe, und ein `display: none` wäre eine Aussage über Sichtbarkeit,
   wo es gar nichts zu sehen gibt. */
.zen-queue-form { margin: 0; }

/* ── Die Arbeitsreihenfolge: Arbeitsplan und echte Queue (Issue #115) ────── */

/*
 * Eine Planzeile ist eine Queue-Zeile mit einem Zusatz: der Lagezeile aus der
 * Zustandsmarke und – wenn es etwas zu sagen gibt – dem Satz, woran sie hängt
 * und warum. Sie steht in der nächsten Zeile über die volle Breite, und zwar
 * auf **jedem** Gerät, anders als bei den
 * Aufgabenzeilen darüber. Der Grund ist die Kachel: `Automatik und
 * Warteschlange` ist auch am Schreibtisch nur halb breit (Issue #110), und
 * „blockiert durch #107" neben einem Aufgabentitel in einer halben Spalte
 * quetschte beide. Eine Zeile darf umbrechen; eine waagerechte Scrollleiste
 * ist keine Lösung.
 */
.zen-plan-eintrag {
    grid-template-columns: auto auto 1fr;
    border-left: 3px solid transparent;
    padding-left: 0.5rem;
}

/*
 * `.zen-plan-titel` stand hier bis Issue #233: eine kleine, leise `h3` über
 * der Warteschlange, untergeordnet der Überschrift `Arbeitsreihenfolge`
 * darüber. Die gibt es nicht mehr, und damit ist die Warteschlange die
 * Überschrift dieser Kachel – eine `h2` wie in jeder anderen Karte und mit
 * deren Regel (`.zen-abschnitt-titel`). Eine zweite, kleinere Gestalt
 * derselben Ebene wäre genau die Rangordnung, die hier wegfällt.
 */

/*
 * Farbe **und** Form. Die Kante links trägt die Farbe des Zustands, die Marke
 * rechts sein Zeichen und sein Wort. Wer Grün und Rot nicht auseinanderhält,
 * liest weiterhin „blockiert" – dieselbe Zusage wie bei den Zustandsgruppen.
 */
.zen-plan-arbeit { border-left-color: var(--warnung-kraeftig); }
.zen-plan-naechstes { border-left-color: var(--hinweis-kante); }
.zen-plan-warteschlange { border-left-color: var(--hinweis-kante); }
.zen-plan-geplant { border-left-color: var(--rand-still); }
.zen-plan-wartet { border-left-color: var(--info-kante); }
.zen-plan-blockiert { border-left-color: var(--fehler-kraeftig); }
.zen-plan-fehler { border-left-color: var(--fehler-kraeftig); }
.zen-plan-widerspruch { border-left-color: var(--warnung-kraeftig); }
.zen-plan-abnahme-offen { border-left-color: var(--warnung-kraeftig); }
/* Die beiden Lagen aus Issue #122. Sie stehen an einer Aufgabenzeile und
   nicht in der Reihenfolge; ihre Wörter kommen trotzdem aus derselben
   Tabelle, und deshalb steht ihre Farbe hier bei den übrigen. */
.zen-plan-zurueckgestellt { border-left-color: var(--rand-still); }
.zen-plan-entscheidung { border-left-color: var(--warnung-kraeftig); }

/*
 * Marke und Nachsatz gehören in **eine** Zeile: „wartet auf" und „#112 – der
 * Live-Chat wird in den neuen Player integriert" sind ein Satz, und zwei
 * getrennte Zeilen zerlegten ihn (Nacharbeit zu Issue #115). Umbrechen darf
 * er trotzdem – in einer halb breiten Kachel bringt ein Satz mit Begründung
 * mehr Text mit als eine Nummer.
 */
.zen-plan-lage {
    grid-column: 1 / -1;
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.3rem;
}

/*
 * Die Marke gibt es in zwei Gestalten, und beide sind hier zu Hause: als Teil
 * der Lagezeile einer Planzeile (`.zen-plan-lage`) und seit Issue #226 als
 * Kästchen im Titel einer Queue-Zeile (siehe oben). In beiden ist sie ein
 * Inline-Element im Fluss – ein `grid-column` stand hier bis dahin für eine
 * dritte, die es nicht mehr gibt: die Marke als eigene Rasterzeile unter dem
 * Eintrag.
 */
.zen-plan-marke {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    font-size: 0.78rem;
    color: var(--text-leise);
}

/*
 * Unter der Statuszeile fehlte die Luft (Nacharbeit zu Issue #110).
 *
 * Gemessen hat der Kasten immer gestimmt: `.zen-queue-eintrag` hat oben wie
 * unten 0,45 rem Innenabstand. Gesehen hat man trotzdem etwas anderes – die
 * Statuszeile klebte am Trenner, während der Trenner vom nächsten Titel
 * deutlich weiter weg stand. Der Grund ist der **Durchschuss** und nicht der
 * Innenabstand: Der Titel ist 1 rem groß und steht in einer 24 px hohen
 * Zeile, hat also rund 4 px Luft über seinen Buchstaben; die Statuszeile ist
 * 0,78 rem groß und steht in einer 15 px hohen Zeile – rund 1,3 px. Oben kam
 * damit gut 11 px sichtbarer Abstand heraus, unten knapp 8,5 px.
 *
 * Ausgeglichen wird genau dieser Unterschied, und zwar dort, wo er entsteht:
 * an der Statuszeile selbst. Ein größerer Innenabstand an der ganzen Zeile
 * risse auch die Einträge auseinander, die gar keine Statuszeile haben – in
 * der Liste `Bereit und nicht im Arbeitsplan` ist das die Mehrheit.
 *
 * Ein Selektor, seit die Marke der Queue-Zeile im Titel steht (Issue #226):
 * Nur noch die Lagezeile des Arbeitsplans ist die letzte Zeile ihres Eintrags
 * und stößt ohne diesen Ausgleich an den Trenner. Das Kästchen im Titel teilt
 * dessen Zeile und braucht keinen eigenen.
 */
.zen-plan-lage { padding-bottom: 0.2rem; }

.zen-plan-symbol { font-size: 0.9em; }
.zen-plan-wort { font-weight: 600; }

.zen-plan-genauer {
    font-size: 0.78rem;
    color: var(--warnung-text);
}

.zen-plan-blockiert .zen-plan-wort,
.zen-plan-fehler .zen-plan-wort { color: var(--fehler-text); }
.zen-plan-arbeit .zen-plan-wort,
.zen-plan-widerspruch .zen-plan-wort,
.zen-plan-abnahme-offen .zen-plan-wort { color: var(--warnung-text); }
.zen-plan-wartet .zen-plan-wort { color: var(--info-text); }

/* ── Kacheln anordnen (Nacharbeit zu Issue #105) ────────────────────────── */

/*
 * Ausgeblendet heißt ausgeblendet – dieselbe Zeile und derselbe Grund wie auf
 * der Statusseite: Mehrere Bausteine dieser Seite bekommen hier ein eigenes
 * `display`, und das schlägt die eingebaute Regel des Browsers für `hidden`.
 * Ohne sie stünde der Hinweissatz zum Anordnen von Anfang an da, obwohl noch
 * niemand den Bearbeitungsmodus geöffnet hat. Für die Knöpfe im Kopf gilt
 * dasselbe noch einmal – siehe `.zen-kopf [hidden]` weiter unten.
 */
.zen-seite [hidden] { display: none !important; }

/*
 * Das Raster der Zentrale (Issue #110).
 *
 * Was hier steht, ist die Fassung für das Telefon: eine Spalte – dort gibt es
 * keine Paare, sondern ein Oben und ein Unten. Das `grid` tut dabei nichts
 * weiter, als den Abstand von 1 rem zwischen den Karten zu setzen; gleich
 * hohe Nachbarn gibt es auf einer Spalte nicht zu verteilen.
 *
 * Am Schreibtisch werden daraus **zwei Spalten mit gemeinsamer Unterkante**:
 * Der Block `min-width: 900px` stellt das Raster auf zwei Spalten, und jede
 * Kachel bringt ihren Platz darin mit (`--zen-spalte`, `--zen-zeile`,
 * `--zen-spanne`). Warum dort seit Issue #200 wieder ein Raster steht und kein
 * Spaltensatz, steht bei der Regel selbst.
 *
 * Welche Kachel welche ist und wo sie steht, sagt `app/kachelordnung.py` und
 * kommt als Klasse bzw. als Zahl aus der Vorlage – eine Liste von Kachelnamen
 * in dieser Datei wäre eine zweite Quelle, die beim nächsten Umbenennen stehen
 * bliebe.
 *
 * Verschoben wird weiterhin im Dokument und nicht über `order`: Bild,
 * Vorlesereihenfolge und Tabulatorweg bleiben dieselbe Folge.
 */
.zen-raster {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    /* Ein Abstand, zwei Stellen: der Rasterabstand hier und der des
       zweispaltigen Rasters am Schreibtisch. Sie waren immer dieselbe 1 rem
       und sollen es bleiben – ein Wert, der einmal dasteht, läuft nicht
       auseinander. */
    --zen-kachel-abstand: 1rem;
    gap: var(--zen-kachel-abstand);
}

/*
 * ── Das Menü `Ansicht` in der Navigationsleiste (Issue #123) ───────────────
 *
 * Bis hierher standen an dieser Stelle die Regeln für `.zen-ordnen` – eine
 * Leiste aus zwei großen Schaltflächen über dem Inhalt. Martins Befund vom
 * 12.09.2026: zu viel Platz für zwei selten gebrauchte Aktionen, auf dem
 * Telefon ein ganzer Bedienblock vor dem ersten Wort der Seite.
 *
 * Geblieben ist von der Leiste der Hinweissatz weiter unten; die Bedienung
 * steckt jetzt in einem `<details>` in der Navigation.
 *
 * **Mobil zuerst, und hier heißt das wirklich etwas anderes.** Was ohne
 * Breitenabfrage steht, ist die Bedienung für Telefon und Tablet: Das Menü ist
 * ein **Blatt am unteren Rand** (`position: fixed`), über die volle Breite,
 * mit Zeilen in Lesegröße und 44 px Tippfläche. Am Schreibtisch (ab 900 px) wird
 * daraus ein kleines Klappmenü unter dem Zahnrad – dort zeigt eine Maus auf
 * den Pixel genau, und ein Blatt über die ganze Fensterbreite wäre für zwei
 * Zeilen zu viel Apparat.
 *
 * Ein fester Farbwert kommt auch hier nirgends vor: Das Blatt steht auf der
 * Kartenfläche des Bestands und trägt dessen Kante und Schatten. Der Auslöser
 * oben bleibt im Kopf und nimmt seine Farben aus den `--kopf-`-Token.
 */
.zen-ansicht-zone {
    display: flex;
    align-items: center;
    gap: 0.25rem;
}

/*
 * Das `<details>` selbst ist nur die Hülle. Am Schreibtisch wird es zum
 * Bezugspunkt des Klappmenüs (siehe dort); auf dem Telefon bleibt es
 * unpositioniert, denn das Blatt hängt dort am Fenster und nicht am Zahnrad.
 */
.zen-ansicht { display: inline-flex; }

/*
 * Der Auslöser trägt zusätzlich `.zen-nav-link` und damit Höhe, Polster,
 * Schriftgrad und Farbe der Einträge daneben. Hier steht nur, was ein
 * `<summary>` darüber hinaus braucht: kein Dreieck – weder das des Browsers
 * noch das von Safari –, ein Zeiger und der Abstand zwischen Zeichen und Wort.
 */
.zen-ansicht-taste {
    gap: 0.4rem;
    cursor: pointer;
    list-style: none;
    user-select: none;
}

.zen-ansicht-taste::-webkit-details-marker { display: none; }

.zen-ansicht-taste:focus-visible {
    outline: 2px solid var(--kopf-text-hell);
    outline-offset: -2px;
}

/*
 * Das Zahnrad. Es ist eine Linienzeichnung in `currentColor` und keine
 * Schriftzeichen: `⚙` sieht in jeder Schrift anders aus, im Handbuch-PDF ist
 * es ein leerer Kasten, und auf manchen Geräten kommt es bunt daher. Die Größe
 * steht in Pixeln und nicht in `em` – der Eintrag ist auf dem Telefon kleiner
 * gesetzt als am Schreibtisch, das Zeichen soll es nicht sein.
 */
.zen-ansicht-zeichen {
    width: 19px;
    height: 19px;
    flex: 0 0 19px;
    display: block;
}

/* Geöffnet ist der Auslöser so hell wie ein aktiver Eintrag – sonst wäre am
   Zahnrad nicht zu sehen, dass das Blatt darunter zu ihm gehört. */
.zen-ansicht[open] > .zen-ansicht-taste { color: var(--kopf-text-hell); }

/*
 * Das Blatt am unteren Rand – die Fassung für Telefon und Tablet.
 *
 * `fixed` und nicht `absolute`: Die Navigation ist auf dem Telefon die zweite
 * Kopfzeile, und ein Menü, das an ihr hinge, stünde je nach Breite einmal
 * links und einmal rechts an ihrem Rand – bei 320 px auch schon einmal über
 * ihn hinaus. Am Fensterrand ist es immer dasselbe Blatt.
 */
.zen-ansicht-menue {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 40;
    display: flex;
    flex-direction: column;
    padding: 0.4rem 0.4rem max(0.4rem, env(safe-area-inset-bottom));
    background: var(--flaeche-karte);
    border-top: 1px solid var(--rand);
    border-radius: 14px 14px 0 0;
    box-shadow: var(--schatten-dialog);
    /* Ein Blatt, das länger wird als der Bildschirm, scrollt in sich selbst.
       Zwei Zeilen tun das nie – eine dritte irgendwann schon. */
    max-height: 70vh;
    overflow-y: auto;
}

.zen-ansicht-form { display: flex; flex-direction: column; }

/*
 * Eine Aktion ist eine ganze Zeile: Auf dem Telefon ist das die Tippfläche,
 * die der mobile Feinschliff verlangt (44 px, Issue #35), und auf jedem Gerät
 * ist es die Form, in der zwei verschieden lange Beschriftungen gleich
 * aussehen.
 */
.zen-ansicht-punkt {
    display: flex;
    align-items: center;
    width: 100%;
    min-height: 44px;
    padding: 0.5rem 0.7rem;
    border: none;
    border-radius: 8px;
    background: none;
    color: var(--text-normal);
    font: inherit;
    font-size: 0.95rem;
    text-align: left;
    cursor: pointer;
}

.zen-ansicht-punkt:hover:not(:disabled) { background: var(--flaeche-zart); }
.zen-ansicht-punkt:focus-visible {
    outline: 2px solid var(--kontur);
    outline-offset: -2px;
}

/* Gesperrt heißt hier „steht schon so" und nicht „geht nicht": Der Punkt
   bleibt lesbar und sagt im `title`, warum er gerade nichts zu tun hat. */
.zen-ansicht-punkt:disabled { color: var(--text-still); cursor: default; }

/*
 * Die beiden Aktionen des Bearbeitungsmodus, an derselben Stelle wie das Menü.
 * Klein, aber nicht kleiner als ein Tippziel – auch hier gelten die 44 px.
 */
.zen-modus-taste {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
    padding: 0 0.7rem;
    border: 1px solid var(--kopf-text);
    border-radius: 8px;
    background: none;
    color: var(--kopf-text-hell);
    font: inherit;
    font-size: 0.85rem;
    cursor: pointer;
}

.zen-modus-taste:hover { border-color: var(--kopf-text-hell); }
.zen-modus-taste:focus-visible {
    outline: 2px solid var(--kopf-text-hell);
    outline-offset: 2px;
}

/* `Fertig` ist die Aktion, um die es geht – sie trägt die Hausfarbe. Sie ist
   damit auch die einzige gefüllte Fläche im Kopf, und das ist Absicht. Die
   Paarung ist die schon vorhandene aus `.abmelden:hover`; `--fuellung-text`
   bleibt im dunklen Modus mit Bedacht weiß. */
.zen-modus-taste-stark {
    border-color: var(--kopf-akzent);
    background: var(--kopf-akzent);
    color: var(--fuellung-text);
    font-weight: 600;
}

.zen-modus-taste-stark:hover { border-color: var(--kopf-akzent); }

/*
 * Ausgeblendet heißt auch im Kopf ausgeblendet – dieselbe Zeile und derselbe
 * Grund wie an `.zen-seite` weiter oben: Menü und Knöpfe bekommen hier ein
 * eigenes `display`, und das schlüge sonst die eingebaute Regel des Browsers
 * für `hidden`.
 */
.zen-kopf [hidden] { display: none !important; }

.zen-ordnen-hinweis {
    margin: 0;
    font-size: 0.85rem;
    color: var(--text-leise);
}

.zen-ordnen-meldung {
    margin: 0;
    font-size: 0.85rem;
    color: var(--erfolg-text);
}

/* Der Griff über der Kachel – im Ruhezustand gibt es ihn nicht. */
.zen-kachel-ordnen { display: none; }

.zen-ordnen-aktiv .zen-kachel-ordnen {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.4rem;
    margin: 0 0 0.6rem;
    padding-bottom: 0.5rem;
    border-bottom: 1px dashed var(--rand-stark);
}

.zen-kachel-name {
    font-size: 0.85rem;
    font-weight: 600;
    color: var(--text-stark);
    margin-right: auto;
}

.zen-kachel-grund { font-size: 0.75rem; color: var(--text-still); }

/* Was der Bearbeitungsmodus über die Breite einer Kachel sagt – dieselbe
   Angabe und dieselbe Größe wie auf dem Entwicklungsstatus. */
.zen-kachel-breite { font-size: 0.75rem; color: var(--text-still); }

.zen-kachel-pfeil {
    /*
     * 44 px im Quadrat, damit die Taste auch auf dem Telefon zu treffen ist –
     * dort ist sie die einzige Bedienung, weil Ziehen auf Touchgeräten nicht
     * geht. Die Breite kommt aus dem Seitenverhältnis und nicht aus einer
     * `min-width` in Pixeln: Diese Seite darf nirgends eine feste
     * Mindestbreite bekommen, sonst entstünde auf einem schmalen Bildschirm
     * eine waagerechte Scrollleiste.
     */
    min-height: 44px;
    aspect-ratio: 1;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: 1px solid var(--rand-stark);
    border-radius: 6px;
    background: var(--flaeche-sanft);
    color: var(--text-normal);
    font-size: 1rem;
    cursor: pointer;
}

.zen-kachel-pfeil:hover { background: var(--flaeche-neutral); }

/*
 * Im Bearbeitungsmodus sieht man, was verschiebbar ist – und was nicht. Eine
 * Kachel, die sich stumm weigert, wäre eine Panne; eine, die ihren Grund
 * danebenschreibt, ist eine Grenze.
 */
.zen-ordnen-aktiv [data-zen-kachel] {
    outline: 2px dashed var(--rand-akzent);
    outline-offset: 2px;
    cursor: grab;
}

.zen-ordnen-aktiv [data-zen-kachel][data-zen-fest="1"] {
    outline-style: dotted;
    cursor: default;
}

/*
 * Solange angeordnet wird, führt kein Verweis in einer Kachel mehr weg. Ein
 * Klick auf „#88" mitten im Verschieben verließe die Seite – und mit ihr die
 * noch nicht gespeicherte Reihenfolge. Zugleich bekommt damit die Kachel den
 * Zeiger und nicht der Verweis darin: Ziehen beginnt auch dann, wenn man sie
 * an einer Stelle anfasst, an der zufällig ein Verweis liegt. Die Tasten des
 * Griffs sind ausgenommen – sie sind die Bedienung dieses Modus.
 */
.zen-ordnen-aktiv [data-zen-kachel] a { pointer-events: none; }
.zen-ordnen-aktiv .zen-kachel-ordnen { pointer-events: auto; }

.zen-kachel-zieht { opacity: 0.5; }

/*
 * Die Kennzahlen sind seit der Nacharbeit eine eigene Kachel. Sie hat keinen
 * Rahmen – die Zahlen darin tragen ihren eigenen –, braucht aber einen Kasten,
 * an dem der Griff und die gestrichelte Umrandung hängen können.
 */
.zen-zahlen-kachel { display: flex; flex-direction: column; }

/* ── Die Gesamtansicht aller Aufgaben (Nacharbeit zu Issue #105) ────────── */

/*
 * Der Weg zwischen Übersicht und Gesamtansicht – in beide Richtungen derselbe
 * Verweis. Er steht im Kopf der jeweiligen Liste und ist bewusst kein Knopf:
 * Er wechselt die Seite und löst keine Aktion aus.
 */
.zen-alle-anzeigen {
    color: var(--verweis);
    font-size: 0.85rem;
    text-decoration: none;
    white-space: nowrap;
}

.zen-alle-anzeigen:hover,
.zen-alle-anzeigen:focus-visible { text-decoration: underline; }

/*
 * Die Filterreihe. Sie bricht um statt zu scrollen: Sieben Filter passen auf
 * einem 390 px breiten Telefon nicht in eine Zeile, und eine waagerechte
 * Scrollleiste ist dort keine Lösung, sondern ein Befund.
 */
.zen-filterreihe { display: flex; flex-wrap: wrap; gap: 0.4rem; }

.zen-filterknopf {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    padding: 0.35rem 0.7rem;
    border: 1px solid var(--rand);
    /* Die Zustandsfarbe sitzt an der linken Kante – dieselbe Stelle und
       dieselbe Farbe wie an der Gruppe, auf die der Filter zeigt. */
    border-left: 3px solid var(--rand-stark);
    border-radius: 999px;
    background: var(--flaeche-karte);
    color: var(--text-stark);
    font-size: 0.85rem;
    text-decoration: none;
}

.zen-filterknopf:hover { border-color: var(--kontur); }

/* Der gewählte Filter trägt Fläche **und** `aria-current`. Die Farbe allein
   sagte es nur denen, die sie sehen. */
.zen-filterknopf.aktiv {
    background: var(--flaeche-gewaehlt);
    border-color: var(--kontur);
    font-weight: 600;
}

.zen-filter-anzahl {
    padding: 0 0.4rem;
    border-radius: 999px;
    background: var(--flaeche-sanft);
    color: var(--text-leise);
    font-size: 0.78rem;
    font-variant-numeric: tabular-nums;
}

/*
 * Das Suchfeld. Auf dem Telefon nimmt es die volle Breite und der Knopf steht
 * darunter; am Schreibtisch stehen sie nebeneinander. `flex-wrap` erledigt
 * beides ohne eine zweite Regel.
 */
.zen-suche {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem;
}

.zen-suche-label {
    flex: 1 0 100%;
    font-size: 0.8rem;
    color: var(--text-leise);
}

.zen-suche-feld {
    flex: 1 1 12rem;
    min-width: 0;
    padding: 0.45rem 0.6rem;
    border: 1px solid var(--rand);
    border-radius: 8px;
    background: var(--flaeche-eingabe);
    color: var(--text-stark);
    font-size: 0.95rem;
    font-family: inherit;
}

.zen-suche-feld:focus { outline: none; border-color: var(--kontur); }

.zen-suche-knopf {
    padding: 0.45rem 0.9rem;
    border: 1px solid var(--kontur);
    border-radius: 8px;
    background: var(--flaeche-sanft);
    color: var(--text-stark);
    font-size: 0.9rem;
    cursor: pointer;
}

.zen-suche-knopf:hover { background: var(--flaeche-gewaehlt); }

.zen-suche-zurueck {
    color: var(--verweis);
    font-size: 0.85rem;
    text-decoration: none;
}

.zen-suche-zurueck:hover,
.zen-suche-zurueck:focus-visible { text-decoration: underline; }

/*
 * Die Zustandsmarke an einer Zeile der Gesamtliste.
 *
 * In den Gruppen der Übersicht sagt die Überschrift, in welchem Zustand alles
 * darunter steht; in der gemischten Liste muss es an der Zeile stehen. Zeichen
 * **und** Wort, und die Farbe nur dazu – dieselbe Zusage wie überall in diesem
 * Bestand: Farbe nie allein.
 */
.zen-marke-zustand {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    padding: 0.05rem 0.5rem;
    border-radius: 999px;
    border: 1px solid var(--rand-still);
    background: var(--flaeche-sanft);
    color: var(--text-leise);
    white-space: nowrap;
}

.zen-marke-arbeit { border-color: var(--warnung-kraeftig); color: var(--warnung-text); }
.zen-marke-abnahme-offen { border-color: var(--warnung-kraeftig); color: var(--warnung-text); }
.zen-marke-fehler { border-color: var(--fehler-kraeftig); color: var(--fehler-text); }
.zen-marke-vorgemerkt { border-color: var(--info-kante); color: var(--info-text); }
/* Nacharbeit, die von selbst zurueckkommt (Issue #160): dieselbe Kante wie die
   vorgemerkte Wartung. Beide heissen dasselbe – der Worker erledigt das, hier
   ist nichts zu tun –, und zwei Farben fuer dieselbe Aussage waeren eine zu
   viel. Unterschieden wird ueber die Form: das Symbol ist ein anderes. */
.zen-marke-nacharbeit { border-color: var(--info-kante); color: var(--info-text); }
.zen-marke-warteschlange { border-color: var(--hinweis-kante); color: var(--verweis); }
.zen-marke-entscheidung { border-color: var(--warnung-kraeftig); color: var(--warnung-text); }
.zen-marke-geplant { border-color: var(--rand-still); color: var(--text-leise); }
.zen-marke-nicht-freigegeben { border-color: var(--rand-still); color: var(--text-leise); }

/* Dieselben Kanten am Filterknopf: Wer den Zustand an der Zeile erkennt, soll
   ihn am Knopf wiedererkennen. */
.zen-filterknopf.zen-marke-arbeit { border-left-color: var(--warnung-kraeftig); }
.zen-filterknopf.zen-marke-abnahme-offen { border-left-color: var(--warnung-kraeftig); }
.zen-filterknopf.zen-marke-fehler { border-left-color: var(--fehler-kraeftig); }
.zen-filterknopf.zen-marke-vorgemerkt { border-left-color: var(--info-kante); }
.zen-filterknopf.zen-marke-nacharbeit { border-left-color: var(--info-kante); }
.zen-filterknopf.zen-marke-warteschlange { border-left-color: var(--hinweis-kante); }
.zen-filterknopf.zen-marke-entscheidung { border-left-color: var(--warnung-kraeftig); }
.zen-filterknopf.zen-marke-geplant { border-left-color: var(--rand-still); }
.zen-filterknopf.zen-marke-nicht-freigegeben { border-left-color: var(--rand-still); }
.zen-filterknopf.zen-marke-alle { border-left-color: var(--kontur); }

/* ── Der eigene Zugang der Zentrale (Issue #110) ────────────────────────── */

/*
 * Eine Zeile je Zugang, und in ihr zwei kleine Formulare. Mobil zuerst: Alles
 * steht untereinander, jedes Feld über die ganze Breite, jeder Knopf 44 px
 * hoch. Am Schreibtisch bleibt es dabei – es sind Passwortfelder, und die
 * dürfen Platz haben; eine Zeile mit fünf Feldern nebeneinander wäre eng und
 * bei zwei gleich aussehenden Feldern („Neu", „Wiederholen") auch
 * verwechselbar.
 */
.zen-zugang {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    padding: 0.8rem 0;
    border-bottom: 1px solid var(--rand);
}

.zen-zugang:last-child { border-bottom: none; }

.zen-zugang-kennung {
    font-weight: 600;
    color: var(--text-stark);
    word-break: break-word;
}

.zen-zugang-name { color: var(--text-leise); font-size: 0.9rem; }

/*
 * Die Stufe an einer Zugangszeile (Issue #141).
 *
 * Sie steht nur beim Prüfzugang da und sieht deshalb aus wie eine Marke und
 * nicht wie eine Zeile Text: Der volle Zugang ist der Normalfall, und was vom
 * Normalfall abweicht, soll man sehen, ohne es zu lesen. Dieselbe Bauart wie
 * die Rollenmarke des Handbuchs – runde Fläche, leise Farbe, kleine Schrift.
 */
.zen-zugang-stufe {
    align-self: flex-start;
    padding: 0.1rem 0.55rem;
    border-radius: 999px;
    background: var(--flaeche-sanft);
    color: var(--text-leise);
    font-size: 0.78rem;
}

/*
 * Das Häkchen im Anlegeformular. Kästchen und Text liegen nebeneinander und
 * auf einer Grundlinie; die ganze Zeile ist die Tippfläche, weil das `<label>`
 * das Kästchen umschließt.
 */
.zen-zugang-haken {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    min-height: 44px;
    font-size: 0.9rem;
    color: var(--text-normal);
    cursor: pointer;
}

.zen-zugang-haken input[type="checkbox"] {
    width: 20px;
    height: 20px;
    flex: none;
    accent-color: var(--kontur);
}

.zen-zugang-form {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
    /* Nichts darin darf die Seite waagerecht scrollen lassen. */
    min-width: 0;
    max-width: 26rem;
}

.zen-zugang-label {
    font-size: 0.8rem;
    color: var(--text-leise);
}

/*
 * Die Felder. Das Passwortfeld kommt aus `_passwortfeld.html` und bringt nur
 * seine Positionierung samt Augensymbol mit – Rahmen, Polster und Höhe holt
 * es sich sonst aus `.formfeld`/`.formular-zeile`, und beide gibt es in
 * dieser Seite nicht. Es steht deshalb hier ausdrücklich neben dem
 * Textfeld: gleiches Aussehen, gleiche Tippfläche, kein Feld im Rohzustand
 * des Browsers.
 */
.zen-zugang-form input[type="text"],
.zen-zugang-form .passwortfeld-eingabe {
    min-height: 44px;
    padding: 0.4rem 0.6rem;
    border: 1px solid var(--rand-stark);
    border-radius: 8px;
    background: var(--flaeche-eingabe);
    color: var(--text-normal);
    font-size: 1rem;
    width: 100%;
    min-width: 0;
}

/*
 * Und der rechte Abstand für das Augensymbol wieder zurück: Die Kurzform
 * `padding` eine Regel höher wiegt genauso schwer wie die allgemeine Regel
 * bei `.passwortfeld` und steht später – ohne diese Zeile liefe der
 * eingegebene Text unter das Symbol. Derselbe Fall und dieselbe Lösung wie
 * bei `.formular-zeile` weiter oben.
 */
.zen-zugang-form .passwortfeld .passwortfeld-eingabe { padding-right: 2.9rem; }

/* Dieselbe Tippfläche wie überall in diesem Bestand (Issue #35). */
.zen-zugang-form .schaltflaeche {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-height: 44px;
    align-self: flex-start;
}

/* ── Der Schreibtisch ───────────────────────────────────────────────────── */

@media (min-width: 900px) {
    .zen-haupt { padding: 1.5rem; }

    /*
     * Zwei Spalten (Issue #110), die auf **derselben Unterkante** enden
     * (Issue #200).
     *
     * Die Geschichte dieser Regel ist die Geschichte zweier Löcher.
     *
     * Bis zum 11.09.2026 stand hier ein Raster mit **Zeilen**: zwei halbe
     * Kacheln nebeneinander, volle über beide. Ein Raster gibt allen Zellen
     * einer Zeile dieselbe Höhe, und mit `align-items: start` stand die
     * kürzere Karte oben in einer Zeile, die die längere aufspannte – unter
     * `Was läuft gerade` blieb eine halbe Bildschirmhöhe leer, während der
     * Betrieb weit unten allein stand. Darauf wurde daraus ein
     * **Spaltensatz** (`columns: 2`): Der Browser sucht sich die Teilung, die
     * beide Spalten ungefähr gleich hoch macht, und aus drei Karten wurden
     * zwei gefüllte Spalten.
     *
     * Ungefähr gleich hoch ist aber nicht gleich hoch. Martins Befund vom
     * 25.09.2026 ist genau der Rest: `Was läuft gerade` und `Fortschritt`
     * links, `Automatik und Warteschlange` mit elf Zeilen Arbeitsreihenfolge
     * rechts – und weil eine Karte nicht zerschnitten werden darf, blieb
     * unter der linken Spalte eine große freie Fläche stehen. Ein
     * Spaltensatz kann das nicht beheben: Er verteilt Blöcke, er streckt
     * keine.
     *
     * Ein Raster kann es. Es ist deshalb wieder eines – aber kein
     * Zeilenraster von vorher, sondern eines, in dem **jede Kachel ihren
     * Platz kennt**:
     *
     *   * Die zwei Spalten stehen hier (`repeat(2, minmax(0, 1fr))`), der
     *     Abstand ist derselbe `gap` wie auf dem Telefon.
     *   * **Wo** eine Kachel steht, sagt der Server:
     *     `kachelordnung.rasterplaetze()` rechnet aus der eingestellten
     *     Reihenfolge Spalte, Anfangszeile und Zeilenspanne und schreibt sie
     *     als `--zen-spalte`, `--zen-zeile` und `--zen-spanne` an die Kachel.
     *     Eine Liste von Kachelnamen in dieser Datei wäre eine zweite
     *     Reihenfolge; `static/zentrale-kacheln.js` rechnet dieselbe Regel
     *     beim Verschieben nach.
     *   * Die Karten eines Bandes teilen sich dieselben Zeilen: links zwei
     *     Kacheln in zwei Zeilen, rechts eine über beide. Ist die rechte
     *     höher als die beiden linken zusammen, verteilt das Raster den
     *     Zuschlag **gleichmäßig auf die beiden Zeilen** – die linken Karten
     *     wachsen mit, und die Unterkanten liegen auf einer Linie. Ist die
     *     linke Spalte höher, wächst die rechte Karte mit. Das ist die
     *     Zusage des Auftrags: keine tote Lücke und keine starre Höhe.
     *   * **Wie hoch** eine Zeile werden darf, sagt ebenfalls der Server:
     *     `kachelordnung.rasterzeilen()` schreibt `--zen-zeilen` ans Raster –
     *     `auto` je Zeile und `1fr` für die dehnbare. Martins Abnahme vom
     *     28.09.2026 war genau dieser Punkt: Ein Raster verteilt den
     *     Höhenzuschlag einer überspannenden Karte **gleichmäßig** auf die
     *     Zeilen, die sie überspannt, und damit bekam die kurze Karte oben die
     *     Hälfte davon als leere Fläche. Eine Karte, die eine flexible Zeile
     *     überspannt, wächst die intrinsischen Zeilen daneben nicht mehr auf –
     *     oben bleibt die Inhaltshöhe, unten liegt der Rest. Bündig bleibt das
     *     Band dabei; gemessen hat es `tests/browser_zentrale.mjs`.
     *   * Gestreckt und nicht gestaucht: `align-items` bleibt bei seinem
     *     `stretch`. Ein `start` wäre genau das Loch von vorher.
     *
     * Was hier ausdrücklich **nicht** mehr steht: `columns`, `break-inside`,
     * `column-span` und der `margin-bottom` je Kachel. Im Raster steht der
     * Abstand einmal als `gap` zwischen allen Karten – auch über einer
     * überspannenden Kachel, wo der Spaltensatz ihn abgeschnitten hatte
     * (Issue #116).
     */
    .zen-raster {
        display: grid;
        grid-template-columns: repeat(2, minmax(0, 1fr));
        /* Die Zeilenhöhen kommen vom Server – ohne Angabe bleibt es beim
           `none` des Browsers, also bei lauter `auto`-Zeilen wie vorher. */
        grid-template-rows: var(--zen-zeilen, none);
        gap: var(--zen-kachel-abstand);
    }

    .zen-raster > [data-zen-kachel] {
        grid-column: var(--zen-spalte, 1);
        grid-row: var(--zen-zeile, auto) / span var(--zen-spanne, 1);
        /* Nichts in einer halben Spalte darf sie sprengen – dieselbe Zeile
           und derselbe Grund wie an `.zen-seite`. */
        min-width: 0;
    }

    /*
     * Die vollen Kacheln überspannen beide Spalten. Sie tragen alle dieselbe
     * Klasse – die von Haus aus vollen ebenso wie die halbe, die in dieser
     * Ordnung allein in ihrem Band steht (`volle_breite()`). Ihre Zeile steht
     * trotzdem am Element: Ohne sie müsste der Browser raten, und zwischen
     * zwei Bändern gibt es nichts zu raten.
     */
    .zen-raster > [data-zen-kachel].zen-voll { grid-column: 1 / -1; }

    /* Am Schreibtisch ist Platz für eine Zeile: Marke, Bereiche, Zugang. */
    .zen-nav { order: 1; }

    /*
     * Und dort wird aus dem Blatt am unteren Rand ein Klappmenü unter dem
     * Zahnrad (Issue #123).
     *
     * Der Bezugspunkt ist erst hier das `<details>` selbst: Auf dem Telefon
     * hängt das Blatt am Fenster, am Schreibtisch soll das Menü unter seinem
     * Auslöser stehen. `left: 0` und nicht `right: 0` – der Auslöser sitzt am
     * Ende der Navigation und damit weit links vom Fensterrand; nach rechts
     * ist Platz, nach links stünden die Bereiche im Weg.
     */
    .zen-ansicht { position: relative; }

    .zen-ansicht-menue {
        position: absolute;
        top: calc(100% + 0.4rem);
        left: 0;
        right: auto;
        bottom: auto;
        width: max-content;
        max-width: 22rem;
        max-height: none;
        padding: 0.3rem;
        border: 1px solid var(--rand);
        border-radius: 10px;
        box-shadow: var(--schatten-vorschlaege);
    }

    /* Dieselbe Stufe wie bei den Navigationseinträgen: 44 px ist die
       Tippfläche des Telefons (Issue #35), am Schreibtisch zeigt eine Maus auf
       den Pixel genau. */
    .zen-ansicht-punkt { min-height: 34px; font-size: 0.875rem; }

    /*
     * Und dasselbe für den Projektumschalter am anderen Ende der Leiste
     * (Issue #232). Er ist dieselbe Sorte Bedienelement und bekommt deshalb
     * dieselbe Umstellung – aus dem Blatt am unteren Rand wird ein Klappmenü
     * unter seinem Auslöser.
     *
     * `left: 0` und nicht `right: 0`: Der Umschalter steht am **Anfang** der
     * Navigation, links ist nur die Marke, und nach rechts ist Platz.
     */
    .zen-projektwahl { position: relative; }

    .zen-projektwahl-menue {
        position: absolute;
        top: calc(100% + 0.4rem);
        left: 0;
        right: auto;
        bottom: auto;
        width: max-content;
        min-width: 14rem;
        max-width: 24rem;
        max-height: none;
        padding: 0.3rem;
        border: 1px solid var(--rand);
        border-radius: 10px;
        box-shadow: var(--schatten-vorschlaege);
    }

    .zen-projektwahl-punkt { min-height: 34px; font-size: 0.875rem; }

    /*
     * Und die Leiste rückt zusammen (Issue #232).
     *
     * Die knappe Laptopbreite von 1152 px ist die Stelle, an der sich das
     * entscheidet: Dort misst `automatisierung/browserqm.py` die vereinbarten
     * 72 px Kopfhöhe (Issue #164), und die Zentrale trägt seit diesem Auftrag
     * ein Bedienelement mehr als vorher. Gemessen im Browser waren es 57 px zu
     * viel; diese drei Zeilen holen sie herein, ohne an Schriftgrad oder
     * Tippfläche etwas zu ändern.
     *
     * Der eigene Auslöser gibt dabei am meisten her – er steht übereinander
     * gesetzt (siehe oben) und braucht innen weniger Polster als ein Eintrag
     * mit einem einzigen Wort. Der engere Abstand **zwischen** den Einträgen
     * steht auf `.zen-kopf .zen-nav` und nicht auf `.zen-nav` selbst: Die
     * Grundregel dort ist Zeichen für Zeichen die des Monitors
     * (`tests/test_monitor.py`, L3), und der hat weder Umschalter noch
     * Zahnrad in seiner Leiste.
     */
    .zen-projektwahl-taste { gap: 0.25rem; padding: 0 0.45rem; max-width: 12rem; }
    .zen-kopf .zen-nav { gap: 0.2rem; }

    /*
     * Und der angemeldete Zugang bekommt eine engere Obergrenze als die
     * 12 rem des Grundlayouts.
     *
     * Das ist die Vorkehrung gegen den Fall, den diese Suite nicht messen
     * kann: Der Prüfstand meldet sich als `Administrator` an, ein Zugang in
     * der Zentrale kann anders heißen. Nach dem gemessenen Stand bleiben bei
     * 1152 px rund 16 px Luft – ein deutlich längerer Name äße sie auf, und
     * die Kopfzeile bräche in eine zweite Zeile um. Sichtbar geht dabei nichts
     * verloren: Der Name kürzt sich mit Auslassungspunkten und steht
     * vollständig im `title`.
     */
    .zen-kopf .zen-benutzer { max-width: 8rem; }

    .zen-modus-taste { min-height: 34px; font-size: 0.8rem; }

    /*
     * Dieselbe Stufe für die drei Tasten einer wartenden Zeile (Martins
     * Abnahme vom 28.09.2026). Sie sind der Grund, aus dem eine Queue-Zeile
     * überhaupt höher ist als ihr Text: 44 px ist die Tippfläche des Telefons,
     * und unter 900 px bleibt sie es. Am Schreibtisch zeigt eine Maus auf den
     * Pixel genau, und die 34 px sind hier dieselbe Zahl wie an den Einträgen
     * darüber – gespart wird damit knapp ein Viertel der Zeilenhöhe, ohne dass
     * eine neue Größe in dieses Stylesheet käme.
     */
    .zen-queue-griff,
    .zen-queue-pfeil { min-height: 34px; }

    /*
     * Und dort auch die Maße der Redaktion (Issue #111).
     *
     * 44 px ist die **Tippfläche des Telefons** (Issue #35). Am Schreibtisch
     * zeigt eine Maus auf einen Pixel genau, und dort steht in der Navigation
     * der Redaktion (`.menue-link`, 0.875 rem) und in der Statistik seit ihrer
     * dritten Nacharbeit ein 34 px hoher Eintrag mit 14 px Schrift. Der
     * Auftrag zu #111 verlangt die Top-Navigationen „auf Desktop konsistent,
     * kompakt und platzsparend" und ausdrücklich das vorhandene Muster statt
     * einer neuen Variante – hier ist es.
     *
     * Unter 900 px ändert sich nichts: Dort gilt weiter die 44er Regel aus der
     * Grundeinstellung darüber. Dieselben zwei Zeilen stehen im Monitor
     * (`monitor/static/style.css`, `.mon-nav-link`).
     */
    .zen-nav-link,
    .zen-abmelden { min-height: 34px; font-size: 0.875rem; }

    /*
     * Erst hier bekommen die Aufgabenzeilen ihre Spalten: Nummer, Titel und
     * die Randangaben nebeneinander. Auf dem Telefon blieben von jeder Spalte
     * zwei Wörter übrig – dort stehen sie untereinander.
     *
     * Die beiden Klammern an der mittleren und der rechten Spalte sind Issue
     * #128, und sie beheben genau den Befund, der übrig blieb, nachdem #141
     * den Überlauf beseitigt hatte.
     *
     * Vorher stand hier `minmax(0, 1fr) minmax(0, auto)`. Das ist eine
     * Rangfolge zugunsten der Randangaben: Eine `auto`-Spalte bekommt ihren
     * **maximalen** Inhalt zugeteilt, bevor die `fr`-Spalte den Rest sieht –
     * und der maximale Inhalt einer Randangabe ist die ungebrochene Zeile aus
     * Zustandsmarke, Zusatzauskunft, Laufbilanz und Datum. Für den Titel blieb
     * übrig, was danach noch da war, und weil seine Untergrenze `0` ist,
     * durfte das auch nichts sein. Nachgemessen im Browser
     * (`tests/browser_zentrale.mjs`) waren es bei 900 und bei 1024 px genau
     * **0 px** – der Titel stand einen Buchstaben breit untereinander, bei
     * 1280 px in 150 px zu elf Zeilen mit je einem Wort. Das ist Martins
     * Screenshot vom 16.09.2026.
     *
     * Umgedreht wird die Rangfolge, nicht das Raster:
     *
     *   * Der **Titel** bekommt eine echte Untergrenze – 20 rem, und wo die
     *     Zeile dafür zu schmal ist, 45 % von ihr. Die zweite Zahl ist die
     *     Sicherung: Eine feste Untergrenze in `rem` wächst mit einer größer
     *     gestellten Schrift und risse die Zeile irgendwann doch auf.
     *   * Die **Randangaben** nehmen weiter, was sie brauchen, aber nie mehr
     *     als ein Drittel der Zeile (`fit-content`). Was darüber hinausgeht,
     *     bricht um – dürfen sie seit #141 ja.
     *
     * Unter 900 px ändert das nichts: Dort stehen die Spalten ohnehin
     * untereinander, und das ist auch die Antwort des Auftrags auf enge
     * Fenster – lieber umbrechen als den Titel zusammenquetschen.
     */
    .zen-eintrag {
        grid-template-columns:
            5rem minmax(min(20rem, 45%), 1fr) fit-content(33%);
        align-items: baseline;
    }

    /*
     * Die Randangaben dürfen umbrechen – und zwar seit Issue #141.
     *
     * Hier stand bis dahin `flex-wrap: nowrap; white-space: nowrap`, und genau
     * daran lief die Zentrale zwischen 900 und 1280 px waagerecht über: Eine
     * Rasterspalte wird nie schmaler als ihr Mindestinhalt, und der Mindest-
     * inhalt einer Zeile, die nicht umbrechen darf, ist die ganze Zeile. Am
     * angemeldeten Produktivstand waren das am 16.09.2026 bis zu 540 px zu
     * viel auf `/zentrale/aufgaben` – mit einer waagerechten Bildlaufleiste
     * auf einem gewöhnlichen Laptop.
     *
     * Umbrechen dürfen heißt nicht mittendrin: Zahl und Datum bleiben
     * zusammen (`white-space: nowrap` an ihnen), die längere Auskunft
     * daneben bricht wie gewöhnlicher Text. Kurze Randangaben stehen
     * weiterhin in einer Zeile, rechtsbündig – seit #128 ist der Platz dafür
     * allerdings auf ein Drittel der Zeile begrenzt, und eine lange
     * Zusatzauskunft bricht deshalb auch am Schreibtisch um. Das ist der
     * Tausch, den dieser Auftrag verlangt: eine Zeile höher rechts ist
     * weniger schlimm als ein Titel, der einen Buchstaben breit wird.
     *
     * Das Umbrechen ist auch die Bedingung dafür, dass die Deckelung der
     * rechten Spalte oben überhaupt trägt: Eine Spalte wird nie schmaler als
     * ihr Mindestinhalt, und der Mindestinhalt dieser Randangaben ist seit
     * #141 nicht mehr die ganze Zeile, sondern ihr längstes unteilbares Stück
     * – die Laufbilanz oder das Datum.
     */
    .zen-eintrag-rand {
        grid-column: auto;
        justify-content: flex-end;
        min-width: 0;
    }

    .zen-eintrag-bilanz,
    .zen-eintrag-datum { white-space: nowrap; }

    .zen-eintrag-hinweis { grid-column: 2 / -1; }

    /*
     * Der Kopf am Schreibtisch – seit Issue #164 auf derselben Höhe wie der
     * der Redaktion.
     *
     * Bis hierher gab das Portrait die Höhe vor: 68 px zuzüglich zweimal
     * 0,6 rem Polster waren knapp 88 px, die Redaktion daneben kam auf 62.
     * Martin hat beide Vollbilder nebeneinander gelegt und um die Mitte
     * gebeten. Die Zahl steht jetzt in `--kopf-hoehe` (72 px) und gilt für
     * beide Häuser; das Portrait geht dafür auf 56 px zurück – deutlich
     * kleiner als die 68, aber immer noch die Stufe über den 44/56 px von
     * #105 und klar präsent.
     *
     * Das Polster wird dabei kleiner (0,4 statt 0,6 rem), und das ist die
     * Bedingung dafür, dass die gemeinsame Zahl auch wirklich trägt: 56 px
     * zuzüglich zweimal 0,6 rem wären 75 px und damit wieder mehr als die
     * Mindesthöhe – der Kopf wüchse über sie hinaus und stünde neben der
     * Redaktion erneut drei Bildpunkte zu hoch. Mit 0,4 rem bleibt der Inhalt
     * darunter, und die sichtbare Luft um das Portrait kommt aus der
     * Mindesthöhe: (72 − 56) / 2, also 8 px oben und unten.
     *
     * Der Monitor macht das **nicht** mit (siehe `.zen-signet` oben).
     */
    .zen-kopf {
        min-height: var(--kopf-hoehe);
        padding: 0.4rem 1rem;
    }

    .zen-signet { width: 56px; height: 56px; flex-basis: 56px; border-radius: 14px; }

    /*
     * Am Schreibtisch ist Platz für mehr Protokoll. Das eine verbliebene
     * Fenster bleibt trotzdem begrenzt: Ein Fenster, das so hoch ist wie sein
     * Inhalt, scrollt nicht – und dann kann ihm auch nichts nachlaufen.
     *
     * Seit Issue #200 steht hier `.zen-laufliste` und nicht mehr
     * `.zen-protokoll`. Letzteres ist der gemeinsame Rahmen **beider** Listen
     * und hätte die Stationen weiter mitbegrenzt – und zwar ohne Bildlauf, denn
     * den hat die Stationenliste nicht mehr: Bei elf Stationen liegen die
     * 20 rem nur rund zehn Bildpunkte über dem Inhalt, und ein einziger
     * umbrechender Stationsname hätte die Liste unten aus ihrem Kasten laufen
     * lassen.
     */
    .zen-laufliste { max-height: 20rem; }

    /* Ein Lauf in einer Zeile: Marke, Nummer, Titel, Ergebnis, Ende. */
    .zen-lauf {
        grid-template-columns:
            1.2rem minmax(0, 4.5rem) minmax(0, 1fr) minmax(0, 8rem) minmax(0, 8rem);
    }

    .zen-lauf .zen-eintrag-nummer { grid-column: 2; }
    .zen-lauf .zen-eintrag-titel { grid-column: 3; }
    .zen-lauf-ergebnis { grid-column: 4; }
    .zen-lauf-zeit { grid-column: 5; text-align: right; }

    /* Elf Stationen untereinander sind viel Papier – am Schreibtisch stehen
       Nummer und Name auf derselben Grundlinie und die Zeilen enger. */
    .zen-station { padding: 0.35rem 0.7rem; }
}

/* ── Der Kopf auf dem Telefon (Nacharbeit zu Issue #110) ─────────────────────
 *
 * Martins Abnahme auf dem iPhone am 09.09.2026: „Die Kopfzeile ist deutlich zu
 * groß und überdeckt beim Scrollen zu viel Inhalt."
 *
 * Nachgemessen im Browser bei 390 px waren es **226 px** – gut ein Viertel
 * eines 844-px-Bildschirms, und in Safari mit eingeblendeter Adressleiste
 * mehr. Sie kamen aus vier Blöcken untereinander: das Portrait mit zwei
 * Textzeilen, darunter der angemeldete Zugang, darunter die Navigation in
 * **zwei** Zeilen. Und weil der Kopf klebte, blieb dieser Vierteil dauerhaft
 * stehen.
 *
 * Drei Eingriffe, alle nur hier und keiner am Schreibtisch:
 *
 * 1. **Er klebt nicht mehr.** Dieselbe Entscheidung und derselbe Grund wie im
 *    Hauptmenü der Redaktion (`.hauptmenue` im Block `max-width: 640px`):
 *    Klebend bliebe der Streifen auch schmal im Weg. Auf dem Telefon gehört
 *    die Höhe dem Inhalt; die Navigation ist einen Wisch weit entfernt. Die
 *    zweite mögliche Antwort – eine beim Scrollen zusammenschrumpfende
 *    Kopfzeile – bräuchte ein Skript, einen zweiten Zustand und einen
 *    Umschaltpunkt, an dem der Inhalt springt. Diese Oberfläche kommt ohne
 *    aus.
 * 2. **Der Kopf steht in zwei Zeilen statt in vier Blöcken.** Oben Marke und
 *    Zugang nebeneinander, darunter die Navigation über die ganze Breite.
 *    Festgelegt wird das hier als Raster und nicht länger dem Umbruch
 *    überlassen: Ein Flexumbruch entscheidet an der *Wunschbreite* der Zonen,
 *    und die hängt am Anzeigenamen und am Rollennamen. Genau daran lag es,
 *    dass derselbe Kopf bei 430 px zwei und bei 390 px drei Zeilen hatte. Im
 *    Raster hat jede Zone ihren Platz; wird es eng, bricht der Zugang in sich
 *    um – zwei kurze Zeilen rechts oben statt einer weiteren Kopfzeile.
 * 3. **Die Navigation bleibt eine Gruppe.** Vier Einträge in einer Zeile,
 *    eng gesetzt und deutlich abgesetzt vom Zugang darüber – Zusammengehörig-
 *    keit über Nähe und nicht über eine zusätzliche Kante, die im Kopf einen
 *    festen Farbwert bräuchte (dieses Stylesheet hat nirgends einen).
 *
 * Was **nicht** angetastet ist: die 44-px-Tippfläche jedes Eintrags (Issue
 * #35) und die Kantenlänge des Portraits (52 px, Issue #108 – dieselbe wie im
 * Monitor).
 *
 * Im Browser gemessen, mit dem längsten Namen, den diese Oberfläche kennt
 * („Administrator", Rolle „Admin"):
 *
 *     Breite   vorher   nachher
 *     320 px   226 px   172 px   (Navigation in zwei Zeilen)
 *     375 px   226 px   126 px
 *     390 px   226 px   126 px
 *     430 px   175 px   114 px
 *    1440 px    87 px    87 px   (unverändert, klebend)
 *
 * Die 126 px bei 390 px sind der umgebrochene Zugang: `Administrator Admin`
 * und darunter `Abmelden`. Ein kürzerer Anzeigename bleibt einzeilig und
 * kommt auf dieselben 114 px wie bei 430 px.
 */
@media (max-width: 640px) {
    /*
     * Zwei Spalten, zwei Zeilen.
     *
     * Die **Marke** bekommt ihre Breite zuerst (`auto`), der Zugang den Rest
     * (`1fr`). Andersherum probiert und im Bild gesehen: Dann gab die Marke
     * nach, und im Kopf stand `Entwickl…` neben einem ausgeschriebenen
     * Anzeigenamen. Der Name der Plattform ist kurz, feststehend und die
     * Auskunft, wo man ist – der Anzeigename daneben ist der, der sich
     * kürzen lässt.
     *
     * Beide Spalten sind `minmax(0, …)`: Ein sehr langer Anzeige- oder
     * Rollenname bekäme sonst seine volle Breite und schöbe den Kopf über den
     * Rand hinaus.
     */
    .zen-kopf {
        position: static;
        display: grid;
        grid-template-columns: minmax(0, auto) minmax(0, 1fr);
        padding: 0.4rem 0.7rem;
        gap: 0.3rem 0.5rem;
    }

    /* Auch die Marke darf schmaler werden als ihr Inhalt – ohne diese Zeilen
       kann eine Zone in einem Raster nicht unter ihre Mindestbreite, und der
       Kopf liefe über den Rand. Derselbe Fall und dieselbe Zeile wie bei
       `.menue-rechts` in der Redaktion. */
    .zen-marke { grid-area: 1 / 1; gap: 0.5rem; min-width: 0; }
    .zen-marke-text { min-width: 0; }

    /*
     * Der Untertitel steht auf dem Telefon nicht.
     *
     * „Entwicklung" und darunter „Entwicklungszentrale" ist zweimal dasselbe;
     * den vollen Namen trägt hier ohnehin der Tab-Titel und die Überschrift
     * der Seite. Weg ist damit nicht nur eine Textzeile, sondern die Breite,
     * die sie neben dem Portrait belegte – erst dadurch passt der Zugang
     * daneben. Am Schreibtisch bleibt er stehen: dort ist Platz.
     */
    .zen-untertitel { display: none; }

    /* Und wenn es doch einmal zu eng wird, gibt der Name als Letztes nach:
       erst die Breite, dann die Auslassungspunkte. Ohne sie liefe er in den
       Zugang daneben. */
    .zen-name {
        font-size: 0.88rem;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }

    /*
     * Der Zugang steht rechts in derselben Zeile und bricht bei Enge in sich
     * um – deshalb `flex-wrap` und der rechtsbündige Satz. Zwei kurze Zeilen
     * rechts oben sind immer noch flacher als eine eigene Kopfzeile.
     */
    .zen-rechts {
        grid-area: 1 / 2;
        flex-wrap: wrap;
        justify-content: flex-end;
        margin-left: 0;
        gap: 0.3rem;
        font-size: 0.72rem;
        min-width: 0;
    }
    /* Er nimmt, was die Restspalte hergibt, und kürzt sich sonst selbst –
       `overflow: hidden` steht schon oben und macht ihn schrumpffähig. */
    .zen-benutzer { max-width: 6rem; }
    .zen-rolle { font-size: 0.64rem; padding: 0.05rem 0.35rem; white-space: nowrap; }
    .zen-abmelden { padding: 0 0.35rem; font-size: 0.78rem; }

    /*
     * Die Navigation nimmt die zweite Zeile ganz. Bis hierher entschied der
     * Platz, ob sie umbrach; jetzt ist sie immer eine geschlossene Zeile und
     * der Zugang immer über ihr. Eng gesetzt passen die vier Einträge bei
     * 390 px nebeneinander; wird es schmaler, brechen sie um, statt
     * waagerecht zu scrollen.
     */
    .zen-nav {
        grid-area: 2 / 1 / 3 / -1;
        gap: 0.15rem;
    }

    /* Kleiner und enger, aber keinen Millimeter niedriger: 44 px bleibt das
       Tippziel dieses Bestands (Issue #35). */
    .zen-nav-link { padding: 0 0.4rem; font-size: 0.8rem; }

    /* Dasselbe engere Polster für die vier Seitenpunkte (Issue #342) – als
       Rand und Abdruck aufgeteilt wie im Grundlayout. */
    .zen-nav-seite {
        --zen-seite-polster: 0.4rem;
        --zen-seite-rand: 0.1rem;
        padding-inline: var(--zen-seite-rand);
    }

    /*
     * Der Projektumschalter wird hier so eng gesetzt, wie es ohne Verlust
     * geht (Issue #232) – das Wort `Projekt:` bleibt stehen, weil der Auftrag
     * auf dem Telefon ausdrücklich `Projekt · Radio GUDE` verlangt und ein
     * Name allein in dieser Reihe wie ein sechster Bereich aussähe.
     *
     * Dass die Reihe damit auf manchen Telefonen **umbricht**, ist die
     * Antwort und nicht der Fehler: Der Auftrag schließt eine waagerechte
     * Scrollfalle aus, und `flex-wrap: wrap` steht im Grundlayout. Eine
     * zweite Zeile in der Leiste kostet gut 40 px – die Kachel, die dafür
     * weggefallen ist, kostete auf dem Telefon das Dreifache.
     */
    .zen-projektwahl-taste { gap: 0.25rem; padding: 0 0.4rem; }
    .zen-projektwahl-name { max-width: 9rem; }

    /*
     * Und `Ansicht` ist hier nur noch das Zahnrad (Issue #123).
     *
     * Martins Vorgabe: „Smartphone: möglichst nur das hochwertige
     * Zahnrad-Icon, damit die Kopfzeile kompakt bleibt." Das Wort geht dabei
     * nicht verloren – der Auslöser trägt es als `aria-label`, und wer die
     * Seite hört, hört weiterhin „Ansicht". Was verschwindet, ist die Breite,
     * die es neben vier Bereichen belegte.
     */
    .zen-ansicht-wort { display: none; }

    /* Die beiden Aktionen des Bearbeitungsmodus stehen hier in derselben
       Reihe wie die vier Bereiche – enger gesetzt, damit sie nicht schon bei
       390 px eine zweite Zeile aufspannen. Die Höhe bleibt unangetastet. */
    .zen-modus-taste { padding: 0 0.5rem; font-size: 0.78rem; }

    /* Ohne feststehenden Kopf braucht eine angesprungene Gruppe auch keinen
       Platz mehr unter ihm – die 5 rem aus dem Grundlayout ließen sonst beim
       Sprung aus einer Kennzahl eine Handbreit Leere über der Überschrift. */
    .zen-gruppe { scroll-margin-top: 0.75rem; }
}

/* ── Der externe Navigationsverweis der Redaktion (Issue #105) ───────────── */

/*
 * Der Pfeil hinter „Entwicklung" in der Hauptnavigation.
 *
 * Er kommt aus dem Stylesheet und nicht als Element aus der Vorlage, und das
 * ist eine Zusage an alles, was diese Leiste liest: Der Verweis behält genau
 * einen Textknoten – seinen Namen. Ein Zeichen daneben im Dokument machte aus
 * „Entwicklung" ein „Entwicklung ↗", und zwar für jede Prüfung, jedes Skript
 * und jeden Vergleich, der die Beschriftung liest.
 *
 * Wer die Seite hört statt sie zu sehen, bekommt dieselbe Auskunft aus dem
 * `aria-label` am Verweis. Der Pfeil ist deshalb reine Darstellung und trägt
 * keine Bedeutung, die sonst nirgends stünde.
 *
 * `static/menue.js` hängt denselben Verweis samt Klassen unter „Mehr" um; die
 * Regel folgt ihm dorthin, ohne dass das Skript davon wissen muss.
 */
/*
 * Der Pfeil hat seit Issue #163 einen **festen Kasten**: 0.7 rem breit, das
 * sind genau die 11,2 px, die sein Zeichen bei 0.8 em belegt.
 *
 * Der Grund steht bei `.menue-link[data-titel]::before`. Dort hält jeder
 * Verweis die Breite seiner fetten Beschriftung frei, damit die Leiste beim
 * Bereichswechsel stillsteht – aber der Messabdruck kann nur **Text** tragen,
 * kein zweites Pseudoelement. Solange der Pfeil daneben seine Breite aus der
 * Schriftstärke bezöge, wüchse der externe Bereich im markierten Zustand um
 * gemessene 11,45 px: die Beschriftung und den Pfeil zusammen. Mit einem
 * festen Kasten ist die Zugabe eine bekannte Zahl, und der Abdruck hält sie
 * als `padding-right` mit frei.
 *
 * Am Aussehen ändert das nichts: Der Kasten ist so breit, wie das Zeichen
 * ohnehin war, und `text-align: center` hält es darin an derselben Stelle.
 */
.menue-extern::after {
    content: "↗";
    display: inline-block;
    width: 0.7rem;
    text-align: center;
    margin-left: 0.25rem;
    font-size: 0.8em;
    line-height: 1;
}

/* ── Das Downloadregal der Plugins (Issue #153) ──────────────────────────── */

/*
 * Eine Karte je Plugin, dasselbe Raster wie bei den Rollenkarten: Auf dem
 * Schreibtisch stehen zwei nebeneinander, auf dem Telefon eine – ohne eigenen
 * Breakpoint, weil `auto-fill` mit `min(…, 100%)` genau das von selbst tut.
 * Das `min()` ist dabei kein Beiwerk: Ohne es erzwänge die Mindestbreite bei
 * 375 px eine Spalte, die breiter ist als das Fenster, und die Seite liefe
 * waagerecht über.
 */
.downloadkarten {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(28rem, 100%), 1fr));
    gap: 0.9rem;
    margin-bottom: 1.4rem;
}

.downloadkarte {
    display: flex;
    flex-direction: column;
    gap: 0.7rem;
    padding: 1rem 1.1rem;
    background: var(--flaeche-karte);
    border: 1px solid var(--rand);
    border-radius: 10px;
    box-shadow: var(--schatten-zart);
    /* Eine lange Prüfsumme und ein langer Dateiname sind das einzige, was
       diese Karte sprengen könnte. Beide dürfen umbrechen – die Karte nicht. */
    min-width: 0;
}
.downloadkarte:hover { border-color: var(--rand-akzent); }

/*
 * Die beiden Hälften einer Karte – und warum es sie gibt.
 *
 * Martins Befund vom 19.09.2026: Die Beschreibungen der Plugins sind
 * unterschiedlich lang, und dadurch sprangen Knopf, Dateiname und die
 * Aufklapper darunter von Karte zu Karte auf eine andere Höhe. Die Zeile wirkte
 * unruhig, obwohl an jeder einzelnen Karte nichts falsch war.
 *
 * Gelöst wird das dort, wo das Problem entsteht: im Raster. `subgrid` gibt den
 * beiden Hälften jeder Karte **gemeinsame Zeilen** – Zeile 1 für alles, was
 * unterschiedlich lang sein darf (Kopf, Fassung, Beschreibung, Einsatzhinweis),
 * Zeile 2 ab dem Downloadknopf. Die Zeilenhöhe bestimmt die längste Karte der
 * Zeile; der Fuß beginnt damit bei allen Karten nebeneinander an derselben
 * Linie. Ohne feste Pixelhöhe und ohne ein Skript, das nachmisst.
 *
 * Auf dem Telefon steht genau eine Karte je Rasterzeile – dann sind „die
 * längste Karte der Zeile" und „diese Karte" dasselbe, und jede Karte wächst
 * wieder natürlich. Es entsteht keine künstliche Leerfläche, weil es nichts
 * gibt, woran sie sich ausrichten müsste.
 *
 * Der Weg ohne `subgrid` bleibt daneben stehen: Dort ist die Karte weiterhin
 * eine Flexspalte, und `margin-top: auto` drückt den Fuß nach unten. Die
 * Knöpfe sitzen dann nicht auf derselben Linie, aber die Karte bleibt
 * vollständig bedienbar – dieselbe Haltung wie überall in diesem Stylesheet.
 */
.downloadkarte-oben,
.downloadkarte-fuss {
    display: flex;
    flex-direction: column;
    gap: 0.7rem;
    min-width: 0;
}
.downloadkarte-fuss { margin-top: auto; }

@supports (grid-template-rows: subgrid) {
    .downloadkarte {
        display: grid;
        grid-row: span 2;
        grid-template-rows: subgrid;
        /* Die eigene Zeilenlücke der Karte. Sie überschreibt innerhalb ihrer
           beiden Zeilen die 0.9rem des äußeren Rasters – zwischen zwei Karten
           bleibt es dabei. */
        row-gap: 0.7rem;
    }
    .downloadkarte-fuss { margin-top: 0; }
}

.downloadkarte-kopf {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 0.5rem;
}
.downloadkarte-name { margin: 0; font-size: 1rem; }

/* „Aktuelle Version" ist die Antwort auf die häufigste Frage dieser Seite –
   deshalb grün und nicht grau wie die übrigen Statusmarken. */
.marke-aktuell {
    background: var(--erfolg-flaeche);
    color: var(--erfolg-text);
    border: 1px solid var(--erfolg-rand);
}

.downloadkarte-fassung {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.2rem 0.6rem;
    margin: 0;
}
.downloadkarte-version { font-size: 1.05rem; font-weight: 600; }
.downloadkarte-datum { font-size: 0.85rem; color: var(--text-leise); }

.downloadkarte-beschreibung {
    margin: 0;
    font-size: 0.88rem;
    line-height: 1.45;
    color: var(--text-mittel);
}

/*
 * Wo ein Plugin hingehört – und wo nicht. Derselbe gelbe Kasten wie der
 * Rollback-Hinweis weiter unten, und aus demselben Grund: Beide Sätze sollen
 * eine Entscheidung beeinflussen, bevor jemand klickt. Er steht oben in der
 * Karte und nicht unter „Details", weil er dort erst gelesen würde, wenn die
 * Datei längst im Downloadordner liegt.
 */
.downloadkarte-einsatz {
    margin: 0;
    padding: 0.4rem 0.6rem;
    border: 1px solid var(--warnung-kraeftig);
    border-radius: 6px;
    background: var(--warnung-flaeche);
    color: var(--warnung-text);
    font-size: 0.82rem;
    line-height: 1.45;
}

/*
 * Der primäre Knopf und die Dateiangabe darunter. Untereinander und nicht
 * nebeneinander: Der Knopf soll auf jeder Breite die volle Tippfläche haben,
 * und die Angabe daneben ist Beiwerk, das ihn sonst schmal drückte.
 */
.downloadkarte-aktion {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 0.35rem;
}
.downloadkarte-datei {
    font-size: 0.8rem;
    color: var(--text-still);
    overflow-wrap: anywhere;
}

/*
 * Die drei Aufklapper der Karte – Details, „Was ist neu?" und das Archiv.
 *
 * Ausdrücklich **ohne** eigenes `display`: Das Dreieck des Browsers ist die
 * einzige Auskunft darüber, dass sich hier etwas aufklappen lässt, und jedes
 * `display` außer `list-item` nimmt es weg. Die Mindesthöhe von 44 px setzt
 * auf dem Telefon die gemeinsame Regel für `summary` weiter oben; hier genügt
 * das Polster, weil dort mit der Maus gezielt wird.
 */
.downloadkarte-details > summary,
.downloadkarte-archiv > summary {
    cursor: pointer;
    padding: 0.35rem 0;
    font-size: 0.85rem;
    font-weight: 600;
    color: var(--text-mittel);
}
.downloadkarte-details > summary:hover,
.downloadkarte-archiv > summary:hover { color: var(--text-normal); }

.downloadkarte-angaben {
    display: grid;
    grid-template-columns: max-content 1fr;
    gap: 0.2rem 0.8rem;
    margin: 0.2rem 0 0;
    font-size: 0.82rem;
}
.downloadkarte-angaben dt { color: var(--text-still); }
.downloadkarte-angaben dd { margin: 0; color: var(--text-mittel); }
/* Vierundsechzig Zeichen ohne Leerzeichen: Sie brechen, wo Platz ist, und
   werden nirgends zur Ursache einer waagerechten Bildlaufleiste. */
.downloadkarte-pruefsumme {
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 0.75rem;
    overflow-wrap: anywhere;
}

.downloadkarte-changelog {
    margin: 0.2rem 0 0;
    padding-left: 1.1rem;
    font-size: 0.83rem;
    line-height: 1.45;
    color: var(--text-mittel);
}
.downloadkarte-changelog li { margin-bottom: 0.35rem; }

/* Die Zahl der älteren Fassungen am Aufklapper – dieselbe Form wie die Zähler
   auf den Rollenkarten. */
.downloadkarte-zahl {
    display: inline-block;
    margin-left: 0.35rem;
    padding: 0 0.4rem;
    border-radius: 999px;
    background: var(--flaeche-sanft);
    color: var(--text-leise);
    font-size: 0.75rem;
}

/*
 * Der Rollback-Hinweis. Er ist der einzige Satz dieser Seite, der eine
 * Entscheidung beeinflussen soll – „nimm die aktuelle, außer du weißt, warum
 * nicht" –, und steht deshalb als gelber Kasten da und nicht als kleingedruckte
 * Zeile. `.hinweis` daneben gibt ihm die Schriftgröße; Farbe und Fläche kommen
 * von hier, weil diese Regel später steht.
 */
.downloadkarte-rollback {
    margin: 0.2rem 0 0.6rem;
    padding: 0.4rem 0.6rem;
    border: 1px solid var(--warnung-kraeftig);
    border-radius: 6px;
    background: var(--warnung-flaeche);
    color: var(--warnung-text);
    font-size: 0.8rem;
}

.downloadkarte-liste {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
    /* Dreiundvierzig Sendeplan-Fassungen sind eine Liste und keine Seite:
       Sie bekommt ihren eigenen Scrollbereich, statt die Karte zu strecken. */
    max-height: 22rem;
    overflow-y: auto;
}

/*
 * Eine Archivzeile: Fassung, Angaben, Knopf. Am Schreibtisch in einer Reihe,
 * auf schmalen Geräten untereinander – `flex-wrap` und eine Mindestbreite für
 * die Angaben erledigen das ohne eigenen Breakpoint.
 */
.downloadkarte-alt {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.3rem 0.6rem;
    padding: 0.35rem 0.5rem;
    border: 1px solid var(--rand-zart);
    border-radius: 8px;
    background: var(--flaeche-sanft);
}
.downloadkarte-alt-version {
    font-weight: 600;
    font-size: 0.85rem;
    min-width: 3.5rem;
}
.downloadkarte-alt-datei {
    flex: 1 1 12rem;
    min-width: 0;
    font-size: 0.75rem;
    color: var(--text-still);
    overflow-wrap: anywhere;
}
.downloadkarte-alt .schaltflaeche { margin-left: auto; }

.downloadkarte-leer { margin: 0; font-size: 0.8rem; }

.downloadkarte-fusshinweis {
    margin-top: 0.4rem;
    font-size: 0.85rem;
    line-height: 1.5;
}

/* ── Die beiden Abschnitte der Seite: Plugins und Dokumente ──────────────── */

/*
 * Seit Martins Rückmeldung vom 19.09.2026 hat die Seite zwei Abschnitte. Sie
 * bekommen eine echte Überschrift und keinen bloßen Fettdruck: Wer mit der
 * Tastatur oder einem Vorleseprogramm durch die Seite geht, springt damit
 * zwischen „WordPress-Plugins" und „Dokumente" – und sieht am Titel sofort,
 * dass unten etwas anderes steht als oben.
 */
.downloadabschnitt {
    margin: 1.6rem 0 0.6rem;
    font-size: 1rem;
    letter-spacing: 0.02em;
    text-transform: uppercase;
    color: var(--text-leise);
}
.downloadabschnitt:first-of-type { margin-top: 0.2rem; }

.downloadabschnitt-vorwort {
    margin: 0 0 0.9rem;
    max-width: 62ch;
    font-size: 0.88rem;
    line-height: 1.5;
    color: var(--text-mittel);
}

/*
 * Die Dokumentkarten. Dasselbe Raster wie bei den Plugins, nur schmaler: Auf
 * einer Dokumentkarte steht ein Titel, zwei Zeilen Erklärung und ein Knopf –
 * sie braucht keine 28rem.
 *
 * Auch hier zwei Hälften mit `subgrid`, damit die Knöpfe nebeneinander auf
 * derselben Linie sitzen; die Erklärungen sind unterschiedlich lang.
 */
.dokumentkarten {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(20rem, 100%), 1fr));
    gap: 0.9rem;
    margin-bottom: 1.2rem;
}

.dokumentkarte {
    display: flex;
    flex-direction: column;
    gap: 0.7rem;
    padding: 1rem 1.1rem;
    background: var(--flaeche-karte);
    border: 1px solid var(--rand);
    border-radius: 10px;
    box-shadow: var(--schatten-zart);
    min-width: 0;
}
.dokumentkarte:hover { border-color: var(--rand-akzent); }

.dokumentkarte-oben,
.dokumentkarte-fuss {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 0.35rem;
    min-width: 0;
}
.dokumentkarte-fuss { margin-top: auto; }

@supports (grid-template-rows: subgrid) {
    .dokumentkarte {
        display: grid;
        grid-row: span 2;
        grid-template-rows: subgrid;
        row-gap: 0.7rem;
    }
    .dokumentkarte-fuss { margin-top: 0; }
}

/* „Für wen" steht über dem Titel und nicht darunter – es ist die Frage, die
   diese Liste beantworten muss: nicht „wie heißt es", sondern „ist das meins".
   Dieselbe Reihenfolge wie in der Handbuchauswahl. */
.dokumentkarte-fuer {
    margin: 0;
    font-size: 0.75rem;
    letter-spacing: 0.02em;
    text-transform: uppercase;
    color: var(--text-still);
}
.dokumentkarte-name {
    margin: 0;
    font-size: 1rem;
    line-height: 1.35;
    overflow-wrap: anywhere;
}
.dokumentkarte-kurz {
    margin: 0;
    font-size: 0.85rem;
    line-height: 1.45;
    color: var(--text-mittel);
}
.dokumentkarte-stand { font-size: 0.8rem; color: var(--text-still); }

/* ── Der Partnerplayer-Test: die interne Vorschau (Nacharbeit zu #109) ─────
 *
 * Die Seite zeigt einen fremden Rahmen in einer eigenen Umgebung. Sie bringt
 * deshalb bewusst **keine** Regel für den Rahmen selbst mit: Seine Maße stehen
 * im Schnipsel, den ein Webmaster kopiert (`partnereinbettung.schnipsel()`),
 * und ein Stylesheet, das sie hier überschriebe, zeigte etwas anderes als das,
 * was beim Partner ankommt. Was hier steht, ist nur die Seite drumherum.
 */
.vorschau-seite .karte { margin-bottom: 1.4rem; }

/* Auf dieser Seite stehen Adressen im Fließtext – die Einbettungsadresse ist
   46 Zeichen ohne Leerzeichen. Auf 320 px bräche sie sonst aus der Karte
   heraus und zöge eine waagerechte Bildlaufleiste hinter sich her. */
.vorschau-seite code { overflow-wrap: anywhere; }

.vorschau-angaben {
    display: grid;
    grid-template-columns: max-content 1fr;
    gap: 0.3rem 1rem;
    margin: 0.7rem 0 0.9rem;
    font-size: 0.86rem;
}
.vorschau-angaben dt { color: var(--text-leise); font-weight: 600; }
.vorschau-angaben dd { margin: 0; color: var(--text-mittel); overflow-wrap: anywhere; }

.vorschau-schritte {
    margin: 0.6rem 0 1rem;
    padding-left: 1.2rem;
    font-size: 0.86rem;
    line-height: 1.5;
    color: var(--text-mittel);
}
.vorschau-schritte li { margin-bottom: 0.4rem; }

.vorschau-schalter {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    margin: 0.2rem 0 0.8rem;
}

/* Die Zeile zum Kopieren. Sie ist lang und kennt kein Leerzeichen – sie bricht
   deshalb um, statt eine waagerechte Bildlaufleiste zu erzeugen (dieselbe
   Regel wie bei den Prüfsummen im Downloadregal). */
.vorschau-quelltext pre {
    margin: 0.5rem 0 0;
    padding: 0.7rem 0.8rem;
    background: var(--flaeche-sanft);
    border: 1px solid var(--rand);
    border-radius: 6px;
    font-size: 0.78rem;
    line-height: 1.45;
    white-space: pre-wrap;
    overflow-wrap: anywhere;
}

.vorschau-aktionen {
    display: flex;
    flex-wrap: wrap;
    gap: 0.6rem;
    margin: 0.6rem 0 0.4rem;
}

/* Platz unter der festen Leiste: Ohne ihn läge das Seitenende unter dem
   Rahmen, und der letzte Absatz wäre nicht mehr lesbar. Genau dieser Platz
   fehlt auf einer Partnerseite auch – der Hinweis dazu steht im Handbuch. */
.vorschau-fussraum { padding-bottom: 6rem; }

/* ── Die GUDE KI: Wissensbasis und Testdialog (Issue #175) ────────────────
 *
 * Ein kurzer Abschnitt, und das ist Absicht: Fast alles auf diesen Seiten
 * benutzt, was es schon gibt – `.formular`, `.datentabelle`, `.listenkarten`,
 * `.aufgaben-filter`, `.meldung`, `.badge`, `.schaltflaeche`. Hier steht nur,
 * was es vorher nicht gab: die Reiterleiste des Bereichs, die drei
 * Ebenenkarten, die Zahlenreihe, die Wissensliste und die beiden
 * Textkästen für Regelwerk und Antwort.
 *
 * Kein einziger fester Farbwert – alles über die Tokens aus dem Kopf dieser
 * Datei. Der dunkle Modus entsteht dadurch von selbst; eine zweite Fassung
 * dieser Regeln gibt es nicht und darf es nicht geben.
 */

/* Die Reiterleiste. Sie ist eine Navigation und keine Knopfreihe: Was hier
   steht, wechselt die Seite, und deshalb sind es Verweise. */
.ki-reiter {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
    margin-bottom: 1.2rem;
    border-bottom: 1px solid var(--rand);
    padding-bottom: 0.6rem;
}

.ki-reiter-punkt {
    padding: 0.45rem 0.85rem;
    border-radius: 6px;
    font-size: 0.9rem;
    font-weight: 600;
    text-decoration: none;
    color: var(--text-mittel);
    background: var(--flaeche-sanft);
    /* Dieselbe Tippfläche wie überall in diesem Bestand (Issue #35). */
    min-height: 44px;
    display: inline-flex;
    align-items: center;
}

.ki-reiter-punkt:hover { background: var(--flaeche-neutral); }

/* Der aktive Reiter trägt Fläche **und** `aria-current` (im Markup). Die
   Farbe allein wäre für alle, die die Seite hören, keine Auskunft. */
.ki-reiter-punkt.aktiv {
    background: var(--fuellung);
    color: var(--fuellung-text);
}

/* Die drei Ebenen des Auftrags, nebeneinander. */
.ki-ebenen {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
    gap: 0.9rem;
    margin-bottom: 1.6rem;
}

/* Alle drei tragen dieselbe Kante (Issue #205). Die mittlere hatte einmal die
   Kante der Marke, als Merkhilfe für die Ebene, die hier gepflegt wird –
   gelesen wurde sie als Zustand: ausgewählt, aktiv oder gestört. Keinen davon
   gibt es hier; die drei Karten sind Erklärung und kein Reiter. */
.ki-ebene {
    background: var(--flaeche-karte);
    border: 1px solid var(--rand);
    border-radius: 8px;
    padding: 1rem 1.2rem;
}

.ki-ebene h3 {
    margin: 0 0 0.4rem;
    font-size: 0.95rem;
    color: var(--text-stark);
}

.ki-ebene p {
    margin: 0;
    font-size: 0.88rem;
    line-height: 1.5;
    color: var(--text-mittel);
}

.ki-abschnitt {
    margin: 1.6rem 0 0.7rem;
    font-size: 1rem;
    color: var(--text-stark);
}

/* Die Zahlenreihe der Startseite. Jede Zahl ist ein Filter auf die Liste –
   deshalb Verweise und keine Kästen. */
.ki-zahlen {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
    gap: 0.7rem;
}

.ki-zahl {
    display: flex;
    flex-direction: column;
    gap: 0.2rem;
    padding: 0.9rem 1rem;
    border: 1px solid var(--rand);
    border-radius: 8px;
    background: var(--flaeche-karte);
    text-decoration: none;
    min-height: 44px;
}

.ki-zahl:hover { background: var(--flaeche-zart); }
.ki-zahl-wert { font-size: 1.6rem; font-weight: 700; color: var(--text-stark); }
.ki-zahl-text { font-size: 0.82rem; color: var(--text-leise); }

/* Die Verbrauchsanzeige der Startseite (Nacharbeit zu Issue #177).

   Keine Verweise wie bei der Zahlenreihe darüber: Hinter einem Zeitraum liegt
   keine Liste, die man öffnen könnte – die Zeilen der Buchführung tragen
   keinen Text und wären als Tabelle nur lang. Deshalb Kästen und nicht
   Links. */
.ki-verbrauch-einleitung {
    margin: 0 0 0.8rem;
    font-size: 0.89rem;
    color: var(--text-mittel);
    max-width: 62ch;
}

.ki-verbrauch-zeitraeume {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
    gap: 0.7rem;
    margin-bottom: 1rem;
}

.ki-verbrauch-karte {
    padding: 0.9rem 1rem;
    border: 1px solid var(--rand);
    border-radius: 8px;
    background: var(--flaeche-karte);
}

.ki-verbrauch-karte h4 {
    margin: 0 0 0.35rem;
    font-size: 0.85rem;
    color: var(--text-leise);
}

.ki-verbrauch-wert {
    margin: 0;
    font-size: 1.6rem;
    font-weight: 700;
    color: var(--text-stark);
    /* Große Zahlen nebeneinander liest man nur, wenn sie an derselben Stelle
       beginnen; die Ziffern dieser Schrift sind sonst verschieden breit. */
    font-variant-numeric: tabular-nums;
}

.ki-verbrauch-text { margin: 0.15rem 0 0; font-size: 0.82rem; color: var(--text-leise); }

/* Die Kacheln der Unterbereiche. */
.ki-karten {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
    gap: 0.7rem;
}

.ki-karte {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    padding: 0.9rem 1.1rem;
    border: 1px solid var(--rand);
    border-radius: 8px;
    background: var(--flaeche-karte);
    text-decoration: none;
    min-height: 44px;
}

.ki-karte:hover { background: var(--flaeche-zart); }
.ki-karte-titel { font-weight: 600; color: var(--text-stark); }
.ki-karte-text { font-size: 0.85rem; color: var(--text-leise); line-height: 1.45; }

/* Ein Kasten je Ebene auf den Anwendungsfall- und Testseiten.
 *
 * Er nimmt die Breite der Seite und **nicht** die Flucht des Formulars. Bis zur
 * Abnahme von #175 tat er das: Regelwerk, Testergebnis, technischer Beleg,
 * verwendetes Wissen und Live-Kontext standen in einer 720 px schmalen Spalte
 * am linken Rand, während rechts daneben knapp 380 px der Inhaltsbreite leer
 * blieben. Jede Zeile brach dadurch früher um, und die drei Seiten wurden
 * unnötig hoch – Martins Befund vom 24.09.2026.
 *
 * Auf den beiden Anwendungsfallseiten bleibt die Flucht der Seite davon
 * unberührt – dort steht überhaupt kein Formular. Auf der Testseite dagegen
 * ist sie mitgezogen: Sie steht seit der zweiten Nacharbeit zu #175 nicht
 * mehr bei 720 px, sondern über `.formular-ki` bei 1100 px (weiter oben, in
 * derselben Regel wie die Rollenmaske). Die Frage ist zwar ein Formular mit
 * Eingabefeldern, alles darunter gehört aber zu genau dieser Anfrage – eine
 * Kante, die mitten im Ablauf um 360 px springt, liest sich wie zwei Seiten.
 * Frage und Ergebnis tragen deshalb dieselbe Außenkante.
 *
 * Eine Höchstbreite steht hier gar nicht mehr – der Wrapper ist die Grenze,
 * auf dem Telefon also die volle Breite wie zuvor.
 */
.ki-block {
    background: var(--flaeche-karte);
    border: 1px solid var(--rand);
    border-radius: 8px;
    padding: 1.1rem 1.3rem;
    margin-bottom: 1.1rem;
}

.ki-block h3 { margin: 0 0 0.6rem; font-size: 1rem; color: var(--text-stark); }

/* Das Regelwerk und die Antwort: fertiger Text, der so bleiben soll, wie er
   ist – Zeilenumbrüche inklusive. Er bricht trotzdem um, statt eine
   waagerechte Bildlaufleiste zu erzeugen. */
.ki-regelwerk,
.ki-antworttext {
    margin: 0.4rem 0 0.6rem;
    padding: 0.8rem 1rem;
    background: var(--flaeche-still);
    border: 1px solid var(--rand);
    border-radius: 6px;
    font-size: 0.86rem;
    line-height: 1.55;
    color: var(--text-lesetext);
    white-space: pre-wrap;
    overflow-wrap: anywhere;
}

/* Die Antwort ist Fließtext eines Modells und kein Quelltext – sie bekommt
   deshalb die Schrift der Seite und nicht die der Konsole. */
.ki-antworttext { font-family: inherit; }

/* Die Auskunftsliste: links die Frage, rechts die Antwort. */
.ki-diagnose {
    display: grid;
    grid-template-columns: 12rem 1fr;
    gap: 0.35rem 1rem;
    margin: 0 0 0.6rem;
    font-size: 0.89rem;
}

.ki-diagnose dt { font-weight: 600; color: var(--text-leise); }
/* Eine Antwortkennung ist ein Wort ohne Leerzeichen. Ohne diese Zeile schöbe
   sie die Wertspalte auf, sobald der Beleg in einem schmalen Streifen steht. */
.ki-diagnose dd { margin: 0; color: var(--text-normal); overflow-wrap: anywhere; }

/* Dieselbe Auskunftsliste in einem schmalen Streifen: Frage und Wert stehen
   dort untereinander. Zwei Spalten ließen für den Wert keine 10 rem übrig, und
   „1234 hinein, 567 heraus, 1801 gesamt" stünde dreizeilig da. */
.ki-diagnose-schmal { grid-template-columns: 1fr; gap: 0.1rem; }
.ki-diagnose-schmal dd { margin-bottom: 0.45rem; }

/* ── Zwei Spalten, wo eine Spalte zu viel Platz hätte ─────────────────────
 *
 * Zwei Stellen dieses Bereichs bestehen aus einem langen Text und einer kurzen
 * Auskunft dazu: das Regelwerk mit seinen Kenndaten auf den
 * Anwendungsfallseiten und die Antwort mit ihrem technischen Beleg im
 * Testdialog. Untereinander ergeben sie eine sehr hohe Seite, und der Text
 * selbst hätte von der vollen Breite nichts – das Regelwerk bringt seine
 * Zeilenumbrüche mit, und eine Antwort über 1000 px hätte Zeilen von rund 150
 * Zeichen. Nebeneinander stimmt beides: Der Text behält seine Lesebreite, die
 * Auskunft füllt den Rest, und die Seite ist um die Höhe des kürzeren Teils
 * kürzer.
 *
 * Kein neuer Umschaltpunkt, und das ist hier keine Sparsamkeit: `flex-wrap`
 * entscheidet an der Sache statt an einer Fensterbreite. Passen beide Teile
 * mit ihrer Mindestbreite nebeneinander, stehen sie nebeneinander; sonst
 * rutscht der zweite unter den ersten und nimmt die ganze Breite – auf dem
 * Tablet, im Hochformat und auf dem Telefon. Eine Zahl in einer Medienabfrage
 * müsste dagegen raten, wie breit der Inhalt an dieser Stelle gerade ist.
 */
.ki-regelraster,
.ki-ergebnis-raster {
    display: flex;
    flex-wrap: wrap;
    /* Der kürzere Teil fängt oben an und wächst nicht auf die Höhe des
       anderen – sonst stünde unter ihm eine leere Fläche. */
    align-items: flex-start;
}

/* Das Regelraster braucht den Abstand in beide Richtungen: Seine beiden Teile
   – Regelwerk und Kenndaten – bringen keinen eigenen mit. Das Ergebnisraster
   darunter schreibt seine beiden Richtungen einzeln hin, weil dort das untere
   Polster der Karten mitspielt (siehe weiter unten). */
.ki-regelraster { gap: 1.1rem; }

/* Die Mindestbreiten sind gemessene Zahlen und keine gegriffenen: 48 rem ist
   die Breite, ab der keine Zeile des Regelwerks umbricht, die es nicht selbst
   umbricht – seine längste hat 78 Zeichen in Schreibmaschinenschrift –, und
   14 rem genügen den vier Kenndaten daneben. Reicht der Platz für beides
   nicht, steht die Auskunft wieder unter dem Text, und der nimmt die ganze
   Breite der Karte – mehr als vorher, wo er bei 720 px endete. */
.ki-regelraster > .ki-regelwerk { flex: 3 1 48rem; }
.ki-regelraster > .ki-diagnose  { flex: 1 1 14rem; }

/* ── Der Prüfablauf des Testdialogs: zwei Spalten über vier Karten ────────
 *
 * Zweite Runde der Nacharbeit zu #175. Vorher standen Antwort und Beleg als
 * einziges Paar nebeneinander, und die beiden Listen darunter nahmen jede für
 * sich die ganze Breite. Das ergab drei Kantenbilder auf einer Seite: ein
 * breiter flacher Kasten neben einem schmalen hohen, unter dem schmalen eine
 * tote Spalte – und darunter der Sprung zurück auf die volle Breite.
 *
 * Jetzt ist das Ergebnis **ein** Raster mit zwei Spalten, und jede trägt zwei
 * Karten: links das Ergebnis (Antwort, verwendetes Wissen), rechts der Beleg
 * dazu (Technik, Live-Kontext). Damit stimmen die Kanten über den ganzen
 * Ablauf, keine Spalte endet allein, und die Antwort behält bei rund 580 px
 * ihre Lesebreite von etwa 75 Zeichen – über die ganze Inhaltsbreite gezogen
 * hätte sie 150.
 *
 * Zwischen allen vier Karten steht dieselbe Zahl, und sie kommt aus zwei
 * Quellen: innerhalb einer Spalte aus dem unteren Polster der Karte
 * (`margin-bottom: 1.1rem` an `.ki-block`), zwischen den gestapelten Spalten
 * aus `row-gap`. Beides ist nötig und verdoppelt sich nicht: Die letzte Karte
 * einer Spalte gibt ihr Polster ab (siehe unten), also trägt genau dort der
 * Reihenabstand allein. Ohne ihn stießen die zweite und die dritte Karte auf
 * dem Tablet und dem Telefon ohne jeden Abstand aneinander.
 */
.ki-ergebnis-raster {
    column-gap: 1.1rem;
    row-gap: 1.1rem;
}

/* Drei Teile für das Ergebnis, zwei für den Beleg. Die Mindestbreiten sind
   gemessen: 34 rem hält die Antwort über der Lesegrenze, und 21 rem genügen
   der Auskunftsliste, ohne dass „1234 hinein, 567 heraus, 1801 gesamt" in
   drei Zeilen fällt. Zusammen mit dem Abstand sind das rund 900 px – darunter
   steht alles untereinander, und zwar ohne einen einzigen Umschaltpunkt. */
.ki-ergebnis-raster > .ki-ergebnis-haupt       { flex: 3 1 34rem; }
.ki-ergebnis-raster > .ki-ergebnis-nebenspalte { flex: 2 1 21rem; }

/* Die letzte Karte einer Spalte gibt ihr unteres Polster ab: Nebeneinander
   kommt darunter nichts mehr – ohne diese Zeile stünde der Seitenabschluss
   18 px tiefer als die Kante der Nachbarspalte –, und übereinander übernimmt
   der Reihenabstand darüber die Lücke, statt sie zu verdoppeln. */
.ki-ergebnis-raster > div > .ki-block:last-child { margin-bottom: 0; }

/* Die Antwort ist der Grund, warum man diese Seite aufgerufen hat: Sie trägt
   die Kante der Marke, den Schatten des Formularkastens und eine Spur größere
   Schrift. Das Polster links nimmt die zwei zusätzlichen Punkte der Kante
   wieder heraus – die Außenkanten aller Karten bleiben bündig, und das ist bei
   vier Karten in zwei Spalten der ganze Punkt. */
.ki-block-primaer {
    border-left: 3px solid var(--akzent);
    padding-left: calc(1.3rem - 2px);
    box-shadow: var(--schatten-karte);
}

.ki-block-primaer > h3 { font-size: 1.05rem; }

.ki-antwort .ki-antworttext {
    font-size: 0.95rem;
    line-height: 1.6;
    margin-bottom: 0;
}

/* Der Beleg ist die kompakte Nebenauskunft: Frage links, Wert rechts – und
   nicht untereinander wie auf den Anwendungsfallseiten, wo die Auskunft in
   einem schmalen Streifen neben dem Regelwerk steht. Untereinander wären es
   14 Zeilen statt sieben, und genau daraus wurde die hohe schmale Spalte aus
   Martins Befund. Die Wertspalte ist nach oben begrenzt: Stapeln sich die
   Spalten, stünde „gpt-4o-mini" sonst in einer Zeile von 700 px.

   Die Fragespalte ist so breit wie ihre längste Beschriftung und keinen
   Punkt breiter (`max-content`). Eine feste Zahl war hier zuerst 7 rem – im
   Browser stand dann „AnwendungsfallRedaktion und News" ohne Lücke
   nebeneinander, weil eine Beschriftung über ihre Spalte hinauslief. Nach
   unten darf die Spalte trotzdem nachgeben (`minmax(0, …)`), damit auf dem
   Telefon nichts aus der Karte ragt. */
.ki-diagnose-beleg {
    grid-template-columns: minmax(0, max-content) minmax(0, 26rem);
    gap: 0.3rem 0.9rem;
}

.ki-beleg .ki-diagnose { margin-bottom: 0; }

/* Im Ergebnisraster beginnt der Inhalt eines Wissenseintrags immer in einer
   eigenen Zeile – wie die Quelle darunter es ohnehin tut. In der schmaleren
   Spalte entschied sonst die Länge des Titels darüber, ob der Inhalt daneben
   oder darunter steht: drei Einträge untereinander mit drei verschiedenen
   Textkanten. Auf den Anwendungsfallseiten bleibt es, wie es ist; dort steht
   die Liste über die ganze Inhaltsbreite. */
.ki-ergebnis-raster .ki-wissenszeile .ki-inhalt { flex: 1 0 100%; }

/* ── Die Frage: zwei Felder in einer Zeile ────────────────────────────────
 *
 * Der Formularkasten des Testdialogs nimmt die Inhaltsbreite der Seite (siehe
 * `--flucht` weiter oben). Ein Auswahlfeld über 1000 px gewinnt dabei nichts –
 * die kurze Wahl steht deshalb links, das lange Feld rechts daneben. Auch das
 * bricht an der Sache um: Reichen 18 und 30 rem nebeneinander nicht, steht die
 * Wahl wieder über dem Textfeld, und beide nehmen die ganze Breite.
 */
.ki-fragezeile {
    display: flex;
    flex-wrap: wrap;
    column-gap: 1.4rem;
    align-items: flex-start;
}

.ki-fragezeile > .ki-fragewahl { flex: 1 1 18rem; }
.ki-fragezeile > .ki-fragetext { flex: 2 1 30rem; }

/* Die Erklärung der Funktionen unter der Fragezeile (Issue #376): über die
   ganze Formularbreite, damit die Auswahl selbst kurz bleibt. Die beiden
   Funktionen stehen nebeneinander, solange je 16 rem Platz haben, sonst
   untereinander – derselbe Umbruch an der Mindestbreite wie darüber, ohne
   Medienabfrage. Schrift eine Stufe größer als `.hinweis`, weil das hier
   gelesen und nicht nur überflogen werden soll. */
.ki-funktionshilfe {
    margin: 0.2rem 0 1rem;
    padding: 0.7rem 1rem;
    background: var(--flaeche-zart);
    border: 1px solid var(--rand-zart);
    border-radius: 6px;
    font-size: 0.875rem;
    line-height: 1.5;
    color: var(--text-mittel);
}

.ki-funktionsliste {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem 1.4rem;
    margin: 0 0 0.5rem;
}

.ki-funktion { flex: 1 1 16rem; min-width: 0; }
.ki-funktion dt { font-weight: 600; color: var(--text-normal); }
.ki-funktion dd { margin: 0; }
.ki-funktionshilfe p { margin: 0; }

.ki-getestet { margin: 0 0 0.6rem; font-size: 0.9rem; color: var(--text-mittel); }

/* Die Wissensliste – dieselbe Darstellung auf drei Seiten. */
.ki-wissensliste { list-style: none; margin: 0; padding: 0; }

.ki-wissenszeile {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.4rem;
    padding: 0.6rem 0;
    border-bottom: 1px solid var(--rand-zart);
    font-size: 0.9rem;
    color: var(--text-normal);
}

.ki-wissenszeile:last-child { border-bottom: none; }

/* Ein Vorschlag ist noch keine Wahrheit – er bekommt eine eigene Kante, damit
   er in einer Liste freigegebener Einträge nicht wie einer aussieht. */
.ki-wissenszeile-vorschlag { border-left: 3px solid var(--rand-akzent); padding-left: 0.6rem; }

.ki-inhalt { color: var(--text-mittel); line-height: 1.5; flex: 1 1 14rem; }
.ki-quelle { font-size: 0.8rem; color: var(--text-leise); }

/* In der Liste steht die Herkunft immer in einer eigenen Zeile. Vorher hing
   sie davon ab, wie lang der Inhalt daneben war: bei kurzem Text rechts in
   derselben Zeile, bei langem darunter – in einer Liste untereinander sah das
   aus wie zwei verschiedene Bauarten. Auf der breiten Fläche fiel es auf. */
.ki-wissenszeile .ki-quelle { flex: 1 0 100%; }

.ki-zeilenaktionen { display: flex; flex-wrap: wrap; gap: 0.35rem; }

.ki-quellenliste { margin: 0.2rem 0 0.6rem 1.1rem; font-size: 0.89rem; color: var(--text-normal); }
.ki-quellenliste li { margin-bottom: 0.3rem; line-height: 1.5; }

/* Die Zustandsmarke an einem Eintrag. Sie trägt Fläche **und** Schrift –
   dieselbe Regel wie bei allen Statusfarben dieses Bestands, damit sie im
   dunklen Modus nicht verschwindet. */
.ki-marke-aktiv { background: var(--erfolg-flaeche); color: var(--erfolg-text); }
.ki-marke-kandidat { background: var(--flaeche-gewaehlt); color: var(--text-stark); }
.ki-marke-inaktiv { background: var(--flaeche-sanft); color: var(--text-leise); }
.ki-marke-archiviert { background: var(--flaeche-neutral); color: var(--text-leise); }
.ki-marke-ruht { background: var(--warnung-flaeche); color: var(--warnung-text); }

/* Ein Knopf in einer Zeile ist ein Formular für sich – ohne diese Zeile
   stünde jeder auf einer eigenen. */
.ki-knopfform { display: inline-block; }

/* Der Audit-Trail unter der Maske. */
.ki-verlauf {
    background: var(--flaeche-karte);
    border: 1px solid var(--rand);
    border-radius: 8px;
    padding: 1.1rem 1.3rem;
    margin-top: 1.2rem;
}

.ki-verlauf h3 { margin: 0 0 0.4rem; font-size: 1rem; color: var(--text-stark); }
.ki-verlauf-liste { list-style: none; margin: 0.6rem 0 0; padding: 0; }

.ki-verlauf-zeile {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
    padding: 0.5rem 0;
    border-bottom: 1px solid var(--rand-zart);
    font-size: 0.86rem;
}

.ki-verlauf-zeile:last-child { border-bottom: none; }
.ki-verlauf-kopf { color: var(--text-normal); }
.ki-verlauf-text { color: var(--text-leise); }

/* ── Der feststehende Kopf der Wissensliste ───────────────────────────────
 *
 * Die Wissensliste wächst: Wer weit unten steht, sieht sonst sieben Spalten
 * ohne Beschriftung und weiß nicht mehr, ob „90" die Gewichtung oder ein
 * Datum ist. `.datentabelle thead th` steht ohnehin auf `sticky` – auf den
 * Arbeitsansichten mit `top: 0`, weil dort ein eigener Scrollbereich
 * (`.arbeitsflaeche-scroll`) unterhalb von Menü und Seitenkopf liegt und die
 * Bezugskante deshalb schon die richtige ist.
 *
 * Diese Seite hat keinen solchen Bereich: Hier scrollt das Dokument selbst,
 * und die Bezugskante ist der obere Fensterrand. `top: 0` schöbe die Kopfzeile
 * damit unter das Hauptmenü, das dort klebt – sichtbar wäre sie nicht mehr.
 * Der Versatz ist deshalb genau dessen Höhe, und er steht als dieselbe Zahl
 * (`--kopf-hoehe`), die auch das Menü selbst setzt; eine zweite wäre bei der
 * nächsten Kopfänderung falsch.
 *
 * Über- oder untereinander geraten kann dabei nichts: Das Menü liegt auf
 * `z-index: 100`, die Kopfzeile auf 2 – die Zeilen der Tabelle verschwinden
 * unter dem Kopf, der Kopf unter dem Menü. Auf dem Telefon trägt die Regel
 * nichts bei: Dort steht die Kartenliste, und die Tabelle ist ausgeblendet.
 */
.ki-wissenstabelle thead th { top: var(--kopf-hoehe); }

/* Die Titelspalte der Wissenstabelle trägt drei Angaben übereinander – Titel,
   Inhalt und Quelle. Ohne diese Untergrenze verteilt der Browser die Breite
   nach der längsten Zelle jeder Spalte, und ausgerechnet der Inhalt bricht dann
   nach drei Wörtern um.

   Sie hängt an einer eigenen Klasse und nicht mehr an `td:first-child`: Seit
   Issue #199 steht davor die Spalte mit dem Auswahlkästchen – aber nur für
   Rollen, die bearbeiten dürfen. `first-child` traf damit je nach Recht eine
   andere Spalte und blies ausgerechnet die schmalste auf 18 rem auf. */
.ki-wissenstabelle td.ki-titelzelle { min-width: 12rem; }

/* Zehn Spalten vertragen das allgemeine Polster der Datentabelle nicht: Zwei
   Zentimeter je Zelle sind bei zehn Zellen ein Drittel der Seitenbreite. Die
   Zeilenhöhe bleibt, die Trennung zwischen den Spalten auch. */
.ki-wissenstabelle th,
.ki-wissenstabelle td { padding-left: 0.6rem; padding-right: 0.6rem; }

/* Drei Knöpfe je Zeile passen bei dieser Spaltenzahl nicht nebeneinander.
   Sie brechen hier deshalb um, statt die Tabelle über die Seitenbreite
   hinauszuschieben – `white-space: nowrap` gilt für jede andere Aktionsspalte
   weiter. */
.ki-wissenstabelle td.aktionen {
    white-space: normal;
    min-width: 7rem;
}

/* ── Die Wissensbibliothek (Issue #199) ───────────────────────────────────
 *
 * Vier Flächen, die derselbe Auftrag gebracht hat: die Trefferanzeige mit dem
 * Blätterwerk darüber, die Leiste der Mehrfachauswahl, die anklickbaren
 * Spaltenköpfe und die zwei Datumsspalten. Alles davon benutzt die vorhandenen
 * Farbvariablen und keinen einzigen festen Farbwert – hell und dunkel entstehen
 * weiterhin aus denselben Tokens.
 */

/* „37 von 684 Wissenseinträgen" links, das Blätterwerk rechts – und beides in
   einer Zeile, solange Platz ist. Unter rund 40 rem Inhalt stapelt es sich von
   selbst, ohne einen eigenen Umschaltpunkt: Das entscheidet `flex-wrap` an der
   Mindestbreite der Trefferzeile. */
.ki-bibliothekskopf {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.5rem 1rem;
    margin: 0 0 0.8rem;
}

.ki-treffer {
    flex: 1 1 18rem;
    margin: 0;
    font-size: 0.9rem;
    color: var(--text-mittel);
}

.ki-filterweg { margin-left: 0.5rem; font-size: 0.85rem; }

.ki-blaettern {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem;
}

.ki-blaettern-fuss { justify-content: center; margin-top: 1rem; }

.ki-seitenzahl { font-size: 0.85rem; color: var(--text-leise); }

/* Die Leiste der Mehrfachauswahl. Sie ist ein **eigenes** Formular neben der
   Liste und nicht um sie herum: In den Zeilen stehen schon Formulare, und
   verschachtelte gibt es in HTML nicht. Die Kästchen in den Zeilen gehören über
   ihr `form`-Merkmal hierher. */
.ki-sammelleiste {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem 0.8rem;
    padding: 0.6rem 0.9rem;
    margin: 0 0 0.9rem;
    background: var(--flaeche-sanft);
    border: 1px solid var(--rand-zart);
    border-radius: 8px;
}

.ki-sammeltext { font-size: 0.88rem; font-weight: 600; color: var(--text-normal); }

.ki-sammelalle {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    font-size: 0.85rem;
    color: var(--text-mittel);
}

/* Die Auswahlspalte. Schmal und oben ausgerichtet: Eine Zeile dieser Tabelle
   ist drei Zeilen hoch, und ein in der Mitte schwebendes Kästchen gehörte
   optisch zu keiner davon. */
.ki-hakenspalte {
    width: 2.2rem;
    text-align: center;
    vertical-align: top;
    color: var(--text-leise);
}

.ki-haken { width: 1.05rem; height: 1.05rem; margin: 0.2rem 0 0; cursor: pointer; }

/* Ein anklickbarer Spaltenkopf. Der Verweis füllt die Zelle, damit die ganze
   Breite trifft und nicht nur das Wort – und er trägt die Schriftfarbe des
   Kopfes weiter, weil ein blauer Spaltenkopf wie ein Fremdkörper aussähe. */
.ki-sortkopf { white-space: nowrap; }

.ki-sortkopf a {
    display: block;
    color: inherit;
    text-decoration: none;
}

.ki-sortkopf a:hover,
.ki-sortkopf a:focus-visible { text-decoration: underline; }

.ki-sortkopf-aktiv { color: var(--text-stark); }

.ki-sortpfeil { margin-left: 0.3rem; font-size: 0.75em; }

/* Die beiden Datumsspalten. In der Zelle ohne Umbruch, damit „27.09.2026"
   nicht auf zwei Zeilen fällt; die Uhrzeit darunter steht in `.ki-quelle` und
   ist damit schon leiser.

   Im **Kopf** dagegen ausdrücklich mit Umbruch: „Zuletzt geändert" ist die
   längste Zeile dieser Spalte und bestimmte sonst ihre Breite – gemessene
   177 px für eine Zelle, in der zehn Zeichen stehen. Zweizeilig umgebrochen
   sind es rund 90, und genau diese Differenz entscheidet, ob die Tabelle auf
   ein Tablet im Querformat passt. */
.ki-wissenstabelle td.ki-zeitspalte {
    white-space: nowrap;
    vertical-align: top;
}

.ki-sortkopf.ki-zeitspalte { white-space: normal; }

/*
 * Das Tablet: der Kasten scrollt, nicht die Seite.
 *
 * Neun Spalten brauchen gemessene rund 1000 px. Am Schreibtisch (1400 px
 * Inhaltsbreite) ist das kein Thema; auf einem Tablet im Hochformat wandert
 * der Überlauf sonst an das **Dokument** – 250 px waagerechtes Scrollen der
 * ganzen Seite samt Kopfzeile und Filter. Genau das hat der Mobile-Audit zu
 * Issue #35 für die Typenliste abgestellt, und hier gilt dieselbe Antwort.
 *
 * Der Preis steht dazu: Ein Scrollbereich wird zum Bezugsrahmen für
 * `position: sticky`, und deshalb bleibt die Kopfzeile unter 900 px nicht mehr
 * stehen. Gemessen wurde beides; die Abwägung ist bewusst so herum. Am
 * Schreibtisch – dort, wo lange Listen wirklich durchgesehen werden – klebt
 * die Kopfzeile unverändert.
 *
 * 900 px ist **kein neuer Umschaltpunkt**: Er trägt in diesem Stylesheet schon
 * die Rechtematrix und das Wochenraster.
 */
@media (max-width: 900px) {
    .tabelle-quer:has(.ki-wissenstabelle) { overflow-x: auto; }
}

/* Eine Auskunft in einer Formularzeile – dort, wo sonst ein Feld stünde. Die
   Herkunft in der Wissensmaske ist genau das: eine Angabe, die man liest und
   nicht ändert. */
.ki-auskunft {
    margin: 0;
    padding: 0.45rem 0;
    color: var(--text-normal);
    overflow-wrap: anywhere;
}

/* ===========================================================================
   Externe Newsquellen und der redaktionelle Eingang (Issue #176)
   ===========================================================================

   Zwei Flächen mit einem gemeinsamen Vorrat an Klassen: der Eingang unter
   `/meldungen` und die Quellenliste unter `/newsquellen`. Alles hier benutzt
   die vorhandenen Farbvariablen und keine eigenen Hexwerte – der Dunkelmodus
   entsteht dadurch von selbst, ohne einen zweiten Block weiter unten. */

.news-zahlen {
    display: flex;
    flex-wrap: wrap;
    gap: 0.6rem;
    margin: 1rem 0 1.4rem;
}

.news-zahl {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
    padding: 0.55rem 0.9rem;
    border: 1px solid var(--rand);
    border-radius: 8px;
    background: var(--flaeche-sanft);
    text-decoration: none;
    color: var(--text-mittel);
    /* Dieselbe Tippfläche wie überall in diesem Bestand (Issue #35). */
    min-height: 44px;
    justify-content: center;
}

.news-zahl:hover { background: var(--flaeche-neutral); }

.news-zahl.aktiv {
    border-color: var(--erfolg-kraeftig);
    background: var(--erfolg-zart-flaeche);
    color: var(--erfolg-zart-text);
}

.news-zahl-wert { font-size: 1.3rem; font-weight: 700; line-height: 1.1; }
.news-zahl-text { font-size: 0.8rem; }

.news-liste, .news-quellen {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 0.9rem;
}

.news-eintrag {
    display: flex;
    gap: 0.9rem;
    padding: 0.9rem;
    border: 1px solid var(--rand);
    border-radius: 10px;
    background: var(--flaeche-karte);
}

/* Die neuen Meldungen heben sich ab – das ist der ganze Zweck eines
   Eingangs. Eine linke Kante und keine Hintergrundfarbe: Der Text darüber
   bleibt dadurch auf derselben Fläche wie überall lesbar. */
.news-eintrag.news-status-neu { border-left: 4px solid var(--erfolg-kraeftig); }
.news-eintrag.news-status-vorgemerkt { border-left: 4px solid var(--warnung-kraeftig); }
.news-eintrag.news-status-ignoriert { opacity: 0.72; }
.news-eintrag.news-status-erledigt { opacity: 0.85; }

.news-bild {
    width: 140px;
    height: 90px;
    object-fit: cover;
    border-radius: 6px;
    flex: 0 0 auto;
    background: var(--flaeche-sanft);
}

.news-text { min-width: 0; flex: 1 1 auto; }

.news-kopf {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem;
    margin: 0 0 0.3rem;
    font-size: 0.8rem;
    color: var(--text-leise);
}

.news-marke {
    padding: 0.1rem 0.45rem;
    border-radius: 999px;
    font-weight: 700;
    font-size: 0.75rem;
    background: var(--flaeche-sanft);
    color: var(--text-mittel);
}

/* Der gespeicherte Zustand einer Meldung – die eine Stelle, die ihn behauptet
   (Issue #187).

   Vorher stand er nur als graue Marke da, während der Knopf „Vorgemerkt"
   dauerhaft die Primärfarbe trug. Bei einer neuen Meldung war damit der
   auffälligste Kasten der Zeile ein Zustand, in dem sie gerade nicht war.
   Jetzt ist es umgekehrt: Die Knöpfe sind gleich neutral, und diese Marke
   trägt die Hausfarbe.

   Die Hausfarbe steht dabei als **Kante** und nicht als Fläche. Weiß auf
   `--akzent` (#e63946) kommt auf 4,2:1 und bleibt damit unter den 4,5:1, die
   ein 12-Pixel-Text braucht; als 2-Pixel-Ring um kräftigen Text ist derselbe
   Rotton eindeutig und der Text bleibt voll lesbar. So wird `--akzent` in
   dieser Datei ohnehin überall benutzt – Konfliktblock, Werkwahl, Zentrale
   sind alle Kanten –, und im Dunkelmodus wird er gar nicht getauscht: Es
   braucht deshalb keine zweite Regel weiter unten. */
.news-marke-status {
    background: var(--gefahr-flaeche-zart);
    color: var(--text-stark);
    border: 2px solid var(--akzent);
}

/* Das Wort vor dem Zustand. Der Ring trägt die Auffälligkeit, aber nicht die
   Information: Wer ihn nicht sieht – Graustufen, erhöhter Kontrast, ein
   Ausdruck –, liest trotzdem „Zustand: Neu".

   `--text-mittel` und nicht `--text-leise`: Die Marke ist 12 Pixel groß, und
   `--text-leise` käme auf ihrer hellen Fläche auf 4,5:1 – bestanden, aber ohne
   jeden Abstand. Das Wort ist leiser als der Zustand und bleibt trotzdem gut
   lesbar. */
.news-marke-status .news-marke-wort {
    font-weight: 600;
    color: var(--text-mittel);
}

.news-marke-geaendert {
    background: var(--warnung-flaeche);
    color: var(--warnung-text);
}

.news-herkunft { font-weight: 600; color: var(--text-mittel); }

/* Die beiden Zeitpunkte einer Meldung (Issue #188).

   `nowrap` liegt auf dem **Wert** und nicht auf der ganzen Angabe, und das ist
   nachgemessen: Auf 320 Pixern ist „Bei Radio GUDE: 25.09.2026, 23:41 Uhr" am
   Stück breiter als die Karte und wurde rechts abgeschnitten. Liegt die Sperre
   dagegen nur auf „25.09.2026, 23:41 Uhr", rutscht die Beschriftung in ihre
   eigene Zeile und das Datum bleibt heil – zerfallen darf es nicht, ein
   Umbruch zwischen Datum und Uhrzeit wäre zwei Angaben und nicht eine.

   Die Beschriftung trägt die Unterscheidung, nicht die Farbe: „Veröffentlicht"
   und „Bei Radio GUDE" stehen ausgeschrieben da. Der Eingangszeitpunkt bekommt
   zusätzlich dieselbe sanfte Fläche wie `.news-marke` – dieselbe erprobte
   Paarung aus `--flaeche-sanft` und `--text-mittel`, die im Dunkelmodus über
   die Variablen mitwechselt und deshalb keine zweite Regel weiter unten
   braucht. */
.news-datum-wort { font-weight: 600; }
.news-datum-wert { white-space: nowrap; }

.news-datum-eingang {
    padding: 0.1rem 0.45rem;
    border-radius: 999px;
    background: var(--flaeche-sanft);
    color: var(--text-mittel);
}

.news-titel { margin: 0 0 0.35rem; font-size: 1.05rem; line-height: 1.3; }
.news-titel a { text-decoration: none; }
.news-titel-gross { margin: 0.4rem 0 0.8rem; font-size: 1.4rem; line-height: 1.25; }

.news-auszug {
    margin: 0 0 0.5rem;
    font-size: 0.92rem;
    color: var(--text-mittel);
}

/* Der Bearbeitungsstand einer Meldung (Issue #191).

   Er steht unter dem Auszug und nicht oben in der Kopfzeile, und das ist die
   Anforderung „Workflow-Status und Bearbeitungsstand visuell nicht
   vermischen": Oben sagt `.news-marke-status` mit rotem Ring, wo die Meldung
   organisatorisch steht; hier unten steht, ob jemand schon daran gearbeitet
   hat. Stünden beide nebeneinander in derselben Zeile, wären es zwei Marken in
   einer Reihe und die Frage, welche wovon spricht.

   Deshalb auch eine andere Farbfamilie und eine andere Form: `--info-flaeche`
   statt Rot, eine Fläche statt eines Rings. Auf 12 Pixeln kommt
   `--info-text` darauf hell auf 8,1:1 und dunkel auf 7,0:1 – beides weit über
   den 4,5:1, die ein Text dieser Größe braucht, und im Dunkelmodus wechselt es
   über die Variablen mit. Eine zweite Regel weiter unten braucht es nicht.

   „Gut erkennbar, aber nicht dominant" ist dabei wörtlich zu nehmen: Die Marke
   ist so groß wie jede andere `.news-marke`, sie trägt keinen Rahmen, und die
   Zeile daneben steht in derselben Leise wie die Kopfzeile darüber. */
.news-arbeit {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem;
    margin: 0 0 0.5rem;
    font-size: 0.8rem;
    color: var(--text-leise);
}

.news-arbeit-marke {
    padding: 0.1rem 0.45rem;
    border-radius: 999px;
    font-weight: 700;
    font-size: 0.75rem;
    background: var(--info-flaeche);
    color: var(--info-text);
}

/* Dieselbe Aufteilung wie bei den beiden Zeitpunkten darüber: Die Sperre liegt
   auf dem **Datum** und nicht auf der ganzen Angabe. Auf 320 Pixern ist
   „Zuletzt bearbeitet: Mona Muster · 24.09.2026, 15:35 Uhr" am Stück breiter
   als die Karte; darf die Beschriftung umbrechen und der Name auch, bleibt
   „24.09.2026, 15:35 Uhr" heil – ein Umbruch zwischen Datum und Uhrzeit wären
   zwei Angaben und nicht eine. */
.news-arbeit-wort { font-weight: 600; }
.news-arbeit-person { font-weight: 600; color: var(--text-mittel); }
.news-arbeit-wert { white-space: nowrap; }

.news-wege, .news-quelle-wege {
    display: flex;
    flex-wrap: wrap;
    gap: 0.9rem;
    margin: 0 0 0.5rem;
    font-size: 0.85rem;
}

/* ── Die Verweise dieses Bereichs, unabhängig vom Browserverlauf (Issue #201)
 *
 * Bis hierher hatten die Meldungsüberschrift, „Ganz lesen", „Original öffnen"
 * und die Adresse unter „Original" **gar keine** Farbe. Sie trugen damit die
 * Voreinstellung des Browsers – und die kennt zwei: eine für ungesehen, eine
 * für besucht. Im dunklen Modus springt das ins Auge: türkis neben rosa,
 * Zeile für Zeile verschieden, obwohl an den Meldungen nichts verschieden
 * ist. Der Eingang beantwortet die Frage „wo steht diese Meldung?" an einer
 * einzigen Stelle – der roten Zustandsmarke oben in der Kopfzeile. Eine
 * zweite Farbaussage daneben, die nur sagt, welcher Browser auf welchem
 * Gerät den Link schon einmal geöffnet hat, ist keine Auskunft über die
 * Meldung, sondern über das Gerät. Sie sieht aber aus wie eine.
 *
 * Deshalb steht hier die Hausfarbe für Verweise (`--verweis`) – dieselbe,
 * die der Handbuchtext und die Wege der Entwicklungszentrale tragen –, und
 * `:visited` bekommt sie ausdrücklich noch einmal zugewiesen. Die ausdrück-
 * liche Zeile ist nicht überflüssig: Sie ist die Stelle, an der die Absicht
 * steht, und ohne sie hinge die Gleichheit daran, dass niemand später eine
 * `:visited`-Regel ergänzt.
 *
 * **Angefasst bleibt sichtbar.** Was fehlt, ist der Verlauf – nicht die
 * Rückmeldung: Überfahren, Tastaturfokus und Druck heben denselben Verweis
 * auf die stärkste Textfarbe der Seite (im Hellen fast schwarz, im Dunklen
 * fast weiß) und legen eine doppelt so dicke Unterstreichung darunter. Zwei
 * Merkmale, beide in beiden Modi deutlich – die Überschrift bekommt ihre
 * Unterstreichung dabei überhaupt erst beim Anfassen. Der Tastaturfokus trägt
 * zusätzlich die Kontur außerhalb des Wortes und ist damit die einzige der
 * drei Formen, die man auch ohne Maus erkennt.
 *
 * `.news-quelle-wege` steht mit in der Aufzählung, weil es dieselbe Linkart
 * in derselben Zeilenform ist – ein Bereich, der sich nur dadurch
 * unterschiede, dass an ihn niemand gedacht hat, wäre dieselbe Ungleichheit
 * eine Tür weiter. `.leerhinweis a` ebenfalls: Das ist der eine Verweis, der
 * im leeren Eingang übrig bleibt, und er wäre sonst der letzte auf dieser
 * Seite ohne eigene Farbe. Die Leerhinweise der Uploads tragen gar keinen
 * Verweis; die Regel greift dort also nichts an, sondern hält die Stelle.
 */
.news-titel a,
.news-titel a:visited,
.news-wege a,
.news-wege a:visited,
.news-quelle-wege a,
.news-quelle-wege a:visited,
.news-herkunftsangaben a,
.news-herkunftsangaben a:visited,
.leerhinweis a,
.leerhinweis a:visited {
    color: var(--verweis);
}

.news-titel a:hover,
.news-titel a:focus-visible,
.news-titel a:active,
.news-wege a:hover,
.news-wege a:focus-visible,
.news-wege a:active,
.news-quelle-wege a:hover,
.news-quelle-wege a:focus-visible,
.news-quelle-wege a:active,
.news-herkunftsangaben a:hover,
.news-herkunftsangaben a:focus-visible,
.news-herkunftsangaben a:active,
.leerhinweis a:hover,
.leerhinweis a:focus-visible,
.leerhinweis a:active {
    color: var(--text-stark);
    text-decoration: underline;
    text-decoration-thickness: 2px;
    text-underline-offset: 2px;
}

.news-titel a:focus-visible,
.news-wege a:focus-visible,
.news-quelle-wege a:focus-visible,
.news-herkunftsangaben a:focus-visible,
.leerhinweis a:focus-visible {
    outline: 2px solid var(--kontur);
    outline-offset: 2px;
}

.news-aktionen {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.4rem;
    margin: 0;
}

/* „Wechseln zu:" vor den Knöpfen (Issue #187). Der aktuelle Zustand fehlt in
   dieser Reihe – er steht als Marke oben –, und ohne dieses Wort wäre das eine
   offene Frage. Es ist leise gesetzt: Es ordnet die Reihe ein und konkurriert
   nicht mit der Marke, die den Zustand trägt. */
.news-aktionen-wort {
    font-size: 0.8rem;
    color: var(--text-leise);
}

/* Hover und Tastaturfokus dürfen nicht wie ein gespeicherter Zustand aussehen
   (Issue #187). Sie sind es auch nicht: Hover ist eine zweite graue Fläche
   (`.schaltflaeche-neutral:hover` – hell dunkler, dunkel heller), der Fokus
   eine Kontur in `--kontur` **außerhalb** des Knopfes – und der Zustand ein
   roter Ring um einen Text,
   der nicht anfassbar ist. Drei Formen, drei Farben, keine davon rot außer
   dem Zustand. Der Fokus steht hier ausdrücklich in der Stildatei und nicht
   als Vorgabe des Browsers: Verlassen kann man sich nur auf das Erste. */
.news-aktionen .schaltflaeche:focus-visible {
    outline: 2px solid var(--kontur);
    outline-offset: 2px;
}

/* Der Volltext der Einzelansicht. `pre-wrap`, weil in der Datenbank reiner
   Text mit Absatzumbrüchen steht und kein HTML – das ist Absicht und nicht
   Sparsamkeit (siehe app/models.ExterneMeldung.text). */
.news-volltext {
    white-space: pre-wrap;
    line-height: 1.6;
    margin: 0.8rem 0 1.2rem;
    max-width: 62ch;
}

.news-einzeln { margin-top: 1rem; }

.news-bildgross { margin: 0 0 1rem; max-width: 520px; }
.news-bildgross img { width: 100%; border-radius: 8px; display: block; }
.news-bildgross figcaption {
    font-size: 0.8rem;
    color: var(--text-leise);
    margin-top: 0.35rem;
}

.news-herkunftsangaben, .news-quelle-daten {
    display: grid;
    grid-template-columns: max-content 1fr;
    gap: 0.25rem 0.9rem;
    font-size: 0.87rem;
    margin: 0 0 1rem;
}

.news-herkunftsangaben dt, .news-quelle-daten dt {
    font-weight: 600;
    color: var(--text-leise);
}

.news-herkunftsangaben dd, .news-quelle-daten dd { margin: 0; overflow-wrap: anywhere; }

.news-quelle {
    padding: 0.9rem 1rem;
    border: 1px solid var(--rand);
    border-left-width: 4px;
    border-radius: 10px;
    background: var(--flaeche-karte);
}

.news-quelle-kopf {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.6rem;
}

.news-quelle-kopf h3 { margin: 0 0 0.3rem; font-size: 1.05rem; }

.news-ampel {
    font-size: 0.78rem;
    font-weight: 700;
    padding: 0.12rem 0.5rem;
    border-radius: 999px;
    background: var(--flaeche-sanft);
    color: var(--text-mittel);
}

.news-quelle-text { margin: 0 0 0.6rem; font-size: 0.88rem; color: var(--text-mittel); }

/* Die Ampel. Vier Zustände und eine Farbe je Zustand – und zusätzlich immer
   ein Wort daneben: Eine Auskunft, die allein an einer Farbe hängt, ist für
   einen Teil der Leser gar keine. */
.news-ampel-gruen { border-left-color: var(--erfolg-kraeftig); }
.news-ampel-gruen .news-ampel { background: var(--erfolg-flaeche); color: var(--erfolg-text); }
.news-ampel-gelb { border-left-color: var(--warnung-kraeftig); }
.news-ampel-gelb .news-ampel { background: var(--warnung-flaeche); color: var(--warnung-text); }
.news-ampel-rot { border-left-color: var(--fehler-kraeftig); }
.news-ampel-rot .news-ampel { background: var(--fehler-flaeche); color: var(--fehler-text); }
.news-ampel-grau, .news-ampel-aus { border-left-color: var(--flaeche-neutral-stark); }

.news-quelle-aktionen {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.4rem;
}

.news-quelle-aktionen form { margin: 0; }

/* Auf dem Telefon steht das Bild über dem Text statt daneben – 140 Pixel
   Bild neben 180 Pixel Text wäre auf 320 px keine Liste mehr. */
@media (max-width: 640px) {
    .news-eintrag { flex-direction: column; }
    .news-bild { width: 100%; height: 160px; }
    .news-herkunftsangaben, .news-quelle-daten { grid-template-columns: 1fr; gap: 0.1rem 0; }
    .news-herkunftsangaben dd, .news-quelle-daten dd { margin-bottom: 0.4rem; }
}

/* ===========================================================================
   KI-Entwürfe aus importierten Meldungen (Issue #177)
   ===========================================================================

   Der Abschnitt unter dem fremden Originaltext auf `/meldungen/<id>`. Er
   benutzt durchweg die vorhandenen Farbvariablen und keine eigenen Hexwerte –
   der Dunkelmodus entsteht dadurch von selbst, ohne einen zweiten Block weiter
   unten. Dieselbe Zusage wie im Block der Newsquellen darüber. */

.ki-entwurf {
    margin: 2rem 0 1rem;
    padding-top: 1.4rem;
    border-top: 2px solid var(--rand);
}

.ki-entwurf-kopf h3 { margin: 0 0 0.3rem; font-size: 1.2rem; }

.ki-entwurf-einleitung {
    margin: 0 0 1rem;
    max-width: 62ch;
    color: var(--text-mittel);
    font-size: 0.92rem;
}

/* Die vier Knöpfe. Ein Raster und keine Reihe: Jeder trägt drei Zeilen –
   Titel, Erklärung und den Stand –, und drei Zeilen nebeneinander ergeben auf
   einem Laptop vier unleserlich schmale Spalten. */
.ki-entwurf-start {
    margin: 0 0 1.4rem;
    padding: 1rem 1.1rem;
    border: 1px solid var(--rand);
    border-radius: 10px;
    background: var(--flaeche-sanft);
}

.ki-entwurf-frage {
    margin: 0 0 0.7rem;
    font-weight: 700;
    color: var(--text-stark);
}

.ki-entwurf-formen {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
    gap: 0.6rem;
}

/* Je Ausgabeform ein eigenes Formular – die Begründung steht in der Vorlage.
   Es ist die Rasterzelle, der Knopf füllt sie ganz aus. */
.ki-entwurf-formen form { margin: 0; display: flex; }
.ki-entwurf-formen form[data-wartet]::after { align-self: center; margin: 0 0 0 0.6rem; }

.ki-formknopf {
    width: 100%;
    display: flex;
    flex-direction: column;
    gap: 0.2rem;
    text-align: left;
    padding: 0.7rem 0.9rem;
    border: 1px solid var(--rand);
    border-radius: 8px;
    background: var(--flaeche-karte);
    color: var(--text-normal);
    cursor: pointer;
    /* Dieselbe Tippfläche wie überall in diesem Bestand (Issue #35). */
    min-height: 44px;
}

.ki-formknopf:hover { border-color: var(--erfolg-kraeftig); }
.ki-formknopf-titel { font-weight: 700; font-size: 0.95rem; }
.ki-formknopf-kurz { font-size: 0.82rem; color: var(--text-leise); }

.ki-formknopf-stand {
    font-size: 0.78rem;
    font-weight: 700;
    color: var(--erfolg-text);
}

.ki-formknopf-erneut .ki-formknopf-stand { color: var(--text-mittel); }

.ki-entwurf-fussnote {
    margin: 0.8rem 0 0;
    font-size: 0.8rem;
    color: var(--text-leise);
}

/* Ein gesperrter Knopf sieht auch gesperrt aus, und daneben steht, worauf
   gewartet wird. Der Text kommt aus `data-einmal` und steht damit in der
   Vorlage und nicht im Stylesheet – `static/ki-entwurf.js` schiebt ihn nur
   herüber. */
.ki-knopf-wartet { opacity: 0.55; cursor: progress; }

form[data-wartet]::after {
    content: attr(data-wartet);
    display: block;
    margin-top: 0.6rem;
    font-size: 0.85rem;
    font-weight: 700;
    color: var(--text-mittel);
}

/* Eine Karte je Ausgabeform. */
.ki-entwurf-karte {
    margin: 0 0 1.2rem;
    padding: 1rem 1.1rem;
    border: 1px solid var(--rand);
    border-radius: 10px;
    background: var(--flaeche-karte);
}

/* Die Karte, zu der gerade gesprungen wurde – sie soll sich vom Rest
   abheben, ohne dass etwas blinkt. */
.ki-entwurf-karte-offen { border-left: 4px solid var(--erfolg-kraeftig); }

.ki-karte-kopf {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.6rem;
}

.ki-karte-kopf h4 { margin: 0 0 0.5rem; font-size: 1.05rem; }

.ki-karte-marken {
    display: flex;
    flex-wrap: wrap;
    gap: 0.35rem;
    margin: 0 0 0.5rem;
}

.ki-marke-wartet { background: var(--warnung-flaeche); color: var(--warnung-text); }
.ki-marke-bearbeitet { background: var(--erfolg-flaeche); color: var(--erfolg-text); }

/* Die Gegenüberstellung: neue KI-Fassung oben, eigene Fassung darunter. Der
   gelbe Rahmen ist die Aussage – hier wartet eine Entscheidung. */
.ki-gegenueber {
    margin: 0.4rem 0 1rem;
    padding: 0.9rem 1rem;
    border: 1px solid var(--warnung-kraeftig);
    border-radius: 8px;
    background: var(--warnung-flaeche-blass);
}

.ki-gegenueber h5 { margin: 0 0 0.5rem; font-size: 0.95rem; }

.ki-gegenueber-aktionen {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
    margin-top: 0.7rem;
}

/* Ein Entwurf, der nicht bearbeitet werden darf oder noch nicht übernommen
   ist. `pre-wrap` aus demselben Grund wie beim Volltext der Meldung: In der
   Datenbank steht reiner Text mit Absatzumbrüchen und kein HTML. */
.ki-rohtext {
    white-space: pre-wrap;
    line-height: 1.6;
    max-width: 62ch;
    margin: 0;
}

.ki-fassung { margin: 0.4rem 0 1rem; }

.ki-fassung label {
    display: block;
    font-weight: 600;
    margin-bottom: 0.3rem;
    font-size: 0.9rem;
}

.ki-fassung textarea {
    display: block;
    width: 100%;
    max-width: 68ch;
    font-family: inherit;
    font-size: 0.95rem;
    line-height: 1.55;
    padding: 0.7rem 0.8rem;
    border: 1px solid var(--rand);
    border-radius: 8px;
    background: var(--flaeche-eingabe);
    color: var(--text-normal);
    border-color: var(--rand-stark);
    resize: vertical;
}

.ki-fassung button { margin-top: 0.6rem; }

/* Der zweite Satz an einer Fehlermeldung: was jetzt hilft. Eigene Zeile und
   nicht dieselbe wie der Grund – „warten", „noch einmal drücken" und „die
   Technik rufen" sind drei verschiedene Handlungen, und wer sie hinter einem
   Doppelpunkt im selben Absatz sucht, liest sie nicht. */
.ki-fehler-hilfe {
    display: block;
    margin-top: 0.3rem;
    font-weight: 400;
}

/* Die Prüfhinweise. Vier Arten, vier Farben – und immer ein Wort daneben:
   Eine Auskunft, die allein an einer Farbe hängt, ist für einen Teil der
   Leser gar keine. Dieselbe Regel wie bei der Ampel der Newsquellen. */
.ki-hinweise {
    list-style: none;
    margin: 0.6rem 0 1rem;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
}

.ki-hinweis {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.5rem;
    padding: 0.45rem 0.7rem;
    border-left: 3px solid var(--flaeche-neutral-stark);
    border-radius: 0 6px 6px 0;
    background: var(--flaeche-sanft);
    font-size: 0.88rem;
}

.ki-hinweis-marke { font-weight: 700; white-space: nowrap; }
.ki-hinweis-text { color: var(--text-mittel); flex: 1 1 18rem; }

.ki-hinweis-konflikt {
    border-left-color: var(--warnung-kraeftig);
    background: var(--warnung-flaeche-zart);
}

.ki-hinweis-copyright {
    border-left-color: var(--fehler-kraeftig);
    background: var(--fehler-flaeche-zart);
}

.ki-hinweis-fehlt { border-left-color: var(--hinweis-kante); background: var(--hinweis-flaeche); }
.ki-hinweis-form { border-left-color: var(--flaeche-neutral-stark); }

/* Die Belege und der Verlauf stehen zugeklappt da: Sie sind der Beleg und
   nicht die Arbeit. Wer sie braucht, klappt sie auf. */
.ki-belege, .ki-entwurf-verlauf { margin: 0 0 1rem; font-size: 0.88rem; }
.ki-belege summary, .ki-entwurf-verlauf summary { cursor: pointer; font-weight: 600; }
.ki-belege h5 { margin: 0.7rem 0 0.2rem; font-size: 0.9rem; }
.ki-belege ul { margin: 0; padding-left: 1.2rem; color: var(--text-mittel); }
.ki-entwurf-verlauf { background: none; border: none; padding: 0; }

.ki-karte-daten {
    display: grid;
    grid-template-columns: max-content 1fr;
    gap: 0.2rem 0.9rem;
    font-size: 0.84rem;
    margin: 0 0 0.9rem;
    color: var(--text-mittel);
}

.ki-karte-daten dt { font-weight: 600; color: var(--text-leise); }
.ki-karte-daten dd { margin: 0; overflow-wrap: anywhere; }

/* Der Fuß der Karte mit der zerstörenden Aktion (Issue #202).
 *
 * Eine dünne Linie darüber und sonst nichts: Der Knopf trägt die Fehlerfarbe
 * des Hauses und ist damit vom Speichern und Übernehmen unterschieden, ohne
 * die Karte zu beherrschen. Erklärung links, Knopf rechts – so steht die
 * Zusage „die Meldung bleibt" da, bevor die Hand am Knopf ist.
 *
 * Die Farben kommen aus denselben Tokens wie jeder andere leise Text; ein
 * fester Wert hier bliebe im dunklen Modus hell. */
.ki-karte-fuss {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.5rem 1rem;
    margin-top: 0.9rem;
    padding-top: 0.7rem;
    border-top: 1px solid var(--rand);
}

.ki-karte-fuss-satz {
    margin: 0;
    flex: 1 1 18rem;
    font-size: 0.82rem;
    color: var(--text-leise);
}

/* Auf dem Telefon steht alles untereinander – zwei Spalten aus Begriff und
   Wert sind auf 320 px keine Tabelle mehr. */
@media (max-width: 640px) {
    .ki-entwurf-formen { grid-template-columns: 1fr; }
    .ki-karte-daten { grid-template-columns: 1fr; gap: 0.1rem 0; }
    .ki-karte-daten dd { margin-bottom: 0.4rem; }
    .ki-gegenueber-aktionen .schaltflaeche { width: 100%; }
    /* Auf dem Telefon steht der Löschknopf unter seiner Erklärung und nicht
       daneben – zwei Spalten aus Satz und Knopf sind auf 320 px keine Zeile
       mehr. Volle Breite bekommt er trotzdem nicht: Ein zerstörender Knopf,
       der so breit ist wie „Fassung speichern“, lädt zum Verwechseln ein.
       Und `flex` muss dabei zurück auf `auto`: In einer Spalte ist die
       Hauptachse die senkrechte, und aus der Mindestbreite von 18rem würde
       dort eine Mindest**höhe** von 18rem – ein Absatz mit einer Handbreit
       Luft darunter. */
    .ki-karte-fuss { flex-direction: column; align-items: flex-start; }
    .ki-karte-fuss-satz { flex: 0 0 auto; }
}

/* ══ Persönliche Attention-Zentrale und Aufgabenpost (Issue #180) ═════════
 *
 * Drei Dinge, und alle drei beantworten dieselbe Frage aus verschiedenen
 * Entfernungen: **Wird gerade etwas von mir verlangt?**
 *
 *   1. die Zahl an „Aufgaben" in der Kopfleiste – auf jeder Seite,
 *   2. die Kästen oben auf dem Dashboard – beim Ankommen,
 *   3. die Marke „Neu" an der einzelnen Karte – beim Durchgehen der Liste.
 *
 * Kein einziger fester Farbwert, wie im ganzen Modul: Jede Farbe kommt aus
 * den Tokens ganz oben und hat dadurch im dunklen Modus ihre Entsprechung.
 */

/* ── Der Hinweis in der Kopfleiste ──────────────────────────────────────── */
/*
 * Die Zahl kommt aus `data-hinweis` am Verweis und nicht als Element aus der
 * Vorlage – dieselbe Zusage wie beim Pfeil `↗` der externen Bereiche: Der
 * Verweis behält genau einen Textknoten, nämlich seinen Namen.
 *
 * Ein **fester Kasten**, und zwar aus demselben Grund wie dort: Jeder Verweis
 * hält über `.menue-link[data-titel]::before` die Breite seiner fetten
 * Beschriftung frei, damit die Leiste beim Bereichswechsel stillsteht – und
 * dieser Messabdruck kann nur Text tragen, kein zweites Pseudoelement. Mit
 * einer bekannten Breite ist die Zugabe eine Zahl, die der Abdruck als
 * `padding-right` mitreservieren kann.
 *
 * `min-width` statt `width`: Eine zweistellige Zahl darf den Kasten dehnen,
 * eine einstellige soll ihn nicht schrumpfen lassen. Die Reservierung
 * darunter rechnet mit dem Regelfall; dass aus 9 eine 12 wird, verschiebt die
 * Leiste um einen Bruchteil und nicht bei jedem Klick.
 */
.menue-link[data-hinweis]::after {
    content: attr(data-hinweis);
    display: inline-block;
    min-width: 1.1rem;
    padding: 0 0.25rem;
    margin-left: 0.3rem;
    border-radius: 999px;
    background: var(--kopf-akzent);
    color: var(--fuellung-text);
    font-size: 0.72rem;
    font-weight: 700;
    line-height: 1.35;
    text-align: center;
    vertical-align: 0.05rem;
}

/* 1.4 rem sind die beiden Zahlen von oben: 0.3 rem Abstand und 1.1 rem
   Kasten. Ohne diese Zeile wäre die Breite des Bereichs „Aufgaben" im
   markierten Zustand wieder vom Zustand abhängig. */
.menue-link[data-titel][data-hinweis]::before { padding-right: 1.4rem; }

/* ── Die Zentrale auf dem Dashboard ─────────────────────────────────────── */
/*
 * Sie steht **über** den Kacheln und ist keine Kachel unter ihnen: Die
 * Kacheln führen in die Bereiche, die Zentrale sagt, was heute ansteht. Wer
 * sich anmeldet, soll das lesen, bevor er irgendwohin klickt.
 *
 * Und sie steht gar nicht da, wenn nichts ansteht. Das entscheidet
 * `app/aufmerksamkeit.py` und nicht dieses Stylesheet – eine Fläche mit
 * lauter Nullen wäre die Warnung ohne Anlass, die der Auftrag ausschließt.
 */
.attention-zentrale {
    margin-bottom: 1.6rem;
    padding: 1rem 1.2rem 1.2rem;
    background: var(--flaeche-karte);
    border: 1px solid var(--rand);
    border-left: 4px solid var(--kontur);
    border-radius: 10px;
    box-shadow: var(--schatten-zart);
}

/* Ist etwas Dringendes dabei, kippt die Kante – und nur sie. Eine ganze
   rote Fläche wäre auf einer Startseite ein Alarm und keine Auskunft. */
.attention-dringend { border-left-color: var(--fehler-kraeftig); }

.attention-kopf {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.4rem 0.8rem;
    margin-bottom: 0.9rem;
}

.attention-kopf h3 {
    margin: 0;
    font-size: 1.05rem;
    color: var(--text-stark);
}

.attention-satz { margin: 0; font-size: 0.85rem; color: var(--text-leise); }

/* Die Reihenfolge der Kästen (Issue #601): dieselbe Leiste wie in „Meine
   Aufgaben", hier zwischen Kopf und Kästen. */
.attention-zentrale .aufgaben-sortierung { margin-bottom: 0.9rem; }

.attention-karten {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(15rem, 100%), 1fr));
    gap: 0.75rem;
}

.attention-karte {
    display: block;
    padding: 0.75rem 0.9rem;
    background: var(--flaeche-still);
    border: 1px solid var(--rand);
    border-radius: 8px;
    text-decoration: none;
    min-width: 0;
}
.attention-karte:hover { border-color: var(--rand-stark); }
.attention-karte:focus-visible { outline: 2px solid var(--kontur); outline-offset: 2px; }

.attention-karte-zeile {
    display: flex;
    align-items: baseline;
    gap: 0.45rem;
    margin-bottom: 0.2rem;
}

.attention-zahl {
    font-size: 1.45rem;
    font-weight: 700;
    line-height: 1.1;
    color: var(--text-stark);
}

.attention-name { font-size: 0.95rem; font-weight: 600; color: var(--text-normal); }

/* Die dringenden Gruppen tragen ihre Zahl in der Warnfarbe – die Gruppe
   selbst bleibt eine Karte wie die anderen. */
.attention-karte-dringend .attention-zahl { color: var(--fehler-text); }
.attention-karte-dringend { border-color: var(--fehler-rand); }

/* `display: block` an einem `<span>`: Der Kasten ist als ganzer ein Verweis,
   und ein Verweis darf keine Absätze enthalten, die ihn zerreißen. Der
   Unterschied ist reine Darstellung – vorgelesen wird derselbe Satz. */
.attention-erklaerung {
    display: block;
    margin: 0 0 0.4rem;
    font-size: 0.8rem;
    color: var(--text-leise);
}

.attention-titel {
    margin: 0;
    padding: 0;
    list-style: none;
    font-size: 0.82rem;
    color: var(--text-mittel);
}

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

.attention-mehr { color: var(--text-still); }

/* ── Die Marke „Neu" an der Karte ───────────────────────────────────────── */
.aufgabe-marke-neu { background: var(--kopf-akzent); color: var(--fuellung-text); }

/*
 * Eine ungelesene Aufgabe bekommt die Kante der Marke – aber nur, solange sie
 * nicht ohnehin überfällig ist. Das `:not()` ist dabei die ganze Regel:
 * Überfällig ist die stärkere Aussage, und eine Kante kann nur eine Farbe
 * tragen. Ohne die Ausnahme gewönne diese Zeile als die spätere, und eine
 * überfällige Aufgabe sähe aus wie eine bloß neue.
 */
.aufgabe-neu:not(.aufgabe-ueberfaellig) { border-left-color: var(--kontur); }

/* ── Die eigenen E-Mail-Einstellungen unter „Mein Konto" ────────────────── */
/*
 * Eine Zeile je Anlass: links die Frage im Klartext, rechts die Auswahl. Auf
 * dem Telefon steht die Auswahl darunter – zwei Spalten auf 320 px wären ein
 * Auswahlfeld von vier Zeichen Breite.
 */
.mailwuensche {
    display: grid;
    gap: 0.9rem;
    margin-bottom: 1.1rem;
}

.mailwunsch {
    display: grid;
    grid-template-columns: 1fr minmax(11rem, 14rem);
    gap: 0.3rem 1rem;
    align-items: start;
    padding-bottom: 0.9rem;
    border-bottom: 1px solid var(--rand-zart);
}
.mailwunsch:last-child { border-bottom: 0; padding-bottom: 0; }

.mailwunsch-frage { font-weight: 600; color: var(--text-normal); }

.mailwunsch-erklaerung {
    grid-column: 1;
    margin: 0;
    font-size: 0.82rem;
    color: var(--text-leise);
}

.mailwunsch select { grid-column: 2; grid-row: 1 / span 2; }

@media (max-width: 640px) {
    .mailwunsch { grid-template-columns: 1fr; }
    .mailwunsch select { grid-column: 1; grid-row: auto; min-height: 44px; }
    .attention-karten { grid-template-columns: 1fr; }
}

/* ── Der persönliche ntfy-Zugang unter „Mein Konto" (Issue #221) ───────── */
/*
 * Seit der Nacharbeit vom 11.10.2026 zwei Spalten je Zweck: vor dem
 * Einschalten die beiden Wege (vorhandene Anmeldung / erstmalige
 * Einrichtung), danach die beiden Plattformen (iPhone / Android). Auf dem
 * Telefon jeweils untereinander. Der Code bringt seinen weißen Grund im SVG
 * selbst mit – hier steht deshalb keine Farbe: Er bleibt im dunklen Modus
 * schwarz auf weiß, und genau so lesen ihn Kamera-Apps.
 */
.ntfy-wege,
.ntfy-plattformen {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 1rem 1.5rem;
    align-items: start;
    margin-bottom: 1rem;
}

.ntfy-weg,
.ntfy-plattform {
    border: 1px solid var(--rand);
    border-radius: 6px;
    padding: 0.9rem 1rem;
    min-width: 0;
}
.ntfy-weg h4,
.ntfy-plattform h4 {
    margin: 0 0 0.5rem;
    font-size: 0.95rem;
}

/* Der Zustand einer Verknüpfung – „noch nicht fertig" fällt auf. */
.ntfy-stand {
    margin: 0 0 1rem;
    padding: 0.6rem 0.8rem;
    border-left: 3px solid var(--hinweis-kante);
    background: var(--hinweis-flaeche);
    border-radius: 4px;
    font-size: 0.88rem;
}
.ntfy-stand-offen {
    border-left-color: var(--warnung-kraeftig);
    background: var(--warnung-flaeche-blass);
}

.ntfy-bestaetigen { margin-bottom: 1rem; }

/* Kleine Formulare mitten in der Anleitung: Feld und Knopf in einer Zeile. */
.ntfy-knopfform { display: inline-block; margin: 0.2rem 0; }
.ntfy-codeform {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.4rem 0.6rem;
    margin: 0.4rem 0;
}
.ntfy-codeform label { width: 100%; font-size: 0.85rem; font-weight: 600; }
.ntfy-codeform input {
    flex: 1 1 10rem;
    min-width: 0;
    padding: 0.5rem 0.7rem;
    border: 1px solid var(--rand-stark);
    border-radius: 6px;
    background: var(--flaeche-eingabe);
    color: inherit;
    font-family: ui-monospace, monospace;
}

.ntfy-qr { margin: 0 0 0.8rem; max-width: 13rem; }
.ntfy-qr svg {
    display: block;
    width: 100%;
    height: auto;
    border-radius: 6px;
    border: 1px solid var(--rand-zart);
}
.ntfy-qr figcaption {
    margin-top: 0.4rem;
    font-size: 0.82rem;
    color: var(--text-leise);
    text-align: center;
}

.ntfy-schritte p { margin: 0 0 0.3rem; }
.ntfy-schritte ol,
ol.ntfy-schritte { margin: 0 0 0.9rem; padding-left: 1.3rem; }
.ntfy-schritte li { margin-bottom: 0.35rem; }

.ntfy-oeffnen { margin-top: 0.3rem; }

/* Anmeldung, Server und Kanal – je eine Zeile zum Kopieren. */
.ntfy-anmeldung { margin: 0 0 0.9rem; }
.ntfy-anmeldung dt { font-size: 0.85rem; color: var(--text-leise); }
.ntfy-anmeldung dd { margin: 0 0 0.5rem; }

.ntfy-kopierzeile {
    display: flex;
    gap: 0.4rem;
    align-items: stretch;
}
.ntfy-kopie {
    display: block;
    flex: 1 1 auto;
    min-width: 0;
    width: 100%;
    margin-top: 0.25rem;
    padding: 0.4rem 0.6rem;
    border: 1px solid var(--rand-stark);
    border-radius: 6px;
    background: var(--flaeche-sanft);
    color: inherit;
    font-family: ui-monospace, monospace;
    font-size: 0.85rem;
}
.ntfy-kopieren {
    flex: 0 0 auto;
    margin-top: 0.25rem;
    white-space: nowrap;
}

@media (max-width: 640px) {
    .ntfy-wege,
    .ntfy-plattformen { grid-template-columns: 1fr; }
    .ntfy-qr { max-width: 15rem; margin: 0 auto 0.8rem; }
    .ntfy-oeffnen { min-height: 44px; }
    .ntfy-kopie,
    .ntfy-kopieren,
    .ntfy-codeform input,
    .ntfy-codeform button { min-height: 44px; }
}

/* ===========================================================================
   Der KI-Tank (Nacharbeit zu Issue #177)
   ===========================================================================

   Die permanente Anzeige an jeder Stelle, an der ein Mensch eine
   kostenpflichtige GUDE-KI-Aktion auslösen kann – der Kasten aus
   `app/templates/_ki_tank.html`. Vier Einbindungen, ein Block CSS.

   Durchweg vorhandene Farbvariablen und keine eigenen Hexwerte: Der
   Dunkelmodus entsteht dadurch von selbst, ohne einen zweiten Block weiter
   unten. Dieselbe Zusage wie im Block der KI-Entwürfe darüber.

   Der Auftrag verlangt „barrierearm, nicht ausschließlich über Farbe". Die
   Farbe ist hier deshalb die dritte Information und nicht die erste: Vor ihr
   stehen die Prozentzahl, der Eurobetrag und ein Satz, und die Vorlage trägt
   dieselbe Auskunft noch einmal als `aria-label`. Wer die Farben nicht
   unterscheidet, verliert nichts. */

.ki-tank {
    display: flex;
    flex-direction: column;
    gap: 0.45rem;
    padding: 0.7rem 0.9rem;
    margin-bottom: 1rem;
    border: 1px solid var(--rand);
    /* Die Kante links trägt die Lage. Sie ist breit genug, um auch dann
       aufzufallen, wenn der Farbton nicht ankommt. */
    border-left: 5px solid var(--rand);
    border-radius: 8px;
    background: var(--flaeche-karte);
}

/* Sticky, aber nicht im Weg: Der Kasten klebt unter der Kopfleiste und nicht
   am oberen Fensterrand – sonst schöbe er sich beim Scrollen unter die
   Navigation. `--kopf-hoehe` ist dieselbe Größe, mit der auch die übrigen
   klebenden Köpfe dieses Hauses rechnen. */
.ki-tank-fest {
    position: sticky;
    top: calc(var(--kopf-hoehe) + 0.5rem);
    /* Über dem Inhalt, unter der Kopfleiste und unter jedem Dialog. */
    z-index: 30;
    /* Ohne eigenen Hintergrund schiene der Text darunter durch. */
    background: var(--flaeche-karte);
    box-shadow: 0 2px 6px rgba(0, 0, 0, 0.08);
}

.ki-tank-kopf {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: 0.5rem;
}

.ki-tank-titel {
    font-size: 0.78rem;
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--text-leise);
}

.ki-tank-wert {
    font-size: 1.25rem;
    font-weight: 700;
    color: var(--text-stark);
    /* Damit die Zahl beim Sinken nicht springt. */
    font-variant-numeric: tabular-nums;
}

.ki-tank-euro {
    font-size: 0.95rem;
    color: var(--text-mittel);
    font-variant-numeric: tabular-nums;
}

/* Die zwanzig Segmente – 5-%-Schritte, wie der Auftrag sie verlangt.
   `grid` mit zwanzig gleichen Spalten und nicht `flex`: Die Kästchen sollen
   auch bei 320 px Breite gleich groß bleiben, und ein `flex-basis` rundet
   dabei unterschiedlich. */
.ki-tank-balken {
    display: grid;
    grid-template-columns: repeat(20, 1fr);
    gap: 2px;
    height: 0.85rem;
}

.ki-tank-segment {
    border-radius: 2px;
    background: var(--flaeche-zart);
    border: 1px solid var(--rand-zart);
}

.ki-tank-satz {
    margin: 0;
    font-size: 0.85rem;
    color: var(--text-mittel);
}

.ki-tank-fussnote {
    margin: 0;
    font-size: 0.74rem;
    color: var(--text-leise);
}

.ki-tank-unstimmig {
    margin: 0;
    padding: 0.45rem 0.6rem;
    border-radius: 6px;
    border: 1px solid var(--fehler-rand);
    background: var(--fehler-flaeche);
    color: var(--fehler-text);
    font-size: 0.82rem;
}

/* Die fünf Lagen. Sie färben die Kante und die vollen Segmente – und sie
   tun nichts weiter: Was sie bedeuten, steht als Satz daneben. */
/* Der nicht geführte Tank: keine Farbe, keine Warnung. Er behält die
   neutrale Kante aus `.ki-tank` – die rote von „leer" wäre eine Warnung vor
   etwas, das noch niemand eingerichtet hat. */
.ki-tank-unbekannt { border-left-color: var(--rand-stark); }

.ki-tank-gut      { border-left-color: var(--erfolg-kraeftig); }
.ki-tank-mittel   { border-left-color: var(--erfolg-kraeftig); }
.ki-tank-knapp    { border-left-color: var(--warnung-kraeftig); }
.ki-tank-wenig    { border-left-color: var(--warnung-kraeftig); }
.ki-tank-kritisch { border-left-color: var(--fehler-kraeftig); }
.ki-tank-leer     { border-left-color: var(--fehler-kraeftig); }

.ki-tank-gut      .ki-tank-segment-voll { background: var(--erfolg-kraeftig); border-color: var(--erfolg-kraeftig); }
.ki-tank-mittel   .ki-tank-segment-voll { background: var(--erfolg-kraeftig); border-color: var(--erfolg-kraeftig); }
.ki-tank-knapp    .ki-tank-segment-voll { background: var(--warnung-kraeftig); border-color: var(--warnung-kraeftig); }
.ki-tank-wenig    .ki-tank-segment-voll { background: var(--warnung-kraeftig); border-color: var(--warnung-kraeftig); }
.ki-tank-kritisch .ki-tank-segment-voll { background: var(--fehler-kraeftig); border-color: var(--fehler-kraeftig); }
.ki-tank-leer     .ki-tank-segment-voll { background: var(--fehler-kraeftig); border-color: var(--fehler-kraeftig); }

/* Je knapper der Tank, desto deutlicher der Kasten selbst – wieder nicht nur
   über die Farbe: Ab „wenig" bekommt er einen eigenen Hintergrund, und der
   ist auch ohne Farbwahrnehmung ein anderer Ton als die Karte daneben. */
.ki-tank-wenig    { background: var(--warnung-flaeche-blass); }
.ki-tank-kritisch { background: var(--fehler-flaeche-zart); }
.ki-tank-leer     { background: var(--fehler-flaeche-zart); }
.ki-tank-wenig.ki-tank-fest    { background: var(--warnung-flaeche-blass); }
.ki-tank-kritisch.ki-tank-fest { background: var(--fehler-flaeche-zart); }
.ki-tank-leer.ki-tank-fest     { background: var(--fehler-flaeche-zart); }

/* Die Buchungsseite selbst. */
.ki-tank-einleitung {
    margin: 0 0 0.8rem;
    font-size: 0.89rem;
    color: var(--text-mittel);
    max-width: 68ch;
}

.ki-tank-tabelle td.rechts,
.ki-tank-tabelle th.rechts {
    text-align: right;
    font-variant-numeric: tabular-nums;
}

/* Auf schmalen Geräten bleibt der Tank sichtbar, wird aber flacher: Ein
   Kasten, der auf 375 px ein Drittel der Höhe frisst, verdeckt genau die
   Bedienung, neben der er stehen soll. Der Auftrag sagt dazu „ohne wichtige
   Bedienung zu verdecken". */
@media (max-width: 640px) {
    /* Die Angaben des Bestands stehen auf schmalen Geräten untereinander.
       Zweispaltig bleiben von „Wechselkurs 0,9200 € je $ (Stand …)" zwei
       Wortschlangen übrig – die längsten Werte des ganzen Bereichs stehen
       auf dieser Seite. */
    .ki-tank-bestand { display: block; }
    .ki-tank-bestand dt { margin-top: 0.7rem; }
    .ki-tank-bestand dd { margin: 0.1rem 0 0; }

    .ki-tank { padding: 0.5rem 0.7rem; gap: 0.3rem; }
    .ki-tank-balken { height: 0.6rem; gap: 1px; }
    .ki-tank-fussnote { display: none; }
    .ki-tank-fest { top: calc(var(--kopf-hoehe) + 0.25rem); }
}

/* Wer Bewegung reduziert haben will, bekommt keinen klebenden Kasten: Ein
   Element, das beim Scrollen mitwandert, ist genau die Art Bewegung, die
   `prefers-reduced-motion` meint. Sichtbar bleibt er trotzdem – er steht dann
   nur an seinem Platz. */
@media (prefers-reduced-motion: reduce) {
    .ki-tank-fest { position: static; box-shadow: none; }
}

/* ===========================================================================
   Die Upload-Zentrale zur Nextcloud (Issue #195)
   ===========================================================================

   Vier Flächen mit einem gemeinsamen Vorrat an Klassen: das Übergeben unter
   `/uploads`, die Einstellungen unter `/uploads/einstellungen`, die Typmaske
   und die Rückfrage vor dem Entfernen.

   Durchweg vorhandene Farbvariablen und kein einziger eigener Hexwert – der
   Dunkelmodus entsteht dadurch von selbst, ohne einen zweiten Block weiter
   unten. Dieselbe Zusage wie im Block der Newsquellen darüber.

   Die Lage wird nirgends allein über Farbe erzählt: Vor jeder Kante steht ein
   Wort („Verbunden", „Ziel fehlt", „nicht übergeben"), und die Kante ist breit
   genug, um auch dann aufzufallen, wenn der Farbton nicht ankommt. Dieselbe
   Anforderung wie beim KI-Tank. */

/* Die Reiterleiste – dieselbe Bauart wie `.ki-reiter`, eigener Name, weil sie
   zu zwei getrennten Rechtekreisen gehört und deren Zuschnitt später eigene
   Zustände bekommen kann. */
.upl-reiter {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
    margin-bottom: 1.2rem;
    border-bottom: 1px solid var(--rand);
    padding-bottom: 0.6rem;
}

.upl-reiter-punkt {
    padding: 0.45rem 0.85rem;
    border-radius: 6px;
    font-size: 0.9rem;
    font-weight: 600;
    text-decoration: none;
    color: var(--text-mittel);
    background: var(--flaeche-sanft);
    /* Dieselbe Tippfläche wie überall in diesem Bestand (Issue #35). */
    min-height: 44px;
    display: inline-flex;
    align-items: center;
}

.upl-reiter-punkt:hover { background: var(--flaeche-neutral); }

.upl-reiter-punkt.aktiv {
    background: var(--fuellung);
    color: var(--fuellung-text);
}

.upl-abschnitt {
    margin: 1.8rem 0 0.6rem;
    font-size: 1.05rem;
}

/* ── Der Zustand der Anbindung ───────────────────────────────────────────── */

.upl-zustand {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    padding: 0.9rem 1.1rem;
    margin: 1rem 0 1.4rem;
    border: 1px solid var(--rand);
    /* Die Kante links trägt die Lage – breit genug, um auch ohne Farbwahrnehmung
       als Markierung zu wirken. */
    border-left: 5px solid var(--rand);
    border-radius: 8px;
    background: var(--flaeche-karte);
}

.upl-zustand-gut      { border-left-color: var(--erfolg-kraeftig); }
.upl-zustand-offen    { border-left-color: var(--warnung-kraeftig); }
.upl-zustand-schlecht { border-left-color: var(--fehler-kraeftig); }

.upl-zustand-kopf {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.6rem;
}

.upl-zustand-wort {
    font-size: 1.1rem;
    font-weight: 700;
    color: var(--text-stark);
}

.upl-zustand-zeit { font-size: 0.8rem; color: var(--text-leise); }

.upl-zustand-satz {
    margin: 0;
    font-size: 0.9rem;
    color: var(--text-mittel);
}

.upl-zustand-daten {
    display: grid;
    grid-template-columns: max-content 1fr;
    gap: 0.2rem 0.8rem;
    margin: 0.4rem 0 0;
    font-size: 0.88rem;
}

.upl-zustand-daten dt { color: var(--text-leise); }
.upl-zustand-daten dd { margin: 0; overflow-wrap: anywhere; }

.upl-zustand-aktionen {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    margin-top: 0.5rem;
}

.upl-zustand-aktionen form { margin: 0; }

/* ── Das Abhören vor der Übergabe ────────────────────────────────────────── */

.upl-player {
    width: 100%;
    /* Der eingebaute Abspieler des Browsers ist je Plattform anders hoch;
       eine feste Höhe schnitte ihn auf manchen ab. Begrenzt wird deshalb nur
       die Breite. */
    max-width: 32rem;
    margin: 0.2rem 0;
}

/* ── Die Zusammenfassung vor dem Bestätigen ──────────────────────────────── */

.upl-bestaetigung {
    border: 1px solid var(--rand);
    border-radius: 8px;
    padding: 0.8rem 1rem;
    background: var(--flaeche-zart);
}

.upl-zusammenfassung {
    margin: 0 0 0.5rem;
    font-size: 0.95rem;
    font-weight: 600;
    color: var(--text-stark);
    overflow-wrap: anywhere;
}

/* Die gesperrten Upload-Typen hinter einer Zeile (Martins Abnahme vom
 * 01.10.2026). Sie standen bis dahin vollständig unter dem Auswahlfeld und
 * wuchsen mit jedem neuen Workflow mit; jetzt steht dort ihre Zahl, und die
 * Gründe kommen auf Klick.
 *
 * Dieselbe Bauart wie `.anhang-fruehere` an der Aufgabe – auch dieselbe
 * Tippfläche: Ein `summary` ist von Haus aus so hoch wie seine Schrift, und
 * der Mobile-Audit verlangt 44 px. */
.upl-sperrgruende {
    margin-top: 0.4rem;
    font-size: 0.85rem;
    color: var(--text-leise);
}
.upl-sperrgruende summary {
    cursor: pointer;
    padding: 0.5rem 0;
    min-height: 44px;
    display: flex;
    align-items: center;
}
.upl-sperrliste {
    margin: 0;
    padding: 0 0 0 1.1rem;
}
.upl-sperrliste li { padding: 0.2rem 0; }

/* ── Der Ordnerbaum aus der Nextcloud ────────────────────────────────────── */

.upl-ordnerbaum {
    list-style: none;
    margin: 0 0 1rem;
    padding: 0.6rem 0.9rem;
    border: 1px solid var(--rand);
    border-radius: 8px;
    background: var(--flaeche-still);
    font-size: 0.9rem;
}

.upl-ordner {
    padding: 0.18rem 0;
    color: var(--text-normal);
}

/* Die Einrückung macht den Baum sichtbar, ohne dass die Vorlage verschachtelte
   Listen bauen muss: `app/uploads.bekannte_ordner()` liefert eine flache Liste
   in Baumfolge, und die Tiefe steckt in der Zahl der Schrägstriche. Vier Stufen
   genügen – tiefer liest `app/webdav.MAX_TIEFE` ohnehin nicht. */
.upl-ordner-tiefe-0 { padding-left: 0; font-weight: 600; }
.upl-ordner-tiefe-1 { padding-left: 1.2rem; }
.upl-ordner-tiefe-2 { padding-left: 2.4rem; }
.upl-ordner-tiefe-3 { padding-left: 3.6rem; }
.upl-ordner-tiefe-4 { padding-left: 4.8rem; }

.upl-ordner-hinweis,
.upl-ordner-pfad {
    font-size: 0.78rem;
    color: var(--text-leise);
    margin-left: 0.4rem;
}

.upl-ordnerwahl { margin-bottom: 0.4rem; }

/* ── Lange Bereiche mit eigener Scrollfläche (Issue #213) ─────────────────── */

/* Drei Bereiche der Upload-Zentrale wachsen mit der Nutzung: die Upload-Typen,
   die letzten Eingriffe und das Übergabeprotokoll. Sie bekommen **eine**
   gemeinsame Klasse und nicht drei eigene Lösungen – der Auftrag verlangt
   „interne Scrollbereiche sind deutlich als solche erkennbar, aber visuell
   ruhig", und drei Rahmen mit drei Abständen wären das Gegenteil.

   `max-height` und nicht `height`: Genau daran hängt „eigener vertikaler
   Scrollbereich erst dann, wenn er wirklich nötig ist". Bei drei Typkarten ist
   der Bereich so hoch wie die Karten, und es gibt keine Leiste zu sehen; erst
   darüber entsteht eine. Eine feste Höhe hätte dieselbe Kiste auch für einen
   einzigen Eintrag gezeichnet.

   `overflow-x: hidden` steht ausdrücklich dabei. Sobald eine Achse nicht mehr
   `visible` ist, rechnet der Browser die andere von `visible` auf `auto` hoch –
   ohne diese Zeile könnte hier also eine waagerechte Leiste entstehen, und der
   Auftrag verbietet sie für Tablet und Telefon namentlich. Die Inhalte brauchen
   sie nicht: Sie brechen um.

   Kein `overscroll-behavior: contain`. Das wäre die Bedienfalle, die der Auftrag
   ausschließt: Auf dem Telefon soll der Finger am Ende der Liste die Seite
   weiterschieben, statt im Kasten festzuhängen. */
.upl-rolle {
    max-height: 34rem;
    overflow-y: auto;
    overflow-x: hidden;
    padding: 0.55rem;
    border: 1px solid var(--rand-zart);
    border-radius: 10px;
    background: var(--flaeche-sanft);
    /* Auf iOS rollt ein Bereich ohne diese Zeile ruckweise. */
    -webkit-overflow-scrolling: touch;
}

/* Der Bereich ist mit der Tastatur erreichbar (`tabindex="0"` in der Vorlage) –
   sonst käme man ohne Maus nicht an den unteren Teil einer gerollten Liste. Der
   Fokusrahmen ist deshalb sichtbar und nicht weggestellt. */
.upl-rolle:focus-visible {
    outline: 2px solid var(--akzent);
    outline-offset: 2px;
}

/* Die Typkarten brauchen mehr Höhe als die Protokollzeilen: Eine Karte ist rund
   elf Zeilen hoch, und „mehrere Typkarten gleichzeitig sichtbar" heißt drei bis
   vier. Das Audit ist die knappste der drei Listen – dort sind es Zeilen und
   keine Karten. */
.upl-rolle-typen { max-height: 46rem; }
.upl-rolle-audit { max-height: 26rem; }

/* Eine Überschrift mit ihrer eigenen Aktion rechts – „+ Typ hinzufügen" am
   Abschnitt „Upload-Typen". Dieselbe Aufteilung wie `.seite-kopf`, nur eine
   Ebene tiefer, damit die Aktion dort steht, wo man sie braucht. */
.upl-abschnittskopf {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.6rem;
}

.upl-abschnittskopf .upl-abschnitt { flex: 1 1 12rem; }

/* ── Die Upload-Typen ────────────────────────────────────────────────────── */

.upl-typen {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 0.8rem;
}

.upl-typ-karte {
    padding: 0.9rem;
    border: 1px solid var(--rand);
    border-left: 4px solid var(--erfolg-kraeftig);
    border-radius: 10px;
    background: var(--flaeche-karte);
}

/* Ein ausgeschalteter Typ ist zurückgenommen, ein gesperrter fällt auf. Beides
   steht zusätzlich als Wort in der Marke – die Farbe ist die zweite Auskunft. */
.upl-typ-aus {
    border-left-color: var(--rand-still);
    opacity: 0.78;
}

.upl-typ-gesperrt { border-left-color: var(--fehler-kraeftig); }

.upl-typ-kopf {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.5rem;
}

.upl-typ-kopf h4 { margin: 0; font-size: 1.02rem; }

.upl-typ-marke {
    padding: 0.1rem 0.5rem;
    border-radius: 999px;
    font-size: 0.75rem;
    font-weight: 700;
    background: var(--flaeche-sanft);
    color: var(--text-mittel);
}

.upl-typ-gesperrt .upl-typ-marke {
    background: var(--fehler-flaeche);
    color: var(--fehler-text);
}

.upl-typ-daten {
    display: grid;
    grid-template-columns: max-content 1fr;
    gap: 0.15rem 0.8rem;
    margin: 0.5rem 0 0;
    font-size: 0.85rem;
}

.upl-typ-daten dt { color: var(--text-leise); }
.upl-typ-daten dd { margin: 0; overflow-wrap: anywhere; }

.upl-typ-warnung {
    margin: 0.6rem 0 0;
    padding: 0.5rem 0.7rem;
    border-radius: 6px;
    background: var(--fehler-flaeche);
    color: var(--fehler-text);
    font-size: 0.85rem;
}

.upl-typ-aktionen {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
    margin-top: 0.7rem;
}

.upl-typ-aktionen form { margin: 0; }

/* Ein kopierter Entwurf (Issue #541): ausgeschaltet wie `.upl-typ-aus`, aber
   als noch anzupassende Kopie erkennbar – Wort in der Marke, Satz darunter,
   Farbe als zweite Auskunft. Voll deckend, weil hier gearbeitet wird. */
.upl-typ-entwurf {
    border-left-color: var(--warnung-kraeftig);
    opacity: 1;
}

.upl-typ-entwurf .upl-typ-marke {
    background: var(--warnung-flaeche);
    color: var(--warnung-text);
}

.upl-typ-entwurfssatz {
    margin: 0.5rem 0 0;
    padding: 0.5rem 0.7rem;
    border-radius: 6px;
    background: var(--warnung-flaeche-blass);
    color: var(--warnung-text);
    font-size: 0.85rem;
}

/* Die Meldung nach dem Kopieren: Hinweise und die Wege zur Kopie. */
.upl-kopie-hinweis,
.upl-kopie-wege {
    display: block;
    margin-top: 0.35rem;
}

/* Die Rückfrage vor dem Kopieren: was mitkommt, was nicht. */
.upl-kopie-umfang {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
    gap: 0.8rem;
    margin: 0.8rem 0;
}

.upl-kopie-umfang h3 { margin: 0 0 0.3rem; font-size: 0.95rem; }
.upl-kopie-umfang ul { margin: 0; padding-left: 1.1rem; font-size: 0.88rem; }
.upl-kopie-umfang li { overflow-wrap: anywhere; }

/* ── Das Übergabeprotokoll ───────────────────────────────────────────────── */

.upl-protokoll {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 0.6rem;
}

.upl-zeile {
    padding: 0.7rem 0.9rem;
    border: 1px solid var(--rand);
    border-left: 4px solid var(--erfolg-kraeftig);
    border-radius: 8px;
    background: var(--flaeche-karte);
}

.upl-fehler { border-left-color: var(--fehler-kraeftig); }

.upl-zeile-kopf {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.5rem;
}

.upl-marke {
    padding: 0.1rem 0.45rem;
    border-radius: 999px;
    font-size: 0.72rem;
    font-weight: 700;
    background: var(--erfolg-flaeche);
    color: var(--erfolg-text);
}

.upl-fehler .upl-marke {
    background: var(--fehler-flaeche);
    color: var(--fehler-text);
}

.upl-datei { overflow-wrap: anywhere; }

.upl-typ { font-size: 0.85rem; color: var(--text-leise); }

.upl-zeile-text {
    margin: 0.35rem 0 0;
    font-size: 0.88rem;
    color: var(--text-mittel);
    overflow-wrap: anywhere;
}

/* ── Der Importstatus aus mAirList (Issue #499) ──────────────────────────────
   Eine ruhige Zeile unter dem Kopf: Zeichen und Text, die Lage zusätzlich als
   Kante links – nie allein über die Farbe. Nur vorhandene Variablen, damit
   hell und dunkel von selbst stimmen. */
.upl-import {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.35rem 0.5rem;
    margin: 0.35rem 0 0;
    padding: 0.2rem 0.55rem;
    border-left: 3px solid var(--rand);
    border-radius: 6px;
    font-size: 0.85rem;
    background: var(--flaeche-sanft);
    color: var(--text-mittel);
    overflow-wrap: anywhere;
}

.upl-import-gut     { border-left-color: var(--erfolg-kraeftig); background: var(--erfolg-flaeche); color: var(--erfolg-text); }
.upl-import-warnung { border-left-color: var(--warnung-kraeftig); background: var(--warnung-flaeche); color: var(--warnung-text); }

.upl-import-zeichen { font-weight: 700; }
.upl-import-text    { font-weight: 600; }

.upl-import-kopieren {
    min-height: 2rem;
    padding: 0.15rem 0.6rem;
    border: 1px solid currentColor;
    border-radius: 999px;
    background: transparent;
    color: inherit;
    font: inherit;
    font-size: 0.8rem;
    cursor: pointer;
}

.upl-import-kopieren:focus-visible { outline: 2px solid var(--hinweis-kante); outline-offset: 2px; }

.upl-import-grundlage {
    flex-basis: 100%;
    font-size: 0.8rem;
    font-weight: 400;
}

/* Eine Korrekturlieferung (Issue #542): abgeschlossen übergeben, aber ohne
   Austauschbeleg – die Hinweisfarbe, ausdrücklich nicht das Grün einer
   Bestätigung. Hinweis und bisherige Kennung stehen in eigenen Zeilen. */
.upl-import-uebergeben { border-left-color: var(--hinweis-kante); background: var(--hinweis-flaeche); color: var(--hinweis-text); }

.upl-import-hinweis {
    flex-basis: 100%;
    font-size: 0.8rem;
    font-weight: 400;
}

.upl-import-bisher {
    display: flex;
    flex-basis: 100%;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.25rem 0.5rem;
    font-size: 0.8rem;
}

.upl-import-bisher-satz { font-style: italic; }

/* Der Dateinachweis im Musikarchiv (Issue #543): eine eigene Zeile mit
   eigener Kante – das Ergebnis steht im Text, nie allein in der Farbe. */
.upl-import-archiv {
    flex-basis: 100%;
    padding-left: 0.45rem;
    border-left: 3px solid var(--rand);
    font-size: 0.8rem;
    font-weight: 600;
}

.upl-import-archiv-gut     { border-left-color: var(--erfolg-kraeftig); }
.upl-import-archiv-warnung { border-left-color: var(--warnung-kraeftig); }
.upl-import-archiv-satz    { font-weight: 400; }

/* ── Musikarchiv / Hetzner Storage Box (Issue #543) ─────────────────────── */
.mak-ordnerliste {
    display: grid;
    gap: 0.35rem;
    margin: 0.5rem 0;
    padding: 0;
    list-style: none;
}

.mak-ordnerliste li {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.35rem 0.6rem;
    overflow-wrap: anywhere;
}

.mak-pfad {
    margin: 0.35rem 0;
    font-weight: 600;
    overflow-wrap: anywhere;
}

.mak-ungespeichert {
    padding: 0.4rem 0.6rem;
    border-left: 3px solid var(--hinweis-kante);
    background: var(--hinweis-flaeche);
    color: var(--hinweis-text);
}

/* ── Das Protokoll als Logbuch (Issue #213) ──────────────────────────────── */

/* Der Filterkopf. Ein `auto-fit`-Raster und keine feste Spaltenzahl: Auf der
   Übergabefläche stehen drei Felder, in der Verwaltung vier – und in einer
   Workflowansicht zwei. Das Raster richtet sich danach, statt die Vorlage eine
   Breite mitgeben zu lassen.

   `min(13rem, 100%)` und nicht `13rem`: Eine Spur, die nicht unter ihre
   Mindestbreite schrumpft, schiebt auf 320 px die ganze Seite seitlich heraus –
   genau die waagerechte Scrollfläche, die der Auftrag ausschließt. */
.upl-filterkopf {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(13rem, 100%), 1fr));
    gap: 0.6rem 0.8rem;
    align-items: end;
    margin: 0 0 0.7rem;
    padding: 0.7rem 0.8rem;
    border: 1px solid var(--rand-zart);
    border-radius: 10px;
    background: var(--flaeche-sanft);
}

.upl-filterfeld {
    display: flex;
    flex-direction: column;
    gap: 0.2rem;
    min-width: 0;
    font-size: 0.82rem;
    color: var(--text-leise);
}

/* Seit Issue #413 sind die Felder die gewohnten `formular-zeile`-Auswahlfelder
   (Höhe, Innenabstand, Schrift, Rahmen, Fokus, Dunkeldarstellung kommen von
   dort). Hier bleibt nur, was der Filterkopf selbst braucht: Das Raster
   übernimmt den Abstand, die Zeile darf nicht zusätzlich einen tragen. */
.upl-filterkopf .upl-filterfeld {
    margin-bottom: 0;
    font-size: inherit;
    color: inherit;
}

.upl-filterfeld select { width: 100%; min-width: 0; }

/* Die Statuszeile des Live-Filters: leer = kein Platz. */
.upl-ladestatus {
    margin: 0 0 0.5rem;
    font-size: 0.82rem;
    color: var(--text-leise);
}
.upl-ladestatus:empty { display: none; }
.upl-ladestatus-fehler { color: var(--fehler-text); }
.upl-ladestatus .schaltflaeche { margin-left: 0.3rem; }

/* Während des Ladens bleibt die vorige Liste stehen, nur gedämpft. */
.upl-treffer-laedt { opacity: 0.55; transition: opacity 0.15s; }

.upl-zuruecksetzen { margin: 0 0 0.5rem; }

.upl-filteraktionen {
    display: flex;
    flex-wrap: wrap;
    align-items: end;
    gap: 0.4rem;
}

/* Der Umfangssatz – „51–100 von 312 Übergaben". Er steht über der Rolle und
   nicht in ihr: Er beschreibt sie, und eine Beschreibung, die mit ihrem
   Gegenstand wegrollt, ist an der falschen Stelle. */
.upl-umfang {
    margin: 0 0 0.5rem;
    font-size: 0.82rem;
    color: var(--text-leise);
}

/* Die Angaben einer Protokollzeile als Beschriftungspaare. Der Auftrag verlangt
   „Informationen ruhiger gliedern statt als lose Textfolge darstellen" und im
   selben Atemzug „keine unnötige bunte Badge-Sammlung" – deshalb Paare aus
   leiser Beschriftung und normalem Wert und keine Marken.

   Auf dem Schreibtisch nebeneinander, auf schmalen Geräten von selbst
   untereinander: Das `auto-fit` löst das ohne eine zweite Medienabfrage, und
   `min(10rem, 100%)` hält es auch auf 320 px innerhalb der Seite. */
.upl-zeile-daten {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(10rem, 100%), 1fr));
    gap: 0.15rem 0.9rem;
    margin: 0.45rem 0 0;
    font-size: 0.8rem;
}

.upl-zeile-daten > div { min-width: 0; }

.upl-zeile-daten dt {
    color: var(--text-leise);
    font-size: 0.72rem;
    text-transform: uppercase;
    letter-spacing: 0.02em;
}

.upl-zeile-daten dd {
    margin: 0;
    color: var(--text-mittel);
    overflow-wrap: anywhere;
}

/* Der Weg zur Rückfrage „Eintrag löschen" (Issue #501) – nur mit der eigenen
   Freigabe. Bewusst die neutrale kleine Schaltfläche und nicht die rote: Hier
   wird noch nichts gelöscht, und eine rote Fläche in jeder Zeile wäre die
   Warnfarbe als Dauerzustand. Rot ist erst der Knopf auf der Rückfrage. */
.upl-zeile-aktion {
    display: flex;
    justify-content: flex-end;
    margin: 0.45rem 0 0;
}

/* Die Rückfrage selbst: der Satz aus dem Auftrag oben, darunter die Angaben
   des Eintrags in derselben Gliederung wie in der Zeile. */
.upl-verlauf-frage {
    margin: 0 0 0.6rem;
    color: var(--text-normal);
}

.upl-verlauf-angaben {
    margin: 0 0 1rem;
    font-size: 0.9rem;
}

/* Das technische Ziel ist der längste Wert einer Zeile und bekommt deshalb die
   ganze Breite – ein Nextcloud-Pfad neben drei kurzen Angaben wäre eine
   Wortschlange in einer 10rem-Spur. */
.upl-zeile-ziel { grid-column: 1 / -1; }

.upl-blaettern {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem;
    margin: 0.7rem 0 0;
}

.upl-blaetterstand { font-size: 0.78rem; color: var(--text-leise); }

/* ── Das Verwaltungsaudit ────────────────────────────────────────────────── */

.upl-audit {
    list-style: none;
    margin: 0;
    padding: 0;
    font-size: 0.85rem;
}

.upl-audit-zeile {
    display: grid;
    grid-template-columns: max-content max-content 1fr max-content;
    gap: 0.2rem 0.7rem;
    padding: 0.4rem 0;
    border-bottom: 1px solid var(--rand-zart);
    align-items: baseline;
}

.upl-audit-zeit { color: var(--text-leise); white-space: nowrap; }
.upl-audit-art  { font-weight: 700; color: var(--text-stark); }
.upl-audit-sache { color: var(--text-mittel); }
.upl-audit-text {
    grid-column: 1 / -1;
    color: var(--text-leise);
    overflow-wrap: anywhere;
}
.upl-audit-wer { color: var(--text-leise); white-space: nowrap; }

@media (max-width: 640px) {
    /* Zweispaltig bleiben von „Verbunden. Unterhalb von …" und einer
       WebDAV-Adresse zwei Wortschlangen übrig – die längsten Werte dieses
       Bereichs stehen in dieser Liste. */
    .upl-zustand-daten, .upl-typ-daten { grid-template-columns: 1fr; gap: 0.1rem 0; }
    .upl-zustand-daten dd, .upl-typ-daten dd { margin-bottom: 0.4rem; }

    /* Und dieselbe Auflösung für das Audit: Vier Spalten auf 320 px ergeben
       vier Wortschlangen. Untereinander bleibt jede Zeile lesbar, und die
       Trennlinie hält die Einträge auseinander. */
    .upl-audit-zeile { grid-template-columns: 1fr; }
    .upl-audit-wer { white-space: normal; }

    /* Auf dem Telefon stapeln, was am Schreibtisch nebeneinander steht – und
       zwar **nur** dort: Am Dateikopf und in der Workflow-Maske hält das Raster
       `1fr auto` die Schaltfläche oben rechts (Martins Abnahmepunkt 8, „auf
       schmalen Mobilgeräten darf bewusst gestapelt werden"). Unter 640 px
       blieben für einen Dateinamen neben „Entfernen" rund 200 px, und das ist
       die Breite, bei der jeder Name zur Wortschlange wird.

       Diese Zeilen stehen in der vorhandenen Abfrage und nicht in einer neuen:
       Es ist dieselbe Frage („ist das ein Telefon"), und eine zweite Bedingung
       wäre eine zweite Stelle, an der sie beantwortet wird. */
    /* Diese beiden Zeilen stehen **nicht** hier, sondern hinter den Regeln, die
       sie zurücknehmen sollen: `.upl-stueck-kopf` und `.upl-zeilenpaar` entstehen
       weiter unten im Block der Upload-Workflows, und eine Medienabfrage bringt
       keine höhere Spezifität mit – nur die spätere Stelle im Bestand gewinnt.
       Die Auflösung für `.upl-stueck-kopf` steht deshalb in der Abfrage unter
       `.upl-sammelzeile`, zusammen mit derselben Entscheidung für die
       Sammelliste; `.upl-zeilenpaar` entsteht erst noch danach und hat seine
       eigene Abfrage unmittelbar hinter seiner Regel. */

    /* Die Einrückung des Ordnerbaums fällt auf dem Telefon flacher aus –
       fünf Stufen von je 1,2 rem ließen bei tiefen Bäumen keine Breite für
       den Namen. */
    .upl-ordner-tiefe-1 { padding-left: 0.7rem; }
    .upl-ordner-tiefe-2 { padding-left: 1.4rem; }
    .upl-ordner-tiefe-3 { padding-left: 2.1rem; }
    .upl-ordner-tiefe-4 { padding-left: 2.8rem; }
}

/* ══ Dezente Mitteilungen (Issue #197) ═══════════════════════════════════════
 *
 * Wenn der persönliche Stand sich im Betrieb ändert, sagt es die Zahl in der
 * Kopfleiste und – auf dem Dashboard – der Kasten „Das liegt bei dir". Beides
 * ist ruhig und beides steht möglicherweise außerhalb des Blickfelds. Für den
 * einen Augenblick, in dem wirklich etwas **dazugekommen** ist, gibt es
 * deshalb diese Fläche.
 *
 * Drei Entscheidungen stecken darin, und alle drei kommen aus dem Auftrag:
 *
 *   * **Dezent.** Eine Karte in der Ecke, in denselben Farben wie jede andere
 *     Karte dieses Hauses. Kein Rot, kein Blinken, kein Ton: „eine dezente
 *     In-App-Mitteilung" und keine Alarmfläche.
 *   * **Unten rechts und nicht oben.** Oben steht die Kopfleiste mit der Zahl,
 *     zu der die Mitteilung gehört; darüberzulegen hieße, sie zu verdecken.
 *     Auf dem Telefon steht die Fläche über die volle Breite am unteren Rand –
 *     dort, wo der Daumen ohnehin ist.
 *   * **Nie im Weg.** Die Fläche selbst nimmt keine Klicks an
 *     (`pointer-events: none`); nur die Mitteilungen darin tun es. Ohne diese
 *     Zeile läge ein unsichtbarer Streifen über der rechten unteren Ecke jeder
 *     Seite – auch dann, wenn gar nichts zu melden ist.
 *
 * Kein einziger fester Farbwert, wie im ganzen Modul: Jede Farbe kommt aus den
 * Tokens ganz oben und hat dadurch im dunklen Modus ihre Entsprechung.
 */
.mitteilungen {
    position: fixed;
    right: 1rem;
    bottom: 1rem;
    z-index: 60;
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    width: min(22rem, calc(100vw - 2rem));
    pointer-events: none;
}

.mitteilung {
    display: flex;
    align-items: flex-start;
    gap: 0.5rem;
    padding: 0.6rem 0.7rem;
    background: var(--flaeche-karte);
    border: 1px solid var(--rand-stark);
    /* Die Kante links ist dieselbe Geste wie am Kasten des Dashboards: Sie
       sagt „das gehört zu dir", ohne die ganze Fläche einzufärben. */
    border-left: 4px solid var(--kontur);
    border-radius: 10px;
    box-shadow: var(--schatten-hoch);
    pointer-events: auto;
}

.mitteilung-text {
    flex: 1;
    min-width: 0;
    font-size: 0.85rem;
    line-height: 1.35;
    color: var(--text-normal);
    text-decoration: none;
    /* Zwei Zeilen genügen für „Rückfrage an dich: …"; der Titel ist serverseitig
       gekürzt, und was darüber hinausginge, gehört auf die Aufgabe selbst. */
    overflow-wrap: anywhere;
}
a.mitteilung-text:hover { text-decoration: underline; }
a.mitteilung-text:focus-visible { outline: 2px solid var(--kontur); outline-offset: 2px; }

.mitteilung-zu {
    flex: none;
    /* 44 Punkte sind die Tippfläche dieses Hauses – auch für ein Kreuz, das
       nur ein Zeichen breit ist. */
    min-width: 44px;
    min-height: 44px;
    margin: -0.35rem -0.35rem -0.35rem 0;
    padding: 0;
    background: none;
    border: 0;
    border-radius: 8px;
    font-size: 1.1rem;
    line-height: 1;
    color: var(--text-leise);
    cursor: pointer;
}
.mitteilung-zu:hover { color: var(--text-stark); }
.mitteilung-zu:focus-visible { outline: 2px solid var(--kontur); outline-offset: 2px; }

@media (max-width: 640px) {
    /* Volle Breite unten – und über der Tastatur-Sicherheitszone des iPhone,
       wie jede andere feststehende Fläche dieses Bestandes. */
    .mitteilungen {
        left: 0.6rem;
        right: 0.6rem;
        bottom: max(0.6rem, env(safe-area-inset-bottom));
        width: auto;
    }
}

/* ── Rückmeldungen unter „Mein Konto" (Issue #298) ──────────────────────────
 *
 * Dieselbe Karte wie eine Mitteilung, an derselben Stelle: Schreibtisch unten
 * rechts, Telefon über die Breite. Nur die Kante sagt, was es ist – grün für
 * eine Bestätigung, rot für einen Fehler. Ohne JavaScript steht die Hülle
 * `.konto-einblendungen` mit den Regeln von `.mitteilungen` selbst am Rand;
 * mit JavaScript wandern die Karten in die Fläche des Rahmens und gehen nach
 * ein paar Sekunden (`static/konto-rueckmeldung.js`).
 */
.konto-einblendung-erfolg { border-left-color: var(--erfolg-kraeftig); }
.konto-einblendung-fehler { border-left-color: var(--fehler-kraeftig); }
.konto-einblendung {
    transition: opacity 0.2s ease;
}
.konto-einblendung-weg { opacity: 0; }

@media (prefers-reduced-motion: reduce) {
    .konto-einblendung { transition: none; }
}

/* Der Fehler, der unter seinem Formular stehen bleibt. Etwas Luft nach oben,
   damit er nicht am Knopf klebt. */
.konto-feldfehler { margin-top: 0.8rem; }

/* ═══════════════════════════════════════════════════════════════════════════
   Die Upload-Workflows (Issue #206)

   Drei neue Flächen: die Typauswahl der Redaktion, der Ablauf mit seinen
   Dateien und die Workflow-Maske der Verwaltung samt Vorschau. Alle drei
   benutzen die vorhandenen Bausteine – `.formular-zeile`, `.schaltflaeche`,
   `.meldung` – und legen nur das darüber, was es dort nicht gibt.

   Ausdrücklich ohne eine neue Medienabfrage: Die Kästen sind Raster mit
   `auto-fill`, und die fallen von selbst auf eine Spalte, wenn der Platz
   ausgeht. Eine zusätzliche Bedingung wäre eine zweite Stelle, an der über
   dieselbe Breite entschieden wird.
   ═════════════════════════════════════════════════════════════════════════ */

.upl-typenwahl {
    list-style: none;
    margin: 0 0 1.4rem;
    padding: 0;
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
    gap: 0.8rem;
}

.upl-typenwahl-karte {
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    gap: 0.7rem;
    padding: 0.9rem 1rem;
    border: 1px solid var(--rand);
    border-left: 4px solid var(--akzent);
    border-radius: 10px;
    background: var(--flaeche-karte);
}

.upl-typenwahl-karte h4 { margin: 0; font-size: 1.02rem; }

.upl-typenwahl-karte .schaltflaeche { align-self: flex-start; }

/* Die Dateifelder des Ablaufs bekommen ihre Breite ausdrücklich. Ein
   `<input type="file">` hat eine eigene, vom Browser gesetzte Größe – im
   Browser gemessen 388 px – und schiebt die ganze Seite auf einem 320 px
   breiten Gerät zur Seite. Dieselbe Zeile wie bei `.bildfeld-eingabe` und
   `.anhang-feld`; sie steht hier, weil dieser Auftrag die Übergabemaske neu
   baut und ein Ablauf aus zwei Dateislots das Feld gleich zweimal hat.

   Das Nachlege-Feld ist ausgenommen: Es ist unsichtbar geschaltet, und eine
   Breite von 100 % darüber machte aus dem versteckten Feld ein 1600 px breites
   – im Browser gemessen, und die Seite lief danach auch am Schreibtisch über.

   Die Sammelübergabe steht in derselben Zeile und nicht in einer eigenen: Ihr
   Dateifeld ist dasselbe Element mit demselben Befund, und eine zweite Regel
   daneben wäre die Stelle, an der die eine nachgezogen wird und die andere
   nicht. */
#upl-formular input[type="file"]:not(.upl-nachlegen-feld),
#upl-sammlung input[type="file"] {
    width: 100%;
    max-width: 100%;
}

/* Ein einzelnes Stück eines Vorgangs: Name, Größe, Wegnehmen, Abspieler. */
.upl-stueck {
    margin-top: 0.6rem;
    padding: 0.6rem 0.75rem;
    border: 1px solid var(--rand);
    border-radius: 10px;
    background: var(--flaeche-still);
}

.upl-stueck-warnung { border-left: 4px solid var(--warnung-kraeftig); }

/* Name links, „Entfernen" rechts – und zwar **stabil** (Martins Abnahmepunkt 8).
   Vorher stand hier ein Flexkasten mit `flex-wrap: wrap`: Ein langer
   Originalname füllte die Zeile und drückte die Schaltfläche in die nächste, und
   zwar bei jeder Datei an einer anderen Stelle. Ein Raster aus `1fr auto` kann
   das nicht – die rechte Spalte ist so breit wie ihr Inhalt, die linke nimmt den
   Rest und bricht darin um. `min-width: 0` in der linken Spalte ist die Zeile,
   ohne die ein langer Name das Raster trotzdem aufspannen würde.
   `align-items: start` hält die Schaltfläche oben, wenn der Name zwei Zeilen
   braucht – „bleibt rechts oben an derselben Position". */
.upl-stueck-kopf {
    display: grid;
    grid-template-columns: 1fr auto;
    align-items: start;
    gap: 0.5rem;
    margin-bottom: 0.4rem;
}

.upl-stueck-name {
    min-width: 0;
    font-size: 0.9rem;
    color: var(--text-mittel);
    overflow-wrap: anywhere;
}

.upl-weg { padding: 0.25rem 0.6rem; font-size: 0.82rem; }

.upl-listenkasten { margin-top: 0.4rem; }

/* ── Der Zustand eines Stücks (Issue #234) ───────────────────────────────────
 *
 * Seit jede Datei eines mehrteiligen Ablaufs als **eigene** Übertragung
 * hinausgeht, hat jede Karte etwas zu melden: wartet, wird übertragen,
 * übergeben, fehlgeschlagen – und bei einem Fehler einen Knopf, der genau diese
 * eine Datei noch einmal schickt.
 *
 * Es sind dieselben drei Regeln wie an der Sammelliste aus Issue #212, und
 * bewusst dieselbe Bauart: `:empty` lässt die Zustandszeile aus dem Fluss
 * fallen, solange es nichts zu sagen gibt. Eine Karte im Ruhezustand ist
 * dadurch genauso hoch wie vor diesem Auftrag – „keine dauerhaft reservierten
 * Leerflächen für Status-/Fehlerbereiche" gilt hier wie dort.
 *
 * `.upl-stueck` ist ein gewöhnlicher Block und kein Raster; es braucht deshalb
 * keine Spaltenangabe wie `.upl-sammelzustand`. Die Farben kommen aus den
 * vorhandenen Variablen – der Dunkelmodus entsteht davon von selbst. */
.upl-stueck-zustand {
    margin: 0.4rem 0 0;
    font-size: 0.78rem;
    line-height: 1.3;
    color: var(--text-leise);
    overflow-wrap: anywhere;
}

.upl-stueck-zustand:empty { display: none; }

.upl-stueck-fehler .upl-stueck-zustand,
.upl-stueck-warnung .upl-stueck-zustand { color: var(--fehler-kraeftig); }

.upl-stueck-fertig .upl-stueck-zustand { color: var(--erfolg-kraeftig); }

.upl-stueck-fertig { border-left: 4px solid var(--erfolg-kraeftig); }

.upl-stueck-fehler { border-left: 4px solid var(--fehler-kraeftig); }

.upl-stueck-balken {
    width: 100%;
    height: 0.5rem;
    margin-top: 0.4rem;
}

.upl-stueck-nochmal {
    margin-top: 0.4rem;
    padding: 0.25rem 0.6rem;
    font-size: 0.82rem;
}

/* Der Gesamtstand eines Vorgangs – „3 von 4 übergeben". Dieselbe Zurückhaltung:
   Solange nichts gelaufen ist, steht dort nichts, und die Zeile ist dann auch
   nicht da. */
.upl-vorgangsstand {
    margin: 0.2rem 0 0.6rem;
    font-size: 0.85rem;
    color: var(--text-mittel);
}

.upl-vorgangsstand:empty { display: none; }

/* `hidden` ist ein Attribut und kein Befehl – dieselbe Begründung wie bei den
   Knöpfen der Sammelliste weiter unten: `.schaltflaeche` bringt ein eigenes
   `display` mit und gewänne sonst über den Browservorrat. */
.upl-stueck-nochmal[hidden],
.upl-stueck-balken[hidden] { display: none; }

/* Das Nachlege-Feld selbst bleibt unsichtbar; bedient wird sein Etikett, das
   wie eine Schaltfläche aussieht. Nicht `display: none` – ein so verstecktes
   Feld ist für die Tastatur nicht mehr erreichbar. */
.upl-nachlegen-feld {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

.upl-nachlegen { margin-top: 0.6rem; display: inline-block; }

.upl-nachlegen-feld:focus-visible + .upl-nachlegen {
    outline: 2px solid var(--kontur);
    outline-offset: 2px;
}

/* ══ Die Sammelübergabe (Issue #212) ════════════════════════════════════════
 *
 * Eine **Liste** und keine Kachelwand. Der Auftrag ist dazu ausdrücklich: „Keine
 * quadratischen Kacheln als primäres Desktop-Konzept: Bei 10–25 Dateien ist eine
 * Liste deutlich besser scanbar, vergleichbar und platzsparender."
 *
 * Deshalb ist eine Zeile ein Raster aus vier Spalten in **einer** Reihe – Name
 * und Größe, Abspieler, Typauswahl, „Entfernen" –, und der Zustand steht darunter
 * über die ganze Breite. Vier Spalten und nicht ein Flexkasten mit `wrap`: Bei
 * fünfundzwanzig Zeilen müssen Typauswahl und Schaltfläche **untereinander**
 * stehen, damit man die Liste überhaupt abtasten kann. Ein Flexkasten bräche bei
 * jedem Dateinamen an einer anderen Stelle um. Dieselbe Überlegung wie bei
 * `.upl-stueck-kopf` und Martins Abnahmepunkt 8 aus Issue #206.
 *
 * `minmax(0, …)` in jeder flexiblen Spalte ist die Zeile, ohne die ein langer
 * Dateiname das Raster aufspannt und die Seite seitlich schiebt. Die Typauswahl
 * hat `min(15rem, 100%)`: Eine feste Mindestbreite schrumpft **nicht**, und auf
 * 320 px sind 15 rem mehr, als zur Verfügung steht. 15 rem ist die Breite, unter
 * der die Beschriftung neben der Auswahl den längsten Typnamen abschneidet – im
 * Browser an „Frische Töne – Musik" nachgemessen.
 *
 * ## Die Verdichtung aus Martins Abnahme vom 30.09.2026
 *
 * Die Liste war fachlich fertig und trotzdem keine Liste: „eher wie eine Folge
 * großer Karten". Im Browser gemessen waren es **94 px** je Zeile plus 8 px
 * Abstand, und drei Viertel davon waren nicht Inhalt:
 *
 *   * **54 px** der eingebaute Abspieler in seiner Vorgabegröße,
 *   * **47 px** die Typauswahl, weil ihre Beschriftung **über** ihr stand,
 *   * **21 px** eine Zustandszeile, die auch „wartet" sagte, wenn nichts lief,
 *   * **18 px** senkrechtes Polster.
 *
 * Alle vier sind hier angefasst, und zwar ohne eine Funktion wegzunehmen: Der
 * Abspieler bekommt eine Höhe (siehe `.upl-sammelplayer`), die Beschriftung
 * rückt **neben** die Auswahl, die Zustandszeile entsteht nur noch, wenn sie
 * etwas zu sagen hat (`:empty`), und das Polster ist auf das Maß gekürzt, das
 * eine Zeile von der nächsten trennt. Gemessen sind daraus ~48 px je Zeile
 * geworden – auf demselben Bildschirm etwa doppelt so viele Dateien.
 * ════════════════════════════════════════════════════════════════════════════ */

.upl-sammelkopf {
    margin: 0.4rem 0 0.8rem;
    padding: 0.7rem 0.9rem;
    border: 1px solid var(--rand);
    border-radius: 10px;
    background: var(--flaeche-karte);
}

.upl-sammelalle { margin-bottom: 0.3rem; }

.upl-sammelstand {
    margin: 0;
    font-size: 0.88rem;
    font-weight: 600;
    color: var(--text-stark);
}

.upl-sammelstand:empty { display: none; }

.upl-sammelliste {
    list-style: none;
    margin: 0 0 1rem;
    padding: 0;
}

.upl-sammelzeile {
    display: grid;
    grid-template-columns:
        minmax(0, 1.5fr) minmax(0, 1.2fr) minmax(min(15rem, 100%), 0.9fr) auto;
    align-items: center;
    /* Der Reihenabstand trennt die Zustandszeile von der Zeile darüber – und
       zwar nur dann, wenn es sie gibt. Ein Raster setzt keinen Abstand um eine
       Reihe, die gar nicht entsteht. */
    gap: 0.2rem 0.7rem;
    margin-bottom: 0.35rem;
    padding: 0.3rem 0.6rem;
    border: 1px solid var(--rand);
    /* Die linke Kante ist der Zustand der Zeile – dieselbe Sprache wie im
       Protokoll darunter und in den Typkarten darüber. */
    border-left: 4px solid var(--rand);
    border-radius: 10px;
    background: var(--flaeche-still);
}

.upl-sammel-wartet  { border-left-color: var(--akzent); }
.upl-sammel-laeuft  { border-left-color: var(--akzent); }
.upl-sammel-fertig  { border-left-color: var(--erfolg-kraeftig); }
.upl-sammel-fehler  { border-left-color: var(--fehler-kraeftig); }
.upl-sammel-offen   { border-left-color: var(--warnung-kraeftig); }
.upl-sammel-gross   { border-left-color: var(--fehler-kraeftig); }

/* Name und Größe stehen nebeneinander und nicht als ein Textfluss: Ein langer
   Originalname darf gekürzt werden („sinnvoll kürzen/umbrechen innerhalb der
   bestehenden Zeilenlogik"), die Größe dahinter **nicht** – sie ist zwei Worte
   lang und der zweite Wert, nach dem man eine Liste abtastet. Ein gemeinsames
   `text-overflow` hätte am Ende der Zeile gekürzt und damit die Größe
   erwischt. Der volle Name steht im `title` des Namenskastens; darum kümmern
   sich `static/uploadsammlung.js` und `static/uploadmonat.js`.

   Die drei Regeln tragen seit Martins zweiter Abnahme vom 30.09.2026 beide
   Listen – die Sammelliste und die Tagesliste der Monatslieferung. Sie stehen
   deshalb **einmal**: „bestehende Styles soweit sinnvoll wiederverwenden statt
   dieselbe Verdichtungslogik erneut unabhängig zu bauen". Was an der Tagesliste
   wirklich anders ist, steht weiter unten in ihrem eigenen Abschnitt. */
.upl-tagesname,
.upl-sammelname {
    display: flex;
    align-items: baseline;
    min-width: 0;
    font-size: 0.88rem;
    color: var(--text-mittel);
}

.upl-tagesname-text,
.upl-sammelname-text {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Der Abstand steht als Außenabstand und nicht als Leerzeichen im Text: Ein
   Flexkasten schneidet den führenden Leerraum eines Kastens weg, und „ · 3 kB"
   klebte dann am Namen. Im Text bleibt das Leerzeichen trotzdem – wer die Zeile
   kopiert, bekommt „Name · 3 kB" wie vorher. */
.upl-tagesgroesse,
.upl-sammelgroesse {
    flex: none;
    margin-left: 0.3rem;
    white-space: nowrap;
    color: var(--text-leise);
}

/* Im Raster darf der Abspieler weder seine eigene Breite noch seine eigene Höhe
   mitbringen: `.upl-player` begrenzt die Breite auf 32 rem, und das ist hier die
   Breite der Spalte und nicht die des Abspielers.

   Die **Höhe** steht hier und nur hier. `.upl-player` lässt sie bewusst offen,
   weil der eingebaute Abspieler je Plattform anders hoch ist und eine feste Höhe
   ihn abschneiden kann. In einer Liste aus zwanzig Zeilen ist seine Vorgabe von
   54 px aber der größte einzelne Posten – im Browser gemessen mehr als die
   Hälfte der Zeilenhöhe –, und das ist genau der Befund aus Martins Abnahme.
   2,25 rem sind der Kompromiss: deutlich flacher und immer noch über der
   Größe, bei der die Bedienelemente der Browser zusammenrücken (in Chromium
   nachgesehen – Abspieltaste, Laufleiste, Zeit, Lautstärke und Menü stehen bei
   36 px vollständig da). Die **Listen** bekommen das – seit Martins zweiter
   Abnahme vom 30.09.2026 auch die Tagesliste der Monatslieferung, wo derselbe
   Posten derselbe war; der Abspieler an einer einzelnen Datei behält seine
   Vorgabe. */
.upl-tagesplayer,
.upl-sammelplayer {
    min-width: 0;
    max-width: 100%;
    height: 2.25rem;
    margin: 0;
}

.upl-sammelwahl { min-width: 0; }

/* Die Beschriftung steht **neben** der Auswahl und nicht über ihr. Über ihr war
   die Typauswahl mit 47 px höher als jede andere Spalte und bestimmte damit die
   Höhe der ganzen Zeile, sobald der Abspieler flacher wurde. Der Auftrag nennt
   die Zeile ausdrücklich so: „**Upload-Typ:** [ Musik ▼ ]". */
.upl-sammelwahl-titel {
    display: flex;
    align-items: center;
    gap: 0.35rem;
    margin: 0;
    font-size: 0.74rem;
    /* Zweizeilig („Upload-" / „Typ") wäre die Beschriftung wieder das höchste
       Stück der Zeile – genau das, was ihr Platz neben der Auswahl verhindern
       soll. */
    white-space: nowrap;
    color: var(--text-leise);
}

/* Dieselbe Erscheinung wie ein Auswahlfeld in einer Formularzeile – nur ohne
   deren Außenabstände, die das Raster der Zeile aufreißen würden. Die Werte
   stehen hier noch einmal und nicht als zusätzlicher Selektor an der Regel
   oben: Diese Auswahl gehört in eine Listenzeile und nicht in ein Formular, und
   sie ist kleiner. Ohne sie zeichnete der Browser genau hier sein eigenes,
   kantiges Feld – neben dem gerundeten „Typ für alle" darüber. */
.upl-sammelwahl select {
    width: 100%;
    min-width: 0;
    max-width: 100%;
    padding: 0.25rem 0.4rem;
    border: 1px solid var(--rand-stark);
    border-radius: 6px;
    font-size: 0.84rem;
    font-family: inherit;
    background: var(--flaeche-eingabe);
}

.upl-sammelwahl select:focus {
    outline: none;
    border-color: var(--kontur);
}

/* Die Zustandszeile steht unter der Datei, zu der sie gehört – und sie nimmt
   dort **keinen** Platz weg, solange es nichts zu melden gibt. `:empty` ist die
   ganze Regel dafür: Das Skript schreibt in eine ruhige Zeile den leeren Text,
   der Absatz fällt aus dem Raster, und mit ihm die Reihe und ihr Abstand. Ohne
   diese Zeile stünde unter jeder der zwanzig Dateien ein reservierter Streifen –
   genau der „dauerhaft reservierte Leerbereich" aus Martins Abnahme. */
.upl-sammelzustand {
    grid-column: 1 / -1;
    margin: 0;
    font-size: 0.78rem;
    line-height: 1.3;
    color: var(--text-leise);
    overflow-wrap: anywhere;
}

.upl-sammelzustand:empty { display: none; }

.upl-sammel-fehler .upl-sammelzustand,
.upl-sammel-gross .upl-sammelzustand { color: var(--fehler-kraeftig); }

.upl-sammel-fertig .upl-sammelzustand { color: var(--erfolg-kraeftig); }

.upl-sammel-offen .upl-sammelzustand { color: var(--warnung-kraeftig); }

.upl-sammelbalken {
    grid-column: 1 / -1;
    width: 100%;
    height: 0.5rem;
}

.upl-sammelnochmal {
    grid-column: 1 / -1;
    justify-self: start;
    padding: 0.25rem 0.6rem;
    font-size: 0.82rem;
}

/* `hidden` ist ein Attribut und kein Befehl: Das `display: none` dafür steht im
   Browservorrat mit der schwächsten Stärke, und jede Klasse mit eigenem
   `display` gewinnt darüber. `.schaltflaeche` und `.formular-aktionen` haben
   eines – im Browser gemessen: Der Knopf „Noch einmal versuchen" stand an jeder
   wartenden Zeile, und die beiden Knöpfe unter der leeren Liste standen da,
   bevor es etwas zu übergeben gab. Diese Zeile nimmt das zurück, ohne irgendwo
   ein `!important` zu setzen. */
#upl-sammelaktionen[hidden],
.upl-sammelnochmal[hidden],
.upl-sammelbalken[hidden] { display: none; }

/* Dasselbe für den Stand am Übergabeschritt einer Aufgabe (Auftrag 451):
   `.upl-zustand` und `.schaltflaeche` bringen je ein eigenes `display` mit. */
#upl-anhangstand[hidden],
#upl-anhangstand .schaltflaeche[hidden],
#upl-anhangaktionen .schaltflaeche[hidden] { display: none; }
#upl-anhangstand .upl-anhangdatei { overflow-wrap: anywhere; }

.upl-sammelausnahme { margin-top: -0.4rem; }

/* ── Fortschritt und Abschluss einer Übergabe (Issue #478) ───────────────────
 *
 * Martins Befund vom 28.10.2026: Bei vier MP3s à 30 Minuten waren Stückzahlen
 * zu sehen, innerhalb einer laufenden Datei aber nichts – und nach dem Erfolg
 * stand unten nur „Abbrechen". Die Fläche über „Übergeben" sagt deshalb in
 * Worten, welche Datei in welcher Phase ist, mit einem Balken für diese Datei
 * und einem für alle. Sie ist eine Karte mit Rand und Beschriftung, nicht nur
 * ein farbiger Streifen: Was gerade geschieht, steht immer auch als Text da.
 *
 * Die Balken sind gewöhnliche `<progress>`: gemessen mit Wert, unbekannt ohne
 * – dann zeichnet der Browser seinen unbestimmten Lauf. Gemessene Sprünge
 * gleiten kurz; mit `prefers-reduced-motion` springen sie. Alle Farben kommen
 * aus den vorhandenen Variablen, hell und dunkel entstehen davon von selbst. */
.upl-fortschritt {
    margin: 0.6rem 0 0.8rem;
    padding: 0.7rem 0.9rem;
    border: 1px solid var(--rand);
    border-left: 4px solid var(--info-kante);
    border-radius: 6px;
    background: var(--flaeche-zart);
}

.upl-fortschritt-satz {
    margin: 0 0 0.45rem;
    font-weight: 600;
    color: var(--text-mittel);
    overflow-wrap: anywhere;
}

.upl-fortschritt-zeile {
    display: grid;
    grid-template-columns: minmax(5.5rem, auto) 1fr;
    align-items: center;
    gap: 0.6rem;
    margin-top: 0.3rem;
}

.upl-fortschritt-titel {
    font-size: 0.8rem;
    color: var(--text-leise);
}

.upl-fortschritt-datei,
.upl-fortschritt-balken {
    width: 100%;
    height: 0.75rem;
    accent-color: var(--info-kante);
}

.upl-fortschritt-datei::-webkit-progress-value,
.upl-fortschritt-balken::-webkit-progress-value {
    transition: width 0.4s ease-out;
}

.upl-fortschritt-mengen {
    margin: 0.25rem 0 0;
    font-size: 0.78rem;
    color: var(--text-leise);
    font-variant-numeric: tabular-nums;
}

.upl-fortschritt-mengen:empty { display: none; }

/* Der einzelne Balken am Übergabeschritt einer Aufgabe – derselbe Balken ohne
   Karte darum. */
.upl-anhangbalken {
    display: block;
    margin: 0.35rem 0 0.6rem;
}

.upl-abschluss {
    margin: 0.6rem 0 0.8rem;
}

.upl-abschluss-satz {
    margin: 0 0 0.2rem;
    font-weight: 700;
    font-size: 1rem;
}

.upl-abschluss-hinweis {
    margin: 0 0 0.7rem;
}

.upl-abschluss-aktionen {
    display: flex;
    flex-wrap: wrap;
    gap: 0.6rem;
}

/* Auf dem Telefon stehen beide Schritte untereinander und so breit wie die
   Fläche – „Fertig" zuerst, als der eine deutliche Weg hinaus. */
@media (max-width: 640px) {
    .upl-fortschritt-zeile { grid-template-columns: 1fr; gap: 0.15rem; }
    .upl-abschluss-aktionen { flex-direction: column; }
    .upl-abschluss-aktionen .schaltflaeche { width: 100%; text-align: center; }
}

@media (prefers-reduced-motion: reduce) {
    .upl-fortschritt-datei::-webkit-progress-value,
    .upl-fortschritt-balken::-webkit-progress-value { transition: none; }
}

/* `hidden` ist ein Attribut und kein Befehl – dieselbe Begründung wie oben:
   `.formular-aktionen`, `.formular-zeile`, `.meldung` und die Raster hier
   bringen ein eigenes `display` mit. */
.upl-fortschritt[hidden],
.upl-fortschritt-zeile[hidden],
.upl-abschluss[hidden],
.upl-anhangbalken[hidden],
#upl-anhangaktionen[hidden],
#upl-formular .formular-aktionen[hidden],
#upl-formular .upl-bestaetigung[hidden],
#upl-sammelriegel[hidden] { display: none; }

/* ══ Die Monatslieferung mit Tagesdateien (Issue #218) ═══════════════════════
 *
 * Zwei Flächen: der **Lieferstand** eines Monats (was ist da, was fehlt) und
 * die **Tagesliste** (was geht jetzt hinaus). Beide benutzen ausschließlich die
 * vorhandenen Farbvariablen und die vorhandenen Umschaltpunkte – 900 px für das
 * Tablet, 640 px für das Telefon, beide weiter unten in den Abfragen, die die
 * Sammelliste ohnehin schon hat. Ein dritter Umschaltpunkt wäre eine Breite,
 * die es in diesem Haus sonst nirgends gibt.
 */
.upl-lieferstand {
    margin: 0 0 1.2rem;
    padding: 0.9rem 1rem;
    border: 1px solid var(--rand);
    border-left: 4px solid var(--akzent);
    border-radius: 10px;
    background: var(--flaeche-karte);
}

/* Ein vollständiger Monat ist der Zustand, auf den alles zuläuft – und der
   einzige, der sich an der Kante von den übrigen unterscheidet. */
.upl-lieferstand-voll { border-left-color: var(--erfolg-kraeftig); }

.upl-lieferstand h4 {
    margin: 0 0 0.4rem;
    font-size: 1.02rem;
    color: var(--text-stark);
}

.upl-lieferstand-zeile {
    margin: 0 0 0.6rem;
    font-size: 0.9rem;
    color: var(--text-mittel);
}

.upl-lieferliste {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    gap: 0.25rem;
}

.upl-lieferliste li {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) auto;
    align-items: baseline;
    gap: 0.2rem 0.6rem;
    font-size: 0.85rem;
}

.upl-liefertag {
    font-weight: 700;
    color: var(--text-stark);
    font-variant-numeric: tabular-nums;
}

.upl-lieferdatei {
    min-width: 0;
    color: var(--text-mittel);
    overflow-wrap: anywhere;
}

.upl-lieferwer { color: var(--text-leise); }

/* Der Tagesplatz an einer Protokollzeile – „November 2026 · 05.". Dieselbe
   ruhige Schrift wie der Typname daneben; er ist eine Einordnung und keine
   Marke. */
.upl-liefermarke { font-size: 0.85rem; color: var(--text-leise); }

.upl-tagesliste {
    list-style: none;
    margin: 0 0 0.8rem;
    padding: 0;
}

.upl-tagesstand {
    margin: 0 0 0.8rem;
    font-size: 0.86rem;
    color: var(--text-leise);
}

/* Die Zeile selbst: Tag, Datei, Abspieler, Entfernen – und darunter, über die
   volle Breite, Zustand, Korrekturhäkchen und Fortschritt. Dieselbe Bauart wie
   `.upl-sammelzeile`, weil es dieselbe Sache ist: eine ruhige Liste und keine
   Kachelwand.

   ## Die Verdichtung aus Martins zweiter Abnahme vom 30.09.2026
   Die Sammelliste war flach abgenommen, diese Liste dagegen blieb „eine Folge
   sehr hoher Dateikarten mit großen ungenutzten vertikalen Flächen". Im Browser
   gemessen waren es 94 px je Zeile plus 8 px Abstand, und drei Viertel davon
   waren nicht Inhalt:

     * 54 px der eingebaute Abspieler in seiner Vorgabegröße,
     * 30 px die Tagesauswahl, weil ihre Beschriftung über ihr stand,
     * 18 px senkrechtes Polster,
     * 15 px die Zuordnungszeile – der einzige Posten, der bleiben soll.

   Angefasst sind die ersten drei, und zwar ohne eine Funktion wegzunehmen: Der
   Abspieler bekommt seine Höhe aus derselben Regel wie die Sammelliste, die
   Beschriftung rückt neben die Auswahl, und das Polster ist auf das Maß gekürzt,
   das eine Zeile von der nächsten trennt. Die Zuordnungszeile bleibt, weil der
   Auftrag sie wörtlich verlangt – „Zuordnung: 13. Oktober 2026 · wartet" –, und
   sie wird nur dann höher, wenn sie mehr zu sagen hat. Gemessen sind daraus
   ~66 px je Zeile geworden; die Punkte stehen in
   `tests/browser_uploadablauf.mjs` unter T13 bis T16. */
.upl-tageszeile {
    display: grid;
    grid-template-columns:
        minmax(min(9.5rem, 100%), auto) minmax(0, 1.4fr)
        minmax(min(15rem, 100%), 1.3fr) auto;
    align-items: center;
    /* Der Reihenabstand trennt die Zuordnungszeile von der Zeile darüber – und
       zwar nur dann, wenn es sie gibt. Ein Raster setzt keinen Abstand um eine
       Reihe, die gar nicht entsteht. */
    gap: 0.2rem 0.7rem;
    margin-bottom: 0.35rem;
    padding: 0.3rem 0.6rem;
    border: 1px solid var(--rand);
    border-left: 4px solid var(--rand);
    border-radius: 10px;
    background: var(--flaeche-still);
}

.upl-tages-wartet   { border-left-color: var(--akzent); }
.upl-tages-laeuft   { border-left-color: var(--akzent); }
.upl-tages-fertig   { border-left-color: var(--erfolg-kraeftig); }
.upl-tages-fehler   { border-left-color: var(--fehler-kraeftig); }
.upl-tages-offen    { border-left-color: var(--warnung-kraeftig); }
.upl-tages-doppelt  { border-left-color: var(--fehler-kraeftig); }
.upl-tages-konflikt { border-left-color: var(--fehler-kraeftig); }
.upl-tages-ersetzen { border-left-color: var(--warnung-kraeftig); }
.upl-tages-gross    { border-left-color: var(--fehler-kraeftig); }

/* Die Beschriftung steht **neben** der Auswahl und nicht über ihr – dieselbe
   Entscheidung und derselbe gemessene Grund wie bei der Typauswahl der
   Sammelliste: Über ihr war die Tagesauswahl das höchste Stück der Zeile,
   sobald der Abspieler flacher wurde, und bestimmte damit deren Höhe. Der
   Auftrag nennt die Zeile ausdrücklich so: „Tag-Auswahl · Dateiname/Größe ·
   Player · Entfernen". */
.upl-tagesfeldwahl {
    display: flex;
    align-items: center;
    gap: 0.35rem;
    min-width: 0;
}

.upl-tagestitel {
    flex: none;
    margin: 0;
    font-size: 0.76rem;
    /* Umgebrochen wäre die Beschriftung wieder das höchste Stück der Zeile –
       genau das, was ihr Platz neben der Auswahl verhindern soll. Schrumpfen
       darf stattdessen das Feld daneben. */
    white-space: nowrap;
    color: var(--text-leise);
}

/* Dieselbe Erscheinung wie die Typauswahl der Sammelliste – und aus demselben
   Grund noch einmal ausgeschrieben: Diese Auswahl gehört in eine Listenzeile
   und nicht in ein Formular, und ohne sie zeichnete der Browser hier sein
   eigenes, kantiges Feld. */
.upl-tageswahl {
    width: 100%;
    min-width: 0;
    max-width: 100%;
    padding: 0.25rem 0.4rem;
    border: 1px solid var(--rand-stark);
    border-radius: 6px;
    font-size: 0.86rem;
    font-family: inherit;
    font-variant-numeric: tabular-nums;
    background: var(--flaeche-eingabe);
}

.upl-tageswahl:focus {
    outline: none;
    border-color: var(--kontur);
}

/* `.upl-tagesname`, `.upl-tagesname-text`, `.upl-tagesgroesse` und
   `.upl-tagesplayer` werden hier vergeblich gesucht: Sie stehen weiter oben
   bei der Sammelliste, weil es dieselben Regeln sind und sie dort beide Listen
   tragen. */

/* Die Zuordnungszeile unter der Datei – „Zuordnung: 13. Oktober 2026 ·
   wartet". Sie ist der einzige Posten der alten Kachel, der bleiben soll, und
   sie nimmt nur Platz, solange sie etwas zu sagen hat: `:empty` lässt den
   Absatz aus dem Raster fallen, und mit ihm die Reihe und ihren Abstand. Das
   ist dieselbe Sicherung wie bei der Sammelliste – hier verhindert sie den
   reservierten Streifen unter jeder der dreißig Zeilen, sobald eine Fassung
   dieser Liste einmal nichts zu melden hat. */
.upl-tageszustand {
    grid-column: 1 / -1;
    margin: 0;
    font-size: 0.84rem;
    color: var(--text-leise);
    overflow-wrap: anywhere;
}

.upl-tageszustand:empty { display: none; }

.upl-tages-fehler .upl-tageszustand,
.upl-tages-doppelt .upl-tageszustand,
.upl-tages-konflikt .upl-tageszustand,
.upl-tages-gross .upl-tageszustand { color: var(--fehler-kraeftig); }

.upl-tages-fertig .upl-tageszustand { color: var(--erfolg-kraeftig); }

.upl-tages-offen .upl-tageszustand,
.upl-tages-ersetzen .upl-tageszustand { color: var(--warnung-kraeftig); }

.upl-tagesersatz {
    grid-column: 1 / -1;
    margin: 0;
    font-size: 0.84rem;
}

.upl-tagesbalken {
    grid-column: 1 / -1;
    width: 100%;
    height: 0.5rem;
}

.upl-tagesnochmal {
    grid-column: 1 / -1;
    justify-self: start;
    padding: 0.25rem 0.6rem;
    font-size: 0.82rem;
}

/* Dieselbe Zeile wie bei der Sammelliste und aus demselben Grund: `hidden` ist
   das schwächste `display: none` im Vorrat des Browsers, und `.schaltflaeche`
   gewinnt darüber. */
.upl-tagesnochmal[hidden],
.upl-tagesersatz[hidden],
.upl-tagesbalken[hidden] { display: none; }

/* Die Probe der Dateinamenerkennung in der Workflow-Maske. */
.upl-erkennungsprobe {
    margin: 0.2rem 0 0.6rem;
    padding: 0.5rem 0.7rem;
    border-left: 3px solid var(--akzent);
    border-radius: 6px;
    background: var(--flaeche-still);
    font-size: 0.88rem;
    color: var(--text-mittel);
}

.upl-erkennungsprobe[hidden] { display: none; }

/* Das Kalenderfenster eines Sendetags (Issue #480, `static/sendetag.js`).
   Ausschließlich vorhandene Farbvariablen – der Dunkelmodus entsteht davon
   von selbst. Das Fenster steht im Fluss unter dem Knopf und nicht schwebend:
   Auf dem Telefon gäbe es neben einem schwebenden Fenster keinen Platz, und
   so schiebt es den Rest der Maske nur nach unten. */
.upl-sendetag {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 0.35rem;
}

.upl-sendetag-knopf {
    min-height: 2.6rem;
    font-weight: 600;
}

.upl-sendetag-gewaehlt {
    margin: 0;
    font-size: 0.92rem;
    color: var(--text-stark);
}

.upl-sendetag-gewaehlt.upl-sendetag-offen {
    color: var(--text-leise);
    font-style: italic;
}

.upl-sendetag-fehler {
    margin: 0;
    font-size: 0.9rem;
    font-weight: 600;
    color: var(--fehler-kraeftig);
}

.upl-sendetag-fehler[hidden],
.upl-sendetag-fenster[hidden] { display: none; }

.upl-sendetag-fenster {
    width: 100%;
    max-width: 22rem;
    box-sizing: border-box;
    padding: 0.6rem;
    border: 1px solid var(--rand-stark);
    border-radius: 10px;
    background: var(--flaeche-karte);
    color: var(--text-normal);
}

.upl-sendetag-kopf {
    display: flex;
    align-items: center;
    gap: 0.25rem;
    margin-bottom: 0.4rem;
}

.upl-sendetag-monat {
    flex: 1;
    text-align: center;
    font-weight: 700;
    color: var(--text-stark);
}

.upl-sendetag-blaettern,
.upl-sendetag-tag {
    min-width: 2.4rem;
    min-height: 2.4rem;
    border: 1px solid transparent;
    border-radius: 8px;
    background: var(--flaeche-sanft);
    color: var(--text-stark);
    font: inherit;
    cursor: pointer;
}

.upl-sendetag-blaettern:disabled {
    opacity: 0.4;
    cursor: default;
}

.upl-sendetag-tabelle {
    width: 100%;
    border-collapse: separate;
    border-spacing: 2px;
    table-layout: fixed;
}

.upl-sendetag-tabelle th {
    padding: 0.2rem 0;
    font-size: 0.8rem;
    font-weight: 600;
    color: var(--text-leise);
    text-align: center;
}

.upl-sendetag-tabelle td {
    padding: 0;
    text-align: center;
}

.upl-sendetag-tag {
    width: 100%;
    background: transparent;
}

.upl-sendetag-tag:hover {
    background: var(--flaeche-zart);
}

.upl-sendetag-heute {
    border-color: var(--rand-akzent);
}

.upl-sendetag-gewaehlter,
.upl-sendetag-gewaehlter:hover {
    background: var(--fuellung);
    color: var(--fuellung-text);
    font-weight: 700;
}

.upl-sendetag-tag:focus-visible,
.upl-sendetag-blaettern:focus-visible {
    outline: 2px solid var(--kontur);
    outline-offset: 1px;
}

.upl-sendetag-fuss {
    display: flex;
    justify-content: flex-end;
    margin-top: 0.4rem;
}

/* Auf dem Tablet wird aus der vierspaltigen Sammelzeile eine zweispaltige:
   oben Name und „Entfernen", darunter Abspieler und Typauswahl. Vier Spalten auf
   820 px lassen für den Abspieler rund 200 px – im Browser gemessen die Breite,
   unter der seine Bedienknöpfe zusammenrücken. Der Eintrag bleibt dabei **eine**
   Zeile; er bricht nur intern um, und genau das erlaubt der Auftrag ausdrücklich.

   Zwei Reihen und nicht drei (Martins Abnahme vom 30.09.2026): Abspieler und
   Typauswahl stehen **nebeneinander**, weil die Typauswahl neben dem Abspieler
   noch ihre 15 rem bekommt und eine eigene dritte Reihe nur Höhe kostet.

   900 px ist kein neuer Umschaltpunkt: Er trägt in diesem Stylesheet schon die
   Rechtematrix, das Wochenraster und die Wissenstabelle.

   Die Kästen stehen hier **ausdrücklich** im Raster und nicht in ihrer
   Quellreihenfolge: „Entfernen" gehört auf die Höhe des Namens und nicht unter
   die Typauswahl. Ein Raster ordnet nach Angabe und nicht nach Reihenfolge im
   Dokument – und die Reihenfolge im Dokument ist die, die am Schreibtisch und auf
   dem Telefon richtig ist. */
@media (max-width: 900px) {
    .upl-sammelzeile {
        grid-template-columns: minmax(0, 1fr) minmax(min(15rem, 45%), auto);
        align-items: center;
    }
    .upl-sammelname { grid-area: 1 / 1 / 2 / 2; }
    .upl-sammelzeile .upl-weg { grid-area: 1 / 2 / 2 / 3; justify-self: end; }
    .upl-sammelplayer { grid-area: 2 / 1 / 3 / 2; }
    .upl-sammelwahl { grid-area: 2 / 2 / 3 / 3; }

    /* Und dieselbe Umordnung für die Tagesliste (Issue #218) – vier Spalten
       auf 820 px ließen dem Abspieler rund 150 px. Der Tag bleibt dabei oben
       links neben dem Dateinamen: Er ist die Angabe, um die es in dieser Zeile
       geht, und gehört nicht unter den Abspieler. */
    .upl-tageszeile {
        grid-template-columns: minmax(min(9.5rem, 45%), auto) minmax(0, 1fr) auto;
        align-items: center;
    }
    .upl-tagesfeldwahl { grid-area: 1 / 1 / 2 / 2; }
    .upl-tagesname { grid-area: 1 / 2 / 2 / 3; }
    .upl-tageszeile .upl-weg { grid-area: 1 / 3 / 2 / 4; }
    .upl-tagesplayer { grid-area: 2 / 1 / 3 / -1; }

    /* Die gelieferten Tage bekommen ihre Zeitangabe unter den Dateinamen: Drei
       Spalten aus Tag, Name und Zeitpunkt lassen dem Namen sonst nichts. */
    .upl-lieferliste li { grid-template-columns: auto minmax(0, 1fr); }
    .upl-lieferwer { grid-column: 2 / -1; }
}

/* Und auf dem Telefon eine Spalte: „Derselbe Listeneintrag darf intern
   mehrzeilig werden … keine horizontale Scrollfläche." Zwei Spalten auf 320 px
   ließen für „Entfernen" neben einem Dateinamen rund 200 px, und das ist die
   Breite, bei der jeder Name zur Wortschlange wird.

   In dieser Abfrage steht zugleich `.upl-stueck-kopf`, das im Block weiter oben
   nichts bewirken konnte: Die Regel wird erst danach gesetzt, und eine
   Medienabfrage gewinnt nicht über die Reihenfolge im Bestand – hinter ihrer
   Regel steht diese Stelle aber. `.upl-zeilenpaar` entsteht noch weiter unten
   und bekommt deshalb seine eigene Abfrage direkt hinter seiner Regel; hier
   stünde sie wieder davor und damit wirkungslos.

   Es ist dieselbe Frage („ist das ein Telefon") und deshalb dieselbe
   Bedingung – 640 px ist der Telefon-Umschaltpunkt dieses Projekts seit #43. */
@media (max-width: 640px) {
    .upl-sammelzeile,
    .upl-tageszeile,
    .upl-stueck-kopf { grid-template-columns: 1fr; }

    .upl-sammelname,
    .upl-sammelplayer,
    .upl-sammelwahl,
    .upl-sammelzeile .upl-weg { grid-area: auto; }

    /* Dieselbe Rücknahme für die Tagesliste: „Derselbe Listeneintrag darf
       intern mehrzeilig werden … keine horizontale Scrollfläche." */
    .upl-tagesfeldwahl,
    .upl-tagesname,
    .upl-tagesplayer,
    .upl-tageszeile .upl-weg { grid-area: auto; }

    .upl-lieferliste li { grid-template-columns: 1fr; }
    .upl-lieferwer { grid-column: auto; }

    .upl-sammelzeile .upl-weg,
    .upl-tageszeile .upl-weg,
    .upl-stueck-kopf .upl-weg,
    .upl-zeilenpaar .upl-zeile-weg { justify-self: start; }

    /* Auf dem Telefon hat der Name die ganze Breite für sich – dort wird er
       umgebrochen und nicht gekürzt. Gekürzt wird er am Schreibtisch, wo er
       sich eine Zeile mit drei anderen Spalten teilt und der volle Name im
       Hinweis steht; auf einem Telefon gibt es keinen Mauszeiger, der diesen
       Hinweis holen könnte. */
    .upl-tagesname,
    .upl-sammelname { flex-wrap: wrap; }
    .upl-tagesname-text,
    .upl-sammelname-text {
        overflow: visible;
        white-space: normal;
        overflow-wrap: anywhere;
    }

    /* Und die Auswahlfelder werden zum Tippziel: 16 px verhindern zugleich, dass
       iOS beim Antippen die ganze Seite heranzoomt – dieselbe Zahl und derselbe
       Grund wie in der Mobilbasis weiter oben, die hier an der höheren
       Spezifität von `.upl-sammelwahl select` bzw. `.upl-tageswahl` scheitert. */
    .upl-tageswahl,
    .upl-sammelwahl select {
        padding: 0.45rem 0.5rem;
        font-size: 16px;
    }
}

/* Die Blöcke der Workflow-Maske. */
.upl-block {
    margin: 0 0 1.2rem;
    padding: 0.9rem 1rem 0.4rem;
    border: 1px solid var(--rand);
    border-radius: 10px;
    background: var(--flaeche-karte);
}

.upl-block legend {
    padding: 0 0.4rem;
    font-weight: 700;
    color: var(--text-stark);
}

/*
 * Ein Kasten, den die gewählte Dateistruktur nicht kennt (Issue #225).
 *
 * Er bleibt stehen und verschwindet nicht: Martin will sehen können, welche
 * Möglichkeiten es überhaupt gibt. Erkennbar stumm wird er über die ruhigere
 * Fläche und den gestrichelten Rand – **kein** `opacity`, aus demselben Grund
 * wie bei den gesperrten Feldern weiter oben: Ausgeblichener Text ist im
 * dunklen Modus unlesbar. Die Felder darin trägt `fieldset[disabled]`; sie
 * bekommen dadurch die Fläche der gesperrten Felder, ohne dass hier eine Regel
 * dafür stehen muss.
 */
.upl-block-aus {
    border-style: dashed;
    background: var(--flaeche-still);
}

.upl-block-aus legend { color: var(--text-mittel); }

/*
 * Und der Satz, der sagt, wofür der Kasten da ist – „Nur verfügbar bei ‚Feste,
 * benannte Dateislots'.". Er ist das Einzige darin, was gelesen werden **soll**,
 * und bleibt deshalb kontrastreich: Er erklärt das Stumme und ist nicht selbst
 * stumm.
 */
.upl-bereich-aus {
    margin: 0 0 0.7rem;
    font-size: 0.87rem;
    font-weight: 600;
    color: var(--text-normal);
}

.upl-feldzeile {
    margin-bottom: 0.8rem;
    padding-bottom: 0.6rem;
    border-bottom: 1px dashed var(--rand);
}

.upl-feldzeile:last-child { border-bottom: 0; }

/* Eine Zeile der Maske mit einer Schaltfläche daneben – „Slot 3" und
   „Entfernen". Dasselbe Raster wie am Dateikopf: Die Schaltfläche steht rechts
   und wird nicht von einer langen Bezeichnung verdrängt. `align-items: end`
   setzt sie auf die Höhe des Eingabefeldes und nicht der Beschriftung darüber. */
.upl-zeilenpaar {
    display: grid;
    grid-template-columns: 1fr auto;
    align-items: end;
    gap: 0.6rem;
}

.upl-zeilenpaar .formular-zeile { min-width: 0; }

/* Auf dem Telefon stapelt auch diese Zeile – dieselbe Entscheidung und dieselbe
   Bedingung wie bei `.upl-sammelzeile` und `.upl-stueck-kopf` weiter oben.
   Sie steht nur deshalb hier unten und nicht dort oben: Eine Medienabfrage
   bringt keine höhere Spezifität mit, und `.upl-zeilenpaar` wird erst über
   dieser Zeile gesetzt. Vor der Regel, die sie zurücknimmt, wäre sie eine
   Zeile ohne Wirkung. */
@media (max-width: 640px) {
    .upl-zeilenpaar { grid-template-columns: 1fr; }
}

/* Bei einer Zusatzangabe ist die Zeile vier Felder hoch; ihre Schaltfläche steht
   deshalb darunter und rechts – neben dem ersten Feld sähe sie aus, als gehörte
   sie zu ihm. */
.upl-zeile-aktion {
    display: flex;
    justify-content: flex-end;
}

.upl-zeile-weg { padding: 0.25rem 0.6rem; font-size: 0.82rem; }

.upl-zeile-mehr { margin: 0 0 1.2rem; }

.upl-platzhalter {
    /* Die Liste steht seit der Verbreiterung in Spalten, solange Platz ist –
       `auto-fill` und keine Medienabfrage, wie im ganzen Abschnitt. Vierzehn
       Platzhalter untereinander waren auf 1100 px eine sehr lange Spalte neben
       sehr viel Weiß. */
    display: grid;
    /* `min()` in der Mindestbreite ist die Zeile, ohne die diese Liste auf
       einem 320-px-Gerät die ganze Seite zur Seite schöbe: `auto-fill` mit
       einer festen Mindestbreite schrumpft **nicht** unter sie, und 18 rem sind
       mehr, als dort überhaupt zur Verfügung steht. Im Browser gemessen. */
    grid-template-columns: repeat(auto-fill, minmax(min(18rem, 100%), 1fr));
    gap: 0 1.2rem;
    margin: 0.2rem 0 0.6rem;
    padding-left: 1.1rem;
    font-size: 0.85rem;
    color: var(--text-mittel);
}

.upl-platzhalter li { margin-bottom: 0.2rem; }

.upl-vorschau {
    list-style: none;
    margin: 0 0 1rem;
    padding: 0;
}

.upl-vorschau li {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.6rem;
    padding: 0.45rem 0.7rem;
    border: 1px solid var(--rand);
    border-radius: 8px;
    margin-bottom: 0.4rem;
    background: var(--flaeche-still);
    overflow-wrap: anywhere;
}

.upl-vorschau-slot {
    min-width: 8rem;
    font-size: 0.85rem;
    color: var(--text-leise);
}

/* ── Die Kontrollansicht vor dem Übergeben (Nacharbeit zu #206) ─────────────
 *
 * Martins Abnahmepunkt 9: Die Zusammenfassung war „inhaltlich richtig, aber als
 * ein langer Fließtext mit `Teil 1 …, Teil 2 …` zu gedrängt". Jetzt sind es drei
 * Blöcke – Dateien, Zusatzangaben, MP3-Verarbeitung –, und in jedem steht je
 * Sache eine Zeile.
 *
 * Wieder `auto-fill` statt einer Medienabfrage: Am Schreibtisch stehen die drei
 * Blöcke nebeneinander, auf dem Telefon untereinander, und die Entscheidung
 * darüber trifft der Platz und keine Tabelle von Umschaltpunkten. */
.upl-pruef-titel {
    margin: 0 0 0.5rem;
    font-size: 0.95rem;
    color: var(--text-stark);
}

.upl-pruefblick {
    display: grid;
    /* Dieselbe `min()`-Klammer wie bei der Platzhalterliste, aus demselben
       Grund: Unterhalb von 15 rem gibt es eine Spalte, und die ist so breit wie
       der Platz und nicht wie die Vorgabe. */
    grid-template-columns: repeat(auto-fill, minmax(min(15rem, 100%), 1fr));
    gap: 0.8rem;
    margin-bottom: 0.9rem;
}

.upl-pruef-block {
    min-width: 0;
    padding: 0.6rem 0.75rem;
    border: 1px solid var(--rand);
    border-radius: 10px;
    background: var(--flaeche-still);
}

.upl-pruef-block h5 {
    margin: 0 0 0.4rem;
    font-size: 0.82rem;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--text-leise);
}

.upl-pruef-liste {
    list-style: none;
    margin: 0;
    padding: 0;
    font-size: 0.88rem;
}

.upl-pruef-liste li {
    display: flex;
    flex-direction: column;
    gap: 0.05rem;
    padding: 0.25rem 0;
    border-top: 1px solid var(--rand);
}

.upl-pruef-liste li:first-child { border-top: 0; }

.upl-pruef-sache { color: var(--text-leise); font-size: 0.8rem; }

.upl-pruef-wert { color: var(--text-normal); overflow-wrap: anywhere; }

/* Was noch fehlt, steht leiser da – aber es steht da. Ein weggelassener Eintrag
   sähe aus wie ein erledigter. */
.upl-pruef-offen { color: var(--text-leise); font-style: italic; }

/* Die Bestätigung ist von der Kontrollansicht darüber und den Hinweisen darunter
   sichtbar getrennt – derselbe Abnahmepunkt, letzter Satz. */
.upl-riegel {
    margin: 0 0 0.6rem;
    padding: 0.6rem 0.75rem;
    border: 1px solid var(--rand-stark);
    border-radius: 10px;
    background: var(--flaeche-karte);
}

.upl-riegel .checkbox-label { margin: 0; }

/* ── Die Zieldateinamen direkt vor dem Häkchen (Issue #486) ──────────────────
 *
 * Je Datei eine Zuordnung „Original → Zieldateiname". Der Platz entscheidet über
 * das Nebeneinander: Am Schreibtisch stehen Original und Ziel in zwei Spalten,
 * auf dem Telefon untereinander (`auto-fit` mit `min()`, wie bei der
 * Kontrollansicht). Lange und sperrige Namen brechen um (`overflow-wrap`) und
 * schieben die Seite nie in die Breite. Der Status steht immer auch als Wort da
 * – „unverändert", „umbenannt", „noch nicht bestimmbar" –, die Farbe verstärkt
 * ihn nur. */
.upl-zielnamen {
    min-width: 0;
    margin: 0 0 0.9rem;
    padding: 0.6rem 0.75rem;
    border: 1px solid var(--rand);
    border-radius: 10px;
    background: var(--flaeche-still);
}

.upl-zielnamen .upl-pruef-titel { margin-bottom: 0.3rem; }

.upl-ziel-status {
    margin: 0 0 0.5rem;
    font-size: 0.85rem;
    color: var(--text-leise);
}

.upl-ziel-liste {
    list-style: none;
    margin: 0 0 0.5rem;
    padding: 0;
    font-size: 0.9rem;
}

.upl-ziel-zeile {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: 0.2rem 0.9rem;
    padding: 0.45rem 0 0.45rem 0.5rem;
    border-top: 1px solid var(--rand);
    border-left: 4px solid var(--rand-stark);
    min-width: 0;
}

.upl-ziel-zeile:first-child { border-top: 0; }

.upl-ziel-zeile[data-status="ok"] { border-left-color: var(--erfolg-kraeftig); }
.upl-ziel-zeile[data-status="offen"] { border-left-color: var(--warnung-kraeftig); }
.upl-ziel-zeile[data-status="fehler"] { border-left-color: var(--fehler-kraeftig); }

.upl-ziel-teil {
    display: flex;
    flex-direction: column;
    gap: 0.05rem;
    min-width: 0;
}

.upl-ziel-etikett { color: var(--text-leise); font-size: 0.78rem; }

.upl-ziel-text {
    min-width: 0;
    color: var(--text-normal);
    overflow-wrap: anywhere;
    word-break: break-word;
}

.upl-ziel-name {
    font-weight: 600;
    color: var(--text-stark);
    overflow-wrap: anywhere;
    word-break: break-word;
    white-space: normal;
}

.upl-ziel-leer { font-style: italic; color: var(--text-leise); }

.upl-ziel-marke {
    display: inline-block;
    padding: 0 0.4rem;
    border: 1px solid var(--rand-stark);
    border-radius: 999px;
    font-size: 0.74rem;
    color: var(--text-leise);
    white-space: nowrap;
}

/* Schreibtisch: Original, Pfeil und Ziel nebeneinander. */
.upl-ziel-zeile {
    grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
    align-items: start;
}

.upl-ziel-pfeil {
    color: var(--text-leise);
    justify-self: center;
    padding-top: 1.1rem;
    width: 1.2em;
    text-align: center;
}

/* Telefon (der vorhandene Breakpoint): Original oben, Pfeil nach unten,
   Ziel darunter. */
@media (max-width: 640px) {
    .upl-ziel-zeile { grid-template-columns: minmax(0, 1fr); }
    .upl-ziel-pfeil { transform: rotate(90deg); justify-self: start; padding-top: 0; }
}

.upl-ziel-satz {
    grid-column: 1 / -1;
    font-size: 0.84rem;
    color: var(--text-normal);
    overflow-wrap: anywhere;
}

.upl-ziel-zeile[data-status="offen"] .upl-ziel-satz { color: var(--warnung-text); }
.upl-ziel-zeile[data-status="fehler"] .upl-ziel-satz { color: var(--fehler-text); }

/* Der Bereich „Upload-Typen“ in der Benutzermaske. Ausgegraut heißt hier
   wirklich ausgegraut: Die Häkchen sind `disabled`, und der Grund steht
   darüber – nicht als Farbe, sondern als Satz. */
.upl-freigaben {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
    gap: 0.35rem 0.9rem;
    margin: 0.4rem 0 0.6rem;
}

/* Und derselbe Zustand für den Block „Guder Draht“ in derselben Maske
   (Issue #215). Ausdrücklich dieselbe Regel und keine eigene Optik: Es ist
   dasselbe Berechtigungsmuster – erst die Rolle, dann die Person –, und der
   Auftrag verlangt „Optik und Verhalten möglichst an den bereits gut
   funktionierenden gesperrten Upload-Typen-Bereich anlehnen". Gedämpft und
   nicht rot: Es ist ein erklärter inaktiver Zustand und kein Fehler, und der
   Grund steht als Satz darüber. Die Dämpfung wirkt in Hell und Dunkel gleich,
   weil sie keine Farbe setzt, sondern die vorhandenen durchscheinen lässt. */
.upl-freigaben-aus,
.gud-draht-aus { opacity: 0.55; }

/* `.hinweis` setzt `display: block` und schlägt damit das eingebaute
   `[hidden] { display: none }` des Browsers – ohne diese Zeile stünde der
   Satz auch bei einer Rolle mit Chatzugang da. */
.gud-draht-grund[hidden] { display: none; }

.upl-freigabe-aus-wort {
    font-size: 0.8rem;
    color: var(--text-leise);
}

/* ── Zwei Build-Worker und serielles Integrations-Gate ──────────────────── */

/*
 * Der Betriebszustand steht einmal über dem verschiebbaren Raster. Er ist
 * seit Issue #254 wie jeder sichtbare Bereich einzeln anzuordnen.
 */
.zen-build-ampel {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.45rem 0.65rem;
    margin: 0;
    padding: 0.6rem 0.85rem;
    border: 1px solid var(--rand);
    border-radius: 8px;
    background: var(--flaeche-karte);
    color: var(--text-normal);
    font-size: 0.9rem;
}

.zen-build-ampel .zen-kachel-ordnen { flex-basis: 100%; }

.zen-build-ampel-marke {
    width: 0.7rem;
    height: 0.7rem;
    flex: 0 0 0.7rem;
    border-radius: 50%;
    background: var(--rand-still);
}

.zen-build-ampel-bereit .zen-build-ampel-marke {
    background: var(--erfolg-kraeftig);
}
.zen-build-ampel-laeuft .zen-build-ampel-marke,
.zen-build-ampel-eingeschraenkt .zen-build-ampel-marke {
    background: var(--warnung-kraeftig);
}
.zen-build-ampel-fehler .zen-build-ampel-marke {
    background: var(--fehler-kraeftig);
}

.zen-build-ampel-alter {
    color: var(--fehler-text);
    font-weight: 600;
}

/* Beide Worker sprechen dieselbe Statussprache wie die bisherige Laufkarte:
   Farbe an der Kante, Text im Inhalt – nie Farbe allein. */
.zen-worker,
.zen-integration {
    border-left: 4px solid var(--rand-still);
}

.zen-worker-bereit,
.zen-worker-fertig,
.zen-integration-bereit,
.zen-integration-fertig {
    border-left-color: var(--erfolg-kraeftig);
}

.zen-worker-laeuft,
.zen-integration-laeuft {
    border-left-color: var(--warnung-kraeftig);
}

.zen-worker-pausiert,
.zen-worker-wartet,
.zen-integration-pausiert,
.zen-integration-wartet {
    border-left-color: var(--warnung-kraeftig);
}

.zen-worker-fehler,
.zen-integration-fehler {
    border-left-color: var(--fehler-kraeftig);
}

.zen-worker-kopf {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.75rem;
}

.zen-worker-kopf .zen-abschnitt-titel { margin-bottom: 0; }

.zen-worker-status {
    flex: 0 0 auto;
    padding: 0.18rem 0.5rem;
    border: 1px solid var(--rand);
    border-radius: 999px;
    font-size: 0.76rem;
    font-weight: 700;
    color: var(--text-normal);
    background: var(--flaeche-sanft);
}

.zen-worker-status-bereit,
.zen-worker-status-fertig {
    color: var(--erfolg-text);
}

.zen-worker-status-laeuft,
.zen-worker-status-pausiert,
.zen-worker-status-wartet {
    color: var(--warnung-text);
}

.zen-worker-status-fehler { color: var(--fehler-text); }

.zen-worker-provider {
    margin: 0.35rem 0 0;
    color: var(--text-stark);
}

.zen-worker-auftrag {
    margin: 0.35rem 0 0;
    overflow-wrap: anywhere;
}

.zen-worker-hinweis {
    margin: 0.55rem 0 0;
    padding: 0.45rem 0.6rem;
    border-left: 3px solid var(--warnung-kraeftig);
    border-radius: 0 4px 4px 0;
    background: var(--warnung-flaeche-blass);
    color: var(--text-normal);
    font-size: 0.88rem;
}

.zen-worker-schritte {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: 0;
    margin: 0.8rem 0 0;
    padding: 0;
    list-style: none;
    border-top: 1px solid var(--rand);
}

.zen-worker-schritt {
    display: grid;
    grid-template-columns: 1.2rem 1.6rem minmax(0, 1fr);
    align-items: baseline;
    gap: 0.35rem;
    min-width: 0;
    padding: 0.28rem 0.2rem;
    border-bottom: 1px solid var(--rand);
    font-size: 0.82rem;
}

.zen-worker-schritt-marke::before {
    content: "○";
    color: var(--text-leise);
}

.zen-worker-schritt-erledigt .zen-worker-schritt-marke::before {
    content: "✓";
    color: var(--erfolg-kraeftig);
}

.zen-worker-schritt-aktuell {
    background: var(--warnung-flaeche-blass);
}

.zen-worker-schritt-aktuell .zen-worker-schritt-marke::before {
    content: "◐";
    color: var(--warnung-kraeftig);
}

.zen-worker-schritt-fehler {
    background: var(--fehler-flaeche-zart);
}

.zen-worker-schritt-fehler .zen-worker-schritt-marke::before {
    content: "✗";
    color: var(--fehler-kraeftig);
}

.zen-worker-schritt-nummer {
    color: var(--text-leise);
    font-variant-numeric: tabular-nums;
}

.zen-worker-schritt-titel {
    min-width: 0;
    overflow-wrap: anywhere;
    color: var(--text-normal);
}

.zen-worker-schritt-erledigt .zen-worker-schritt-titel {
    color: var(--text-leise);
}

.zen-worker-meta {
    display: flex;
    flex-wrap: wrap;
    gap: 0.35rem 0.8rem;
    margin-top: 0.65rem;
    color: var(--text-leise);
    font-size: 0.76rem;
}

.zen-worker-meta code {
    overflow-wrap: anywhere;
    color: inherit;
}

.zen-worker-meta [data-zen-auftragslaufzeit] {
    display: inline-block;
    min-width: 5ch;
    font-variant-numeric: tabular-nums;
    font-feature-settings: "tnum" 1;
    text-align: left;
}




.zen-build-kosten {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.3rem 0.85rem;
    margin-top: 0.65rem;
    padding-top: 0.55rem;
    border-top: 1px solid var(--rand);
    color: var(--text-leise);
    font-size: 0.76rem;
}

.zen-build-kosten strong {
    color: var(--text-normal);
    font-size: 0.78rem;
}

@media (min-width: 900px) {
    .zen-gate-schritte {
        grid-template-columns: repeat(2, minmax(0, 1fr));
        column-gap: 1rem;
    }
}


/* Belastbarer Unterfortschritt der neuen Build-/Integrationsschritte. */
.zen-build-fortschritt {
    min-width: 0;
    overflow-wrap: anywhere;
    margin-top: 0.55rem;
    padding-top: 0.5rem;
    border-top: 1px solid var(--rand);
    font-size: 0.78rem;
}
.zen-build-fortschritt > strong {
    display: block;
    margin-bottom: 0.3rem;
    color: var(--text-normal);
}
.zen-build-fortschritt-balken {
    height: 6px;
    overflow: hidden;
    border: 1px solid var(--rand);
    border-radius: 999px;
    background: var(--flaeche-sanft);
}
.zen-build-fortschritt-balken > span {
    display: block;
    height: 100%;
    background: var(--erfolg-kraeftig);
}
.zen-build-fortschritt-zahlen {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    gap: 0.2rem 0.6rem;
    margin-top: 0.25rem;
    color: var(--text-leise);
    font-variant-numeric: tabular-nums;
}

/* Issue #322: Beginn, Dauer und geschätzter Zeitfortschritt des seriellen
 * Auftrags. Die Schätzung trägt eine eigene Farbe, damit sie nicht wie der
 * gemessene Phasenbalken darunter aussieht. */
.zen-serial-zeit > p { margin: 0.25rem 0 0; overflow-wrap: anywhere; }
.zen-serial-beginn { font-weight: 600; }
.zen-serial-stationen {
    display: flex;
    flex-wrap: wrap;
    gap: 0.3rem;
    margin: 0.55rem 0 0;
    padding: 0;
    list-style: none;
    font-size: 0.78rem;
}
.zen-serial-station {
    padding: 0.15rem 0.5rem;
    border: 1px solid var(--rand);
    border-radius: 999px;
    color: var(--text-leise);
    overflow-wrap: anywhere;
}
.zen-serial-station-aktuell {
    border-color: var(--info-marke);
    color: var(--text-normal);
    font-weight: 600;
}
.zen-serial-schaetzbalken > span { background: var(--info-marke); }
.zen-serial-ueberzogen > span,
.zen-serial-schaetzung-pausiert .zen-serial-schaetzbalken > span,
.zen-serial-schaetzung-unsicher .zen-serial-schaetzbalken > span { background: var(--warnung-kraeftig); }
.zen-serial-schaetzung-fertig .zen-serial-schaetzbalken > span { background: var(--erfolg-kraeftig); }
.zen-serial-referenz { margin: 0.25rem 0 0; color: var(--text-leise); }
.zen-serial-schaetzung p,
.zen-serial-phasenbalken p { margin-bottom: 0; }
/* Issue #423: Außenphase und innerer Prüflauf. Lange Phasen- und Suitennamen
 * brechen um statt abgeschnitten zu werden; Farben nur aus den Tokens. */
.zen-innenlauf {
    min-width: 0;
    overflow-wrap: anywhere;
    margin-top: 0.55rem;
    padding-top: 0.5rem;
    border-top: 1px solid var(--rand);
    font-size: 0.78rem;
}
.zen-innenlauf p { margin: 0.25rem 0 0; }
.zen-innenlauf .zen-build-fortschritt-balken { margin-top: 0.35rem; }
.zen-innenlauf-aussen { color: var(--text-leise); }
.zen-innenlauf-titel { color: var(--text-normal); }
.zen-innenlauf-werte {
    margin: 0.3rem 0 0;
    padding-left: 1.1rem;
}
.zen-innenlauf-werte li { margin: 0.15rem 0; }
.zen-innenlauf-werte code { overflow-wrap: anywhere; white-space: normal; }
.zen-innenlauf-still { color: var(--warnung-text); }
.zen-innenlauf-veraltet .zen-build-fortschritt-balken > span,
.zen-innenlauf-abgebrochen .zen-build-fortschritt-balken > span { background: var(--warnung-kraeftig); }
.zen-innenlauf-laeuft .zen-build-fortschritt-balken > span { background: var(--info-marke); }

/* Issue #343: Claude-Kontingent als zwei Tankanzeigen (seit Issue #366 der Rest:
 * 0 % leer links, 100 % voll rechts) und eine Reset-Uhr, deren Umlauf die Fensterlänge ist. Ruhig:
 * keine Übergänge oder Animationen – der Uhrzeiger springt höchstens einmal je
 * Sekunde um einen Bruchteil eines Grades. Farben nur aus den Bestandstokens. */
.zen-kontingent {
    margin: 0.6rem 0 0;
    padding: 0.6rem 0.7rem;
    border: 1px solid var(--rand);
    border-radius: 0.55rem;
}
.zen-kontingent-pause { border-color: var(--warnung-kraeftig); }
.zen-kontingent-pruefen { border-color: var(--info-marke); }
.zen-kontingent-verfuegbar { border-color: var(--erfolg-kraeftig); }
.zen-kontingent p { margin: 0.25rem 0 0; overflow-wrap: anywhere; }
.zen-kontingent-titel { margin-top: 0; }
.zen-kontingent-hinweis { color: var(--text-leise); font-size: 0.85rem; }
/* Issue #398: Kopf mit Statuspille, drei gleichwertige Bereiche, dezente Fußzeile. */
.zen-kontingent-kopf {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 0.3rem 0.6rem;
}
.zen-kontingent-kopf .zen-kontingent-titel { margin: 0; }
.zen-kontingent-satz { font-size: 0.9rem; }
.zen-kontingent-bestaetigt { color: var(--text-leise); }
.zen-kontingent-instrumente {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(11rem, 100%), 1fr));
    gap: 1rem 1.5rem;
    margin-top: 0.7rem;
}
.zen-tank,
.zen-uhr {
    display: flex;
    flex-direction: column;
    align-items: center;
    min-width: 0;
    margin: 0;
}
.zen-tank svg,
.zen-uhr svg { display: block; width: 100%; max-width: 10.5rem; height: auto; }
.zen-uhr svg { max-width: 8.5rem; }
.zen-tank figcaption,
.zen-uhr figcaption {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.2rem;
    width: 100%;
    margin-top: 0.4rem;
    text-align: center;
    font-size: 0.85rem;
    overflow-wrap: anywhere;
}
.zen-tank-titel,
.zen-uhr figcaption > strong { color: var(--text-stark); font-size: 0.9rem; }
.zen-tank-wert,
.zen-uhr-digital {
    color: var(--text-normal);
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    font-feature-settings: "tnum" 1;
}
/* Restzahl hervorgehoben, „Rest:“ und „verfügbar“ klein; die Zeile bleibt Fließtext (innerText wie zuvor). */
.zen-tank-gross { font-size: 1.75rem; line-height: 1.15; font-weight: 700; }
.zen-tank-vor,
.zen-tank-einheit { color: var(--text-leise); font-size: 0.8rem; font-weight: 400; }
.zen-tank-verbrauch { color: var(--text-leise); font-size: 0.8rem; font-variant-numeric: tabular-nums; }
.zen-uhr-digital { font-size: 1.5rem; line-height: 1.15; min-height: 1.15em; }
.zen-tank-bindend-text,
.zen-tank-warnung { color: var(--warnung-text); font-weight: 600; }
/* Statuspillen: Text trägt die Aussage, Farbe unterstützt sie nur. */
.zen-pille {
    display: inline-block;
    padding: 0.05rem 0.55rem;
    border-radius: 999px;
    background: var(--flaeche-sanft);
    color: var(--text-normal);
    font-size: 0.78rem;
    font-weight: 600;
}
.zen-pille[hidden] { display: none; }
.zen-pille-pause,
.zen-pille-warn,
.zen-pille-bindend { background: var(--warnung-flaeche); color: var(--warnung-text); }
.zen-pille-pruefen { background: var(--info-flaeche); color: var(--info-text); }
.zen-pille-verfuegbar { background: var(--erfolg-flaeche); color: var(--erfolg-text); }
.zen-tank-leer .zen-pille-warn { background: var(--fehler-flaeche); color: var(--fehler-text); }
/* Zusatzzeilen: ruhig, linksbündig, mit feiner Trennlinie statt eigener Umrandung. */
.zen-meta {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
    width: 100%;
    margin-top: 0.3rem;
    padding-top: 0.35rem;
    border-top: 1px solid var(--rand);
    text-align: left;
    color: var(--text-leise);
    font-size: 0.8rem;
}
.zen-meta-label { color: var(--text-normal); font-weight: 600; }
.zen-meta-label::after { content: ":"; }
.zen-uhr-hinweis { color: var(--text-normal); font-size: 0.8rem; text-align: left; width: 100%; }
.zen-kontingent-fuss {
    margin-top: 0.8rem;
    padding-top: 0.5rem;
    border-top: 1px solid var(--rand);
    color: var(--text-leise);
    font-size: 0.8rem;
}
.zen-kontingent-fuss p { font-size: 0.8rem; }
.zen-kontingent-pruefung { color: var(--text-normal); }
/* Issue #483: Erläuterungsgruppen der Laufkachel, Muster wie „Skala und Erläuterung“. */
.zen-lauf-details { margin-top: 0.25rem; overflow-wrap: anywhere; }
.zen-lauf-details > summary { cursor: pointer; color: var(--text-leise); font-size: 0.85rem; padding: 0.15rem 0; }
.zen-lauf-details > summary:focus-visible { outline: 2px solid currentColor; outline-offset: 2px; }
/* Die Laptopansicht spart Außenabstände statt Schrift oder Inhalt: Auch in
   einer halben Kachel bleiben Instrumente und Prüffortschritt zusammen sichtbar. */
@media (min-width: 900px) {
    .zen-seriell [data-zen-kachel="jetzt"] .zen-lauf-details { margin-top: 0; }
    .zen-seriell [data-zen-kachel="jetzt"] .zen-lauf-details > summary { padding: 0; }
    .zen-seriell [data-zen-kachel="jetzt"] .zen-auftrag .zen-build-fortschritt { margin-top: 0.4rem; padding-top: 0.4rem; }
    .zen-seriell [data-zen-kachel="jetzt"] .zen-serial-stationen { margin-top: 0.25rem; }
    .zen-seriell [data-zen-kachel="jetzt"] .zen-kontingent { margin-top: 0.25rem; padding-top: 0.4rem; padding-bottom: 0.4rem; }
    .zen-seriell [data-zen-kachel="jetzt"] .zen-kontingent-instrumente { margin-top: 0.3rem; }
    .zen-seriell [data-zen-kachel="jetzt"] .zen-kontingent-fuss { margin-top: 0.4rem; padding-top: 0.3rem; }
}

/* Issue #526: „Aktueller Auftrag“ als zweite Innenkachel unter dem Kontingent –
 * gleiche Rundung, Randfarbe und Innenabstände, keine festen Höhen. Titel und
 * Modellkennzeichnung brechen um; die beiden Fortschritte sind je ein eigener,
 * gerahmter Abschnitt (Schätzung und Messung werden nie vermischt). */
.zen-auftrag {
    min-width: 0;
    margin: 0.6rem 0 0;
    padding: 0.6rem 0.7rem;
    border: 1px solid var(--rand);
    border-radius: 0.55rem;
    overflow-wrap: anywhere;
}
.zen-auftrag > p,
.zen-auftrag > .zen-serial-zeit { margin-top: 0.3rem; margin-bottom: 0; }
.zen-auftrag-ueberschrift { margin: 0; color: var(--text-stark); font-size: 0.9rem; font-weight: 700; }
.zen-auftrag .zen-worker-auftrag {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.15rem 0.5rem;
    margin: 0.3rem 0 0;
    color: var(--text-stark);
}
.zen-auftrag-nummer { font-weight: 700; font-variant-numeric: tabular-nums; }
.zen-auftrag-titel { flex: 1 1 12rem; min-width: 0; }
.zen-auftrag .zen-worker-auftrag > .zen-modell { margin-left: 0; }
.zen-auftrag > .zen-worker-auftrag + p { color: var(--text-leise); font-size: 0.85rem; }
.zen-auftrag .zen-serial-zeit { font-size: 0.85rem; }
.zen-auftrag .zen-serial-beginn { font-weight: 600; color: var(--text-normal); }
.zen-auftrag .zen-serial-stationen { margin-top: 0.4rem; }
.zen-auftrag .zen-serial-station-aktuell { background: var(--info-flaeche); color: var(--info-text); }
.zen-auftrag .zen-build-fortschritt,
.zen-auftrag .zen-innenlauf {
    margin-top: 0.5rem;
    padding: 0.4rem 0.55rem;
    border: 1px solid var(--rand);
    border-radius: 0.4rem;
    font-size: 0.85rem;
}
.zen-auftrag .zen-innenlauf { font-size: 0.8rem; }
.zen-auftrag-historie { display: inline-block; margin-top: 0.5rem; font-size: 0.85rem; }
/* Laptopbreite: Die Innenkachel spart Abstände statt Inhalt, damit Tanks und
 * gemessener Prüffortschritt weiter gemeinsam im ersten Bildschirm stehen.
 * Beginn und „Laufzeit und Versuche“ teilen sich eine Zeile. */
@media (min-width: 900px) {
    .zen-seriell [data-zen-kachel="jetzt"] .zen-auftrag { margin-top: 0.25rem; padding: 0.35rem 0.6rem; }
    .zen-seriell [data-zen-kachel="jetzt"] .zen-auftrag-ueberschrift { line-height: 1.2; }
    .zen-seriell [data-zen-kachel="jetzt"] .zen-auftrag > p,
    .zen-seriell [data-zen-kachel="jetzt"] .zen-auftrag > .zen-serial-zeit { margin-top: 0.2rem; }
    .zen-seriell [data-zen-kachel="jetzt"] .zen-auftrag .zen-serial-zeit {
        display: flex;
        flex-wrap: wrap;
        align-items: baseline;
        gap: 0 0.9rem;
    }
    .zen-seriell [data-zen-kachel="jetzt"] .zen-auftrag .zen-serial-zeit > p { margin: 0; }
    .zen-seriell [data-zen-kachel="jetzt"] .zen-auftrag .zen-serial-zeit > details { margin: 0; flex: 1 1 auto; min-width: 0; }
    .zen-seriell [data-zen-kachel="jetzt"] .zen-auftrag .zen-serial-stationen { margin-top: 0.3rem; }
    .zen-seriell [data-zen-kachel="jetzt"] .zen-auftrag .zen-build-fortschritt,
    .zen-seriell [data-zen-kachel="jetzt"] .zen-auftrag .zen-innenlauf { margin-top: 0.3rem; padding: 0.2rem 0.55rem; }
    .zen-seriell [data-zen-kachel="jetzt"] .zen-auftrag-historie { margin-top: 0.3rem; }
}

.zen-kontingent-details { margin-top: 0.4rem; }
.zen-kontingent-details summary { cursor: pointer; color: var(--text-normal); }
.zen-kontingent-details ul { margin: 0.3rem 0 0; padding-left: 1.1rem; }
.zen-tank-bogen { fill: none; stroke: var(--flaeche-sanft); stroke-width: 9; }
.zen-tank-fuellung { fill: none; stroke: var(--info-marke); stroke-width: 9; }
.zen-tank-knapp .zen-tank-fuellung { stroke: var(--warnung-kraeftig); }
.zen-tank-leer .zen-tank-fuellung { stroke: var(--fehler-kraeftig); }
.zen-tank-ueberholt .zen-tank-fuellung { stroke: var(--rand); }
.zen-tank-marke,
.zen-uhr-marke { stroke: var(--text-leise); stroke-width: 1.5; }
.zen-tank-zahl,
.zen-uhr-zahl { fill: var(--text-leise); font-size: 8px; }
.zen-tank-zeiger,
.zen-uhr-zeiger { stroke: var(--text-normal); stroke-width: 3; stroke-linecap: round; }
.zen-tank-ueberholt .zen-tank-zeiger { stroke: var(--text-leise); stroke-dasharray: 4 3; }
.zen-tank-nabe,
.zen-uhr-nabe { fill: var(--text-normal); }
.zen-uhr-ring { fill: none; stroke: var(--rand); stroke-width: 2; }
.zen-uhr-zeiger { stroke: var(--info-marke); }
.zen-uhr-erreicht .zen-uhr-zeiger { stroke: var(--warnung-kraeftig); }
.zen-uhr-erreicht-text { color: var(--warnung-text); font-weight: 600; }
/* Ohne laufende Pause: Zifferblatt ohne Zeiger, Wert ausdrücklich „nicht verfügbar“. */
.zen-uhr-leer .zen-uhr-ring { stroke-dasharray: 4 4; }
.zen-uhr-leer .zen-uhr-nabe { fill: var(--text-leise); }
.zen-uhr-leer-wert { color: var(--text-leise); font-size: 1rem; }

/*
 * Issue #511: Die Kachel „Wartet auf Claude“ trägt zwei getrennte Karten – oben die
 * Warteschlange, unten die Speicherkachel mit vier kleinen Tankanzeigen. Die Kachel
 * selbst (`.zen-karte-gruppe`) ist nur noch die Rasterzelle; Rahmen und Fläche tragen
 * die beiden Teilkarten.
 *
 * Mobil und in einer vollen Kachel bestimmt der Inhalt die Höhe, die Warteschlange
 * scrollt ab einer Grenze in sich. Am Schreibtisch (ab 900 px) folgt die Zelle der
 * Höhe der Nachbarkachel (Claude Code): Die Speicherkarte braucht nur die Höhe ihres
 * Inhalts und sitzt unten, die Warteschlange nimmt den Rest darüber und scrollt in sich. `contain: size`
 * nimmt der Warteschlange ihren Inhalt aus der Höhenrechnung des Rasters – eine lange
 * Liste dehnt die Zeile nicht, sie scrollt. Nichts hängt am Browserfenster.
 */
.zen-karte-gruppe {
    display: flex;
    flex-direction: column;
    gap: var(--zen-kachel-abstand, 1rem);
    min-width: 0;
}
.zen-karte-gruppe > .zen-kachel-ordnen {
    margin: 0;
    padding: 0.5rem 0.7rem;
    border: 1px dashed var(--rand-stark);
    border-radius: 10px;
    background: var(--flaeche-karte);
}
.zen-karte-gruppe > [data-zen-frisch-teil] {
    display: flex;
    flex-direction: column;
    gap: var(--zen-kachel-abstand, 1rem);
    flex: 1 1 auto;
    min-height: 0;
    min-width: 0;
}
.zen-teilkarte { min-width: 0; box-sizing: border-box; }
.zen-queue-karte { display: flex; flex-direction: column; min-height: 0; }
.zen-queue-karte .zen-queue { min-height: 0; max-height: 28rem; overflow-y: auto; overflow-x: hidden; }
.zen-queue-karte .zen-queue-hinweis,
.zen-queue-karte .zen-queue-meldung { flex: 0 0 auto; }

.zen-speicherkarte { flex: 0 0 auto; }
.zen-speicherkarte .zen-abschnitt-titel { margin-bottom: 0.4rem; }
.zen-speicher-tanks {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 0.7rem 0.8rem;
}
.zen-tank-klein svg { max-width: 6.5rem; }
.zen-tank-klein figcaption { gap: 0.1rem; margin-top: 0.2rem; font-size: 0.75rem; overflow-wrap: anywhere; }
.zen-tank-klein .zen-tank-titel { font-size: 0.8rem; }
.zen-tank-klein .zen-tank-gross { font-size: 1.15rem; }
.zen-tank-klein .zen-tank-verbrauch { font-size: 0.72rem; }
.zen-tank-klein .zen-pille { font-size: 0.7rem; padding: 0 0.45rem; }
.zen-tank-messzeit { color: var(--text-leise); font-size: 0.7rem; }
.zen-tank-fehlt .zen-tank-bogen { stroke-dasharray: 4 4; }
.zen-tank-fehlt .zen-tank-wert { color: var(--warnung-text); }
.zen-speicher-fuss { margin: 0.5rem 0 0; color: var(--text-leise); font-size: 0.72rem; line-height: 1.3; }
@media (min-width: 900px) {
    /* Vier nebeneinander, solange jede Anzeige mindestens 7,5 rem bekommt; bei schmaler
       Spalte oder vergrößerter Schrift ordnet sich der Rest ohne Abschneiden darunter an. */
    .zen-speicher-tanks { grid-template-columns: repeat(auto-fit, minmax(7.5rem, 1fr)); gap: 0.5rem; }
    .zen-raster > .zen-karte-gruppe:not(.zen-voll) .zen-queue-karte {
        flex: 1 1 0;
        min-height: 12rem;
        contain: size;
    }
    .zen-raster > .zen-karte-gruppe:not(.zen-voll) .zen-queue-karte .zen-queue {
        flex: 1 1 auto;
        max-height: none;
    }
}

/*
 * Issue #327: Die Abschlussliste scrollt ab einer Höchsthöhe in sich, statt die
 * Seite unbegrenzt zu verlängern. Überschrift, Gesamtzahl und Verweis stehen
 * außerhalb. Die Grenze ist nur ein Maximum: Kurze und leere Listen behalten
 * ihre Inhaltshöhe und – über `overflow-y: auto` – keinen Scrollbalken.
 *
 * In `rem` und nicht in Zeilen: Mit größerer Schrift wächst die Grenze mit, und
 * lange, umbrechende Titel nehmen sich einfach mehr davon. Auf dem Telefon
 * höchstens die halbe kleine Fensterhöhe (`svh`, sonst `vh`), am Schreibtisch
 * Platz für rund sieben gewöhnliche Einträge, aber nie mehr als 70 % des
 * Fensters. Eine einzeilige Zeile misst am Schreibtisch etwa 2,5–2,7 rem
 * (Browserbefund: 26 rem zeigten zehn volle Einträge); 19 rem ergeben sieben
 * volle und einen angeschnittenen. `overscroll-behavior` bleibt bewusst beim Vorgabewert: Am Ende der
 * Liste läuft ein Wischen auf die Seite weiter – keine Scrollfalle.
 * `overflow-anchor: none`, weil `zentrale-frisch.js` die Lesestelle selbst
 * hält; eine zweite Korrektur des Browsers verschöbe sie doppelt.
 */
.zen-abschluesse {
    max-height: 50vh;
    max-height: 50svh;
    margin-top: 0.6rem;
    overflow-x: hidden;
    overflow-y: auto;
    overflow-anchor: none;
    scroll-behavior: auto;
    border-radius: 6px;
}
.zen-abschluesse .zen-liste { margin: 0; }
.zen-abschluesse:focus-visible { outline: 2px solid var(--kontur); outline-offset: 2px; }
.zen-abschluesse .zen-abnahmen-aelter { padding: 0.55rem 0; }
.zen-abschluesse-verweis { margin: 0.55rem 0 0; }

@media (min-width: 900px) {
    .zen-abschluesse { max-height: min(19rem, 70vh); }
}

/* Taktanzeige der neuen Zwei-Worker-Buildstrecke. */
.zen-worker-timer {
    margin: 0.55rem 0 0;
    padding: 0.48rem 0.62rem;
    border: 1px solid var(--rand);
    border-radius: 0.55rem;
    color: var(--text-leise);
    font-size: 0.78rem;
}
.zen-worker-timer strong { display: inline-block; min-width: 5ch; color: var(--text-normal); font-variant-numeric: tabular-nums; font-feature-settings: "tnum" 1; text-align: left; }

/* Dauerhafte Worker-Zuständigkeit an jeder Auftragsnummer (Issue #250). */
.zen-worker-ziel {
    display: inline-flex;
    align-items: center;
    padding: 0.08rem 0.35rem;
    border: 1px solid var(--rand);
    border-radius: 999px;
    color: var(--text-stark);
    font-size: 0.78rem;
    font-weight: 600;
    line-height: 1.35;
    white-space: nowrap;
    vertical-align: baseline;
}

/* W1/W2 am Schreibtisch: gemeinsame Unterkante und Platz für fünf Stationen,
   Testfortschritt und Metadaten; mobil wächst die Höhe mit dem Inhalt. */
.zen-karte.zen-worker {
    box-sizing: border-box;
    height: 27rem;
    min-height: 27rem;
    max-height: 27rem;
    overflow: hidden;
}
@media (max-width: 900px) {
    .zen-karte.zen-worker {
        height: auto;
        min-height: 0;
        max-height: none;
    }
}

/* Die Scrollbar darf die abgerundete Außenkante nicht überdecken. */
.zen-build-inhalt::-webkit-scrollbar { width: 6px; }
.zen-build-inhalt::-webkit-scrollbar-track { background: transparent; }
.zen-build-inhalt::-webkit-scrollbar-thumb {
    background: var(--rand);
    border-radius: 999px;
}

/* Integrator: feste Höhe je Bildschirmbreite, auch im Leerlauf und bei Fehlern.
   Gemessen mit zwölf Stationen, Fortschritt, Auftrag, Hinweisen und Kosten.
   Außergewöhnlich lange Inhalte bleiben innerhalb der Kachel erreichbar. */
.zen-karte.zen-integration {
    box-sizing: border-box;
    height: 53rem;
    min-height: 53rem;
    max-height: 53rem;
    overflow: hidden;
}
/* Die Höhe hängt von der Rasterbreite ab, nie vom wechselnden Inhalt.
   Schmale Desktop-Raster bekommen den gemessenen Platzbedarf; breite
   Raster behalten die kompakte Mindesthöhe. */
.zen-raster:has(> .zen-integration) { container-type: inline-size; }
@supports (height: 1cqw) {
    .zen-karte.zen-integration {
        height: clamp(33rem, calc(76rem - 100cqw), 53rem);
        min-height: clamp(33rem, calc(76rem - 100cqw), 53rem);
        max-height: clamp(33rem, calc(76rem - 100cqw), 53rem);
    }
}
@media (max-width: 900px) {
    .zen-karte.zen-integration {
        height: 46rem;
        min-height: 46rem;
        max-height: 46rem;
    }
}
@media (max-width: 640px) {
    /* Auf dem Handy bestimmt der Inhalt die Höhe; die Seite scrollt. */
    .zen-karte.zen-integration {
        height: auto;
        min-height: 0;
        max-height: none;
    }
    .zen-integration > .zen-build-inhalt {
        height: auto;
        overflow: visible;
        scrollbar-gutter: auto;
    }
}
/* Der Bildlauf gehört ins Innere. Ein Scrollbar-Gutter auf der Außenkachel
   übermalt in Chromium die rechten Rundungen trotz border-radius. */
.zen-build-inhalt {
    height: 100%;
    min-height: 0;
    box-sizing: border-box;
    overflow-y: auto;
    overflow-x: hidden;
    scrollbar-gutter: stable;
    scrollbar-width: thin;
    scrollbar-color: var(--rand) transparent;
}

/* Kompakte Inhalte ohne leere Innenplätze; beide Karten behalten ihre
   gemeinsame äußere Unterkante und eigene verschiebbare Rasterposition. */
.zen-worker .zen-build-inhalt { display: flex; flex-direction: column; gap: 0.4rem; }
.zen-worker .zen-worker-provider { margin: 0; }
.zen-worker-platz { flex: 0 0 auto; min-height: 0; overflow-wrap: anywhere; }
.zen-worker-auftragplatz > p { margin: 0; }
.zen-worker-hinweisplatz .zen-worker-hinweis { margin: 0; line-height: 1.35; }
.zen-worker-statusplatz { min-height: 0; }
.zen-worker-statusplatz .zen-build-fortschritt {
    margin-top: 0.35rem;
    padding-top: 0.25rem;
}
.zen-worker .zen-worker-schritte { flex: 0 0 auto; margin: 0; }
.zen-worker .zen-worker-schritt {
    box-sizing: border-box;
    min-height: 1.65rem;
    align-items: center;
    line-height: 1.2;
    padding-top: 0.2rem;
    padding-bottom: 0.2rem;
}
.zen-worker .zen-worker-meta {
    flex: 0 0 auto;
    display: grid;
    grid-template-columns: minmax(0, 1fr) 5.75rem;
    gap: 0.4rem;
    margin: 0;
    overflow-y: auto;
    overflow-x: hidden;
    line-height: 1.35;
}
.zen-worker-laufzeit {
    text-align: right;
    font-variant-numeric: tabular-nums;
}
.zen-worker-verbrauch {
    flex: 0 0 auto;
    overflow-y: auto;
    overflow-x: hidden;
    color: var(--text-leise);
    font-size: 0.76rem;
    line-height: 1.35;
}
.zen-worker-platz::-webkit-scrollbar { width: 6px; }
.zen-worker-platz::-webkit-scrollbar-track { background: transparent; }
.zen-worker-platz::-webkit-scrollbar-thumb {
    background: var(--rand);
    border-radius: 999px;
}

@media (max-width: 640px) {
    .zen-worker-kopf { gap: 0.4rem; }
    .zen-worker-kopf .zen-abschnitt-titel {
        font-size: 0.7rem;
        letter-spacing: 0.04em;
    }
}

.zen-worker .zen-worker-schritt-titel {
    font-size: 0.78rem;
    line-height: 1.25;
    white-space: normal;
}

/* ── Die Integrationswarteschlange (Issue #255) ─────────────────────────── */

/*
 * Auf dem Telefon: Position links, alles Übrige untereinander daneben – bis
 * 320 px ohne waagerechten Bildlauf, weil jedes Stück umbrechen darf.
 */
.zen-iq-zeile {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    gap: 0.2rem 0.6rem;
    align-items: baseline;
    padding: 0.5rem 0;
    border-top: 1px solid var(--rand-zart);
}
.zen-iq-zeile:first-child { border-top: none; }
.zen-iq-zeile > :not(.zen-iq-position) { grid-column: 2; min-width: 0; }

.zen-iq-position {
    grid-row: 1 / span 3;
    align-self: start;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 1.6rem;
    height: 1.6rem;
    border-radius: 999px;
    background: var(--flaeche-sanft);
    color: var(--text-stark);
    font-size: 0.82rem;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
}
.zen-iq-position-ohne { color: var(--text-leise); font-weight: 400; }

.zen-iq-kopf {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.2rem 0.5rem;
}
.zen-iq-herkunft {
    display: inline-flex;
    align-items: center;
    padding: 0.08rem 0.45rem;
    border: 1px solid var(--rand);
    border-radius: 999px;
    color: var(--text-leise);
    font-size: 0.78rem;
    font-weight: 600;
    line-height: 1.35;
    white-space: nowrap;
}

/*
 * W1 und W2 unterscheiden sich in Farbe **und** Text (Issue #255) – dieselben
 * Töne an der kurzen Pille und an der Herkunft der Integrationswarteschlange.
 * Beide Paare liegen in Hell- und Dunkelmodus deutlich über 4,5 : 1. Die
 * Regel steht bewusst **nach** `.zen-worker-ziel` und `.zen-iq-herkunft`:
 * Bei gleicher Spezifität gewinnt die spätere, und deren neutraler Rand und
 * leiser Text sollen die Herkunftsfarben nicht überschreiben.
 */
.zen-worker-ziel-w1,
.zen-iq-herkunft-w1 {
    border-color: var(--info-kante);
    background: var(--info-flaeche);
    color: var(--info-text);
}
.zen-worker-ziel-w2,
.zen-iq-herkunft-w2 {
    border-color: var(--erfolg-zart-kante);
    background: var(--erfolg-zart-flaeche);
    color: var(--erfolg-zart-text);
}

/*
 * Das Claude-Modell eines Auftrags (Issue #390): eine kleine Pille mit Wort,
 * nie nur Farbe. Sie bricht nicht in sich um und verschiebt weder Titel noch
 * Status oder Sortiertasten – sie steht am Ende des Titels bzw. in der
 * Randzeile. Ein sicherer Rückfall und ein unbekannter Altstand tragen eine
 * gestrichelte Kante; den Grund nennen Tooltip und Vorlesetext.
 */
.zen-modell {
    display: inline-flex;
    align-items: center;
    padding: 0 0.4rem;
    border: 1px solid var(--rand-still);
    border-radius: 999px;
    background: var(--flaeche-sanft);
    color: var(--text-stark);
    font-size: 0.75rem;
    font-weight: 600;
    line-height: 1.4;
    white-space: nowrap;
    vertical-align: baseline;
    /*
     * Issue #481: eigener Bezugsblock für die versteckte Erklärung. Die Pille
     * enthält ein `.nur-vorlesen` (`position: absolute`). Ohne Bezug in der
     * Pille ist `body.zen-koerper` der Bezugsblock; liegt die Pille in der
     * intern scrollenden Abschlussliste, reicht die Lage der Erklärungen in
     * den äußeren Scrollbereich und lässt hinter den Schlusslinks eine leere
     * Fläche (PROD-Messung: Dokument 6628 px statt 3947 px, Schlussabstand
     * 2697 px statt 16 px bei 390 px Breite). Mit `relative` bleibt die
     * Erklärung in der Pille und damit im Scrollbereich der Liste; sichtbarer
     * Inhalt und Vorlesetext ändern sich nicht.
     */
    position: relative;
}
.zen-modell-sonnet {
    border-color: var(--info-kante);
    background: var(--info-flaeche);
    color: var(--info-text);
}
.zen-modell-rueckfall,
.zen-modell-unbekannt { border-style: dashed; }
.zen-modell-unbekannt { font-weight: 400; }
.zen-eintrag-titel > .zen-modell,
.zen-worker-auftrag > .zen-modell { margin-left: 0.35rem; }

.zen-iq-nummer { font-variant-numeric: tabular-nums; }
.zen-iq-nummer a { color: var(--verweis); text-decoration: none; }
.zen-iq-nummer a:hover,
.zen-iq-historie a:hover { text-decoration: underline; }
.zen-iq-titel { flex: 1 1 12rem; min-width: 0; color: var(--text-stark); overflow-wrap: anywhere; }

.zen-iq-zeit,
.zen-iq-historie {
    display: flex;
    flex-wrap: wrap;
    gap: 0.1rem 0.8rem;
    font-size: 0.78rem;
    color: var(--text-leise);
}
.zen-iq-eingang { color: var(--text-normal); font-variant-numeric: tabular-nums; }
.zen-iq-wartet { font-variant-numeric: tabular-nums; }
.zen-iq-unbekannt { color: var(--warnung-text); }
.zen-iq-historie-titel { color: var(--text-still); }
.zen-iq-historie a { color: var(--verweis); text-decoration: none; }

/*
 * Am Schreibtisch eine kompakte Zeile: Position, Herkunft/Issue/Titel, Zeit,
 * Historie nebeneinander. Die beiden rechten Spalten sind gedeckelt und
 * brechen in sich um, damit der Titel nie zusammengequetscht wird.
 */
@media (min-width: 900px) {
    .zen-iq-zeile {
        grid-template-columns: auto minmax(min(20rem, 40%), 1fr)
                               fit-content(16rem) fit-content(13rem);
        padding: 0.4rem 0;
    }
    .zen-iq-zeile > :not(.zen-iq-position) { grid-column: auto; }
    .zen-iq-position { grid-row: auto; }
    .zen-iq-zeit { flex-direction: column; text-align: right; }
    .zen-iq-historie { justify-content: flex-end; }
    .zen-iq-zeile > .zen-eintrag-hinweis { grid-column: 2 / -1; }
}

/* Erfolg schwebt im Fenster und reserviert keinen Platz im Raster. */
.zen-toast {
    position: fixed; z-index: 1000; right: 1rem; bottom: max(1rem, env(safe-area-inset-bottom));
    max-width: min(24rem, calc(100vw - 2rem)); margin: 0; padding: 0.65rem 0.85rem;
    border: 1px solid var(--rand); border-radius: 6px;
    background: var(--flaeche-karte); color: var(--text-stark); box-shadow: var(--schatten-block);
    pointer-events: none;
}
.zen-zahl-gesamt .zen-zahl-wert { font-size: 1.15rem; color: var(--text-leise); }
.zen-zahl-wert { overflow-wrap: anywhere; }

.zen-toast { animation: zen-toast-ausblenden 4s forwards; }
@keyframes zen-toast-ausblenden { 0%, 99% { visibility: visible; } 100% { visibility: hidden; } }

/* Auswahl und Zahlenwechsel verändern die Filtergeometrie nicht. */
.zen-filterknopf { font-weight: 600; max-width: 100%; box-sizing: border-box; }
.zen-filter-titel { min-width: 0; overflow-wrap: anywhere; }
.zen-filter-anzahl { width: 6ch; flex: 0 0 6ch; text-align: center; box-sizing: content-box; }
.zen-filterknopf:focus-visible { outline: 2px solid var(--kontur); outline-offset: 3px; }
.zen-filterreihe + .zen-abschnitt-titel { margin-top: 1rem; }
.zen-eintrag-pruefstand { display: flex; flex-wrap: wrap; gap: .2rem .8rem; min-width: 0; overflow-wrap: anywhere; }
.zen-auftragszeile { grid-template-columns: auto minmax(0, 1fr); }
.zen-auftragszeile .zen-eintrag-rand { grid-column: 1 / -1; display: grid; grid-template-columns: minmax(0, 1fr); justify-content: start; }
.zen-auftragszeile .zen-marke-zustand { justify-self: start; }
.zen-auftragszeile .zen-eintrag-datum { white-space: nowrap; }
/*
 * Gemeinsame Listenspalten ab dem vorhandenen Desktop-Breakpoint. Der
 * Prüfstand steht unter dem Titel statt in einer eigenen Spalte: Fünf
 * Spalten ließen dem Titel bei 900/1024 px nur gut ein Drittel der Zeile
 * (Browserbefund zu #292, Zusage aus #128: zwei Fünftel), und ein eigener
 * breiterer Breakpoint kommt nicht hinzu (Handbuchprüfung M1). Zustand und
 * Datum behalten ihre gemeinsamen Spalten.
 */
@media (min-width: 900px) {
    .zen-auftragszeile { grid-template-columns: 5rem minmax(0, 1fr) 14rem 8rem; }
    .zen-auftragszeile .zen-eintrag-rand { display: contents; }
    .zen-auftragszeile .zen-eintrag-bilanz { white-space: normal; }
    .zen-auftragszeile .zen-eintrag-pruefstand { grid-column: 2; grid-row: 2; }
}

html:has(.zen-auftraege) { scrollbar-gutter: stable; }

.zen-auftragszeile .zen-eintrag-nummer { min-width: 0; overflow-wrap: anywhere; }

/* ── Ungespeicherte Änderungen (Issue #389) ─────────────────────────────────
 *
 * Das Modal des gemeinsamen Schutzes (`static/verlassensschutz.js`). Dieselbe
 * Bauart wie die Rückfrage bei gleichnamigen Anhängen (`.anhang-konflikt`):
 * mittig, in der Kartenfarbe, auf dem Telefon mit einem Rand von je 1 rem und
 * in sich rollend, wenn die Höhe nicht reicht. Drei gleichwertige Wege, keiner
 * vorbelegt; auf dem Telefon untereinander und fingergroß.
 */
.verlassen-dialog {
    position: fixed;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    margin: 0;
    border: 1px solid var(--rand-stark);
    border-radius: 10px;
    padding: 1.25rem 1.25rem 1.1rem;
    width: 100%;
    max-width: min(30rem, calc(100vw - 2rem));
    max-height: calc(100dvh - 2rem);
    overflow-y: auto;
    box-shadow: var(--schatten-dialog);
    background: var(--flaeche-karte);
    color: var(--text-normal);
}
.verlassen-dialog::backdrop { background: var(--dialog-schleier); }
/* `.schaltflaeche` setzt `display` und schlüge sonst das eingebaute `hidden`. */
.verlassen-dialog [hidden] { display: none !important; }
.verlassen-titel {
    margin: 0 2rem 0.5rem 0;
    font-size: 1.15rem;
    color: var(--text-stark);
}
.verlassen-titel:focus { outline: none; }
.verlassen-titel:focus-visible {
    outline: 3px solid var(--hinweis-kante);
    outline-offset: 2px;
}
.verlassen-text { margin: 0 0 0.6rem; font-size: 0.95rem; }
.verlassen-mehrere {
    margin: 0 0 0.6rem;
    padding: 0.55rem 0.7rem;
    border-radius: 6px;
    background: var(--flaeche-zart);
    font-size: 0.87rem;
}
.verlassen-zu {
    position: absolute;
    top: 0.6rem;
    right: 0.6rem;
    width: 2.25rem;
    height: 2.25rem;
    border: 0;
    border-radius: 6px;
    background: transparent;
    color: var(--text-leise);
    font: inherit;
    font-size: 1.3rem;
    line-height: 1;
    cursor: pointer;
}
.verlassen-zu:hover { background: var(--flaeche-zart); color: var(--text-stark); }
.verlassen-zu:focus-visible {
    outline: 3px solid var(--hinweis-kante);
    outline-offset: 2px;
}
.verlassen-aktionen {
    display: flex;
    flex-wrap: wrap;
    justify-content: flex-end;
    gap: 0.5rem;
    margin-top: 0.9rem;
}
.verlassen-dialog[aria-busy="true"] { cursor: progress; }
/* Der Hinweis über einem Formular, das nicht gespeichert wurde. Er bekommt
   den Fokus – ohne Ring um den ganzen Kasten. */
.verlassen-hinweis:focus { outline: none; }
@media (max-width: 640px) {
    .verlassen-dialog { padding: 1rem; }
    .verlassen-aktionen .schaltflaeche {
        flex: 1 1 100%;
        min-height: 44px;
    }
    .verlassen-zu { width: 44px; height: 44px; top: 0.4rem; right: 0.4rem; }
}

/* ── Das Archiv (Issue #532) ─────────────────────────────────────────────
   Filterleiste, Tabelle, Karten, Sortierköpfe und Auswahlleiste kommen aus
   den gemeinsamen Bausteinen (`.aufgaben-filter`, `.datentabelle`,
   `.listenkarten`, `.ki-sortkopf`, `.ki-sammelleiste`); hier steht nur, was
   das Archiv zusätzlich braucht – und ausschließlich aus Tokens. */
/* Die Filterkachel (Issue #545): dichter gesetzt, damit die Tabelle weiter oben
   beginnt. Felder, Beschriftungen und Klickziele behalten ihre Größe – gespart
   wird an Innenabständen, Lücken und Außenabständen. */
.archiv-filter {
    grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
    gap: 0.45rem 0.6rem;
    padding: 0.6rem 0.8rem;
    margin-bottom: 0.6rem;
}
.archiv-filter .filter-suche { grid-column: span 2; }
.archiv-filter .filter-feld { gap: 0.15rem; }
.archiv-filter .filter-feld input,
.archiv-filter .filter-feld select { padding-top: 0.3rem; padding-bottom: 0.3rem; }
/* Die Aktionszeile: Suchen, Leeren, Zuletzt hinzugefügt – und der Stand
   rechts davon. Auf schmalen Breiten bricht der Stand in eine eigene Zeile. */
.archiv-aktionen {
    grid-column: 1 / -1;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.4rem 0.5rem;
}
.archiv-status {
    flex: 1 1 14rem;
    margin: 0 0 0 0.3rem;
    font-size: 0.85rem;
    color: var(--text-leise);
    text-align: right;
}
#archiv-seite { scroll-margin-top: var(--kopf-hoehe); }
#archiv-seite .ki-bibliothekskopf { margin-bottom: 0.4rem; }
#archiv-seite .ki-sammelleiste { margin-bottom: 0.5rem; padding: 0.4rem 0.8rem; }
#archiv-seite .ki-blaettern-fuss { margin-top: 0.6rem; }
.archiv-tabelle td { padding-top: 0.45rem; padding-bottom: 0.45rem; }
.archiv-tabelle th { padding-top: 0.55rem; padding-bottom: 0.55rem; }
/* Der feststehende Kopf: Das Dokument scrollt selbst (kein Scrollbereich um
   die Tabelle, `.tabelle-quer` schneidet am Schreibtisch nichts ab), die
   Bezugskante ist also der Fensterrand. `archiv.js` setzt die gemessene Höhe
   des Hauptmenüs; ohne Skript gilt dessen Sollhöhe. Auf dem Telefon steht die
   Kartenliste, die Tabelle ist ausgeblendet. */
.archiv-tabelle thead th { top: var(--archiv-kopf-oben, var(--kopf-hoehe)); }
@media (max-width: 640px) {
    .archiv-filter .filter-suche { grid-column: 1 / -1; }
    .archiv-aktionen .schaltflaeche { min-height: 44px; display: inline-flex; align-items: center; }
    .archiv-status { text-align: left; margin-left: 0; flex-basis: 100%; }
}
.archiv-status[aria-busy="true"] { color: var(--text-normal); font-weight: 600; }
.archiv-id { white-space: nowrap; font-variant-numeric: tabular-nums; }
.archiv-id .upl-import-kopieren { margin-left: 0.35rem; }
.archiv-zahlspalte { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.archiv-tabelle th.archiv-zahlspalte { text-align: right; }
.archiv-tabelle td.ki-zeitspalte { white-space: nowrap; }
/* Der Kopf der Archivtabelle ist gefüllt (`--fuellung`). Das allgemeine
   `.ki-sortkopf-aktiv` färbt in `--text-stark` – hell auf hellem Grund ist
   das richtig, hier stünde die gerade sortierte Spalte dunkel auf dunkel und
   verschwände. Hervorgehoben bleibt sie über Pfeil und Unterstrich. */
.archiv-tabelle .ki-sortkopf-aktiv { color: var(--fuellung-text); }
.archiv-tabelle .ki-sortkopf-aktiv a { text-decoration: underline; text-underline-offset: 0.25em; }
.archiv-zahl { font-size: 0.85rem; color: var(--text-mittel); }
.archiv-grenze { flex-basis: 100%; font-size: 0.8rem; color: var(--text-leise); }
.archiv-nachoben { margin: 1.2rem 0 0; text-align: right; font-size: 0.85rem; }
.archiv-angaben {
    display: grid;
    grid-template-columns: minmax(9rem, max-content) 1fr;
    gap: 0.5rem 1.2rem;
    margin: 0 0 1.2rem;
}
.archiv-angaben dt { font-weight: 600; color: var(--text-mittel); }
.archiv-angaben dd { margin: 0; color: var(--text-normal); overflow-wrap: anywhere; }
.archiv-angaben .hinweis { display: block; font-size: 0.8rem; }
/* Die kompakte Detailansicht (Issue #545): jede Angabe eine kleine Zelle,
   mehrere nebeneinander; Langes (Kommentar, Mehrfachwerte) nimmt die ganze
   Zeile. Umbrüche im Text bleiben erhalten, nichts wird abgeschnitten. */
.archiv-angaben-kompakt {
    grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
    gap: 0.6rem 1.4rem;
    margin-bottom: 0.9rem;
}
.archiv-angaben-kompakt .archiv-angabe { min-width: 0; }
.archiv-angaben-kompakt .archiv-angabe-breit { grid-column: 1 / -1; }
.archiv-angaben-kompakt dt { font-size: 0.78rem; text-transform: uppercase; letter-spacing: 0.03em; }
.archiv-langtext { white-space: pre-wrap; overflow-wrap: anywhere; }
.archiv-mehrfach { margin: 0; padding-left: 1.1rem; }
.archiv-quelle {
    margin: 0 0 0.5rem;
    font-size: 0.78rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.07em;
    color: var(--text-leise);
}
@media (max-width: 640px) {
    .archiv-angaben { grid-template-columns: 1fr; gap: 0.2rem; }
    .archiv-angaben dd { margin-bottom: 0.6rem; }
    .archiv-id .upl-import-kopieren { min-height: 44px; }
    .archiv-nachoben { text-align: center; }
}

/* ── Persönliche Vorbereitungshilfen im Archiv (Issue #534) ──────────────
   Merklisten, Notizen und gespeicherte Suchen. Filterkachel, Sammelleiste,
   Angabenraster und Meldungen kommen aus den Bausteinen darüber; hier steht
   nur Zeile, Karte und Leiste – ausschließlich aus Tokens, damit Hell und
   Dunkel von selbst stimmen. */
.archiv-vorbereitung-leiste {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.4rem 0.6rem;
    margin: 0 0 0.6rem;
    font-size: 0.9rem;
}
.archiv-vorbereitung-leiste input[type="text"],
.archiv-vorbereitung-leiste select { flex: 1 1 14rem; max-width: 26rem; }
.archiv-vorbereitung-weg { margin-left: auto; }
.archiv-merkleiste select { max-width: 20rem; }
.vb-abschnitt { margin: 0 0 1.6rem; }
.vb-abschnitt > h3 { margin: 0 0 0.6rem; }
.vb-neu { grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr)); }
.vb-karten { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.5rem; }
.vb-karte {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.3rem 0.8rem;
    padding: 0.6rem 0.8rem;
    background: var(--flaeche-karte);
    border: 1px solid var(--rand);
    border-radius: 8px;
}
.vb-karte-titel { font-weight: 600; overflow-wrap: anywhere; }
.vb-rueckfrage { display: block; max-width: 40rem; }
.vb-rueckfrage p { margin: 0 0 0.8rem; }
.vb-karte-angaben { font-size: 0.85rem; color: var(--text-mittel); overflow-wrap: anywhere; }
.vb-karte-aktionen { flex-basis: 100%; display: flex; flex-wrap: wrap; align-items: center; gap: 0.4rem; }
.vb-inline { display: inline-flex; flex-wrap: wrap; align-items: center; gap: 0.3rem; margin: 0; }
.vb-inline input[type="text"] { max-width: 16rem; }
.vb-kopfwege { display: flex; flex-wrap: wrap; gap: 0.4rem; }
.vb-bearbeiten { margin: 0 0 1rem; }
.vb-bearbeiten > summary { cursor: pointer; font-weight: 600; color: var(--text-mittel); }
.vb-bearbeiten-felder { display: grid; gap: 0.8rem; margin-top: 0.6rem; }
.vb-feldform { display: grid; gap: 0.3rem; max-width: 36rem; }
.vb-feldform button { justify-self: start; }
.vb-sammelleiste { flex-wrap: wrap; }
.vb-alle, .vb-notizen-mit { display: inline-flex; align-items: center; gap: 0.3rem; font-size: 0.85rem; }
.vb-auswahlsumme { display: block; margin-top: 0.3rem; font-size: 0.85rem; color: var(--text-normal); }
.vb-titel { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.5rem; }
.vb-zeile {
    padding: 0.55rem 0.8rem;
    background: var(--flaeche-karte);
    border: 1px solid var(--rand);
    border-radius: 8px;
    scroll-margin-top: var(--kopf-hoehe);
}
.vb-zeile-kopf {
    display: grid;
    grid-template-columns: auto 2.2rem minmax(0, 1fr) auto 4.5rem;
    align-items: center;
    gap: 0.3rem 0.7rem;
}
.vb-position { color: var(--text-leise); font-variant-numeric: tabular-nums; text-align: right; }
.vb-zeile-titel { min-width: 0; display: flex; flex-direction: column; overflow-wrap: anywhere; }
.vb-kuenstler { font-size: 0.85rem; color: var(--text-mittel); }
.vb-laenge { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.vb-stand { margin: 0.35rem 0 0; font-size: 0.85rem; color: var(--text-mittel); }
.vb-stand-geaendert { color: var(--warnung-text); }
.vb-stand-fehlt { color: var(--fehler-text); }
.vb-zustand-fehlt { border-style: dashed; background: var(--flaeche-still); }
.vb-titelnotiz {
    margin: 0.35rem 0 0;
    padding: 0.3rem 0.6rem;
    border-left: 3px solid var(--rand-akzent);
    font-size: 0.9rem;
    color: var(--text-normal);
}
.vb-zeile-aktionen { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 0.4rem; margin-top: 0.4rem; }
.vb-notizfeld > summary { cursor: pointer; font-size: 0.85rem; padding: 0.2rem 0; }
@media (max-width: 640px) {
    .archiv-vorbereitung-weg { margin-left: 0; flex-basis: 100%; }
    .vb-zeile-kopf { grid-template-columns: auto 1.8rem minmax(0, 1fr); }
    .vb-zeile-kopf .vb-id, .vb-zeile-kopf .vb-laenge { grid-column: 3; text-align: left; }
    .vb-zeile-aktionen .schaltflaeche,
    .vb-karte-aktionen .schaltflaeche,
    .vb-sammelleiste .schaltflaeche,
    .archiv-vorbereitung-leiste .schaltflaeche { min-height: 44px; }
    .vb-inline input[type="text"] { max-width: none; flex: 1 1 10rem; }
}

/* ── Frag die GUDE KI (Issue #533) ────────────────────────────────────────
   Tabelle, Karten, Blättern und Meldungen kommen aus den Bausteinen der
   Liste; hier stehen nur Frage, Kontext und Merkmale – aus Tokens. */
.archiv-ki-erklaerung { margin: 0 0 0.8rem; color: var(--text-mittel); font-size: 0.92rem; }
.archiv-ki-frage {
    display: grid;
    gap: 0.4rem;
    padding: 0.7rem 0.9rem;
    margin-bottom: 0.9rem;
    border: 1px solid var(--rand);
    border-radius: 8px;
    background: var(--flaeche-karte);
}
.archiv-ki-frage label { font-weight: 600; color: var(--text-stark); }
.archiv-ki-frage textarea {
    width: 100%;
    box-sizing: border-box;
    min-height: 3.2rem;
    padding: 0.5rem 0.6rem;
    border: 1px solid var(--rand-stark);
    border-radius: 6px;
    background: var(--flaeche-eingabe);
    color: var(--text-normal);
    font: inherit;
    resize: vertical;
}
.archiv-ki-frage textarea:focus-visible {
    outline: 3px solid var(--hinweis-kante);
    outline-offset: 1px;
}
.archiv-ki-knoepfe { display: flex; flex-wrap: wrap; align-items: center; gap: 0.4rem 0.5rem; }
.archiv-ki-kontext {
    margin: 0 0 0.9rem;
    padding: 0.6rem 0.9rem;
    border-left: 3px solid var(--rand-akzent);
    background: var(--flaeche-zart);
    border-radius: 0 6px 6px 0;
}
.archiv-ki-zeile { margin: 0 0 0.35rem; overflow-wrap: anywhere; }
.archiv-ki-zeit { color: var(--text-leise); font-size: 0.85rem; }
.archiv-ki-filter { display: flex; flex-wrap: wrap; gap: 0.35rem 0.5rem; margin: 0.4rem 0 0; }
.archiv-ki-filterpunkt {
    display: inline-flex;
    gap: 0.35rem;
    padding: 0.2rem 0.55rem;
    border-radius: 999px;
    background: var(--flaeche-sanft);
    font-size: 0.85rem;
}
.archiv-ki-filterpunkt dt { font-weight: 600; color: var(--text-mittel); }
.archiv-ki-filterpunkt dd { margin: 0; color: var(--text-normal); overflow-wrap: anywhere; }
.archiv-ki-hinweise { margin: 0.5rem 0 0; padding-left: 1.1rem; font-size: 0.85rem; color: var(--text-mittel); }
.archiv-ki-satz { margin: 0.4rem 0 0.6rem; }
.archiv-ki-alternativen ul { display: flex; flex-wrap: wrap; gap: 0.4rem; margin: 0 0 0.8rem; padding: 0; list-style: none; }
.archiv-ki-merkmal {
    display: inline-block;
    padding: 0 0.45rem;
    border-radius: 999px;
    background: var(--flaeche-gewaehlt);
    color: var(--text-stark);
    font-size: 0.82rem;
    white-space: nowrap;
}
.archiv-ki-platz { color: var(--text-leise); font-variant-numeric: tabular-nums; }
.archiv-ki-vorschlag { margin: 0.4rem 0 1.2rem; }
.archiv-ki-vorschlag h3, .archiv-ki-zwischentitel { margin: 0.6rem 0 0.5rem; font-size: 1.02rem; color: var(--text-stark); }
.archiv-ki-zusatz { font-weight: 400; font-size: 0.85rem; color: var(--text-mittel); }
.archiv-ki-klassisch { margin: 1rem 0 0; font-size: 0.9rem; }
@media (max-width: 640px) {
    .archiv-ki-knoepfe .schaltflaeche { min-height: 44px; }
    .archiv-ki-alternativen .schaltflaeche { min-height: 44px; }
}

/* ── Audio-Eingang (Issue #567) ─────────────────────────────────────────── */

.ae-liste { list-style: none; margin: 0.8rem 0; padding: 0; display: grid; gap: 0.6rem; }
.ae-eintrag, .ae-karte {
    padding: 0.8rem 1rem;
    border: 1px solid var(--rand);
    border-left: 5px solid var(--rand);
    border-radius: 8px;
    background: var(--flaeche-karte);
    overflow-wrap: anywhere;
}
.ae-karte { margin: 0 0 1rem; }
.ae-karte h3 { margin: 0 0 0.6rem; font-size: 1.05rem; color: var(--text-stark); }
.ae-status-neu { border-left-color: var(--warnung-kraeftig); }
.ae-status-in_bearbeitung, .ae-status-wird_uebergeben { border-left-color: var(--fuellung); }
.ae-status-uebergeben { border-left-color: var(--erfolg-kraeftig); }
.ae-status-fehler, .ae-status-unklar { border-left-color: var(--fehler-kraeftig); }
.ae-kopf { display: flex; flex-wrap: wrap; gap: 0.5rem; align-items: baseline; margin: 0; }
.ae-pille {
    display: inline-block;
    padding: 0.05rem 0.55rem;
    border-radius: 999px;
    background: var(--flaeche-sanft);
    color: var(--text-stark);
    font-size: 0.82rem;
    font-weight: 600;
    vertical-align: middle;
}
.ae-zeit, .ae-absender, .ae-daten, .ae-herkunft, .ae-bearbeiter { font-size: 0.88rem; color: var(--text-mittel); }
.ae-titel { margin: 0.3rem 0; font-size: 1.02rem; }
.ae-daten, .ae-herkunft, .ae-bearbeiter, .ae-dublette, .ae-fehler { margin: 0.15rem 0; }
.ae-dublette { font-size: 0.88rem; color: var(--text-stark); }
.ae-fehler, .feldfehler { color: var(--fehler-kraeftig); font-size: 0.88rem; }
.ae-player { width: 100%; max-width: 32rem; margin: 0.6rem 0 0.2rem; }
.ae-frische, .ae-livestand { font-size: 0.88rem; color: var(--text-mittel); margin: 0.2rem 0; }
.ae-frische-fehler, .ae-frische-speicher { color: var(--fehler-kraeftig); }
.ae-filter { display: flex; flex-wrap: wrap; gap: 0.4rem; margin: 0.6rem 0; }
.ae-typfelder, .ae-konflikte { border: 1px solid var(--rand); border-radius: 8px; padding: 0.6rem 0.9rem; margin: 0.6rem 0; }
.ae-maengel { margin: 0 0 0.6rem; color: var(--fehler-kraeftig); }
.ae-protokoll ul { margin: 0.4rem 0 0; padding-left: 1.1rem; font-size: 0.88rem; }
.ae-protokoll summary { cursor: pointer; min-height: 44px; display: flex; align-items: center; }
/* Die Aktionszeilen (Issue #573): normal große Schaltflächen nebeneinander,
   so breit und hoch wie ihr Inhalt – keine eigene Karte je Knopf. Nur im
   Audio-Eingang; `.formular` und `.formular-aktionen` bleiben unberührt. */
.ae-aktionen { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 0.6rem; margin: 0.4rem 0 0; }
.ae-aktionen .schaltflaeche { display: inline-flex; align-items: center; justify-content: center; }
.ae-aktion { display: flex; flex-wrap: wrap; align-items: center; gap: 0.6rem; margin: 0; }
.ae-aktion .checkbox-label { margin: 0; }
.ae-aktion-block { margin: 0; }
.ae-aktionen-text { flex: 1 1 18rem; margin: 0; color: var(--text-mittel); align-self: center; }
.ae-nebenaktionen { margin-top: 0.9rem; padding-top: 0.8rem; border-top: 1px solid var(--rand); }
.ae-statusaktionen { align-items: center; margin: 0 0 1rem; }
.ae-rueckfrage { margin: 0; max-width: 100%; }
.ae-rueckfrage > summary { list-style: none; cursor: pointer; }
.ae-rueckfrage > summary::-webkit-details-marker { display: none; }
.ae-rueckfrage-feld {
    display: grid;
    gap: 0.45rem;
    max-width: 30rem;
    margin: 0.5rem 0 0;
    padding: 0.7rem 0.9rem;
    border: 1px solid var(--rand);
    border-radius: 8px;
    background: var(--flaeche-sanft);
}
.ae-rueckfrage-feld .hinweis { margin: 0; }
.ae-rueckfrage-feld input[type="text"] { width: 100%; box-sizing: border-box; }
.ae-rueckfrage-feld .ae-aktionen { margin: 0.2rem 0 0; }
.ae-loeschhinweise { max-width: 44rem; margin: 0.6rem 0 0.9rem; padding-left: 1.2rem; }
.ae-loeschhinweise li { margin: 0.3rem 0; }
@media (max-width: 640px) {
    .ae-eintrag .schaltflaeche, .ae-karte .schaltflaeche, .ae-aktionen .schaltflaeche { min-height: 44px; }
    .ae-karte .formular-aktionen { flex-wrap: wrap; }
}

/* ── Der kleine Chat (Issue #560) ─────────────────────────────────────────
   Deine Beiträge rechts, die der GUDE KI links; Rückfragen mit Akzentkante.
   Nur Tokens – Hell und Dunkel kommen aus dem Thema. */
.archiv-ki-chat { margin: 0 0 0.9rem; }
.archiv-ki-chat-titel { margin: 0 0 0.4rem; font-size: 1.02rem; color: var(--text-stark); }
.archiv-ki-verlauf {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    max-height: 26rem;
    margin: 0;
    padding: 0.6rem;
    overflow-y: auto;
    list-style: none;
    border: 1px solid var(--rand);
    border-radius: 8px;
    background: var(--flaeche-zart);
}
.archiv-ki-verlauf:focus-visible { outline: 3px solid var(--hinweis-kante); outline-offset: 1px; }
.archiv-ki-beitrag {
    max-width: min(46rem, 88%);
    padding: 0.45rem 0.7rem;
    border-radius: 10px;
    overflow-wrap: anywhere;
}
.archiv-ki-beitrag p { margin: 0; }
.archiv-ki-beitrag-du {
    align-self: flex-end;
    background: var(--flaeche-gewaehlt);
    color: var(--text-stark);
}
.archiv-ki-beitrag-ki {
    align-self: flex-start;
    border: 1px solid var(--rand);
    background: var(--flaeche-karte);
    color: var(--text-normal);
}
.archiv-ki-beitrag-rueckfrage { border-left: 3px solid var(--rand-akzent); }
.archiv-ki-wer { font-size: 0.8rem; font-weight: 600; color: var(--text-mittel); }
.archiv-ki-beitragstext { margin-top: 0.15rem; }
.archiv-ki-zwischenstand { margin-top: 0.35rem; font-size: 0.85rem; color: var(--text-mittel); }
.archiv-ki-zwischenstand ul { margin: 0.2rem 0 0; padding-left: 1.1rem; }
.archiv-ki-tastenhinweis { margin: 0; font-size: 0.8rem; color: var(--text-leise); }
.archiv-ki-kriterientitel { font-size: 0.9rem; }
.archiv-ki-angaben { display: flex; flex-direction: column; gap: 0.1rem; font-size: 0.85rem; }
.archiv-ki-angabe-name { font-weight: 600; color: var(--text-mittel); }
.archiv-ki-grundlage { color: var(--text-mittel); font-style: italic; }
.archiv-ki-frage[aria-busy="true"] .archiv-status { font-weight: 600; color: var(--text-stark); }
@media (max-width: 640px) {
    .archiv-ki-verlauf { max-height: 20rem; padding: 0.45rem; }
    .archiv-ki-beitrag { max-width: 94%; }
}
