/*
 * Stilvorlage des Admin-Web - die Umsetzung der abgenommenen Runde Layout
 * (docs/admin-web/mockups/mockups-9-layout.html, Brand Style Guide August 2026).
 * Von Hand geschrieben und ohne Baulauf: Es gibt keinen zweiten Build und keine
 * zweite Auslieferung (AD 4, ADR-02). Die Marke wird übertragen, nicht erfunden -
 * Farbwerte und Schriftpaar stammen unverändert aus website/src/styles/tokens.css.
 */

/* ————— Schrift: selbst ausgeliefert, kein fremder Host ————— */
@font-face {
    font-family: "Montserrat Variable";
    font-style: normal;
    font-weight: 100 900;
    font-display: swap;
    src: url("fonts/montserrat-latin-wght-normal.woff2") format("woff2-variations");
}
@font-face {
    font-family: Poppins;
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url("fonts/poppins-latin-400-normal.woff2") format("woff2");
}
@font-face {
    font-family: Poppins;
    font-style: normal;
    font-weight: 600;
    font-display: swap;
    src: url("fonts/poppins-latin-600-normal.woff2") format("woff2");
}
@font-face {
    font-family: Poppins;
    font-style: normal;
    font-weight: 700;
    font-display: swap;
    src: url("fonts/poppins-latin-700-normal.woff2") format("woff2");
}

:root {
    color-scheme: light;

    /* ————— Marke: unverändert aus website/src/styles/tokens.css ————— */
    --ink: #19242D;        /* Midnight Slate */
    --storm: #4E7183;      /* Storm Blue - Sekundärtext */
    --silver: #D2D3D4;     /* Silver Gray - Linien, neutrale Fläche */
    --green: #1CBA76;      /* Emerald Green */
    --green-deep: #0F7A4E; /* abgedunkeltes Emerald - Handlung auf hell, 5.37:1 */
    --midnight-soft: #223140;
    --storm-deep: #2F4552;
    --storm-soft: #88A2AF;
    --steel: #B4C3CC;
    --silver-soft: #F1F2F3;

    /* ————— Zustände: bewusst ohne Grün (Grün ist die Handlung) ————— */
    --amber: #8A5B0B; --amber-bg: #FBF1DC; --amber-line: #D9AE5E;
    --red: #A83228;   --red-bg: #FBEBE9;   --red-line: #D98A82;
    --action-bg: #E4F1EA;
    --action-bg-hover: #D5E9DE;
    /* Helle Storm-Tönung für den ruhigen Banner - die Website hat dafür keinen Token. */
    --calm-bg: #EAF1F5;
    /* Schatten und Schleier: die Tinte, nicht Schwarz - auf dunklem Grund Schwarz. */
    --shade: 25, 36, 45;

    /* ————— Rollen, heller Modus ————— */
    --paper: var(--silver-soft);
    --card: #FFFFFF;
    --rail-bg: var(--ink);
    --rail-soft: var(--midnight-soft);
    --text: var(--ink);
    --text-soft: var(--storm-deep);
    --text-faint: var(--storm);
    /* Neutrales Grau für Fussnoten und Feldhinweise - Storm liest sich neben den
       grünen Links wie ein zweiter Link. */
    --text-hint: #8A9099;
    --rule: var(--silver);
    --rule-soft: #E6E9EA;
    --action: var(--green-deep);
    --action-hover: #0B5F3B;
    --on-action: #FFFFFF;
    --focus: var(--green);
    /* Die Handlungsfarbe auf Flächen, die nicht Karte sind (Balken, Kanten,
       Marken): auf hell das tiefe Grün, auf dunkel das Marken-Emerald. */
    --mark: var(--green-deep);
    /* Die «gesetzte» Fläche (Rasterzelle, Wochenende, Schraffur): auf hell die
       getönte Sektion, auf dunkel eine angehobene Stufe. */
    --set: var(--silver-soft);

    /* ————— Schrift und Geometrie ————— */
    --head: "Montserrat Variable", Montserrat, system-ui, sans-serif;
    --body: Poppins, system-ui, sans-serif;
    --fs-fine: 0.75rem; --fs-small: 0.8125rem; --fs-body: 0.9375rem;
    --fs-h3: 1.0625rem; --fs-h2: 1.25rem; --fs-h1: 1.5rem;
    --r: 6px; --r-sm: 4px; --row: 34px;
    --rail-w: 232px; --rail-w-min: 56px;

    /* ————— Der Zustand der Schiene, breit oder schmal —————
       Das schmale Aussehen steht genau einmal im File: als Satz von Werten, die
       .app, .rail und ihre Teile lesen. Wer ihn setzt, entscheidet die Kaskade -
       :root die Vorgabe (auch die ab 1100px), html[data-rail] die Wahl des
       Admins, die höher wiegt und deshalb überall gewinnt. */
    --rail-now: var(--rail-w);
    --brand-pad: 0 1rem 1.1rem;
    --nav-just: flex-start;
    --nav-pad: 0.42rem 1rem;
    --foot-pad: 0.7rem 1rem 0;
    --foot-align: left;
    --lbl: inline;
    --ic-size: 1em;
    --ic-w: 1.3rem;
    --ic-glyph: 1.15rem;
    --rail-arrow: '‹';
    --logo: block;
    --logomark: none;
}

/* ————— Rollen, dunkler Modus (Mockup 13.4, Website-Tokens) —————
   theme.js schreibt das Ergebnis der Wahl - hell oder dunkel - an das html-Element;
   «System» ist dort schon aufgelöst. Darum steht der dunkle Satz genau einmal
   hier und nicht noch einmal als Media-Query. Beide Modi sind entworfen, nicht
   umgerechnet (Runde Layout): Die Marke bleibt, die Rollen wechseln. Bernstein
   und Rot sind wie im hellen Modus eigene Ableitungen des Admin-Web; alle
   Schrift-auf-Fläche-Paare messen mindestens 4.5:1 auf der Karte. */
:root[data-theme="dark"] {
    color-scheme: dark;

    --paper: #0F171E;          /* midnight-deep - der Seitengrund */
    --card: var(--midnight-soft);
    --rail-bg: var(--ink);
    --rail-soft: #2A3A48;
    --text: #FFFFFF;
    --text-soft: var(--steel);        /* 7.35:1 */
    --text-faint: var(--storm-soft);  /* 4.95:1 */
    --text-hint: #8A9BA8;             /* 4.64:1 */
    --rule: #35485C;                  /* midnight-line */
    --rule-soft: #2A3A48;
    --action: var(--green);           /* 5.27:1 auf der Karte */
    --action-hover: #35D68D;          /* emerald-bright */
    --on-action: var(--ink);          /* 6.26:1 auf Emerald */
    --focus: #35D68D;
    --mark: var(--green);
    --set: #2A3A48;

    --amber: #F0C060; --amber-bg: #3A2F14; --amber-line: #8A6A25;
    --red: #F29288;   --red-bg: #3E2220;   --red-line: #8F433C;
    --action-bg: #1B3A2D;
    --action-bg-hover: #1E3F31;
    --calm-bg: #1E3340;
    --shade: 0, 0, 0;
}

/* Schmal, weil der Admin es so gewählt hat. html[data-rail] wiegt schwerer als
   :root, also schlägt die Wahl auch die Vorgabe der 1100er-Schwelle - unterhalb
   760px zieht die Schublade sie wieder zurück. */
html[data-rail="min"] {
    --rail-now: var(--rail-w-min);
    --brand-pad: 0 0.6rem 1.1rem;
    --nav-just: center;
    --nav-pad: 0.42rem 0;
    --foot-pad: 0.7rem 0.4rem 0;
    --foot-align: center;
    --lbl: none;
    /* Bleibt das Zeichen allein stehen, darf es grösser sein. */
    --ic-size: 1.25rem;
    --ic-w: 1.7rem;
    --ic-glyph: 1.5rem;
    --rail-arrow: '›';
    --logo: none;
    --logomark: block;
}

/* Breit, weil der Admin es so gewählt hat - dieselben Werte wie in :root, aber
   mit dem Gewicht, das die 1100er-Vorgabe überstimmt. */
html[data-rail="max"] {
    --rail-now: var(--rail-w);
    --brand-pad: 0 1rem 1.1rem;
    --nav-just: flex-start;
    --nav-pad: 0.42rem 1rem;
    --foot-pad: 0.7rem 1rem 0;
    --foot-align: left;
    --lbl: inline;
    --ic-size: 1em;
    --ic-w: 1.3rem;
    --ic-glyph: 1.15rem;
    --rail-arrow: '‹';
    --logo: block;
    --logomark: none;
}

* { box-sizing: border-box; margin: 0; }

body {
    font: var(--fs-body)/1.5 var(--body);
    color: var(--text);
    background: var(--paper);
    min-height: 100vh;
}

/* ————— Der Rahmen: dunkle Schiene links, helle Arbeitsfläche —————
   Der Rahmen ist auf Bildschirmhöhe fixiert; gescrollt wird nur die
   Arbeitsfläche (.body). Schiene und Topbar stehen still, der .foot der
   Schiene (Konto, Abmelden) klebt damit am unteren Bildschirmrand. */
.app {
    display: grid;
    grid-template-columns: var(--rail-now) 1fr;
    /* minmax(0, 1fr): die eine Zeile bleibt exakt Bildschirmhöhe - eine
       auto-Zeile wüchse mit langem Inhalt darüber hinaus. */
    grid-template-rows: minmax(0, 1fr);
    height: 100dvh;
}

.rail {
    background: var(--rail-bg);
    color: var(--steel);
    padding: 0.9rem 0 0.6rem;
    display: flex;
    flex-direction: column;
    overflow-y: auto;
}

.rail .brand { padding: var(--brand-pad); }
.rail .logo { display: var(--logo); width: 100%; max-width: 108px; height: auto; color: #FFFFFF; }
/* Die Bildmarke steht mittig im schmalen Balken; sie ist fast quadratisch und
   trägt darum weniger Breite als die Wortmarke. */
.rail .logomark { display: var(--logomark); width: 100%; max-width: 30px; height: auto; color: #FFFFFF; margin: 0 auto; }

/* Der Freiraum sitzt hinter den Einträgen, nicht vor dem Fuss: so stehen
   Schalter und Konto-Fuss gemeinsam am unteren Rand, und der Schalter bleibt
   das letzte Glied der Navigation. Verschwindet er (kein Skript, Schublade),
   rutscht der Fuss an seine Stelle. */
.rail nav { display: flex; flex-direction: column; gap: 1px; margin-bottom: auto; }

.rail nav a {
    display: flex;
    align-items: center;
    justify-content: var(--nav-just);
    gap: 0.6rem;
    padding: var(--nav-pad);
    color: var(--steel);
    text-decoration: none;
    font-size: var(--fs-small);
    border-left: 3px solid transparent;
}
.rail .ic {
    width: var(--ic-w);
    font-size: var(--ic-size);
    /* Ohne 1 wüchse die Zeile mit dem grösseren Zeichen mit. */
    line-height: 1;
    text-align: center;
    opacity: 0.8;
    flex: none;
}
/* Die gezeichneten Zeichen der Navigation (icons.html) tragen ihre eigene Grösse
   und nicht --ic-size: Das ist die Schriftgrösse, an der auch der Pfeil des
   Schalters und das Ein-Aus-Zeichen im Fuss hängen - beides Textzeichen, die
   nicht mitwachsen sollen. Ein Pfeil in Symbolgrösse wäre ein Knopf, kein Hinweis.
   Wie alles Schmale steht auch dieser Wert als Eigenschaft im Satz von
   Richtlinie 8 und wächst darum eingeklappt von selbst mit. */
.rail nav .ic svg {
    display: block;
    width: var(--ic-glyph);
    height: var(--ic-glyph);
    margin: 0 auto;
}
.rail .lbl { display: var(--lbl); }
.rail nav a.on {
    background: var(--rail-soft);
    color: #FFFFFF;
    border-left-color: var(--green);
    font-weight: 600;
}
.rail nav a:hover { color: #FFFFFF; }

/* ————— Der Schalter: eine Navigationszeile, die keine ist —————
   Ohne Skript bliebe er wirkungslos, darum zeigt ihn erst rail.js (data-railjs). */
.railswitch {
    display: none;
    align-items: center;
    justify-content: var(--nav-just);
    gap: 0.6rem;
    width: 100%;
    /* Luft zur Trennlinie des Fusses, an der er sonst klebte. */
    margin-bottom: 0.5rem;
    padding: var(--nav-pad);
    background: none;
    border: 0;
    border-left: 3px solid transparent;
    color: var(--storm-soft);
    font: inherit;
    font-size: var(--fs-fine);
    cursor: pointer;
}
html[data-railjs] .railswitch { display: flex; }
.railswitch:hover { color: #FFFFFF; }
.railswitch .ic::before { content: var(--rail-arrow); }

.rail .foot {
    padding: var(--foot-pad);
    text-align: var(--foot-align);
    border-top: 1px solid var(--midnight-soft);
    font-size: var(--fs-fine);
    color: var(--storm-soft);
    display: flex;
    flex-direction: column;
    gap: 0.2rem;
    overflow-wrap: anywhere;
}
.rail .foot .account { display: var(--lbl); }
/* Abmelden liest sich wie ein Navigationseintrag und verhält sich auch so: Der
   3px-Rand steht auch hier, damit das Zeichen genau unter denen der Navigation
   sitzt - deren Rand ist die Markierung des aktiven Eintrags. */
.rail .foot .btn.quiet {
    display: flex;
    justify-content: var(--nav-just);
    gap: 0.6rem;
    width: 100%;
    /* Statt der 1rem aus «form .btn»: eingeklappt fällt die Adresse darüber weg,
       und der Fuss stünde sonst fast leer. */
    margin-top: 0.4rem;
    /* Waagrecht null - den Abstand gibt das Polster des Fusses, wie bei der
       Adresse darüber; senkrecht dieselbe Zeilenhöhe wie ein Nav-Eintrag. */
    padding: 0.42rem 0;
    border-left: 3px solid transparent;
    color: var(--steel);
}
.rail .foot .btn.quiet:hover { color: #FFFFFF; }
/* Der Einstellungs-Link (Runde 13) trägt ein SVG-Zeichen wie die Navigation -
   dieselbe Grössenregel, nur steht er im Fuss und nicht in nav. Aktiv markiert
   er sich wie ein Nav-Eintrag: Der 3px-Rand ist schon da (btn.quiet). */
.rail .foot .ic svg {
    display: block;
    width: var(--ic-glyph);
    height: var(--ic-glyph);
    margin: 0 auto;
}
.rail .foot .btn.quiet.on {
    color: #FFFFFF;
    border-left-color: var(--green);
    font-weight: 600;
}

/* Die Schublade fürs Telefon: reine CSS, kein Skript. */
.rail-toggle { display: none; }
.rail-handle { display: none; }

.work { min-width: 0; display: flex; flex-direction: column; background: var(--paper); }

.topbar {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding: 0.6rem 1.1rem;
    background: var(--card);
    border-bottom: 1px solid var(--rule);
    flex-wrap: wrap;
}
.topbar .title { font-family: var(--head); font-size: var(--fs-h3); font-weight: 700; }

.body {
    padding: 1.1rem;
    display: flex;
    flex-direction: column;
    gap: 1rem;
    flex: 1;
    min-height: 0;
    overflow-y: auto;
    min-width: 0;
}

/* ————— Karten mit Kante statt Schatten ————— */
.card {
    background: var(--card);
    border: 1px solid var(--rule);
    border-radius: var(--r);
    padding: 0.85rem 1rem 1rem;
    min-width: 0;
}
/* Gestapelte Karten innerhalb einer Seite: Das gap von .body greift nur für
   direkte Kinder, die Karten liegen aber in der section der jeweiligen Maske. */
.card + .card { margin-top: 0.6rem; }
/* Abschnittstitel überall in der Karte - auch in Formularen, nicht nur als
   direktes Kind. Die Luft nach oben gehört hierher und nicht an jede Stelle
   einzeln: Der Titel gehört zum Folgenden, nicht zum Vorigen; nur der erste
   einer Karte sitzt oben an. */
.card h3 {
    font-size: var(--fs-small);
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--text-faint);
    font-weight: 700;
    margin-top: 1.25rem;
    margin-bottom: 0.6rem;
}
.card h3:first-child { margin-top: 0; }

/* ————— Tabellen: waagrecht scrollt die Karte, nie die Seite ————— */
.table-scroll { overflow-x: auto; }

/* Feste Spalten: Die Breite kommt aus der Kopfzeile (Breitenklassen unten),
   nie aus dem Inhalt - lange Inhalte brechen um, statt die Spalte zu dehnen
   oder die Nachbarin zu verdrängen. Spalten ohne Klasse teilen den Rest. */
table.t {
    width: 100%;
    border-collapse: collapse;
    font-size: var(--fs-small);
    table-layout: fixed;
}
table.t th {
    text-align: left;
    font-weight: 600;
    color: var(--text-faint);
    font-size: var(--fs-fine);
    letter-spacing: 0.04em;
    text-transform: uppercase;
    padding: 0.3rem 0.6rem 0.3rem 0;
    border-bottom: 1px solid var(--rule);
}
table.t th, table.t td { overflow-wrap: anywhere; }
th.w3 { width: 3rem; }
th.w4 { width: 4rem; }
th.w5 { width: 5rem; }
th.w6 { width: 6rem; }
th.w7 { width: 7rem; }
th.w9 { width: 9rem; }
th.w12 { width: 12rem; }
th.w13 { width: 13rem; }
th.w14 { width: 14rem; }
/* Unter diese Breite quetscht sich die Tourenliste nicht - sie scrollt. */
table.t.expandable { min-width: 58rem; }
table.t td {
    padding: 0.34rem 0.6rem 0.34rem 0;
    border-bottom: 1px solid var(--rule-soft);
    height: var(--row);
}
/* Rechtsbündige Zahlenspalte; ohne rechten Rand nur als letzte Spalte - sonst
   klebte die Zahl an der Nachbarspalte (aufgefallen in Runde 4). */
table.t td.n, table.t th.n { text-align: right; }
table.t td.n:last-child, table.t th.n:last-child { padding-right: 0; }
table.t tr:hover td { background: var(--rule-soft); }

/* ————— Handlung: Grün, nie der Zustand ————— */
.btn {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    padding: 0.32rem 0.7rem;
    border: 1px solid var(--rule);
    background: var(--card);
    color: var(--text);
    border-radius: var(--r-sm);
    font: inherit;
    font-size: var(--fs-small);
    cursor: pointer;
    text-decoration: none;
}
.btn.primary { background: var(--action); border-color: var(--action); color: var(--on-action); font-weight: 600; }
.btn.primary:hover { background: var(--action-hover); border-color: var(--action-hover); }
.btn.quiet { border-color: transparent; background: none; color: var(--action); padding: 0.32rem 0.2rem; }
/* Richtlinie 17: eine Handlung, die nicht geht, steht sichtbar gesperrt und sagt
   im Titel warum - dieselbe Form wie das kontextfeste Feld aus Richtlinie 14.
   .off ist dasselbe Aussehen für einen Anker: der kennt kein disabled, ohne
   href ist er weder anklickbar noch fokussierbar. */
.btn[disabled], .btn.off { opacity: 0.45; cursor: not-allowed; }
.btn:focus-visible, a:focus-visible, input:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }

/* ————— Zustände: Bernstein «wartet», Rot «stimmt nicht» ————— */
.chip {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    font-size: var(--fs-fine);
    padding: 0.05rem 0.45rem;
    border-radius: 99px;
    border: 1px solid var(--rule);
    color: var(--text-soft);
    background: var(--rule-soft);
    white-space: nowrap;
}
.chip.warn { color: var(--amber); background: var(--amber-bg); border-color: var(--amber-line); }
.chip.bad { color: var(--red); background: var(--red-bg); border-color: var(--red-line); }
.chip .dot { width: 0.4rem; height: 0.4rem; border-radius: 99px; background: currentColor; }

/* Ein <summary> ist immer ein Schalter, nie Text: ohne diese Zeile zeigt der
   Zeiger die Schreibmarke, und die Zeile liest sich als Absatz, den man
   versehentlich markiert. Zwei der drei Aufklapper der Oberfläche sagten es
   bisher einzeln - «+ Filter» über .btn, die Aufklappzeile der Tourenliste über
   details.rowdetails -, der dritte (die übersprungenen Mitarbeiter der
   Statistik) war schlicht vergessen. Also hier, einmal für alle. */
summary { cursor: pointer; }

/* ————— Formulare ————— */
label {
    display: block;
    margin-top: 0.75rem;
    font-size: var(--fs-small);
    color: var(--text-soft);
}

input, select, textarea {
    display: block;
    width: 100%;
    max-width: 24rem;
    padding: 0.45rem 0.55rem;
    margin-top: 0.15rem;
    border: 1px solid var(--rule);
    border-radius: var(--r-sm);
    font: inherit;
    font-size: var(--fs-small);
    background: var(--card);
    color: var(--text);
}

form .btn { margin-top: 1rem; }

/* ————— Auskunftslisten und Meldungen ————— */
.facts dt { font-size: var(--fs-small); color: var(--text-faint); }
.facts dd { margin: 0 0 0.75rem; }

.flash {
    padding: 0.5rem 0.75rem;
    border-left: 3px solid var(--action);
    background: var(--action-bg);
    border-radius: var(--r-sm);
    font-size: var(--fs-small);
}
.notice {
    padding: 0.5rem 0.75rem;
    border-left: 3px solid var(--rule);
    background: var(--rule-soft);
    border-radius: var(--r-sm);
    font-size: var(--fs-small);
}
.error {
    padding: 0.5rem 0.75rem;
    border-left: 3px solid var(--red-line);
    background: var(--red-bg);
    color: var(--red);
    border-radius: var(--r-sm);
    font-size: var(--fs-small);
}
.field-error { display: block; color: var(--red); font-size: var(--fs-fine); }

/* Zwei Felder, die zusammengehören (Start/Ende, Kilometerstand Start/Ende):
   nebeneinander, solange beide ihre Breite bekommen, sonst untereinander.
   <p>
   auto-fit mit einer Untergrenze statt einer Medienabfrage - das Paar richtet
   sich nach der Breite, die es tatsächlich hat, und nicht nach der des Fensters:
   Dieselbe Maske steht bei eingeklappter Schiene 170px breiter da, und eine
   Schwelle müsste raten, welcher Fall gerade gilt. Unter 13.5rem je Spalte passt
   ein «31.08.2026, 06:01» nicht mehr, dann bricht es um. Die leere dritte Spur,
   die eine breite Karte zuliesse, fällt bei auto-fit in sich zusammen - es
   bleiben immer genau zwei gleiche Hälften.
   align-items: start, damit die rechte Hälfte nicht mitwächst, wenn die linke
   einen Hinweis oder eine Fehlermeldung trägt. */
.fieldpair {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(13.5rem, 1fr));
    gap: 0 1rem;
    align-items: start;
}

/* Beiläufige Hinweise («Keine Mail erhalten?», Fussnoten unter Tabellen). */
.hint { display: block; color: var(--text-hint); font-size: var(--fs-fine); margin-top: 0.5rem; }

/* Der Hinweis unter einem Eingabefeld («mindestens 12 Zeichen»): immer sichtbar,
   aber klein und grau - dezent statt aufdringlich. */
.field-hint { display: block; color: var(--text-hint); font-size: var(--fs-fine); margin-top: 0.25rem; }

h1, h2 { font-family: var(--head); }

/* ————— Die Anmeldeseite: Solo-Karte, ohne Schiene ————— */
.solo {
    min-height: 100vh;
    display: grid;
    place-items: center;
    padding: 1.5rem;
}
.solo .card.auth { width: 100%; max-width: 22rem; padding: 1.5rem 1.5rem 1.75rem; }
.solo .wordmark { width: 8.5rem; display: block; margin-bottom: 1rem; }
.solo h1 { font-size: var(--fs-h2); margin-bottom: 0.75rem; }
.solo .error, .solo .notice { margin-bottom: 0.5rem; }

/* ————— Schwellen: 1100 Schiene eingeklappt, 760 Schublade —————
   Beide Schwellen setzen nur noch den Wertesatz, nicht die Regeln. Die 1100er
   ist die blosse Vorgabe (:root) und weicht der Wahl des Admins; die 760er
   schreibt auf :root[data-rail] und wiegt damit schwerer als jede Wahl - eine
   Schublade mit versteckten Beschriftungen wäre keine. */
@media (max-width: 1100px) {
    :root {
        --rail-now: var(--rail-w-min);
        --brand-pad: 0 0.6rem 1.1rem;
        --nav-just: center;
        --nav-pad: 0.42rem 0;
        --foot-pad: 0.7rem 0.4rem 0;
        --foot-align: center;
        --lbl: none;
        --ic-size: 1.25rem;
        --ic-w: 1.7rem;
        --ic-glyph: 1.5rem;
        --rail-arrow: '›';
        --logo: none;
        --logomark: block;
    }
}

@media (max-width: 760px) {
    :root, :root[data-rail] {
        --brand-pad: 0 1rem 1.1rem;
        --nav-just: flex-start;
        --nav-pad: 0.55rem 1rem;
        --foot-pad: 0.7rem 1rem 0;
        --foot-align: left;
        --lbl: inline;
        --ic-size: 1em;
        --ic-w: 1.3rem;
        --ic-glyph: 1.15rem;
        --logo: block;
        --logomark: none;
    }
    .app { grid-template-columns: 1fr; }
    .rail {
        position: fixed;
        inset: 0 30% 0 0;
        z-index: 10;
        transform: translateX(-100%);
        transition: transform 0.15s ease-out;
    }
    .rail-toggle:checked ~ .rail { transform: none; }
    /* In der Schublade steht schon der ☰-Griff; ein zweiter Schalter darin wäre
       einer zu viel. */
    html[data-railjs] .railswitch { display: none; }
    .rail-handle {
        display: inline-flex;
        font-size: 1.1rem;
        cursor: pointer;
        color: var(--text-soft);
    }
    table.t td { height: 44px; }
}

/* ————— Listenkopf: Handlung links, Filter in eigener Zeile darunter ————— */
.actions {
    display: flex;
    align-items: center;
    gap: 1rem;
    flex-wrap: wrap;
    /* Luft zur Karte darunter - ohne sie sass die Schaltfläche direkt auf der Kante. */
    margin-bottom: 0.9rem;
}

/* ————— Filterleiste (Richtlinie 16) —————
   Eine Zeile: «+ Filter», die wirkenden Filter als Chips, am Ende der Ausgang.
   Sie bricht um, statt zu schrumpfen - auf dem Telefon stehen die Chips
   untereinander und «Alle zurücksetzen» rutscht unter die Zeile. */
.filterbar {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.4rem 0.5rem;
    margin-bottom: 0.9rem;
}
/* Luft nach oben nur dort, wo eine Karte über der Leiste steht - erstmals in der
   Fahrzeugliste (Runde 10) mit ihren Bestandskacheln. Sonst folgt die Leiste der
   Aktionsleiste, und die bringt ihren Abstand selbst mit; eine feste Luft an der
   Leiste zählte dort doppelt. Dieselbe Bauart wie «.card + .card». */
.card + .filterbar { margin-top: 0.9rem; }

/* Das Menü hängt über der Liste, nicht in ihr: absolut positioniert, damit die
   aufgeklappte Auswahl die Tabelle darunter nicht verschiebt. */
.fmenu { position: relative; }
.fmenu > summary { display: inline-flex; align-items: center; list-style: none; }
.fmenu > summary::-webkit-details-marker { display: none; }
.fmenu > summary::marker { content: ""; }
.fmenu-body {
    position: absolute;
    z-index: 30;
    top: calc(100% + 0.35rem);
    left: 0;
    min-width: 15rem;
    padding: 0.2rem 0.85rem 0.85rem;
    background: var(--card);
    border: 1px solid var(--rule);
    border-radius: var(--r);
    box-shadow: 0 10px 24px rgba(var(--shade), 0.16);
}
.fmenu-body form + form { border-top: 1px solid var(--rule-soft); }
.fmenu-row { display: flex; align-items: center; gap: 0.4rem; margin: 0.3rem 0 0.7rem; }
/* Gleiche Innenhöhe wie .btn (0.32rem), sonst überragt das Feld die Schaltfläche.
   max-width: Eine Auswahl mit langer Tourbezeichnung darf das Menü nicht
   verzerren; der Browser kappt den gewählten Eintrag selbst mit «…». */
.fmenu-row select, .fmenu-row input { margin-top: 0; width: auto; max-width: 12rem;
                                      padding-top: 0.32rem; padding-bottom: 0.32rem; }
.fmenu-row .btn { margin-top: 0; }

/* Der Chip trägt die Handlungsfarbe - er ist ein Verweis, der etwas wegnimmt.
   Der feste Chip (stehende Vorgabe) bleibt neutral: Er ist eine Auskunft. */
.fchip {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    max-width: 22rem;
    padding: 0.28rem 0.6rem;
    border-radius: 99px;
    border: 1px solid var(--mark);
    background: var(--action-bg);
    color: var(--action);
    font-size: var(--fs-small);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    text-decoration: none;
}
.body a.fchip { color: var(--action); }
.fchip:hover { background: var(--action-bg-hover); }
.fchip .x { font-size: 1rem; line-height: 1; color: var(--text-soft); }
.fchip.fixed { border-color: var(--rule); background: var(--rule-soft); color: var(--text-soft); }

/* Leise am Ende der Zeile: ein Textverweis, kein zweiter Knopf. */
.freset { margin-left: auto; font-size: var(--fs-small); }
@media (max-width: 640px) {
    .freset { margin-left: 0; }
    .fchip { max-width: 100%; }
}

.form-actions { display: flex; align-items: center; gap: 0.75rem; margin-top: 1rem; }
.form-actions .btn { margin-top: 0; }

/* Links der Arbeitsfläche tragen die Handlungsfarbe, nicht das Browserblau. */
.body a { color: var(--action); }
/* Zeilen-Links in Tabellen ohne Unterstrich - die klickbare Zeile trägt die
   Handlung, der Link bleibt nur für die Bedienung ohne Skript. */
table.t a { text-decoration: none; }
/* Sortierbare Köpfe: der Link erbt die Kopf-Optik, der Pfeil zeigt die Richtung. */
table.t th a.sorth { color: inherit; text-decoration: none; }
table.t th a.sorth:hover { color: var(--text); }
.sorth .dir { margin-left: 0.2rem; font-size: 0.8em; color: var(--mark); }

/* ————— Übersicht (Mockup 1.5): Kopf, Kacheln, Tagestabellen ————— */
.card-head { display: flex; align-items: center; gap: 0.75rem; flex-wrap: wrap; }
.card-head h2 { font: 700 var(--fs-h2)/1.3 var(--head); }
.card .sub { color: var(--text-faint); font-size: var(--fs-small); }

.tiles {
    display: grid;
    gap: 0.75rem;
    grid-template-columns: repeat(auto-fit, minmax(10.5rem, 1fr));
}
.tile {
    border: 1px solid var(--rule);
    border-radius: var(--r-sm);
    padding: 0.55rem 0.7rem 0.6rem;
    min-width: 0;
}
.tile .k {
    font-size: var(--fs-fine);
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--text-faint);
}
.tile .v {
    font: 700 1.45rem/1.3 var(--head);
    font-variant-numeric: tabular-nums;
}
.tile .sub { font-size: var(--fs-fine); color: var(--text-faint); }
/* Rot nur, wo wirklich etwas anliegt - die Klasse wird bedingt gesetzt. */
.tile.flag { border-color: var(--red-line); background: var(--red-bg); }
.tile.flag .k, .tile.flag .v { color: var(--red); }

/* Die Live-Karte der Übersicht; die «ohne Signal»-Liste nutzt .rows.silent. */
.live-map {
    height: 17rem;
    border: 1px solid var(--rule);
    border-radius: var(--r-sm);
    margin-bottom: 0.6rem;
}
/* Die eigene Seite (Runde 11) gibt derselben Karte mehr Höhe - sie ist dort der
   Bildschirm und nicht eine Kachel unter anderen. Kein zweiter Kasten, nur ein
   anderes Mass; auf kleinen Geräten bleibt die Höhe der Übersicht, sonst
   schöbe die Karte die Tabelle unter den Falz. */
.live-map.tall { height: 26rem; }
@media (max-width: 760px) { .live-map.tall { height: 17rem; } }
.rows.silent { display: flex; flex-direction: column; }
.rows.silent li { padding: 0.25rem 0; }

/* Zustands-Pills der Detailseiten und die Fehlfarbe in Tabellenzellen (Runde 4). */
.pills { display: flex; gap: 0.4rem; margin-bottom: 0.9rem; }
.chip.ok { color: var(--mark); background: var(--action-bg); border-color: var(--mark); }
td .bad, .bad { color: var(--red); }

/* Die leere Handlungszelle in Tabellen (Richtlinie 15): Der gestrichelte Rahmen
   steht dauerhaft und lädt zum Erfassen ein - wie der freie Slot im Wochenraster;
   Hover und Fokus wechseln nur die Farbe auf Handlungsgrün. */
.freecell { display: inline-block; padding: 0.05rem 0.4rem; border: 1px dashed var(--rule);
            border-radius: var(--r-sm); color: var(--text-faint); text-decoration: none;
            white-space: nowrap; }
.freecell:hover, .freecell:focus-visible { border-color: var(--mark);
                                           color: var(--mark); }
form.inline { display: inline; margin: 0; }
form.inline .btn { margin-top: 0; }

/* Zeitraumzeilen der Bearbeiten-Seite: ein Formular je Zeile, sofort wirksam.
   Felder mit derselben Innenhöhe wie .btn, damit die Zeile eine Linie bleibt. */
.periodrow { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; }
.periodrow input, .periodrow select {
    width: auto;
    margin-top: 0;
    padding-top: 0.32rem;
    padding-bottom: 0.32rem;
}
.periodrow .btn { margin-top: 0; }
/* Neben weissen Feldern sähe die weisse Sekundär-Schaltfläche wie ein weiteres
   Feld aus - in Inline-Zeilen trägt sie deshalb die getönte Handlungsfarbe. */
.periodrow .btn:not(.primary):not(.quiet) {
    background: var(--action-bg);
    border-color: transparent;
    color: var(--action);
    font-weight: 600;
}
.periodrow .btn:not(.primary):not(.quiet):hover { background: var(--action-bg-hover); }

/* Neue Zeitraumzeilen stapeln sich über den Knöpfen; «+ Zeitraum» bleibt unten. */
#newperiods .periodrow { margin-bottom: 0.5rem; }
.addcontrols { margin-top: 0.6rem; }
form.periodrow { margin-bottom: 0.5rem; }
.periodrow .fldlabel { font-size: var(--fs-small); color: var(--text-soft); }
.error.banner {
    padding: 0.6rem 0.8rem;
    border: 1px solid var(--red-line);
    border-radius: var(--r-sm);
    background: var(--red-bg);
    color: var(--red);
    font-size: var(--fs-small);
    margin-bottom: 0.9rem;
}

/* Bereichsreiter (Runde 4): Touren · Paketkategorien · Preise. */
.tabs {
    display: flex;
    gap: 0.25rem;
    margin-bottom: 0.9rem;
    border-bottom: 1px solid var(--rule);
}
.tabs a {
    padding: 0.4rem 0.75rem;
    font-size: var(--fs-small);
    color: var(--text-soft);
    text-decoration: none;
    border-bottom: 2px solid transparent;
    margin-bottom: -1px;
}
.tabs a.on {
    color: var(--text);
    font-weight: 600;
    border-bottom-color: var(--action);
}
.tabs a:hover { color: var(--text); }

/* Sieben Wochentagsmarken (Mockup 4.2) - gesetzt heisst gefahren. Wird die
   Spalte zu schmal, klappen die Marken in eine zweite Zeile, statt sich zu
   verzerren oder über die Nachbarspalte zu laufen. */
.days { display: inline-flex; gap: 0.15rem; flex-wrap: wrap; }
.day {
    min-width: 1.5rem;
    text-align: center;
    padding: 0.05rem 0.2rem;
    font-size: var(--fs-fine);
    color: var(--text-faint);
    background: var(--rule-soft);
    border-radius: var(--r-sm);
}
.day.on { color: var(--on-action); background: var(--action); }

/* Die Wochentagswahl im Formular: runde Tagesmarken statt roher
   Kontrollkästchen. Das Kästchen bleibt im Markup - es trägt Bindung, Tastatur
   und Screenreader -, liegt aber unsichtbar über der Marke; den Zustand zeigt
   die Marke selbst (gewählt = grün gefüllt, wie die Einsatztage der Liste).
   ~ statt +: th:field rendert hinter jedem Kästchen ein verstecktes
   _weekdays-Feld, der direkte Nachbar der Checkbox ist also nie die Marke. */
/* Die Kategorienliste der Tour-Bearbeitung: zugewiesene Zeilen mit Entfernen,
   darunter Auswahl und Hinzufügen. */
#catrows { display: flex; flex-direction: column; gap: 0.35rem; margin-bottom: 0.5rem; }
.catrow {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    font-size: var(--fs-small);
}
.catrow .catname { font-weight: 600; }
.catrow .removecategory { margin-left: auto; }
/* Ohne den Formular-Knopfabstand (form .btn) - er machte jede Zeile hoch. */
.catrow .btn { margin-top: 0; }

.daypick { display: flex; gap: 0.4rem; flex-wrap: wrap; margin-bottom: 0.25rem; }
.daypick label { position: relative; display: inline-flex; margin: 0; }
.daypick input {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    margin: 0;
    opacity: 0;
    cursor: pointer;
}
.daypick span {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.3rem;
    height: 2.3rem;
    border-radius: 999px;
    border: 1px solid var(--rule);
    background: var(--card);
    color: var(--storm);
    font-size: var(--fs-small);
    font-weight: 600;
    user-select: none;
    transition: background 0.12s, color 0.12s, border-color 0.12s;
}
.daypick input:hover ~ span { border-color: var(--storm-soft); }
.daypick input:checked ~ span {
    background: var(--green);
    border-color: var(--green);
    color: var(--on-action);
}
.daypick input:focus-visible ~ span {
    outline: 2px solid var(--mark);
    outline-offset: 2px;
}
.lbl-line {
    display: block;
    font-size: var(--fs-small);
    color: var(--text-soft);
    margin: 0.75rem 0 0.35rem;
}

/* Die Aufklappzeile der Tourenliste: mehr in der Zeile, kein Seitenwechsel.
   Das Dreieck bleibt sichtbar - es ist die einzige Einladung zum Aufklappen.
   Das <details> in der ersten Zelle ist nur der Schalter; der Inhalt steht in
   der Folgezeile (.detailrow, colspan) über die volle Breite - im eigenen Feld
   läge er über den Nachbarspalten, sobald deren Text mehrzeilig umbricht. */
details.rowdetails summary {
    font-weight: 600;
    list-style-position: inside;
    /* Fixe Spaltenbreite: eine lange Kurzbezeichnung muss im Wort umbrechen
       können, sonst läuft sie über die Bezeichnungsspalte. */
    overflow-wrap: anywhere;
}
tr.detailrow { display: none; }
tr:has(details[open]) + tr.detailrow { display: table-row; }
/* Offen gehören beide Zeilen optisch zusammen - die Trennlinie dazwischen fällt weg. */
tr:has(details[open]) td { border-bottom: none; }
/* Zweispaltig: Beschreibung links, Kategorien rechts - die Zeile bleibt kurz.
   Auf schmalen Schirmen stapeln die Hälften wieder untereinander. */
.descrow {
    padding: 0.15rem 0 0.4rem 1rem;
    font-size: var(--fs-small);
    max-width: 60rem;
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0 1.5rem;
}
.descrow .desc, .descrow .cats { min-width: 0; }
@media (max-width: 60rem) {
    .descrow { grid-template-columns: 1fr; }
}
.descrow b {
    display: block;
    font-size: var(--fs-fine);
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--text-faint);
    margin-top: 0.4rem;
}
.descrow ul { margin: 0.15rem 0 0; padding-left: 1.1rem; }
/* Die Bezeichnung klappt die Zeile mit auf - der Zeiger sagt es an. */
td.rowname { cursor: pointer; }
/* Eine ganze Zeile mit Ziel (data-href) ist klickbar - der Zeiger sagt es an. */
tr[data-href] { cursor: pointer; }
/* Die Zeile der Live-Lage führt nicht auf eine Seite, sondern stellt die Karte
   auf ihren Fahrer (Runde 11). Derselbe Zeiger, denn für den Benutzer ist es
   dasselbe Versprechen: Hier klicken bringt mich zu diesem Ding. Der
   Tastaturfokus bekommt seinen eigenen Rahmen, weil die Zeile kein Link ist und
   ihn also nicht mitbringt. */
tr.jumprow { cursor: pointer; }
tr.jumprow:focus-visible { outline: 2px solid var(--mark); outline-offset: -2px; }
/* Eine Tabelle mit Aufklappzeilen richtet sich an der Grundlinie aus: Text,
   Marken und Schaltflächenbeschriftung teilen sich die Schriftlinie (wirkt wie
   zentriert), und beim Öffnen kommt Inhalt nur unterhalb dazu - die erste Zeile
   springt nicht. Zentrierung spränge beim Öffnen, oben stünde der Text über der
   Schaltflächenmitte. */
table.t.expandable td { vertical-align: baseline; }
.rowactions { white-space: nowrap; }
.rowactions .btn { margin-right: 0.25rem; }

/* Schmale Geräte: aus der Tabelle wird die Kartenliste (Runde Layout).
   Beide stehen im Markup, CSS zeigt genau eine - keine Geräteerkennung. */
.rows { display: none; list-style: none; }
@media (max-width: 420px) {
    /* auto-fit hielte hier an seiner Mindestbreite fest und liefe über den
       Rand - zwei flexible Spalten schrumpfen mit. */
    .tiles { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    /* Nur die Tabelle mit Zwilling verschwindet - eine Karte, die keine
       Kartenliste (.cardlist) und keine Zeilenliste (.rows) trägt, behält ihre
       Tabelle und scrollt waagrecht. Vor dem Review vom 01.09.2026 traf die
       Regel jede Tabelle einer Karte, und neun Tabellen ohne Zwilling waren am
       Telefon ersatzlos weg: Live-Tabellen, erfasste Mengen im Einsatzdetail,
       die Formulartabellen von Korrektur und Nachtrag, «Seine eigenen Zeilen». */
    .card:has(.rows) table.t, .card:has(.cardlist) table.t { display: none; }
    .rows { display: flex; flex-direction: column; }
    .rows li {
        display: flex;
        flex-direction: column;
        gap: 0.1rem;
        padding: 0.5rem 0;
        border-bottom: 1px solid var(--rule-soft);
    }
    .rows li:last-child { border-bottom: 0; }
}

/* ————— Seitenpanel (Mockup 4b, Variante B) —————
   Anzeigen und Bearbeiten öffnen als Panel über der Tourenliste: von rechts,
   halbe Breite, volle Höhe. Der Zustand lebt in der URL - der Scrim ist ein
   Link zurück zur Liste, kein Skript. */
.scrim {
    position: fixed;
    inset: 0;
    background: rgba(var(--shade), 0.45);
    z-index: 40;
    cursor: default;
}
.panel {
    position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    /* Die Breite folgt den Bausteinen, nicht dem Bildschirm: Felder enden bei
       24rem, das Panel bei 30rem - halbe Bildschirmbreite liess rechts nur
       Leerfläche. .wide (44rem) steht für Inhalte bereit, die wirklich mehr
       brauchen (Richtlinie 1); zurzeit kommt jedes Panel mit 30rem aus. */
    width: 30rem;
    max-width: 100vw;
    background: var(--card);
    border-left: 1px solid var(--rule);
    box-shadow: -12px 0 30px rgba(var(--shade), 0.25);
    display: flex;
    flex-direction: column;
    z-index: 41;
}
.panel-head {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding: 0.7rem 1.1rem;
    border-bottom: 1px solid var(--rule);
}
/* Richtlinie 13 im Panelkopf: Eine lange Bezeichnung - «Stopps LS CP Sonderfahrt
   Wochenende» - sprengte sonst die Panelbreite und schöbe das Schliesskreuz aus dem
   Bild. min-width: 0 ist der Teil, ohne den text-overflow hier nichts tut: Ein
   Flex-Kind schrumpft von sich aus nie unter seinen Inhalt. Der volle Wortlaut hängt
   als title an der Überschrift, wie in den Tabellen. */
.panel-head h2 {
    margin: 0;
    font-size: 1.05rem;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.panel-close {
    /* Das Kreuz gibt nichts ab: Gekappt wird die Überschrift, nicht der Weg hinaus. */
    flex: none;
    margin-left: auto;
    color: var(--storm);
    text-decoration: none;
    font-size: 1.1rem;
    line-height: 1;
    padding: 0.2rem 0.4rem;
}
.panel-body {
    flex: 1;
    overflow-y: auto;
    padding: 0.9rem 1.1rem 1.1rem;
    min-width: 0;
}
.panel-body h3 { margin-top: 1.25rem; }
.panel-body > h3:first-child, .panel-body form > h3:first-child { margin-top: 0; }
/* Der Sofort-Hinweis steht direkt unter der Abschnittsüberschrift. */
.hint.immediate { display: block; margin: -0.35rem 0 0.5rem; }
/* Die Begründung einer gesperrten Löschung (Richtlinie 17) steht als eigenes
   Flex-Kind zwischen Rumpf und Fuss - also ausserhalb des Rumpfs, von dem jede
   andere Zeile ihren Einzug bezieht. Denselben Einzug wie .panel-body und
   .panel-foot, sonst klebte genau der eine Satz am Rand. */
.panel > .hint { padding: 0 1.1rem; }
.panel-foot {
    display: flex;
    gap: 0.75rem;
    justify-content: flex-end;
    align-items: center;
    padding: 0.65rem 1.1rem;
    border-top: 1px solid var(--rule);
    background: var(--paper);
}
/* Die destruktive Handlung (Entfernen) steht abgesetzt ganz links im Fuss. */
.panel-foot .btn.footleft { margin-right: auto; }
/* Stehen zwei davon nebeneinander - heute «Löschen» und «Beenden» im Lohnpanel,
   künftig weitere Paare -, trägt nur der letzte den Abstandhalter: Sonst teilten
   sich zwei auto-Ränder den freien Platz und rissen das Paar mitten im Fuss
   auseinander. */
.panel-foot .btn.footleft:has(+ .btn.footleft) { margin-right: 0; }
/* Zeilenknopf als ✕-Icon: rund, gefasst und von Grund auf rot - Entfernen ist
   die destruktive Handlung. Hover/Fokus füllen ihn satt statt nur zu tönen.
   Der Wortlaut bleibt als title und aria-label erhalten. */
.btn.iconbtn {
    width: 1.6rem;
    height: 1.6rem;
    padding: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: 1px solid var(--red-line);
    border-radius: 99px;
    background: var(--red-bg);
    color: var(--red);
    font-size: 0.8rem;
    line-height: 1;
}
/* Gesperrt bleibt es blass: .off setzt nur opacity und cursor, und ein Kreuz,
   das unter der Maus satt rot aufleuchtete, läse sich wie ein handelndes -
   genau die Verwechslung, die die Sperre verhindern soll (Richtlinie 17). */
.btn.iconbtn:not(.off):hover, .btn.iconbtn:not(.off):focus-visible {
    border-color: var(--red);
    background: var(--red);
    color: var(--on-action);
}
/* Beide ✕ auf einer Flucht am rechten Rand - die Zeitraumzeile stellt ihren
   sonst mitten in der Zeile ab, die Kategorienzeile tut es schon. */
.periodrow .iconbtn { margin-left: auto; }
/* Vorbelegtes, nicht wählbares Feld (Tour aus der Rasterzelle): Wert als Text. */
.panel-body .lockedfld { margin: 0.15rem 0 0.6rem; font-weight: 600; }
/* Modellabhängige Feldgruppen (Lohn-Maske): das fieldset ist nur der Schalter
   fürs Umblenden, kein sichtbarer Rahmen. */
.panel-body fieldset { border: 0; margin: 0; padding: 0; min-width: 0; }
.panel-body .checkline { display: flex; align-items: center; gap: 0.5rem; margin-top: 0.9rem; }
.panel-body .checkline input { width: auto; margin: 0; }
/* Mehrfachauswahl der Träger (Nachtrag Runde 3, 01.09.2026): eine scrollende Box
   statt einer langen Checkbox-Kolonne - ein Klick wählt an oder ab, kein
   Ctrl-Klick. Die Zeilen rücken enger als freistehende .checkline. */
.panel-body .pickbox {
    border: 1px solid var(--rule);
    border-radius: var(--r-sm);
    max-height: 11rem;
    overflow-y: auto;
    padding: 0.15rem 0.6rem 0.45rem;
    margin-top: 0.35rem;
}
.panel-body .pickbox .checkline { margin-top: 0.45rem; }
/* Satzliste je Kategorie (Lohn-Maske und Anzeige): Sie steht ohne Scrollrahmen
   im Panel und darf deshalb nirgends überstehen. Das Zahlenfeld füllt seine
   Spalte, statt mit fester Breite (.t td input, 6.5rem) breiter zu sein als
   sie - der Überstand erzeugte im Rahmen erst den waagrechten Balken und mit
   ihm den senkrechten. Lange Kategorienamen kappt die Zelle mit «…»
   (Richtlinie 13); der volle Wortlaut steht als title-Tooltip.
   table.t.ratetable, nicht .ratetable: Die feste Breite steht weiter unten in
   der Datei und gewänne bei gleicher Spezifität die Kaskade - dasselbe
   Kräftemessen wie beim Planungsraster. */
.ratetable td.namecell { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* Lange Bezeichnungen und Namenslisten kappen mit «…», der Titel trägt sie ganz
   (Richtlinie 13) - seit V033 tragen Lohn- und Abzugsregeln einen frei gewählten
   Namen, und die Spalte «Mitarbeiter» zählt drei Nachnamen auf. Die Regel galt
   bisher nur in der Satztabelle des Panels; sie gilt jetzt in jeder Tabelle. */
.t td.namecell { max-width: 16rem; white-space: nowrap; overflow: hidden;
                 text-overflow: ellipsis; }
/* Die Namen hinter der Zahl - dieselbe Zeile, leiser Ton. */
.t td.namecell .faint { color: var(--text-faint); }
table.t.ratetable td input[type="number"] { width: 100%; }

/* Schmale Schirme: das Panel nimmt die ganze Breite. */
.panel.wide { width: 44rem; }
@media (max-width: 72rem) {
    .panel, .panel.wide { width: 100vw; }
}

/* Das hidden-Attribut gewinnt immer - sonst überstimmt jede display-Regel
   (etwa .btn) das display:none des Browsers, und «Anlegen» stünde von Anfang
   an neben «+ Zeitraum». */
[hidden] { display: none !important; }

/* Bei offenem Panel scrollt nur dessen Körper - der Hintergrund steht still,
   sonst wandert beim Rollen die Liste unter dem Panel mit. */
.app:has(.panel) .body { overflow: hidden; }

/* Wochenraster der Planung (Mockup 5.1). Die Matrix scrollt in ihrer Karte,
   nie die Seite; unter 420px übernimmt die Tagesansicht (Runde-5-Nachtrag). */
.weekbar { align-items: center; gap: .5rem; }
.weekbar .weeklabel { font-weight: 700; }
.grid-t th.wd { text-align: center; min-width: 7rem; }
.grid-t th.wd.we { opacity: .6; }
/* «heute» erkennbar (01.09.2026): Markenfarbe statt gleicher Tinte - und ein
   Wochenend-Heute bleibt voll deckend, sonst dämpfte .we die Hervorhebung. */
.grid-t th.wd.today { color: var(--mark); }
.grid-t th.wd.we.today { opacity: 1; }
/* Lange Bezeichnungen sprengen die Spalte nicht: gekappt mit «…», der volle
   Wortlaut steht als title-Tooltip an der Zelle - im Raster wie in der Liste. */
.rowlinked td.tourcell { max-width: 14rem; white-space: nowrap;
                         overflow: hidden; text-overflow: ellipsis;
                         font-weight: 600; }
/* Die Matrix trägt feine Trennlinien rechts wie im Mockup - erst sie rahmen
   die leeren Tageszellen («+ planen»); die Tourspalte mit kräftigerer Kante.
   Symmetrisches Padding statt des Tabellenstandards (links 0, rechts 0.6rem),
   sonst klebt die Zellbox am linken Spaltenrand. */
/* table.t.grid-t, nicht .grid-t: Das Padding muss die Standardregel
   «table.t td» (0-1-2) überstimmen - .grid-t td (0-1-1) verlor die Kaskade,
   und die Box klebte weiter am linken Spaltenrand. */
table.t.grid-t td { border-right: 1px solid var(--rule-soft); padding: 0.25rem 0.3rem; }
table.t.grid-t td:last-child { border-right: 0; }
table.t.grid-t td.tourcol { white-space: nowrap; max-width: 11rem;
                     border-right: 1px solid var(--rule); }
.grid-t td.tourcol b { display: block; overflow: hidden; text-overflow: ellipsis; }
.grid-t td.tourcol .sub { display: block; color: var(--text-faint); font-size: var(--fs-fine);
                          overflow: hidden; text-overflow: ellipsis; }
.cell { display: block; min-height: 2.2rem; padding: .15rem .3rem; border: 1px solid transparent;
        border-radius: var(--r-sm); text-decoration: none; color: inherit; }
.cell + .cell { margin-top: .15rem; }
.cell .who { display: block; }
.cell .st { display: block; font-size: .72rem; color: var(--text-faint); }
.cell.set { border-color: var(--rule); background: var(--set); }
.cell.done { border-color: var(--rule); background: var(--card); }
.cell.warn { border-color: var(--red); background: var(--red-bg); color: var(--red); }
.cell.warn .st { color: var(--red); }
/* Der leere Slot trägt immer den gestrichelten Rahmen - er zeigt den freien
   Platz; beim Überfahren wechselt nur die Farbe auf Handlungsgrün. */
.cell.free { color: var(--text-faint); border: 1px dashed var(--rule); }
.cell.free:hover, .cell.free:focus-visible { border-color: var(--mark);
                                             color: var(--mark); }
.cell.off { background: repeating-linear-gradient(135deg, transparent, transparent 4px,
            var(--set) 4px, var(--set) 8px); }
.cell.self { border-left: 3px solid var(--mark); }
.legend { display: flex; gap: 1.25rem; flex-wrap: wrap; margin-top: .6rem;
          font-size: .72rem; color: var(--text-faint); }
/* Jede Legendenmarke ist ein Mini-Abbild ihrer Zelle (Mockup 5.1) - auch die
   Herkunftskante «selbst gestartet oder übernommen» als grüne linke Kante.
   «Abgeschlossen» bleibt die weisse Karte mit Rand: erledigt trägt bewusst
   keine Farbe (Runde Layout). */
.legend span::before { content: ""; display: inline-block; width: .7rem; height: .7rem;
                       margin-right: .3rem; border-radius: 2px; vertical-align: -1px;
                       border: 1px solid var(--rule); background: var(--card); }
.legend .l-set::before { background: var(--set); }
.legend .l-warn::before { border-color: var(--red); background: var(--red-bg); }
.legend .l-off::before { background: repeating-linear-gradient(135deg, transparent,
                         transparent 2px, var(--set) 2px, var(--set) 4px); }
.legend .l-self::before { border-left: 3px solid var(--mark); }
.legend .l-done::before { background: var(--card); border-color: var(--rule); }

/* Tagesansicht: die Antwort des Telefons auf die Matrix. */
.dayview { display: none; }
.dayview .daycard { border: 1px solid var(--rule); border-radius: 6px;
                    padding: .5rem .6rem; margin-top: .5rem; }
@media (max-width: 420px) {
    .gridwrap { display: none; }
    .dayview { display: block; }
}

/* Warnliste unter dem Wochenraster (Mockup 5.1): alarmfarbene Sätze mit linkem
   Rand, die ruhigen Hinweise in Storm Blue - ein Ton je Zeile. */
.notices { margin-top: 0.9rem; }
.notices .subhead { font-size: var(--fs-h3); margin-bottom: 0.4rem; }
.banner {
    padding: 0.5rem 0.75rem;
    border-left: 3px solid var(--red-line);
    background: var(--red-bg);
    color: var(--red);
    border-radius: var(--r-sm);
    font-size: var(--fs-small);
}
.notices .banner { margin: 0.4rem 0; }
/* Dieselben Sätze im Anzeige-Panel des Einsatzes: gleiche Luft zwischen zwei
   Warnungen. Eigene Klasse statt «.panel-body .banner» - die griffe auch den
   Fehlerbannern der übrigen Panels in die Kaskade. */
.panelnotices .banner { margin: 0.4rem 0; }
.banner.calm {
    border-left-color: var(--storm-soft);
    background: var(--calm-bg);
    color: var(--text-soft);
}
/* READ_ONLY (Runde 12): Bernstein, kein Rot - «wartet auf Zahlung» ist ein
   Wartezustand, kein Fehler. */
.banner.amber {
    border-left-color: var(--amber-line);
    background: var(--amber-bg);
    color: var(--amber);
}
.banner.amber .btn { margin-left: 0.6rem; }

/* Die Sperre als Rahmenlogik (Mockup 12.3): eine Regel schaltet die
   Schreibhandlungen aller Bereiche stumm - nicht erst eine Fehlermeldung nach
   dem Klick; der Server weist den Rest ab. Auf der Abo-Seite selbst läuft der
   Wächter nicht (offener Pfad), dort trägt der Rahmen kein Attribut und alles
   bleibt bedienbar - bezahlt wird ja genau dort. */
.app[data-subscription="readonly"] .body button[type="submit"],
.app[data-subscription="readonly"] .body a.btn.primary {
    pointer-events: none;
    opacity: 0.45;
}

/* Einsatzliste (Mockup 5.2): Herkunft als Hinweiszeile, «fehlt» alarmrot,
   Kartenliste unter 420px statt der Tabelle - dieselbe Antwort wie die
   Tagesansicht auf die Matrix. */
.hintline { display: block; font-size: .72rem; color: var(--text-hint); }
td.alarm, .alarm { color: var(--red); }
.cardlist { display: none; }
.cardlist .listcard { display: block; border: 1px solid var(--rule);
                      border-radius: var(--r); padding: .5rem .6rem; margin-top: .5rem;
                      text-decoration: none; color: inherit; }
.cardlist .listcard span { display: block; font-size: var(--fs-small); }
/* Die leise Beifügung bleibt leise: «.cardlist .listcard span» (0,3,0) schlüge
   sonst .hintline (0,1,0) auf die Schriftgrösse, und «läuft» stünde so gross da
   wie die Wertzeile darunter. Dieselben .72rem wie in Zeile 1121. */
.cardlist .listcard .hintline { font-size: .72rem; }
/* Seit Runde 3 Paket A tragen auch die Tabellen der Abzugsarten und der
   drei Gültigkeitsgruppen der globalen Abzüge die Klasse .rowlinked - eine
   eigene Regel dafür wäre nur eine zweite Fassung derselben gewesen. */
@media (max-width: 420px) {
    .card:has(.cardlist) .rowlinked { display: none; }
    .cardlist { display: block; }
}
.pager { display: flex; align-items: center; gap: .5rem; margin-top: .75rem;
         font-size: var(--fs-small); }

/* Einsatz-Detail (Runde 5 Paket B, Mockup 5.4): Zahlen links, Foto rechts -
   der Admin hält die gemeldete Zahl gegen den Beleg. Unter 72rem stapeln sich
   die beiden Karten, das Foto rutscht unter die Tabelle. */
.detail-split { display: grid; grid-template-columns: minmax(0, 1fr) 22rem;
                gap: 0.6rem; align-items: start; }
@media (max-width: 72rem) {
    .detail-split { grid-template-columns: minmax(0, 1fr); }
}
/* Im Raster stellt das gap den Abstand - der Stapelabstand von .card + .card
   schöbe die rechte Karte sonst unter die Oberkante der linken. */
.detail-split > .card + .card { margin-top: 0; }
/* Die Zeilenknöpfe («Mitarbeiter öffnen») stehen neben dem Wert, nicht darunter. */
.facts dd .btn.quiet { margin: 0 0 0 0.4rem; }
.facts dd .hint { margin-top: 0.1rem; }
/* Die Bemerkung des Fahrers als Zitatblock: einmal geschrieben, nie geändert. */
.quote { margin: 0; padding: 0.5rem 0.75rem; border-left: 3px solid var(--storm-soft);
         background: var(--paper); border-radius: var(--r-sm); font-style: italic; }
.quote .src { display: block; margin-top: 0.3rem; font-style: normal;
              font-size: var(--fs-fine); color: var(--text-hint); }
/* Das Belegfoto mit der Lupe auf dem Bild - wer vergrössern will, klickt aufs
   Foto; die Schaltfläche darunter wäre ein zweiter Ort für dieselbe Absicht. */
.photo { position: relative; margin: 0; }
.photo img { display: block; width: 100%; max-height: 24rem; object-fit: contain;
             border: 1px solid var(--rule); border-radius: var(--r-sm);
             background: var(--paper); cursor: zoom-in; }
.photo .zoom { position: absolute; top: 0.5rem; right: 0.5rem; width: 1.8rem;
               height: 1.8rem; display: inline-flex; align-items: center;
               justify-content: center; border: 1px solid var(--rule);
               border-radius: 99px; background: var(--card); color: var(--storm);
               cursor: zoom-in; }
.photo .zoom:hover, .photo .zoom:focus-visible { color: var(--text); }
/* Ohne Beleg eine gestrichelte Leerfläche (Richtlinie 15): eine Auskunft,
   kein Fehler - der Fahrer lädt in der App nach. */
.photoempty { border: 1px dashed var(--rule); border-radius: var(--r-sm);
              padding: 2.5rem 1rem; text-align: center; color: var(--text-faint); }
.photoempty b { display: block; font-weight: 600; }
/* Die Überlagerung: das Bild in gross, Klick oder Escape schliessen. */
.lightbox { border: 0; padding: 0; background: transparent; max-width: 92vw;
            max-height: 92vh; }
.lightbox img { display: block; max-width: 92vw; max-height: 92vh;
                cursor: zoom-out; border-radius: var(--r-sm); }
.lightbox::backdrop { background: rgba(var(--shade), 0.7); }

/* Korrekturmaske (Mockup 5.5): das Belegfoto als kleines Vorschaubild neben der
   Tabelle - die Lupe öffnet dieselbe Überlagerung wie auf der Detailseite. */
.photo.thumb { max-width: 20rem; margin: 0.5rem 0 0; }
/* Die Neu-Spalte: ein schmales Zahlenfeld statt der vollen Formularbreite.
   display: inline-block ist der Teil, ohne den es nicht sitzt: Die Grundregel
   für Felder stellt jedes auf «block», und ein Block-Kasten kümmert sich nicht
   um das text-align seiner Zelle - er klebte links, während «Menge» und «Preis»
   rechts standen. Die Ziffern richten sich zusätzlich im Feld selbst aus, damit
   sie unter ihrer Kopfzeile stehen und nicht bloss der Rahmen. */
.t td input[type="number"] {
    display: inline-block;
    width: 6.5rem;
    margin: 0;
    text-align: right;
}
/* Bisherige Korrekturen: je Vorgang eine Zeile - wann und wer, was alt → neu,
   und die Begründung als eigener Satz darunter. */
.changelog { margin: 0.4rem 0 0; padding: 0; list-style: none; }
.changelog li { border-bottom: 1px solid var(--rule); padding: 0.45rem 0; }
.changelog li:last-child { border-bottom: 0; }
.changelog .when { display: block; font-size: var(--fs-fine); color: var(--text-faint); }
.changelog .delta b { font-weight: 700; }
.changelog .why { display: block; font-size: var(--fs-small); color: var(--text-soft);
                  font-style: italic; }

/* Runde 7: Der Arbeitsstapel (Mockup 7.1) - drei Fächer untereinander, jedes
   eine Karte mit ihrem Titel; Abstand und Titel kommen aus .card und .card h3.
   Neu ist nur der Erklärsatz des Krankmeldungs-Fachs: Er gehört unter seinen
   Titel und nicht als beiläufige Fussnote unter die Tabelle. */
.stack .lead { margin: -0.2rem 0 0.7rem; font-size: var(--fs-small);
               color: var(--text-soft); max-width: 60rem; }
/* Unter 420px wird jedes Fach eine Kartenliste - dieselbe Antwort wie die
   Einsatzliste, deren .cardlist/.listcard hier unverändert weiterbenutzt sind.
   Zu verstecken ist der Scrollrahmen und nicht die Tabelle: Sonst bliebe seine
   Kante als leerer Kasten stehen. Ein zweites .card in der Fachkarte gäbe es
   nicht - die Karte der Zeile bleibt .listcard, und weil sie ihren Knopf
   trägt, ist sie ein div und kein Link (ein Link im Link ist keiner). */
.stack .cardlist .listcard .btn { margin-top: 0.4rem; }
@media (max-width: 420px) {
    .stack .table-scroll { display: none; }
}

/* Das Abwesenheitspanel (Mockup 7.2): Unter jeder Abschnittsüberschrift steht
   Luft, und die betroffenen Einsätze sind je ein Satz und kein durchgehender
   roter Block. Eigens hier und nicht an .panel-body h3 bzw. .banner: Der
   globale Reset nimmt jedem Absatz seinen Rand, und .banner trägt seinen nur
   innerhalb von .notices - beide Regeln zu ändern stellte fremde Masken um. */
.absencepanel .panel-body > h3 { margin-bottom: 0.4rem; }
.absencepanel .panel-body > p { margin-bottom: 0.4rem; }

/* Runde 7: Monatsübersicht (Mockup 7.4) - Mitarbeiter mal Tag, F/K/offen. Das
   Raster ist eng, weil ein Monat einunddreissig Spalten hat: eine eigene
   Tabelle statt .t, deren Zeilenhöhe für Text gedacht ist. Die Farben kommen
   aus den Haustoken - Handlungsgrün für die genehmigten Ferien, Bernstein für
   die Krankheit, der gestrichelte Rand für das noch nicht Entschiedene
   (dieselbe Sprache wie die freie Zelle des Wochenrasters). */
.mgridwrap { overflow-x: auto; }
.mgrid { border-collapse: collapse; width: 100%; }
.mgrid th, .mgrid td { border: 1px solid var(--rule-soft); text-align: center;
                       min-width: 1.6rem; padding: 0.15rem 0.2rem;
                       font-size: var(--fs-fine); }
.mgrid thead th { color: var(--text-faint); font-weight: 600; }
.mgrid .who { text-align: left; white-space: nowrap; max-width: 11rem;
              overflow: hidden; text-overflow: ellipsis;
              padding: 0.15rem 0.5rem; font-size: var(--fs-small);
              border-right: 1px solid var(--rule); }
.mgrid .we { background: var(--set); }
.mgrid .today { outline: 2px solid var(--mark); outline-offset: -2px; }
.mcell { font-weight: 700; }
.mcell.f { background: var(--action-bg); color: var(--mark); }
.mcell.k { background: var(--amber-bg); color: var(--amber); }
/* Zwei Pixel, nicht einer: In der zusammengelegten Tabelle (border-collapse)
   entscheidet zuerst die Breite und erst dann der Stil - bei gleicher Breite
   gewönne der solide Rand der Nachbarzelle jede gemeinsame Kante, und der
   gestrichelte Rahmen des offenen Antrags wäre nie zu sehen. 2px ist zugleich
   die Sprache der heutigen Spalte. */
.mcell.open { border: 2px dashed var(--mark); color: var(--mark); }
.legend .l-f::before { background: var(--action-bg); border-color: var(--mark); }
.legend .l-k::before { background: var(--amber-bg); border-color: var(--amber-line); }
.legend .l-o::before { border: 2px dashed var(--mark); }
/* Jahresraster (Nachtrag Runde 7): dasselbe Raster eine Stufe gröber - zwölf
   Monatsspalten statt einunddreissig Tagen. Breiter, weil in der Zelle bis zu
   zwei Zahlen stehen und kein einzelnes Zeichen; die Summenspalte rechts wie die
   Namensspalte links durch eine kräftigere Kante abgesetzt. Der letzte
   Legendeneintrag ist eine Lesehilfe und keine Marke - er trägt kein Kästchen.
   Die Zelle selbst bleibt farblos: Sie kann zwei Arten tragen - fünf Tage
   Ferien und fünf Tage Krankheit sind zwei Zahlen, jede in ihrer Farbe, und
   keine gemeinsame Zehn. Die Summenspalte zählt dagegen verschiedene
   Kalendertage und ist deshalb manchmal kleiner als die Summe der Teilzahlen. */
.yeargrid th, .yeargrid td { min-width: 3.4rem; }
.yeargrid .total { border-left: 1px solid var(--rule); font-weight: 700; }
.yeargrid .p { display: inline-block; min-width: 1.1rem; padding: 0 0.2rem;
               border-radius: 3px; font-weight: 700; }
.yeargrid .p + .p { margin-left: 0.2rem; }
.yeargrid .p.f { background: var(--action-bg); color: var(--mark); }
.yeargrid .p.k { background: var(--amber-bg); color: var(--amber); }
.yeargrid .p.open { border: 1px dashed var(--mark); color: var(--mark); }
.legend .l-note::before { content: none; }
/* Der Wochenausschnitt fürs Telefon - dieselben Zellen, sieben Spalten. */
.monthnarrow { display: none; }
@media (max-width: 420px) {
    .monthwide { display: none; }
    .monthnarrow { display: block; }
}
/* Statistik (Runde 8): Balkendiagramme aus reinem CSS. Drei getrennte
   Diagramme zeigen je eine Grösse und brauchen keine Reihenfarben - eine
   Markierungsfarbe je Diagramm genügt, und das Vorjahr bleibt ein Umriss.
   Zwei Skalen in einem Bild liessen sich beliebig gegeneinander stauchen,
   bis jede gewünschte Aussage entsteht; darum hat jedes Diagramm seine
   eigene Achse und keines eine zweite. */
.chart { margin: 0 0 1.5rem; }
.chart figcaption { font-weight: 700; margin-bottom: 0.25rem; }
.chart figcaption .unit { font-weight: 400; color: var(--text-faint); margin-left: 0.5rem; }
/* Die senkrechte Achse trägt die Zahlen zu den Rasterlinien. Sie steht neben
   dem Rollkasten und nicht darin: Innen rollte sie mit dem Plot davon und wäre
   ab dem Juni nicht mehr zu sehen - eine Skala, die man verlieren kann, ist
   keine.
   Ihre Höhe ist die des Plots, ihr oberer Rand dessen Polster: So beginnt der
   Kasten genau an der Oberkante des Bildes, und die Zahlen sitzen auf ihren
   Linien. Jede hängt an ihrem Prozentwert und wird um ihre halbe Zeilenhöhe
   nach unten geschoben - bottom setzt die Unterkante, gemeint ist die Mitte.
   Die Breite steht fest, weil absolut gesetzte Kinder nichts zu ihr beitragen.
   Fünf Zeichen Zahl, ein Trennzeichen, zwei Nachkommastellen: 5rem trägt
   «14'000.00», und alle drei Diagramme beginnen dadurch an derselben Kante. */
.chart .plotrow { display: flex; align-items: flex-start; gap: 0.5rem; }
.chart .plotrow > .chartscroll { flex: 1; min-width: 0; }
.chart .yaxis { position: relative; flex: none; width: 5rem; height: 9rem;
                margin-top: 1.7rem; font-size: var(--fs-small);
                color: var(--text-faint); font-variant-numeric: tabular-nums; }
.chart .yaxis span { position: absolute; right: 0; transform: translateY(50%);
                     line-height: 1; white-space: nowrap; }
/* Der Engpass auf dem Telefon sind die Diagramme, nicht die Tabellen: Zwölf
   Säulen mit Vorjahresumriss auf 420 px wären rund 26 px je Säule und
   unlesbar. Das Diagramm scrollt darum waagrecht in seiner Karte, wie das
   Wochenraster - es staucht sich nicht.

   Das Polster unten ist keine Zierde, sondern die Bedingung dafür, dass die
   Monatsnamen überhaupt zu sehen sind: overflow-x: auto macht aus dem
   überstehenden overflow-y ein auto (CSS Overflow 3), und die Beschriftung
   hängt als absolut gesetzter Kasten 1.4rem unter dem Inhaltskasten. Ohne
   Polster läge sie im Rollbereich statt im Bild.

   1.6rem und nicht mehr: Die Zeile misst 0.8125rem mal 1.5 Zeilenhöhe, also
   1.21875rem, und reicht damit von 0.18rem bis 1.4rem unter den Plot - 0.2rem
   bleiben frei. Ein platzraubender Rollbalken sitzt am inneren Randkasten und
   damit unter dem Polster (CSS Overflow 3, «Scrollbars and Layout»), also
   nicht über der Beschriftung; auf einem System mit klassischen Rollbalken
   nachgemessen ist das nicht. */
/* Oben ein Polster aus demselben Grund: Das Wert-Etikett beim Überfahren steht
   über dem Plot, misst rund 1.65rem samt Abstand, und über ihm liegt nichts
   mehr, seit die Skala als Achse nach links gewandert ist. Ohne diesen Streifen
   ragte es über die Oberkante des Rollkastens - und nach oben lässt sich nicht
   rollen, das wäre schlicht abgeschnitten. Die Achse daneben trägt denselben
   Wert als margin-top, sonst stünden ihre Zahlen um das Polster verschoben. */
.chartscroll { overflow-x: auto; padding-top: 1.7rem; padding-bottom: 1.6rem; }
/* Vier Rasterlinien statt zweier Zahlen: Der yhint nennt nur 0 und die
   Obergrenze, und zwischen ihnen wäre jede Höhe zu schätzen. Der Verlauf legt
   sie bei 25, 50 und 75 % darunter - eine Deklaration, keine zusätzliche
   Auszeichnung.
   Die Wiederholung beginnt unten (to top) und misst ein Viertel: Ihre erste
   Linie liegt auf der Grundlinie und verschwindet unter deren border-bottom,
   ihre fünfte begänne genau an der Oberkante und fällt aus dem Kasten. Es
   bleiben die drei dazwischen. */
.chart .plot { display: flex; align-items: flex-end; gap: 0.4rem;
               height: 9rem; border-bottom: 1px solid var(--rule);
               background-image: repeating-linear-gradient(to top,
                                 var(--rule) 0 1px, transparent 1px 25%); }
.chart .colgroup { position: relative; display: flex; align-items: flex-end;
                   flex: 1; min-width: 2.4rem; height: 100%; text-decoration: none; }
.chart .colgroup .x { position: absolute; bottom: -1.4rem; left: 0; right: 0;
                      text-align: center; font-size: var(--fs-small);
                      color: var(--text-faint); }
.chart .bar { flex: 1; background: var(--mark); min-height: 1px; }
/* Ein Monat im Minus trägt dieselbe Marke wie ein negativer Lohn in der
   Monatstabelle (.bad, Zeile 670) - keine eigene Sprache für dieselbe Sache.
   Seine Höhe ist der Betrag: Ein Balken ohne Nulllinie kann nicht nach unten
   zeigen, und ihn auf null zu zeichnen hiesse «da war nichts». */
.chart .bar.bad { background: var(--red); }
.chart .bar.prev { background: none; border: 1px solid var(--rule); }
.chart .bar.prev.bad { border-color: var(--red); }
.chart .bar.running { background: repeating-linear-gradient(135deg,
                      var(--mark), var(--mark) 2px,
                      transparent 2px, transparent 5px);
                      border: 1px solid var(--mark); }
.chart .bar.running.bad { background: repeating-linear-gradient(135deg,
                          var(--red), var(--red) 2px,
                          transparent 2px, transparent 5px);
                          border-color: var(--red); }
/* ————— Der Aufbau: die Säulen wachsen aus der Grundlinie —————
   Die Bewegung ist keine Zierde, sie ist die Leserichtung des Bildes: Sie
   zeigt, wo die Achse liegt und in welche Richtung die Höhe gemeint ist.
   Animiert wird die Höhe und nicht transform: scaleY, weil der
   Vorjahresbalken ein 1-px-Umriss ist - gestaucht würde seine Rahmenstärke
   mitgestaucht und der Umriss zerliefe. Das Zielbild bleibt offen: Ohne
   to-Angabe nimmt der Browser den berechneten Wert, also die Inline-Höhe aus
   der Vorlage. Es gibt damit keine zweite Stelle, an der eine Höhe steht. */
@keyframes chartgrow { from { height: 0; } }
.chart .bar { animation: chartgrow 520ms cubic-bezier(0.2, 0.7, 0.3, 1) both;
              transition: transform 120ms ease-out, filter 120ms ease-out; }
/* Der Versatz je Spalte macht aus zwölf gleichzeitigen Sprüngen einen Lauf von
   links nach rechts - dieselbe Richtung, in der die Monate gelesen werden.
   Zwölf Regeln statt einer Inline-Angabe: Die Stelle einer Spalte ist
   Gestaltung und kein Datum, und Inline steht in diesem Bild nur die Höhe. */
.chart .colgroup:nth-child(1) .bar { animation-delay: 0ms; }
.chart .colgroup:nth-child(2) .bar { animation-delay: 40ms; }
.chart .colgroup:nth-child(3) .bar { animation-delay: 80ms; }
.chart .colgroup:nth-child(4) .bar { animation-delay: 120ms; }
.chart .colgroup:nth-child(5) .bar { animation-delay: 160ms; }
.chart .colgroup:nth-child(6) .bar { animation-delay: 200ms; }
.chart .colgroup:nth-child(7) .bar { animation-delay: 240ms; }
.chart .colgroup:nth-child(8) .bar { animation-delay: 280ms; }
.chart .colgroup:nth-child(9) .bar { animation-delay: 320ms; }
.chart .colgroup:nth-child(10) .bar { animation-delay: 360ms; }
.chart .colgroup:nth-child(11) .bar { animation-delay: 400ms; }
.chart .colgroup:nth-child(12) .bar { animation-delay: 440ms; }
/* ————— Überfahren: die Säule wächst und nennt ihren Betrag —————
   Die Vergrösserung läuft über transform und nicht über die Breite: Eine
   wirklich breitere Säule schöbe ihre Nachbarn, und der Verlauf verschöbe sich
   unter der Hand des Lesenden. Die Spalte ist ein <a>, also gilt dasselbe für
   die Tastatur - :focus-visible steht überall neben :hover. */
.chart .colgroup:hover, .chart .colgroup:focus-visible { z-index: 2; }
.chart .colgroup:hover .bar,
.chart .colgroup:focus-visible .bar { transform: scaleX(1.1); filter: brightness(1.12); }
/* Das Etikett steht über dem Plot und deckt dabei den yhint zu - wer den
   genauen Betrag liest, braucht die Skala in diesem Moment nicht. Es steht
   nicht im Plot: Dort läge es bei einer hohen Säule auf ihr.
   Der ganze Satz passt in keine Spaltenbreite, darum nowrap und über die
   Nachbarn hinaus. Die je drei äusseren Spalten richten ihn an ihrer eigenen
   Kante aus, statt ihn zu zentrieren: Zentriert ragte er aus dem Rollkasten
   und würde abgeschnitten (overflow-x: auto). */
.chart .colgroup .v { position: absolute; bottom: 100%; left: 50%;
                      transform: translateX(-50%); margin-bottom: 0.25rem;
                      white-space: nowrap; font-size: var(--fs-small);
                      font-variant-numeric: tabular-nums; color: var(--text);
                      background: var(--card); border: 1px solid var(--rule);
                      border-radius: 3px; padding: 0.05rem 0.4rem;
                      opacity: 0; pointer-events: none;
                      transition: opacity 120ms ease-out; }
.chart .colgroup:nth-child(-n+3) .v { left: 0; transform: none; }
.chart .colgroup:nth-last-child(-n+3) .v { left: auto; right: 0; transform: none; }
.chart .colgroup:hover .v, .chart .colgroup:focus-visible .v { opacity: 1; }
/* Wer Bewegung abbestellt hat, bekommt das fertige Bild - und den Betrag
   weiterhin, denn er ist Auskunft und keine Bewegung. */
@media (prefers-reduced-motion: reduce) {
    .chart .bar { animation: none; transition: none; }
    .chart .colgroup .v { transition: none; }
}
/* Nur noch der Abstand zur Legende: Der Platz für die Monatsnamen steckt im
   Polster des Rollkastens und nicht mehr in diesem Rand. */
.chart .clegend { display: flex; gap: 1rem; flex-wrap: wrap; margin-top: 0.75rem;
                  font-size: var(--fs-small); color: var(--text-faint); }
.chart .clegend i { display: inline-block; width: 0.7rem; height: 0.7rem;
                    margin-right: 0.3rem; background: var(--mark); }
.chart .clegend i.prev { background: none; border: 1px solid var(--rule); }
.chart .clegend i.bad { background: var(--red); }
.chart .clegend i.part { background: repeating-linear-gradient(135deg,
                         var(--mark), var(--mark) 2px,
                         transparent 2px, transparent 5px);
                         border: 1px solid var(--mark); }
/* Kennzahlen und Filterzeile. */
.kpi { display: flex; gap: 1.5rem; flex-wrap: wrap; margin-bottom: 1rem; }
.kpi .n { font-size: 1.35rem; font-weight: 700; font-variant-numeric: tabular-nums; }
.kpi .d { font-size: var(--fs-small); color: var(--text-faint); }
/* «Umsatz Jan–Jul» ist ein Satzteil und braucht keine Marke - der Ausschnitt
   gehört zur Beschriftung. Abgesetzt gehört erst, was danach kommt: Ohne
   diesen Abstand liefe die Unterzeile zu «Umsatz Jan–Jul Vorjahr 179'420.00
   +9.0 %» zusammen. Die Veränderung bleibt bei ihrer Vorjahreszahl. */
.kpi .d .prevyear { margin-left: 0.75rem; }
.kpi .d .share { margin-left: 0.35rem; }
/* Die Notiz der vierten Kachel («ohne Art …») ist ein ganzer Satz und keine
   Beschriftung: Ohne Breitengrenze zöge sie die Kachel im flex-Kasten auf
   Satzbreite und schöbe die drei anderen aus der Zeile. */
.kpi .k .hint { max-width: 16rem; }
.filterform { display: flex; gap: 1rem; align-items: flex-end; flex-wrap: wrap;
              margin-bottom: 1rem; }
/* Der Haken in der Filterzeile trägt denselben Namen wie der im Panel
   (.checkline, Zeile 999), ist aber eine eigene Regel: Die dortige ist auf
   .panel-body gestellt und greift ausserhalb eines Panels nicht. */
.filterform .checkline { display: flex; align-items: center; gap: 0.5rem; }
.filterform .checkline input { width: auto; margin: 0; }
@media (max-width: 420px) { .kpi { display: block; } .kpi > * + * { margin-top: 0.75rem; } }
/* Ein Monat (Mockup 8.2): die Mengen als Tabelle, der Balken nur als Lesehilfe
   in der letzten Spalte - der Admin hält die Zeile gegen die Abrechnung der
   Post und liest dabei Zahlen, statt Balken zu schätzen. */
.barcell { width: 8rem; }
.inbar { display: block; height: 0.35rem; background: var(--mark); min-width: 1px; }
.split { display: grid; gap: 1.5rem; grid-template-columns: 1fr 1fr; align-items: start; }
/* Die beiden Karten stehen nebeneinander und nicht untereinander: Der Abstand
   kommt vom gap, der Stapelrand aus .card + .card schöbe die rechte nach unten. */
.split > .card + .card { margin-top: 0; }
/* Auf einen Mitarbeiter eingeschränkt fällt die Mitarbeitertabelle weg - die
   verbleibende Karte nimmt dann die Breite, statt halb dazustehen. */
.split.one { grid-template-columns: 1fr; }
@media (max-width: 52rem) { .split { grid-template-columns: 1fr; } }
/* Die Summenzeile ist keine vierte Mitarbeiterzeile: kräftiger, mit einer
   Oberkante abgesetzt und ohne die Zeilenmarkierung beim Überfahren - die
   gehört zu Zeilen, die für einen Menschen stehen. */
table.t tfoot td { font-weight: 700; border-top: 1px solid var(--rule);
                   border-bottom: none; }
table.t tfoot tr:hover td { background: none; }
/* Schmale Geräte: Die Statistik braucht dafür keine einzige eigene Regel. Ihre
   vier Tabellen sind table.t in einer .card und fallen darum unter 420px über
   «.card table.t { display: none }» (Zeile 878) weg; die Kartenlisten daneben
   erscheinen über «.cardlist { display: block }» (Zeile 1137). Beides steht seit
   der Runde Layout beziehungsweise Runde 7 da und gilt hier unverändert.
   Für die Diagramme gibt es keine Kartenliste: Sie stehen und rollen waagrecht
   (.chartscroll) - sie sind der Engpass, und eine Säulenreihe lässt sich nicht
   in Karten auflösen, ohne den Verlauf zu verlieren. Die Kacheln stapeln sich
   über die Regel von .kpi. */

/* Monatsliste der Abrechnungen (Runde 6, Aufgabe 4, Mockup 6.1): eigene Klassen
   statt der Hausklasse .n - «Umsatz» und «Lohn brutto» sind Geld und keine
   blosse Zahl, und die Geldkachel oben trägt ohnehin schon .n/.d aus .kpi.
   Keine :last-child-Zeile wie bei .n: Die letzte Spalte der Zeile ist hier die
   Aktionsspalte (.w9), keine Geldzelle - sie träfe nichts. */
table.t td.num, table.t th.num { text-align: right; }
/* Ein negativer Lohn (AD 9.5) ist kein Fehler, aber ein Grund, hinzusehen -
   dieselbe Warnfarbe wie .alarm, eigens benannt, weil sie hier auf einer
   Geldzelle sitzt und nicht auf einem Zustand. */
.neg { color: var(--red); }
/* Die Erklärzeile einer Abrechnung, die sich nicht rechnen lässt: derselbe Ton
   wie .banner, hier auf eine Tabellenzeile gestellt statt auf einen Absatz -
   die Zeile bleibt in der Tabelle, wo ihr Name genannt ist. */
tr.problem td { background: var(--red-bg); color: var(--red);
                border-bottom: 1px solid var(--red-line); }
tr.problem .btn { margin-left: 0.5rem; }

/* ————— Die Abrechnungsseite (Runde 6, Aufgabe 8, Mockup 6.2/6.4): Layout B ————— */
/* Links die Rechnung, rechts die Werkzeugspalte (Aufgabe 9). Der Bruch liegt
   bei 760px - derselben Schwelle, unter der auch die Schiene der Schublade
   weicht (Zeile 100) -, nicht bei den 52rem von .split: Die Rechnung braucht
   mehr Platz als die gleich breiten Diagrammpaare der Statistik. */
/* Der Abstand der beiden Hälften ist der Hausabstand und kein eigener: 0.6rem,
   dasselbe Mass wie .detail-split der Einsatz-Detailseite (Zeile 1159) und wie
   der Stapelabstand .card + .card - eine Seitenausnahme darf ein zweites
   Raster brauchen, aber kein zweites Abstandsmass. */
.statementlayout { display: grid; gap: 0.6rem; grid-template-columns: 1.6fr 1fr; align-items: start; }
@media (max-width: 760px) { .statementlayout { grid-template-columns: 1fr; } }

/* Die Rechnung ist ein Dokument, keine Kartenliste: eine eigene, schlichte
   Tabelle statt table.t, deren Sortierkopf und feste Spaltenbreiten hier
   nichts zu suchen haben - sie wird nie sortiert und bricht nie auf 420px um. */
table.invoicetable { width: 100%; border-collapse: collapse; font-size: var(--fs-small); }
table.invoicetable td { padding: 0.3rem 0; border-bottom: 1px solid var(--rule-soft);
                         vertical-align: baseline; }
table.invoicetable td.n { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
table.invoicetable td.q { text-align: right; color: var(--text-faint); white-space: nowrap;
                           padding-right: 0.75rem; }
/* Die Gruppenüberschrift (Umsatz/Kommission/…) trägt keine Trennlinie - sie
   ist eine Zwischenüberschrift, keine eigene Zeile mit Zahlen. */
table.invoicetable tr.grp td { padding-top: 0.9rem; border-bottom: 0; font-weight: 700;
                                font-size: var(--fs-fine); text-transform: uppercase;
                                letter-spacing: 0.06em; color: var(--text-faint); }
table.invoicetable tr.grp:first-child td { padding-top: 0; }
table.invoicetable tr.sub td, table.invoicetable tr.round td { font-weight: 600; }
/* Der Lohn brutto ist der Endbetrag - kräftiger und mit einer Oberkante
   abgesetzt, dasselbe Muster wie table.t tfoot (Zeile 1485). */
table.invoicetable tr.total td { font-weight: 700; font-size: var(--fs-body);
                                  border-top: 1px solid var(--rule); border-bottom: 0; }
table.invoicetable .tourtag { color: var(--text-faint); }

/* Die Werkzeugspalte selbst (Aufgabe 9, Fund 4 der ersten Korrekturrunde): Zeile
   1009 stellt .lockedfld auf .panel-body, das es auf dieser Seite nicht gibt -
   dieselbe Lücke, die .checkline schon einmal hatte und die dort mit
   .filterform .checkline (Zeile 1466) statt einer zweiten .panel-body-Annahme
   geschlossen wurde. Hier ebenso: eine eigene, auf .tools gestellte Regel. */
.tools .lockedfld { margin: 0.15rem 0 0.6rem; font-weight: 600; }

/* Die Handeingaben als kleine Tabelle statt als lose Zeilen (Nachtrag zu Runde 6).
   .periodrow ist eine Flexbox - darüber kann keine Kopfzeile ausrichten, und ohne
   Kopfzeile trägt eine frisch hinzugefügte Zeile unbeschriftete Felder. Zeilen und
   Kopfzeile teilen sich deshalb ein Raster; .periodrow bleibt unangetastet, damit
   die Zeiträume der Touren nichts davon abbekommen. Abzug und Zuschlag tragen
   dieselbe Form - die Abzugsart ist keine Spalte, sondern eine Eingabehilfe unter
   «+ Zeile» (AD 8.2, siehe statements/detail.html). */
.deductionrow, .supplementrow, .rowhead {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 6rem 1.6rem;
}
/* Im Raster füllt das Feld seine Spalte - width: auto aus .periodrow liesse es
   auf seiner Eigenbreite stehen und die Kopfzeile ginge daneben. Die letzte
   Spalte ist genau so breit wie .btn.iconbtn (1.6rem), damit Kopfzeile und
   Zeilen dieselben Spaltenbreiten ausrechnen - zwei getrennte Raster teilen
   ihre Reste sonst verschieden auf und die Beschriftung stünde daneben. */
.deductionrow input, .supplementrow input { width: 100%; }
/* Die Kopfzeile ist zugleich die graue Trennlinie zwischen Titel und Eingaben:
   Sie steht auch über einer leeren Gruppe, denn dort erklärt sie die Spalten
   schon vor dem ersten «+ Zeile» - und der Knopf klebt nicht mehr am Titel. */
.rowhead {
    align-items: end;
    gap: 0.5rem;
    /* display und Spalten stehen oben, gemeinsam mit den Zeilen. */
    padding-bottom: 0.3rem;
    margin-bottom: 0.45rem;
    border-bottom: 1px solid var(--rule-soft);
}
.rowhead span {
    font-size: var(--fs-fine);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--text-faint);
}
.deductionrow + .deductionrow, .supplementrow + .supplementrow { margin-top: 0.35rem; }
/* «+ Zeile» steht als eigene Zone unter der Liste, nicht als deren vierte Zeile. */
.rowadd { margin-top: 0.75rem; }

/* Einstellungen (Runde 13, Mockup 13.1): eine linke Bereichsleiste, kein
   Reiter-Karussell. Die Leiste bleibt stehen, der Inhalt rechts wechselt mit der
   Auswahl; unter 760 px stapeln sich beide, die Leiste als Zeile zuoberst. */
.settingswrap { display: flex; gap: 1rem; align-items: flex-start; }
.settingsnav { flex: 0 0 12rem; display: flex; flex-direction: column; padding: 0.5rem; }
.settingsnav a {
    display: block;
    padding: 0.5rem 0.75rem;
    border-radius: 0.5rem;
    color: var(--text);
    text-decoration: none;
}
/* --bg gab es nie (Review 01.09.2026): die Sektion hob sich nicht ab. */
.settingsnav a:hover { background: var(--set); }
.settingsnav a.on { background: var(--action-bg); font-weight: 600; }
.settingsbody { flex: 1 1 auto; min-width: 0; }
.settingsbody .card + .card { margin-top: 1rem; }
/* Der globale Reset nimmt jedem Absatz seinen Rand - auf der Abo-Seite stehen
   die Banner aber als eigene Zeilen zwischen Titel, Fakten und Formular
   (Runde 12) und brauchen ihre Luft nach unten. */
.settingsbody .banner { margin-bottom: 0.9rem; }
.settingsbody .banner + .banner { margin-top: 0.4rem; }
@media (max-width: 760px) {
    .settingswrap { flex-direction: column; }
    .settingsnav { flex-basis: auto; width: 100%; flex-direction: row; flex-wrap: wrap; }
}

/* ————— Nachträge aus dem Review vom 01.09.2026 —————
   Klassen, die die Vorlagen längst trugen, ohne dass eine Regel sie kannte. */
/* Kurze Zahlenfelder (Sortierung, Betrag, Satz): ein Feld für «10.50» muss nicht
   24 rem breit sein. */
.field input.short { max-width: 9rem; }
/* Zwei Knöpfe nebeneinander mit Luft - die Abo-Karte. */
.acts { display: flex; flex-wrap: wrap; gap: 0.5rem; margin: 0.75rem 0; }
/* Ein Chip im Kartentitel bleibt ein Chip: .card h3 setzt Versalien und
   Sperrung, und «TESTPHASE» las sich wie ein zweiter Titel. */
.card h3 .chip { text-transform: none; letter-spacing: normal; }
/* Zeilenknöpfe untereinander statt nebeneinander (Abzüge je Mitarbeiter): Bei
   fester Spaltenaufteilung liefen «Eigenen Wert setzen» und «Für ihn
   deaktivieren» in einer Zeile bis 223 px über den Kartenrand. */
.rowactions.stacked { white-space: normal; }
.rowactions.stacked .btn { display: block; width: max-content; margin: 0 0 0.25rem; }
.rowactions.stacked .btn.iconbtn { display: inline-flex; }

/* ————— Einstellungen → Darstellung (Mockup 13.4) ————— */
/* Ohne Skript blieben die Radios wirkungslos; darum zeigt sie erst theme.js
   (data-themejs), und bis dahin steht der Satz, der das sagt. */
.themepick { display: none; gap: 0.5rem; flex-wrap: wrap; margin-top: 0.35rem; }
html[data-themejs] .themepick { display: flex; }
html[data-themejs] .themepick-noscript { display: none; }
.themepick label {
    display: inline-flex;
    align-items: center;
    gap: 0.45rem;
    padding: 0.4rem 0.8rem;
    border: 1px solid var(--rule);
    border-radius: var(--r-sm);
    background: var(--card);
    cursor: pointer;
    font-weight: 400;
}
.themepick input { display: inline; width: auto; max-width: none; margin: 0; }
.themepick label:has(input:checked) { border-color: var(--action); background: var(--action-bg); font-weight: 600; }

/* Die Karte bleibt eine Karte: Die Kacheln von OpenStreetMap sind hell, auch im
   dunklen Modus - eine Karte trägt ihren eigenen Grund. Was Leaflet darüber
   zeichnet (Fenster, Tooltip, Zoomknöpfe), folgt den Rollen. */
:root[data-theme="dark"] .leaflet-popup-content-wrapper,
:root[data-theme="dark"] .leaflet-popup-tip,
:root[data-theme="dark"] .leaflet-tooltip,
:root[data-theme="dark"] .leaflet-bar a,
:root[data-theme="dark"] .leaflet-control-attribution {
    background: var(--card);
    color: var(--text);
}
:root[data-theme="dark"] .leaflet-tooltip { border-color: var(--rule); }
:root[data-theme="dark"] .leaflet-bar a { border-bottom-color: var(--rule); }
:root[data-theme="dark"] .leaflet-control-attribution a { color: var(--action); }
