/*
 * Komponentenschicht: die wiederverwendbaren Bausteine aus PRD 7.3.
 *
 * HERKUNFT: Diese Datei ist die verwertbare Fassung des Layoutmusters, das am
 * 2026-07-28 unter .projektinformationen/layoutmuster.html lag. Das Muster
 * selbst ist nicht einsetzbar — es laedt Tailwind, Google Fonts und
 * FontAwesome von fremden Servern, benutzt Inline-Styles und traegt rund 180
 * feste Farbwerte im Markup. Alles vier ist hier verboten (CSP aus PRD 9.2,
 * Offline-Betrieb, DSGVO, Design-Token-Test). Uebernommen wurde deshalb die
 * GESTALTUNGSSPRACHE, nicht der Code: gerundete Karten, Kennwert-Kacheln,
 * Statusabzeichen, dunkle Kopf- und Fusszeile, Filterreihe, Kartenraster.
 * Herleitung im Einzelnen: docs/design/layoutmuster.md
 *
 * REGELN, die in dieser Datei nie gebrochen werden:
 *   - kein roher Farbwert (tests/Feature/DesignTokenTest.php)
 *   - keine Abstandswerte ausserhalb --abstand-1 bis -7 (PRD 12.3)
 *   - jede Trefflaeche mindestens 44 x 44 px (PRD 12.4, Bedienung mit Handschuh)
 *   - Farbe ist nie das einzige Merkmal (PRD 12.7)
 *
 * Sichtbar sind alle Bausteine in docs/design/styleguide.html.
 */

/* ---- Seitenraster ------------------------------------------------------- */

/*
 * Im Muster: max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 an jedem Abschnitt.
 * Hier eine Klasse, damit die Breite an einer Stelle steht. 80rem entspricht
 * dem max-w-7xl des Musters.
 */
.inhalt-breite {
    width: 100%;
    max-width: 80rem;
    margin-inline: auto;
    padding-inline: var(--abstand-4);
}

/* ---- Dunkle Kopf- und Fusszeile ----------------------------------------- */

/*
 * Das auffaelligste Merkmal des Musters. Gebaut aus vorhandenen Tokens statt
 * aus einer neuen Rohfarbe: --flaeche-dunkel IST die Textfarbe. Damit faerbt
 * ein spaeterer Themenwechsel (Aufgabe 10.2) die Kopfzeile automatisch mit.
 */
.shell-kopf--dunkel {
    background: var(--flaeche-dunkel);
    color: var(--auf-flaeche-dunkel);
    border-bottom-color: color-mix(in srgb, var(--auf-flaeche-dunkel) 14%, transparent);
}

.shell-kopf--dunkel .shell-untertitel {
    color: color-mix(in srgb, var(--auf-flaeche-dunkel) 72%, transparent);
}

/*
 * Erster Navigationslink des Projekts (Dashboard). Die Marke bleibt links,
 * der Link rutscht mit justify-content:space-between an den rechten Rand,
 * ohne die bisherige Grundflex-Regel aus app.css (.shell-kopf) anzufassen —
 * dieselbe Scoping-Technik wie .hinweisleiste .inhalt-breite weiter unten.
 */
/*
 * flex-wrap seit dem Layout-Audit 2026-08-06: Marke, Nav und Benutzermenue
 * liefen auf dem Handy (375px) ohne diesen Umbruch 308px ueber den Rand
 * hinaus, weil die Kopfzeile ihre Kinder in einer starren Reihe hielt.
 * min-width:0 an den Kindern, weil ein Flex-Kind sonst seine Inhaltsbreite
 * haelt und trotz Umbruch nicht schrumpft (derselbe Trick wie bei
 * .geraete-karte-fuss > :first-child weiter unten in dieser Datei).
 */
.shell-kopf .inhalt-breite {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--abstand-3);
}

.shell-kopf .inhalt-breite > * {
    min-width: 0;
}

/* ---- Seitenleiste (TODO 10.10, PRD 7.2/7.3) ------------------------------
 *
 * Ersetzt die frühere .shell-nav (drei feste Links neben der Marke) durch
 * eine einklappbare linke Leiste mit allen sieben Screens, die von Rechts
 * wegen erreichbar sein sollen. Mobil startet sie eingeklappt und legt sich
 * als Overlay über den Inhalt (position:absolute); ab 1024px steht sie fest
 * in der Reihe und ist standardmäßig ausgeklappt. seitenleiste.js setzt dafür
 * ausschließlich das Attribut data-zustand — welche Einträge im DOM stehen,
 * entscheidet allein das Blade-Layout (Autorisierung::darf()).
 *
 * Basis-Regeln hier gelten für ALLE Breiten (mobile first); die
 * breitenabhängigen Überschreibungen stehen gebündelt im Abschnitt
 * "Responsives Verhalten" am Dateiende, wie die übrigen @media-Regeln auch.
 */
/* flex:1 traegt hier die Aufgabe, die vorher .shell-inhalt direkt an .shell
   erfuellte (app.css): den restlichen Platz der Seite fuellen, damit die
   Fusszeile bei kurzem Inhalt am unteren Rand bleibt statt direkt hinter
   dem Inhalt zu stehen. .shell-inhalt ist jetzt kein direktes Kind von
   .shell mehr, sondern von .shell-rumpf — sein eigenes flex:1 (app.css)
   wirkt seither nur noch INNERHALB dieses Wrappers. min-height:0 verhindert
   den ueblichen Flex-Kollaps-Fallstrick (ein flex:1-Kind in einer Spalte
   verweigert sonst, unter seine Inhaltsgroesse zu schrumpfen). */
.shell-rumpf {
    position: relative;
    flex: 1;
    min-height: 0;
}

/* Hintergrund liegt hier UND auf .seitenleiste-liste (unten): mobil traegt
   allein die Liste ihn (sie ist die einzige sichtbare Flaeche, .seitenleiste
   selbst ist dort nur so gross wie der Schalter). Ab 1024px wird .seitenleiste
   per Flex-Spalte + .seitenleiste-liste{flex:1} auf die volle Hoehe des
   Hauptbereichs gestreckt (siehe .shell-rumpf oben) — erst dann traegt diese
   Zeile hier etwas bei, naemlich dass die dunkle Flaeche nahtlos bis zum
   unteren Rand durchlaeuft. */
.seitenleiste {
    position: relative;
    background: var(--flaeche-dunkel);
}

/* Gleiche Form wie .benutzermenue-schalter (icon-only, 44px Trefflaeche),
   aber dauerhaft sichtbar gefuellt statt transparent — der Pfeil ist auf
   jeder Seite das einzige, dauerhaft erreichbare Navigationselement. */
.seitenleiste-schalter {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-height: 44px;
    min-width: 44px;
    border: 1px solid color-mix(in srgb, var(--auf-flaeche-dunkel) 24%, transparent);
    border-radius: var(--radius-1);
    background: var(--flaeche-dunkel);
    color: var(--auf-flaeche-dunkel);
    font: inherit;
    cursor: pointer;
}

.seitenleiste-schalter:hover,
.seitenleiste-schalter[aria-expanded='true'] {
    border-color: color-mix(in srgb, var(--auf-flaeche-dunkel) 40%, transparent);
}

.seitenleiste-schalter:focus-visible {
    outline: 2px solid currentColor;
    outline-offset: 2px;
}

/* Aus zwei Rahmenlinien gebaut statt aus einem Icon-Font-Zeichen (das
   Projekt laedt keine Icon-Fonts, siehe PRD 12.2) — zeigt per Drehung immer
   in die Richtung, die ein Klick als Naechstes oeffnet. */
.seitenleiste-pfeil {
    display: inline-block;
    width: var(--abstand-3);
    height: var(--abstand-3);
    border-right: 2px solid currentColor;
    border-bottom: 2px solid currentColor;
    transform: rotate(-45deg);
}

.seitenleiste[data-zustand='offen'] .seitenleiste-pfeil {
    transform: rotate(135deg);
}

/* Mobil/Tablet-Grundzustand: eingeklappt, als Overlay ueber dem Inhalt statt
   im Fluss (verschiebt sonst main beim Aufklappen nach unten). Ab 1024px
   ueberschreibt der Breakpoint-Block am Dateiende auf position:static im
   Fluss und display:flex als Voreinstellung. */
.seitenleiste-liste {
    display: none;
    flex-direction: column;
    /* Traegt seit dem Gruppen-Umbau den Abstand ZWISCHEN den Gruppen (die
       direkten Kinder sind jetzt die drei Gruppen-Container, nicht mehr die
       Links). Innerhalb einer Gruppe setzt .seitenleiste-gruppe-liste ihr
       eigenes, engeres gap. */
    gap: var(--abstand-3);
    padding: var(--abstand-2);
    position: absolute;
    top: calc(100% + var(--abstand-2));
    left: 0;
    z-index: 20;
    /* FESTE Breite statt min-width: sonst richtet sich die Leiste nach ihrem
       breitesten SICHTBAREN Eintrag und wird beim Aufklappen einer Gruppe
       breiter — mobil ein Zucken des Overlays, ab 1024px (siehe unten) ein
       Ruck des ganzen Seiteninhalts. Gemessen am 24.08.2026: 191,4px zu,
       201,5px offen. */
    width: 15rem;
    /* Bei einem kurzen Fenster (Notebook quer, aufgeklappte Verwaltung) ist
       die Liste hoeher als der Platz — dann scrollt sie in sich selbst statt
       aus der Flaeche zu laufen. overscroll-behavior haelt das Scrollen in
       der Leiste, statt am Ende die Seite darunter mitzuziehen. */
    overflow-y: auto;
    overscroll-behavior: contain;
    border-radius: var(--radius-2);
    background: var(--flaeche-dunkel);
    box-shadow: var(--schatten-karte);
}

.seitenleiste[data-zustand='offen'] .seitenleiste-liste {
    display: flex;
}

.seitenleiste-eintrag {
    display: inline-flex;
    align-items: center;
    gap: var(--abstand-3);
    min-height: 44px;
    padding-inline: var(--abstand-3);
    border-radius: var(--radius-1);
    color: var(--auf-flaeche-dunkel);
    font-weight: 600;
    text-decoration: none;
}

/*
 * Eigene, schlichte Strich-Icons je Navigationsziel (minimalist-ui §6,
 * Umbau 20.08.2026) — kein Fremd-Icon-Paket, kein gefuellter Hintergrund
 * hinter dem Zeichen. Feste Groesse, weil currentColor sich sonst mit der
 * umgebenden Schriftgroesse mitskalieren wuerde.
 */
.seitenleiste-eintrag-icon {
    width: 20px;
    height: 20px;
    flex: none;
}

.seitenleiste-eintrag:hover,
.seitenleiste-eintrag:focus-visible {
    text-decoration: underline;
}

.seitenleiste-eintrag:focus-visible {
    outline: 2px solid currentColor;
    outline-offset: 2px;
}

/* Aktiver Eintrag: derselbe aria-current/aria-pressed-Grundsatz wie
   .filter-knopf[aria-pressed='true'] weiter unten — Zustand haengt nie an
   Farbe allein, deshalb bleibt Fettschrift + Unterstreichungsfaehigkeit
   unveraendert, die Flaeche kommt nur dazu. */
.seitenleiste-eintrag[aria-current='page'] {
    background: color-mix(in srgb, var(--auf-flaeche-dunkel) 18%, transparent);
}

/*
 * Navigationsgruppen (24.08.2026). Sechzehn Ziele in einer flachen Liste
 * liessen niemanden mehr etwas finden — sie stehen jetzt in drei Gruppen,
 * jede auf- und zuklappbar.
 *
 * Bauweise: <button aria-expanded> statt <details>/<summary>. Der native Weg
 * waere weniger Code, aber sobald man sein eingebautes Dreieck durch einen
 * eigenen Pfeil ersetzt (und genau das verlangt das Design hier), sagen
 * VoiceOver, JAWS und NVDA den Auf/Zu-Zustand nicht mehr zuverlaessig an.
 * Ausserdem traegt das Projekt dasselbe Disclosure-Muster ohnehin schon
 * dreimal: Seitenleisten-Schalter oben, Benutzermenue, Hilfe-Bauteil.
 *
 * Den Startzustand setzt Blade (data-zustand + aria-expanded), nicht das JS —
 * die Navigation steht damit schon im ersten Paint richtig da, ohne dass eine
 * Gruppe sichtbar nachtraeglich zuklappt.
 */
/* KEIN margin-top hier: den Abstand zwischen den Gruppen traegt allein das
   `gap` von .seitenleiste-liste (oben). Vor dem Gruppen-Umbau waren deren
   direkte Kinder die Links selbst, das gap wirkte zwischen ihnen; seither
   sind es die drei Gruppen-Container, und ein zusaetzliches margin-top
   ADDIERTE sich zum gap — gemessen 16px statt der gemeinten 12px
   (Kalt-Review 24.08.2026). Eine Regel je Abstand, nicht zwei. */

/* Hierarchie aus Groesse, Gewicht und Abstand statt aus einem verwaschenen
   Ton: die Ueberschrift ist kleiner und leichter als die Links darunter, hat
   aber dieselbe volle Textfarbe — ein gedaempftes Grau haette gegen
   --flaeche-dunkel den Kontrast gekostet, den KontrastTest einfordert.
   Bewusst KEINE Versalien mit Laufweite: die traegt in diesem Projekt bereits
   .status-abzeichen, und ein zweites Element in derselben Verkleidung
   verwischt, was ein Etikett und was eine Ueberschrift ist. */
.seitenleiste-gruppe-schalter {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--abstand-2);
    width: 100%;
    min-height: 44px;
    padding-inline: var(--abstand-3);
    border: 0;
    border-radius: var(--radius-1);
    background: transparent;
    color: var(--auf-flaeche-dunkel);
    font-family: inherit;
    font-size: var(--schriftgroesse-meta);
    font-weight: 500;
    text-align: left;
    cursor: pointer;
}

.seitenleiste-gruppe-schalter:hover {
    background: color-mix(in srgb, var(--auf-flaeche-dunkel) 10%, transparent);
}

.seitenleiste-gruppe-schalter:focus-visible {
    outline: 2px solid currentColor;
    outline-offset: 2px;
}

/* Gleiche Bauart wie .seitenleiste-pfeil oben: zwei Rahmenlinien statt eines
   Icon-Font-Zeichens (PRD 12.2 laedt keine Icon-Fonts). Kleiner als der
   Leisten-Pfeil, weil er hier eine Gruppe betrifft und nicht die ganze
   Leiste. Zeigt zu, wenn die Gruppe zu ist; auf, wenn sie offen ist. */
.seitenleiste-gruppe-pfeil {
    flex: none;
    width: 0.5rem;
    height: 0.5rem;
    border-right: 2px solid currentColor;
    border-bottom: 2px solid currentColor;
    transform: rotate(45deg) translate(-2px, -2px);
    transition: transform 160ms ease;
}

.seitenleiste-gruppe[data-zustand='offen'] .seitenleiste-gruppe-pfeil {
    transform: rotate(225deg) translate(-2px, -2px);
}

.seitenleiste-gruppe-liste {
    display: flex;
    flex-direction: column;
    gap: var(--abstand-1);
    margin-top: var(--abstand-1);
}

/* Zugeklappt heisst hier wirklich WEG, nicht nur unsichtbar: display:none
   nimmt die Links auch aus der Tab-Reihenfolge und aus dem Vorlesefluss.
   Ein bloss transparenter Block waere ein Dutzend unsichtbarer Tab-Stops. */
.seitenleiste-gruppe[data-zustand='zu'] .seitenleiste-gruppe-liste {
    display: none;
}

/*
 * Benutzermenü im Kopf (Feature 9.24, PRD 7.3). Disclosure-Muster wie das
 * Hilfe-Bauteil (hilfe.css): Trefflaeche 44px, Tiefe aus Ton statt aus einem
 * breiten Schatten, Fokusrahmen bleibt immer sichtbar (PRD 12.7).
 */
/* Zwei eigenstaendige Geschwister seit der Trennung von Avatar-Schalter und
 * Namens-Link (2026-08-06): der Container ordnet sie selbst nebeneinander an,
 * das uebernahm vorher der einzelne Button. */
.benutzermenue {
    position: relative;
    display: flex;
    align-items: center;
    gap: var(--abstand-2);
}

/* Nur noch der Avatar-Kreis als Inhalt (der Name ist jetzt .benutzermenue-name
 * daneben) — deshalb min-width statt der froeheren impliziten Breite durch
 * den Namenstext, sonst unterschreitet der Icon-only-Button die 44px-
 * Trefflaeche aus PRD 12.7. */
.benutzermenue-schalter {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-height: 44px;
    min-width: 44px;
    padding-inline: var(--abstand-2);
    border: 1px solid transparent;
    border-radius: var(--radius-1);
    background: transparent;
    color: var(--auf-flaeche-dunkel);
    font: inherit;
    cursor: pointer;
}

.benutzermenue-schalter:hover,
.benutzermenue-schalter[aria-expanded='true'] {
    border-color: color-mix(in srgb, var(--auf-flaeche-dunkel) 40%, transparent);
}

.benutzermenue-schalter:focus-visible {
    outline: 2px solid currentColor;
    outline-offset: 2px;
}

/* Der sichtbare Name, jetzt ein eigener Link auf die Profilseite statt Teil
 * des Auf-/Zuklapp-Buttons (2026-08-06) — Farbe/Gewicht wie zuvor im
 * Schalter, Stil im Muster von .seitenleiste-eintrag. */
.benutzermenue-name {
    color: var(--auf-flaeche-dunkel);
    font-weight: 600;
    text-decoration: none;
}

.benutzermenue-name:hover {
    text-decoration: underline;
}

.benutzermenue-name:focus-visible {
    outline: 2px solid currentColor;
    outline-offset: 2px;
    text-decoration: underline;
}

/* Schnellzugriff-Abmelden neben dem Namen: dieselben Farben wie
 * .knopf-primaer (app.css), nur als 44px-Icon-Kachel statt als Textknopf —
 * ein deutlicherer, sofort erreichbarer zweiter Weg zum "Abmelden" aus dem
 * Dropdown darunter (data-benutzermenue-abmelden traegt beide Knoepfe). */
.benutzermenue-abmelden-schnell {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-height: 44px;
    min-width: 44px;
    border: none;
    border-radius: var(--radius-1);
    background: var(--color-primary);
    color: var(--color-on-primary);
    cursor: pointer;
}

.benutzermenue-abmelden-schnell svg {
    width: 20px;
    height: 20px;
}

.benutzermenue-abmelden-schnell:hover {
    background: var(--color-accent);
}

.benutzermenue-abmelden-schnell:focus-visible {
    outline: 2px solid currentColor;
    outline-offset: 2px;
}

.benutzermenue-abmelden-schnell:disabled {
    background: var(--color-muted);
    cursor: not-allowed;
}

/* Initialen-Platzhalter statt Bild (Avatar-Upload folgt in Aufgabe 9.23). */
.benutzermenue-avatar {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    flex-shrink: 0;
    border: 1px solid currentColor;
    border-radius: 50%;
    font-size: var(--schriftgroesse-3);
    font-weight: 700;
    line-height: var(--zeilenhoehe-1);
    /* Aufgabe 9.23: der Kreis ist jetzt ein Rahmen um zwei mögliche Inhalte
       (Bild ODER Initiale, siehe layouts/app.blade.php) statt der Initiale
       direkt — overflow:hidden schneidet ein Profilbild automatisch auf die
       Kreisform, ohne dass das Bild selbst einen eigenen Rahmen bräuchte. */
    overflow: hidden;
}

.benutzermenue-avatar-bild {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.benutzermenue-liste {
    position: absolute;
    z-index: 20;
    top: calc(100% + var(--abstand-2));
    right: 0;
    display: flex;
    flex-direction: column;
    min-width: 14rem;
    padding-block: var(--abstand-2);
    border: 1px solid var(--flaeche-rahmen);
    border-radius: var(--radius-2);
    background: var(--color-surface);
    color: var(--color-text);
    box-shadow: var(--schatten-karte);
}

.benutzermenue-liste[hidden] {
    display: none;
}

.benutzermenue-eintrag {
    display: flex;
    align-items: center;
    width: 100%;
    min-height: 44px;
    padding-inline: var(--abstand-4);
    border: 0;
    background: transparent;
    color: var(--color-text);
    font: inherit;
    text-align: left;
    text-decoration: none;
    cursor: pointer;
}

.benutzermenue-eintrag:hover {
    background: color-mix(in srgb, var(--color-primary) 8%, transparent);
}

.benutzermenue-eintrag:focus-visible {
    outline: 2px solid var(--color-accent);
    outline-offset: -2px;
}

@media (prefers-reduced-motion: no-preference) {
    .benutzermenue-liste {
        animation: benutzermenue-erscheinen 120ms ease-out;
    }

    @keyframes benutzermenue-erscheinen {
        from {
            transform: translateY(-2px);
        }
    }
}

.fusszeile--dunkel {
    background: var(--flaeche-dunkel);
    color: color-mix(in srgb, var(--auf-flaeche-dunkel) 78%, transparent);
    border-top-color: color-mix(in srgb, var(--auf-flaeche-dunkel) 14%, transparent);
}

.fusszeile--dunkel a {
    color: var(--auf-flaeche-dunkel);
}

/*
 * Die Versionsnummer neben dem Datenschutz-Link hatte bis zum Layout-Audit
 * 2026-08-06 keine einzige Regel — sie stand ungestylt und ohne Abstand direkt
 * neben dem Link im Textfluss. min-height wie beim Link daneben, damit beide
 * auf derselben Grundlinie stehen.
 */
.fusszeile__version {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
    margin-left: var(--abstand-3);
}

/*
 * Die Marke. Im Muster sitzt das Zeichen in einer gefuellten, gerundeten
 * Kachel — genau die Kachel, die PRD 12.5 ausschliesst ("Jedes Icon steht ohne
 * Kachel oder farbigen Hintergrund direkt auf der Flaeche"). Uebernommen ist
 * deshalb nur der Aufbau: Name gross, Zusatz klein darunter. Das Zeichen selbst
 * (marke.svg, als Inline-SVG eingebettet) steht dementsprechend nackt auf der
 * Flaeche, ohne eigene Kachel.
 */
.marke {
    display: inline-flex;
    align-items: center;
    gap: var(--abstand-3);
    color: inherit;
    text-decoration: none;
}

/*
 * Die Marke fuehrt zurueck auf die Startseite. Sie ist ein Link und muss sich
 * auch so anfuehlen — ohne Unterstreichung, die im Kopf wie ein Fehler
 * aussaehe, aber mit sichtbarem Fokusrahmen fuer die Tastatur (PRD 12.7:
 * Fokusrahmen wird nie entfernt).
 */
.marke:focus-visible {
    outline: 2px solid currentColor;
    outline-offset: var(--abstand-1);
}

/*
 * Groesse des eingebetteten SVG (marke.svg). --abstand-6 statt einer neuen
 * Stufe, PRD 12.3 laesst keine Werte ausserhalb der Vierer-Skala zu.
 * flex-shrink: 0, sonst quetscht ein langer @yield('untertitel')-Text das
 * Zeichen auf dem Handy schmal.
 */
.marke-symbol {
    display: block;
    flex-shrink: 0;
    width: var(--abstand-6);
    height: var(--abstand-6);
}

.marke-symbol svg {
    display: block;
    width: 100%;
    height: 100%;
}

.marke-text {
    display: flex;
    flex-direction: column;
    gap: var(--abstand-1);
    /* Ohne min-width: 0 schrumpft eine Flex-Spalte nie unter ihren laengsten
       Inhalt - ein ausgereizter Marken-Untertitel (60 Zeichen, Einstellung
       `marke_untertitel`) schob die GANZE Seite seitlich aus dem Bild
       (gemessen 24.08.2026: 864 px Inhalt auf einem 390-px-Handy). */
    min-width: 0;
    /* Anker fuer den Erklaerkasten (siehe .marke-hinweis weiter unten). Bewusst
       hier und nicht an .marke-name: haengt der Kasten am Produktnamen, legt er
       sich beim Aufklappen ueber den Untertitel darunter (gemessen 24.08.2026,
       Screenshot 02-chrome-1440-hover). Vom ganzen Marken-Block aus faellt er
       unter beide Zeilen und verdeckt nichts vom Kopf. */
    position: relative;
}

.marke-name {
    font-size: var(--schriftgroesse-10);
    font-weight: 600;
    line-height: var(--zeilenhoehe-2);
    /* align-self, weil .marke-text eine Flex-Spalte ist: ohne diese Zeile zieht
       die Vorgabe (stretch) den Produktnamen auf die volle Spaltenbreite, und
       der Zeiger loeste den Erklaerkasten dann auch NEBEN dem Wort aus - meist
       ueber dem laengeren Untertitel darunter. Sichtbar aendert sich nichts,
       das Element traegt weder Flaeche noch Rahmen. */
    align-self: flex-start;
    max-width: 100%;
}

/*
 * Erklaerkasten zum Produktnamen (24.08.2026, siehe layouts/app.blade.php).
 * Optik bewusst wie die .hilfe-box (hilfe.css), aber als EIGENE Klasse: hilfe.js
 * behandelt jeden Klick innerhalb eines .hilfe-box-Elements als "nicht nach
 * aussen geklickt" (closest('.hilfe-box')). Traegt der Kasten im Marken-Link
 * diese Klasse mit, bliebe eine offene Hilfe-Box beim Klick aufs Logo faelschlich
 * stehen. Zwei kurze Blocks Doppelung sind billiger als diese Kopplung.
 *
 * Schriftgroesse, -gewicht und Zeilenhoehe stehen ausdruecklich da, sonst erbt
 * der Kasten die 600er Displayschrift des Produktnamens.
 */
.marke-hinweis {
    position: absolute;
    top: calc(100% + var(--abstand-2));
    left: 0;
    z-index: 40;
    /* max-content laesst den Kasten so breit werden, wie der Satz braucht; die
       Deckelung haelt ihn auf einem 390-px-Handy im Bild, damit die Seite nie
       seitlich scrollt. */
    width: max-content;
    max-width: min(320px, calc(100vw - var(--abstand-6)));
    padding: var(--abstand-3) var(--abstand-4);
    border: 1px solid var(--flaeche-rahmen);
    border-radius: var(--radius-2);
    background: var(--color-surface);
    color: var(--color-text);
    font-size: var(--schriftgroesse-4);
    font-weight: 400;
    line-height: var(--zeilenhoehe-3);
    /* Tiefe aus Ton und Kante, nicht aus einem breiten Schatten: eng, leicht
       nach unten versetzt, in der Textfarbe getoent statt schwarz. */
    box-shadow: 0 2px 6px color-mix(in srgb, var(--color-text) 12%, transparent);
    opacity: 0;
    /* Der Kasten faengt keine Klicks ab - er liegt im Marken-Link, ein Klick
       darauf soll wie ueberall im Kopf aufs Dashboard fuehren. */
    pointer-events: none;
}

/* Maus auf dem Wort, oder Tastaturfokus auf dem Marken-Link. Kein Touch-Pfad:
   ein Tipp aufs Logo navigiert, dafuer ist der Link da. */
.marke-name:hover .marke-hinweis,
.marke:focus-visible .marke-hinweis {
    opacity: 1;
}

/* Wer reduzierte Bewegung eingestellt hat, bekommt keine (wie .hilfe-box). */
@media (prefers-reduced-motion: no-preference) {
    .marke-hinweis {
        transition: opacity 120ms ease-out;
    }
}

.marke-zusatz {
    font-size: var(--schriftgroesse-4);
    color: var(--color-muted);
    /* Genau eine Zeile, und was nicht passt, endet sichtbar mit "..." statt
       den Kopf zu sprengen. Der Wert kommt aus dem Admin und kann lang sein;
       die Kopfhoehe darf davon nicht abhaengen. */
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Leerer Marken-Untertitel (Admin-Einstellung `marke_untertitel`, 2026-08-24):
   Das Element bleibt im Markup, damit kopf.js es auf den offline
   ausgelieferten Seiten nachtragen kann - sichtbar Platz einnehmen soll es
   dann aber nicht, sonst rutscht der Produktname aus der Mitte. */
.marke-zusatz:empty {
    display: none;
}

.shell-kopf--dunkel .marke-zusatz {
    color: color-mix(in srgb, var(--auf-flaeche-dunkel) 72%, transparent);
}

/* ---- Hinweisleiste (PRD 7.3: Sync-Anzeige) ------------------------------ */

/*
 * Im Muster die schmale Leiste ganz oben mit der Notfall-Hotline. Hier traegt
 * sie den Zustand, den die App wirklich melden muss: online oder offline und
 * wie viele Buchungen noch auf Uebertragung warten (PRD 7.6).
 *
 * Der Zustand steht IMMER als Text da. Ein farbiger Punkt allein waere fuer
 * jemanden mit Farbfehlsichtigkeit keine Information.
 */
/*
 * Das Band laeuft ueber die volle Breite, sein Inhalt sitzt auf demselben
 * Raster wie alles andere. Ohne diese Trennung staende der Text am linken
 * Bildschirmrand, waehrend Kopfzeile und Karten mittig ausgerichtet sind.
 */
.hinweisleiste {
    margin: 0;
    padding: var(--abstand-2) 0;
    /*
     * --flaeche-dunkel statt --color-text (Fund beim Dark-Theme-Bau,
     * O-26, 2026-08-13): --color-text ist nur im hellen Theme dunkel. Im
     * dunklen Theme ist --color-text hell, dann waere diese Leiste die
     * einzige helle Flaeche auf einer sonst dunklen Seite -- und die
     * Verbindungspunkt-Farben (fuer dunklen Grund gedacht) verloeren dort
     * fast jeden Kontrast. --flaeche-dunkel ist genau fuer diesen Fall da:
     * in beiden Themes eine echte dunkle Flaeche, siehe tokens.css und
     * tokens-dark.css.
     */
    background: color-mix(in srgb, var(--flaeche-dunkel) 92%, transparent);
    color: var(--auf-flaeche-dunkel);
    font-size: var(--schriftgroesse-4);
}

.hinweisleiste .inhalt-breite {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--abstand-2) var(--abstand-4);
}

.hinweisleiste-punkt {
    display: inline-block;
    width: var(--abstand-2);
    height: var(--abstand-2);
    border-radius: 50%;
    background: currentColor;
    flex: none;
}

/*
 * Die beiden Zustaende unterscheiden sich in der GEWICHTUNG, nicht im Farbton
 * des TEXTES. Verbunden ist der Normalfall und tritt zurueck. Fehlt das Netz,
 * steht der Text in voller Staerke da — auffaellig genug, ohne dass Farbe
 * allein die Aussage traegt (WCAG 2.2 SC 1.4.1, PRD 12.7).
 *
 * Der PUNKT daneben traegt zusaetzlich eine eigene Farbe (gruen/rot ueber
 * --color-netz-verbunden/-getrennt). Das ist bewusst NICHT die Status-Ampel
 * des Geraets: eine vierte Farbe fuer eine andere Aussage waere sonst leicht
 * mit "Geraet verfuegbar/in Wartung" zu verwechseln. Der Text bleibt trotzdem
 * die Pflichtangabe — die Farbe ist eine schnelle Zusatzinformation, kein
 * Ersatz dafuer.
 */
.hinweisleiste[data-verbindung='verbunden'] {
    color: color-mix(in srgb, var(--auf-flaeche-dunkel) 65%, transparent);
}

.hinweisleiste[data-verbindung='getrennt'] {
    color: var(--auf-flaeche-dunkel);
    font-weight: 600;
}

.hinweisleiste[data-verbindung='verbunden'] .hinweisleiste-punkt {
    background: var(--color-netz-verbunden);
}

.hinweisleiste[data-verbindung='getrennt'] .hinweisleiste-punkt {
    background: var(--color-netz-getrennt);
}

/*
 * Aufgabe 8.8: leer, solange die Outbox leer ist. `:empty` statt eines
 * JS-gesetzten Attributs — ein Flex-Kind ohne Inhalt bliebe sonst als
 * unsichtbarer Platzhalter im `gap` stehen und risse eine Luecke auf, die
 * nichts traegt.
 */
[data-verbindung-wartend]:empty {
    display: none;
}

/*
 * Aufgabe 8.9: "erneut senden" sitzt IN der schmalen Hinweisleiste, nicht
 * daneben als eigenstaendiger Formular-Knopf — deshalb bewusst kein
 * .knopf-sekundaer (44px Mindesthoehe wuerde die duenne Leiste sprengen),
 * sondern eine unterstrichene Textaktion in derselben Schriftgroesse wie
 * der Rest der Zeile.
 */
.verbindung-erneut-senden {
    font: inherit;
    font-weight: 600;
    color: inherit;
    background: none;
    border: none;
    padding: 0;
    text-decoration: underline;
    text-underline-offset: 0.2em;
    cursor: pointer;
}

.verbindung-erneut-senden:focus-visible {
    outline: 2px solid var(--color-primary);
    outline-offset: 2px;
}

/* ---- Karte (Grundflaeche) ----------------------------------------------- */

/*
 * Die gerundete Flaeche des Musters. Rahmen UND ein sehr enger Schatten: der
 * Rahmen traegt die Kante, der Schatten hebt die Karte gerade so weit ab, dass
 * man sie als Gegenstand liest.
 */
.flaeche {
    background: var(--color-surface);
    border: 1px solid var(--flaeche-rahmen);
    border-radius: var(--radius-3);
    box-shadow: var(--schatten-karte);
}

/* ---- Kartenraster (PRD 12.6) -------------------------------------------- */

/*
 * Erstes responsives Raster des Projekts. Mobile first: eine Spalte, ab
 * Tablet zwei, ab Desktop drei — genau die Stufen aus PRD 12.6.
 * Die Breakpoints stehen weiter unten gebuendelt, damit man sie an einer
 * Stelle liest statt ueber die Datei verteilt.
 */
.karten-raster {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--abstand-5);
}

/* ---- Kalenderwoche (Aufgabe 4.3, PRD 12.6) ------------------------------ */

/*
 * Der Belegungsplan ist eine seitlich scrollende Tabelle, keine Lesebreite-
 * Kachel wie ein Formular — die 44rem-Grundbreite von .karte (app.css) und der
 * 80rem-Deckel von .inhalt-breite (Zeile 30) sind hier zu eng. Bewusst per
 * :has() nur auf DIESER Seite ausgehebelt (gleiche Scoping-Technik wie
 * .karte--anmeldung in app.css:83 und .feld-marke:has(...) in app.css:347) —
 * keine neue Blade-Klasse, kein Risiko fuer andere Seiten. Ohne Deckel: der
 * Kalender waechst ab hier mit jeder Fensterbreite mit, auch ueber die
 * sonst systemweite 80rem-Grenze hinaus (Entscheidung 17.08.2026).
 *
 * 24.08.2026: der Deckel steht jetzt auf 100% statt auf 95%. Die 95% waren
 * eine zusaetzliche Verengung, die nur der Kalender kannte — im Browser
 * gemessen sass die Karte bei 1440px Fensterbreite auf 325..1379px, waehrend
 * jede andere Seite ihren Inhalt auf 296..1408px stellt (dieselbe
 * padding-inline von .inhalt-breite, nur ohne die 95%-Zentrierung). Der
 * Kalenderblock hatte damit als einziger einen anderen Seitenrand als der Rest
 * der Seite; genau das war der Befund. Die Entscheidung vom 17.08.2026 bleibt
 * dabei gewahrt: mit 100% waechst der Kalender weiterhin ueber die 80rem
 * hinaus mit, sogar konsequenter als vorher. Kopf- und Fusszeile bleiben bei
 * 80rem, ihr Versatz auf sehr breiten Fenstern ist unveraendert und gewollt.
 */
.shell-inhalt:has(> .inhalt-breite > [data-kalender]) > .inhalt-breite {
    max-width: 100%;
}

.karte[data-kalender] {
    max-width: 100%;
}

/*
 * Wochennavigation: Reihe statt der .feld-Spalte (Blade-Kommentar dort),
 * kompaktere Knoepfe. Hoehe bleibt bei den 44px aus .knopf-sekundaer
 * (PRD 12.4, Trefflaeche mit Handschuh) — nur das seitliche Padding und die
 * Schrift schrumpfen, dieselbe Hoehe wie die Umschalter Woche/Monat und
 * Liste/Belegung direkt darueber (.filter-knopf, ebenfalls 44px).
 *
 * Zentriert seit dem 24.08.2026 (Wunsch des Eigentuemers, Council-Entscheidung
 * E1 in .code-guardian-autopilot.md): die Reihe steht als GANZES mittig ueber
 * dem Belegungsplan, Pfeilknoepfe eingeschlossen. Vorher stand alles links und
 * die Pfeile per margin-left:auto rechts am Rand; eine Zentrierung nur der vier
 * Textelemente haette die Gruppe auf die Mitte der Restflaeche LINKS der Pfeile
 * gesetzt, also sichtbar neben die echte Mitte (gemessen ~30–40px bei 1440px).
 * justify-content wirkt bei flex-wrap auf JEDE umgebrochene Zeile gleich — auf
 * 390px stehen damit auch die drei Umbruchzeilen mittig statt links und rechts
 * gedrueckt. Die DOM-Reihenfolge bleibt unangetastet (kein order, kein
 * row-reverse), Tab-Reihenfolge und Lesereihenfolge bleiben also gleich.
 *
 * Die Abstaende trennen die drei Bloecke der Seite: oben --abstand-5 loest die
 * Navigation vom Einstellungen/Filter-Block ab (vorher klebten beide auf 0px
 * aneinander), unten --abstand-3 haelt sie dicht am Plan, damit sie als dessen
 * Kopfzeile liest und nicht als eigener Absatz.
 */
.kalender-wochennavigation {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    gap: var(--abstand-2);
    margin-block: var(--abstand-5) var(--abstand-3);
}

.kalender-wochennavigation .knopf-sekundaer {
    padding-inline: var(--abstand-3);
    font-size: var(--schriftgroesse-4);
}

/*
 * Pfeil-Knoepfe (Plan-Hub #7): in derselben Zeile wie die Textknoepfe.
 * Quadratisch statt breit, weil sie nur ein Symbol tragen — bleiben trotzdem
 * bei den PRD-12.4-Pflicht-44px Trefflaeche.
 *
 * Das margin-left:auto ist am 24.08.2026 entfallen; die Pfeile stehen jetzt
 * direkt rechts neben der Zeitraum-Anzeige und wandern mit der zentrierten
 * Reihe (Council-Entscheidung E1, siehe Kommentar bei
 * .kalender-wochennavigation). Vom Grill-Beschluss des 17.08.2026 faellt damit
 * nur das Umsetzungsdetail "rechtsbuendig"; sein Kern — die Pfeile ERGAENZEN
 * die Textknoepfe und ersetzen sie nicht — bleibt unveraendert: beide Knoepfe
 * existieren weiter, mit derselben Funktion, denselben aria-labels und
 * derselben 44px-Trefflaeche. Der gap von --abstand-2 (8px) trennt sie
 * weiterhin von der Zeitraum-Anzeige und voneinander.
 */
.kalender-wochennavigation-pfeile {
    display: flex;
    gap: var(--abstand-2);
}

.kalender-pfeil-knopf {
    min-width: 44px;
    padding-inline: 0;
    font-size: var(--schriftgroesse-8);
    line-height: var(--zeilenhoehe-1);
}

/*
 * Ein-/ausklappbarer Einstellungen/Filter-Block (Plan-Hub #7): spart Platz
 * auf gemeinsam genutzten Feld-Tablets (Grill-Entscheidung 18.08.2026,
 * startet zugeklappt). hidden-Attribut-Muster wie ueberall im Projekt, kein
 * <details>-Element.
 */
.kalender-einstellungen-schalter {
    display: flex;
    align-items: center;
    justify-content: space-between;
    width: 100%;
    min-height: 44px;
    padding: var(--abstand-2) var(--abstand-4);
    font: inherit;
    font-weight: 600;
    color: var(--color-text);
    background: var(--color-surface);
    border: 1px solid var(--flaeche-rahmen);
    border-radius: var(--radius-2);
    cursor: pointer;
    transition: border-color var(--dauer-ruhig) var(--kurve-ruhig);
}

.kalender-einstellungen-schalter:hover {
    border-color: var(--color-accent);
}

.kalender-einstellungen-schalter:focus-visible {
    outline: 2px solid var(--color-primary);
    outline-offset: 1px;
}

/* Kleiner Pfeil, der beim Aufklappen dreht — reines CSS, kein Bild/Icon. */
.kalender-einstellungen-pfeil {
    width: 10px;
    height: 10px;
    border-right: 2px solid currentColor;
    border-bottom: 2px solid currentColor;
    transform: rotate(45deg);
    transition: transform 0.15s ease;
}

.kalender-einstellungen-schalter[aria-expanded='true'] .kalender-einstellungen-pfeil {
    transform: rotate(-135deg);
}

.kalender-einstellungen-panel {
    display: flex;
    flex-direction: column;
    gap: var(--abstand-4);
    padding: var(--abstand-4) var(--abstand-1) 0;
}

/*
 * Zeitraum/Darstellung nebeneinander ab Tablet-Breite (600px, siehe
 * "Responsives Verhalten" weiter unten in dieser Datei — derselbe Wert wie
 * sonst im Projekt), mobil weiterhin gestapelt (Grid-Default: 1 Spalte).
 */
.kalender-einstellungen-reihe {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--abstand-4);
}

@media (min-width: 600px) {
    .kalender-einstellungen-reihe {
        grid-template-columns: repeat(2, 1fr);
    }
}

/*
 * Kategorie-Mehrfachauswahl-Panel (Plan-Hub #7): eigener kleiner
 * Auf-/Zuklapp-Schalter innerhalb des grossen Einstellungen-Blocks, echte
 * Checkboxen statt Knopfreihe (Grill-Entscheidung 18.08.2026 — kein
 * natives <select multiple>, auf Tablets ohne Strg-Klick nicht bedienbar).
 */
.kalender-kategorie-schalter {
    display: flex;
    align-items: center;
    justify-content: space-between;
    width: 100%;
    min-height: 44px;
    padding: var(--abstand-2) var(--abstand-3);
    font: inherit;
    color: var(--color-text);
    background: var(--color-surface);
    border: 1px solid var(--flaeche-rahmen);
    border-radius: var(--radius-1);
    cursor: pointer;
}

.kalender-kategorie-schalter:hover {
    border-color: var(--color-accent);
}

.kalender-kategorie-schalter:focus-visible {
    outline: 2px solid var(--color-primary);
    outline-offset: 1px;
}

.kalender-kategorie-auswahl-text {
    color: var(--color-muted);
    font-weight: 400;
}

.kalender-kategorie-panel {
    display: flex;
    flex-direction: column;
    gap: var(--abstand-1);
    padding: var(--abstand-3);
    margin-top: var(--abstand-1);
    border: 1px solid var(--flaeche-rahmen);
    border-radius: var(--radius-1);
}

/*
 * Eine Checkbox-Zeile je Kategorie: dieselbe 44px-Trefflaechen-Regel wie
 * .feld-marke:has(input[type='checkbox']) in app.css, hier als eigene Klasse
 * (mehrere Zeilen untereinander statt einem einzelnen Feldlabel, deshalb
 * nicht font-weight:600 wie .feld-marke).
 */
.kalender-kategorie-zeile {
    display: flex;
    align-items: center;
    gap: var(--abstand-2);
    min-height: 44px;
    cursor: pointer;
}

/*
 * 3-Wochen-Ansicht (2026-08-17): .kalender-woche selbst ist jetzt nur noch
 * der senkrechte Stapel der drei Wochengruppen (vorherige/aktuelle/naechste),
 * das bisherige Grid (mobil einspaltig, ab 1024px sieben Spalten in voller
 * Wochenbreite) sitzt jetzt auf .kalender-wochengruppe-tage — Breakpoint
 * steht weiterhin gebuendelt weiter unten, wie beim Kartenraster.
 */
.kalender-woche {
    display: flex;
    flex-direction: column;
    gap: var(--abstand-5);
}

.kalender-wochengruppe-titel {
    margin: 0 0 var(--abstand-3);
    font-size: var(--schriftgroesse-7);
    font-weight: 600;
}

/*
 * Dezente Hervorhebung der mittleren Gruppe — Text traegt die Information
 * ohnehin schon ("Aktuelle Woche"), die Farbe ist reine Verstaerkung, nie
 * das einzige Signal (dieselbe Regel wie beim Konflikt-Hinweis anderswo in
 * dieser Datei). Tonale Faerbung mit vorhandenem Token statt einer neuen
 * Rohfarbe (PRD 12.1).
 */
.kalender-wochengruppe--aktuell .kalender-wochengruppe-titel {
    padding-left: var(--abstand-2);
    border-left: 3px solid var(--color-primary);
}

.kalender-wochengruppe-tage {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--abstand-4);
}

.kalender-tag-titel {
    margin: 0 0 var(--abstand-2);
    font-size: var(--schriftgroesse-6);
    font-weight: 600;
}

.kalender-eintrag {
    display: block;
    padding: var(--abstand-2) var(--abstand-3);
    margin-bottom: var(--abstand-2);
    border-radius: var(--radius-1);
    background: color-mix(in srgb, var(--color-primary) 12%, var(--color-surface));
    font-size: var(--schriftgroesse-4);
}

.kalender-eintrag--konflikt {
    background: color-mix(in srgb, var(--color-status-wartung) 18%, var(--color-surface));
}

/*
 * Monatsraster (Plan-Hub #2): echte <table>, kein role="grid" (Recherche im
 * Plan: role="grid" braucht Pfeiltasten-Navigation, die hier nicht existiert
 * — Zellen enthalten Interaktives, sind aber nicht selbst editierbar). Die
 * Breite ab 1024px steht gebuendelt weiter unten, wie beim Kalenderwoche-
 * Block darueber.
 */
.kalender-monat {
    width: 100%;
    border-collapse: collapse;
    table-layout: fixed;
}

.kalender-monat th {
    padding: var(--abstand-2);
    font-size: var(--schriftgroesse-3);
    font-weight: 600;
    text-align: center;
    border-bottom: 1px solid var(--flaeche-rahmen);
}

.kalender-monat td {
    height: 4.5rem;
    padding: var(--abstand-1);
    vertical-align: top;
    border: 1px solid var(--flaeche-rahmen);
}

/*
 * Heute-Markierung (Plan-Hub #7, kontrastreich statt nur Rahmenfarbe):
 * gefuellter Primaerfarbe-Hintergrund mit heller Schrift, dieselbe bereits
 * gegen 4,5:1 geprüfte Kombination wie .kalender-tag-anzahl weiter unten
 * (kein neuer Farbwert, keine erneute Kontrastrecherche noetig — siehe
 * tests/Feature/KontrastTest.php).
 */
.kalender-monat td[aria-current='date'] {
    color: var(--color-on-primary);
    background: var(--color-primary);
    border-color: var(--color-primary);
}

.kalender-tag-zahl {
    display: block;
    font-size: var(--schriftgroesse-3);
    font-weight: 600;
}

.kalender-tag--fremd .kalender-tag-zahl {
    color: var(--color-muted);
}

/*
 * Randtag-Faerbung weicht der Heute-Markierung: ein "fremder" Tag (Padding
 * aus dem Vor-/Folgemonat) kann beim Blaettern selbst der heutige Tag sein
 * — ohne diese Regel wuerde die gedaempfte Randtag-Farbe die helle Schrift
 * auf dem gefuellten Hintergrund ueberschreiben und den Kontrast brechen.
 */
.kalender-monat td[aria-current='date'] .kalender-tag-zahl {
    color: inherit;
}

/*
 * Die Anzahl-Marke (mobil, unter 600px) traegt selbst schon Primaerfarbe als
 * Flaeche — auf dem jetzt ebenfalls primaerfarbenen Heute-Hintergrund wuerde
 * sie sonst unsichtbar verschmelzen. Getauschte Flaeche/Schrift haelt sie
 * als eigenes Element erkennbar.
 */
.kalender-monat td[aria-current='date'] .kalender-tag-anzahl {
    color: var(--color-primary);
    background: var(--color-surface);
}

/*
 * Der Tagesknopf traegt keinen eigenen Rahmen und keine eigene Flaeche —
 * er soll wie ein Textblock wirken, nicht wie ein weiterer Knopf im Raster.
 * Ein Tag ohne Reservierungen bekommt gar keinen Knopf (Position 5).
 */
.kalender-tag-knopf {
    display: block;
    width: 100%;
    padding: 0;
    margin-top: var(--abstand-1);
    font: inherit;
    text-align: left;
    color: inherit;
    background: none;
    border: none;
    cursor: pointer;
}

.kalender-tag-knopf:focus-visible {
    outline: 2px solid var(--color-primary);
    outline-offset: 1px;
}

.kalender-tag-kurz-eintrag {
    display: block;
    overflow: hidden;
    font-size: var(--schriftgroesse-1);
    white-space: nowrap;
    text-overflow: ellipsis;
}

.kalender-tag-anzahl {
    display: inline-block;
    padding: 0 var(--abstand-2);
    margin-top: var(--abstand-1);
    font-size: var(--schriftgroesse-1);
    font-weight: 600;
    color: var(--color-on-primary);
    background: var(--color-primary);
    border-radius: var(--radius-2);
}

/* Mobil zaehlt nur die Anzahl, ab 600px die kurzen Geraetenamen (Entscheidung
   E2 im Plan) — Umschaltpunkt steht gebuendelt weiter unten. */
.kalender-tag-kurz {
    display: none;
}

.kalender-tagesliste {
    margin-top: var(--abstand-4);
    padding: var(--abstand-4);
    border: 1px solid var(--flaeche-rahmen);
    border-radius: var(--radius-2);
}

.kalender-tagesliste-titel {
    margin: 0 0 var(--abstand-2);
    font-size: var(--schriftgroesse-6);
    font-weight: 600;
}

/*
 * Belegungsplan (TODO 4.3 Balkendarstellung): eine Zeile je Geraet, Balken
 * ueber die belegten Tage. tabindex + role="region" macht den seitlich
 * scrollenden Wrapper per Tastatur erreichbar (WCAG 2.1.1), border-collapse:
 * separate ist Pflicht — sticky funktioniert auf einem th nicht mit collapse
 * (anders als .kalender-monat oben, das keine sticky-Spalte braucht).
 */
/*
 * Dauerhaft sichtbare Scrollleiste (18.08.2026, loeste die Pfeil-Knoepfe aus
 * Plan-Hub #5 ab): macOS/moderne Browser blenden Scrollleisten sonst aus,
 * solange nicht gescrollt wird — ohne Knoepfe faellt dann die einzige
 * sichtbare Spur weg, dass rechts noch mehr Tage kommen. scrollbar-width
 * (Firefox) und das gesetzte ::-webkit-scrollbar (Chrome/Safari/Edge, schaltet
 * die ausblendende Overlay-Leiste ab) erzwingen die klassische, stehende
 * Leiste. overflow-x: auto bleibt unveraendert — die Leiste erscheint nur,
 * wenn die Tabelle wirklich breiter ist als der Platz.
 */
.kalender-belegung-scroll {
    max-width: 100%;
    overflow-x: auto;
    scrollbar-width: thin;
    scrollbar-color: var(--color-muted) var(--flaeche-rahmen);
}

.kalender-belegung-scroll::-webkit-scrollbar {
    height: 10px;
}

.kalender-belegung-scroll::-webkit-scrollbar-track {
    background: var(--flaeche-rahmen);
    border-radius: var(--radius-1);
}

.kalender-belegung-scroll::-webkit-scrollbar-thumb {
    background: var(--color-muted);
    border-radius: var(--radius-1);
}

.kalender-belegung-scroll:focus-visible {
    outline: 2px solid var(--color-primary);
    outline-offset: 1px;
}

.kalender-belegung-tabelle {
    border-collapse: separate;
    border-spacing: 0;
}

.kalender-belegung-tabelle th,
.kalender-belegung-tabelle td {
    padding: var(--abstand-1) var(--abstand-2);
    font-size: var(--schriftgroesse-3);
    text-align: center;
    white-space: nowrap;
    border-bottom: 1px solid var(--flaeche-rahmen);
}

/*
 * Geraetespalte begrenzt und skalierbar (2026-08-17): ohne max-width sprengt
 * ein langer Geraetename (z.B. "Kondensationstrockner mit sehr sehr sehr
 * langem ...") die sticky Kopfspalte unbegrenzt nach rechts. white-space:
 * nowrap kommt schon aus der gemeinsamen Regel oben, hier kommt nur die
 * Kappung + Ellipse dazu — dasselbe Muster wie .kalender-tag-kurz-eintrag
 * weiter oben in dieser Datei.
 */
.kalender-belegung-tabelle th[scope='row'] {
    position: sticky;
    left: 0;
    max-width: 11rem;
    overflow: hidden;
    text-overflow: ellipsis;
    text-align: left;
    background: var(--color-surface);
    border-right: 1px solid var(--flaeche-rahmen);
}

/*
 * Heute-Markierung im Belegungsplan-Kopf (Plan-Hub #7, kontrastreich statt
 * nur Textfarbe): dieselbe gefuellte Kombination wie im Monatsraster oben.
 */
.kalender-belegung-tabelle th[aria-current='date'] {
    color: var(--color-on-primary);
    background: var(--color-primary);
}

/*
 * Gruppen-Kopfzeile der 3-Wochen-Ansicht (2026-08-17): th[scope='colgroup']
 * fasst je 7 Tagesspalten zu einer Wochengruppe zusammen, etwas kraeftiger
 * gesetzt als die Tages-Kopfzeile darunter, damit die Gruppierung optisch
 * erkennbar ist. Dieselbe tonale Hervorhebung der mittleren Gruppe wie in
 * der Liste (.kalender-wochengruppe--aktuell) — Text traegt die Information
 * ohnehin schon, die Faerbung ist reine Verstaerkung.
 */
.kalender-belegung-tabelle th[scope='colgroup'] {
    font-weight: 600;
    border-bottom: 1px solid var(--flaeche-rahmen);
}

.kalender-belegung-wochengruppe--aktuell {
    color: var(--color-primary);
    background: color-mix(in srgb, var(--color-primary) 8%, var(--color-surface));
}

/*
 * Wochenend-Spalten (Sa/So): heller blaugrauer Ton als Standardfarbe, damit
 * das Wochenende auf einen Blick erkennbar ist — aber nur OHNE Vermietung.
 * Eine tatsaechliche Buchung (.kalender-belegung-belegt) oder Wartung
 * (.kalender-belegung-wartung) behaelt ihre eigene Farbe, die :not()-
 * Ausschluesse sichern das. Die Tabelle ist immer Montag-ausgerichtet und
 * in 7er-Wochenbloecken aufgebaut (siehe montagVon() in kalender-screen.js),
 * deshalb trifft die feste nth-child-Formel unabhaengig von Wochen- oder
 * Monatsmodus: Position 7, 14, 21 … ist Samstag, Position 8, 15, 22 … Sonntag
 * (Spalte 1 ist immer die Geraete-Spalte).
 */
.kalender-belegung-tabelle th:nth-child(7n+7),
.kalender-belegung-tabelle th:nth-child(7n+8),
.kalender-belegung-tabelle td:nth-child(7n+7):not(.kalender-belegung-belegt):not(.kalender-belegung-wartung):not(.kalender-belegung-markiert),
.kalender-belegung-tabelle td:nth-child(7n+8):not(.kalender-belegung-belegt):not(.kalender-belegung-wartung):not(.kalender-belegung-markiert) {
    background: color-mix(in srgb, var(--color-muted) 12%, var(--color-surface));
}

.kalender-belegung-belegt {
    background: color-mix(in srgb, var(--color-primary) 22%, var(--color-surface));
    border-left: 2px solid var(--color-surface);
}

.kalender-belegung-belegt--konflikt {
    background: color-mix(in srgb, var(--color-status-wartung) 30%, var(--color-surface));
}

/*
 * Kalt-Review-Fund: ohne einen gesetzten border-left auf .kalender-belegung-belegt
 * oben war diese Regel ein No-op — Segmentanfang und -fortsetzung waren visuell
 * ununterscheidbar, obwohl kalender-screen.js die Unterscheidung extra liefert.
 */
.kalender-belegung-fortsetzung {
    border-left: none;
}

.kalender-belegung-laeuft-weiter {
    font-weight: 700;
}

/*
 * Dritter Zellzustand "in Wartung" (Plan-Hub #5) — derselbe Token wie die
 * Statusabzeichen (.status-abzeichen--wartung), aber staerker gemischt, damit
 * eine ganze Zeile in Wartung sich von der schwaecheren Konflikt-Markierung
 * innerhalb einer belegten Zeile (.kalender-belegung-belegt--konflikt oben)
 * klar unterscheidet.
 */
.kalender-belegung-wartung {
    background: color-mix(in srgb, var(--color-status-wartung) 55%, var(--color-surface));
}

/*
 * Feiertagsmarkierung (Planhub #8) — eigener Farbton (--color-status-
 * unterwegs, Amber), bisher im Kalender ungenutzt, damit sie sich klar von
 * Belegt (Primärfarbe), Wartung (Rot) und der Wochenend-Einfärbung
 * (gedämpftes Grau) unterscheidet. Der Feiertagsname steht in allen drei
 * Ansichten zusätzlich als Text (sichtbar im Wochentitel/als title-Attribut,
 * sonst sr-only), nie nur die Farbe (WCAG 1.4.1). Kontrast nachgerechnet in
 * tests/Feature/KontrastTest.php (belegter Messwert, keine Schätzung).
 *
 * Steht bewusst NACH der Wochenend-Regel oben: der Belegungsplan-Selektor
 * unten hat dieselbe Spezifität (0,4,1) wie die Wochenend-Regel — nur die
 * spätere Position im Stylesheet lässt den Feiertag bei einer Kollision
 * (Feiertag fällt auf ein Wochenende) sichtbar gewinnen, statt sich mit dem
 * Wochenend-Grau zu vermischen.
 *
 * Kalt-Review-Fund 19.08.2026: bis hierhin markierte kalender-screen.js nur
 * die th-Kopfzelle des Belegungsplans — die Feiertagsfarbe erreichte die
 * Geräte-Zeilen darunter nie, die ganze Spalte blieb bis auf den Kopf farblos.
 * Jetzt markiert renderBelegungsZeile() jede Zelle der Spalte, und dieselbe
 * :not()-Technik wie bei der Wochenend-Regel oben lässt eine tatsächliche
 * Belegung/Wartung weiterhin gewinnen (WCAG- und Lesbarkeits-Grund
 * unverändert: ein belegtes/wartendes Gerät muss auf einen Blick erkennbar
 * bleiben, auch an einem Feiertag).
 */
.kalender-monat td.kalender-tag--feiertag,
.kalender-belegung-tabelle th.kalender-belegung-feiertag,
.kalender-belegung-tabelle td.kalender-belegung-feiertag:not(.kalender-belegung-belegt):not(.kalender-belegung-wartung):not(.kalender-belegung-markiert) {
    background: color-mix(in srgb, var(--color-status-unterwegs) 22%, var(--color-surface));
}

/*
 * Wochenliste: die Tagesspalte ist kein einzelnes Zellfeld wie in den beiden
 * anderen Ansichten, sondern ein ganzer Abschnitt mit gestapelten
 * .kalender-eintrag-Blöcken — eine volle Hintergrundfüllung würde dort mit
 * den Einträgen kollidieren. Deshalb dieselbe Randlinien-Betonung wie bei
 * .kalender-wochengruppe--aktuell (Text trägt die Information ohnehin schon,
 * die Farbe ist Verstärkung).
 */
.kalender-tag--feiertag .kalender-tag-titel {
    padding-left: var(--abstand-2);
    border-left: 3px solid var(--color-status-unterwegs);
    color: var(--color-status-unterwegs);
}

/* ---- Zeitraum aufziehen (Plan-Hub #18) ---------------------------------- */

/*
 * Werkzeugleiste ueber dem Plan: der Umschalter und die Textzeile, die den
 * laufenden Vorgang im Klartext nennt. Die Zeile steht NEBEN dem Knopf und
 * nicht darunter, damit die Hoehe des Plans sich beim Ein- und Ausschalten
 * nicht aendert — ein springendes Layout mitten in einer Auswahl ist genau
 * die Sorte Unruhe, die eine Tabelle mit 21 Spalten nicht auch noch braucht.
 */
.kalender-belegung-werkzeuge {
    display: flex;
    flex-wrap: wrap;
    gap: var(--abstand-3);
    align-items: center;
    margin-bottom: var(--abstand-3);
}

.kalender-belegung-markierung {
    margin: 0;
    font-size: var(--schriftgroesse-3);
    color: var(--color-muted);
}

/*
 * Die freie Tageszelle hatte bis hierhin KEINE eigene Regel — der Knopf war
 * ein nackter Browser-Knopf, was in WebKit als Kreis und in Chromium als
 * liegende Pille erschien (offener Punkt aus dem TODO-Index, dort seit dem
 * 24.08.2026 ohne Ursache notiert). Feste Masse statt Browser-Vorgabe loesen
 * das und geben dem Knopf zugleich ein Klickziel, das die ganze Zelle fuellt:
 * die Zelle IST das Ziel, also soll sie es auch sein. 24px ist die Untergrenze
 * aus WCAG 2.2 Erfolgskriterium 2.5.8 (Zielgroesse, Minimum) — die 44px, die
 * dieses Projekt sonst fuer freistehende Knoepfe haelt, wuerden eine Tabelle
 * mit 21 Tagesspalten unbedienbar breit machen.
 */
.kalender-belegung-frei-knopf {
    display: block;
    width: 100%;
    min-height: 24px;
    padding: 0;
    background: none;
    border: none;
    border-radius: var(--radius-1);
    cursor: pointer;
}

.kalender-belegung-frei-knopf:hover {
    background: color-mix(in srgb, var(--color-primary) 8%, transparent);
}

.kalender-belegung-frei-knopf:focus-visible {
    outline: 2px solid var(--color-primary);
    outline-offset: -2px;
}

/*
 * Die Markierung traegt ZWEI Merkmale, und das ist kein Zierrat: der
 * Flaechenton allein wuerde auf einer belegten Zelle verschwinden, ueber die
 * hinweg ausdruecklich markiert werden darf (Council-Entscheid E2/E5d). Der
 * umlaufende Rahmen ist deshalb das tragende Merkmal — er ist Form, nicht
 * Farbe (WCAG 1.4.1) und sitzt als inset-Schatten INNERHALB der Zelle, damit
 * er die Spaltenbreite nicht veraendert und die Tabelle beim Markieren nicht
 * springt. Kontrast beider Toene ist in tests/Feature/KontrastTest.php
 * nachgerechnet, hell und dunkel.
 *
 * Steht bewusst NACH Wochenende und Feiertag: eine Markierung muss auch auf
 * einem markierten Samstag sichtbar sein. Dieselbe Positions-Begruendung wie
 * bei der Feiertagsregel weiter oben; zusaetzlich sind beide dortigen
 * :not()-Ketten um .kalender-belegung-markiert ergaenzt.
 *
 * Kein Eintrag in der Legende: die erklaert die Dauerzustaende des Plans
 * (belegt, Konflikt, Wartung, Feiertag, Wochenende). Die Markierung ist eine
 * laufende Handlung des Anwenders mit eigenem Klartext daneben — sie waere
 * dort ein Fremdkoerper, der bei jedem Blick auf den Plan mitgelesen werden
 * muesste, obwohl er die meiste Zeit gar nicht zutrifft.
 */
.kalender-belegung-tabelle td.kalender-belegung-markiert {
    background: color-mix(in srgb, var(--color-primary) 14%, var(--color-surface));
    box-shadow: inset 0 0 0 2px var(--color-primary);
}

/*
 * Zeitraum ziehen (Plan-Hub #21): waehrend einer laufenden Zeigergeste wird
 * die Textauswahl unterdrueckt — sonst markiert der Zug quer ueber die
 * Tabelle nebenbei die Wochentagsnamen und Geraetebezeichnungen blau, und die
 * eigentliche Auswahl verschwindet unter der Textmarkierung.
 *
 * Sie sitzt an der TABELLE und nur waehrend der Geste — beides absichtlich.
 * Dauerhaft waere sie falsch, weil das Markieren von Text hier sonst ein
 * voellig legitimer Vorgang ist (eine Geraetebezeichnung kopieren, ein Datum
 * herausschreiben). Und am Bereich DARUM waere sie zu weit gefasst: dort
 * liegt auch die Klartext-Live-Region, die den gewaehlten Zeitraum nennt —
 * ausgerechnet der Text, den man am ehesten herauskopiert (Kalt-Review-Fund
 * 28.08.2026, Befund 8).
 *
 * Und bewusst NICHT ueber ein preventDefault() auf pointerdown geloest, was
 * denselben Effekt haette: das nimmt dem Zellknopf den Fokus, und damit
 * fiele die Tastaturbedienung aus Plan-Hub #18 in sich zusammen.
 */
.kalender-belegung-tabelle.kalender-belegung-zieht {
    user-select: none;
    -webkit-user-select: none;
}

/*
 * Ueber eine belegte Zelle wird durchmarkiert — dort behaelt die Belegung
 * ihre eigene Flaeche und nur der Rahmen zeigt die Auswahl an. So bleibt
 * beides gleichzeitig lesbar: dass der Tag belegt ist UND dass er im
 * markierten Zeitraum liegt. Genau diese Doppelinformation braucht der
 * Anwender, bevor er absendet.
 */
.kalender-belegung-tabelle td.kalender-belegung-markiert.kalender-belegung-belegt {
    background: color-mix(in srgb, var(--color-primary) 22%, var(--color-surface));
}

/*
 * Und die Doppelbuchung behaelt ihren eigenen Ton, auch unter der Markierung
 * (Kalt-Review-Fund 27.08.2026). Die Regel darueber hatte ihn geschluckt —
 * ausgerechnet an den Tagen, an denen die Warnung am meisten zaehlt: wer
 * einen Zeitraum ueber eine bereits doppelt belegte Zelle zieht, soll das
 * SEHEN, bevor er absendet, und nicht erst die Kollisionswarnung des
 * Formulars lesen. Steht nach der markiert+belegt-Regel und gewinnt bei
 * gleicher Spezifitaet (0,3,1) durch die spaetere Position.
 */
.kalender-belegung-tabelle td.kalender-belegung-markiert.kalender-belegung-belegt--konflikt {
    background: color-mix(in srgb, var(--color-status-wartung) 30%, var(--color-surface));
}

/*
 * Dasselbe fuer eine Zeile, die waehrend einer laufenden Markierung auf
 * "in Wartung" wechselt: der Wartungston bleibt, die Markierung zeigt sich
 * ueber den Rahmen. Erreichbar nur ueber einen Statuswechsel mitten im
 * Vorgang — selten, aber die Regel darf nicht davon abhaengen, wie oft ein
 * Fall vorkommt.
 */
.kalender-belegung-tabelle td.kalender-belegung-markiert.kalender-belegung-wartung {
    background: color-mix(in srgb, var(--color-status-wartung) 55%, var(--color-surface));
}

/*
 * Legende UNTER der Belegungstabelle (Plan-Hub #5). Stand bis 18.08.2026 in
 * einer eigenen Werkzeugleiste neben den Scroll-Pfeilen — die sind entfallen
 * (dauerhafte Scrollleiste ersetzt sie), den Aussenabstand traegt die Legende
 * seitdem selbst. Seit dem 24.08.2026 steht sie unter dem Plan statt darueber
 * (Wunsch des Eigentuemers, Reihenfolge im Blade geaendert) — deshalb sitzt der
 * Aussenabstand jetzt OBEN statt unten, sonst klebte sie an der letzten
 * Tabellenzeile.
 *
 * Kalt-Review-Fund 19.08.2026: die Legende deckte nur 2 der 5 Flaechenfarben
 * im Plan ab (Belegt, Konflikt und Wochenende fehlten — Belegt ist dabei der
 * haeufigste Zustand ueberhaupt). flex-wrap + zweigeteilter gap, weil fuenf
 * Eintraege auf schmalen Bildschirmen sonst seitlich abgeschnitten wuerden
 * statt umzubrechen; die -legende-eintrag-Huelle haelt Muster und Text dabei
 * als Einheit zusammen.
 *
 * Der Selektor ist mit .karte verschachtelt, und das ist noetig statt huebsch:
 * `.karte p` (app.css:150) traegt eine Klasse PLUS einen Typ und ist damit
 * spezifischer als die blanke Klasse hier — es hat deren margin bisher
 * stillschweigend kassiert. Im Browser gemessen stand die Legende auf
 * margin 0 0 12px statt auf dem, was diese Regel sagte. Aufgefallen ist es nie,
 * weil die 12px von `.karte p` unten zufaellig gepasst haben; oben passt nichts
 * zufaellig. Gleiche Scoping-Technik wie `.shell-kopf .inhalt-breite` weiter
 * oben in dieser Datei.
 */
.karte .kalender-belegung-legende {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    row-gap: var(--abstand-1);
    column-gap: var(--abstand-2);
    margin: var(--abstand-3) 0 0;
    font-size: var(--schriftgroesse-3);
    color: var(--color-muted);
}

.kalender-belegung-legende-eintrag {
    display: inline-flex;
    align-items: center;
    gap: var(--abstand-1);
}

.kalender-belegung-legende-muster {
    display: inline-block;
    width: 0.875rem;
    height: 0.875rem;
    border: 1px solid var(--flaeche-rahmen);
    border-radius: var(--radius-1);
}

/*
 * Jede Muster-Farbe ist bewusst derselbe color-mix()-Ausdruck wie die
 * zugehoerige Zellregel weiter oben (.kalender-belegung-belegt,
 * .kalender-belegung-belegt--konflikt, die Wochenend-Regel,
 * .kalender-belegung-wartung, .kalender-belegung-feiertag) — der neue Test
 * test_belegungsplan_legende_zeigt_jede_flaechenfarbe_des_plans in
 * KalenderTest.php prueft genau diese Gleichheit, damit eine spaetere
 * Farbaenderung an einer Zelle nicht wieder lautlos an der Legende
 * vorbeilaeuft.
 */
.kalender-belegung-legende-muster--belegt {
    background: color-mix(in srgb, var(--color-primary) 22%, var(--color-surface));
}

.kalender-belegung-legende-muster--konflikt {
    background: color-mix(in srgb, var(--color-status-wartung) 30%, var(--color-surface));
}

.kalender-belegung-legende-muster--wartung {
    background: color-mix(in srgb, var(--color-status-wartung) 55%, var(--color-surface));
}

.kalender-belegung-legende-muster--feiertag {
    background: color-mix(in srgb, var(--color-status-unterwegs) 22%, var(--color-surface));
}

.kalender-belegung-legende-muster--wochenende {
    background: color-mix(in srgb, var(--color-muted) 12%, var(--color-surface));
}

/*
 * Inline-Reservierung (Plan-Hub #5): zentriertes Overlay mit Backdrop, kein
 * <dialog>-Element (Entscheidung E1 bleibt technisch bestehen, siehe
 * Blade-Kommentar). Backdrop-Farbe kommt aus --color-text statt einem
 * fremden Schwarz/Grau, damit sie zum Tonsystem der Seite passt. Schatten
 * auf dem Panel ist derselbe --schatten-karte-Token wie jede andere Karte,
 * kein zusaetzlicher Glow.
 */
.kalender-reservierung-popup {
    position: fixed;
    inset: 0;
    z-index: 100;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: var(--abstand-4);
    background: color-mix(in srgb, var(--color-text) 55%, transparent);
}

.kalender-reservierung-popup-panel {
    width: 100%;
    max-width: 32rem;
    max-height: calc(100vh - var(--abstand-7));
    overflow-y: auto;
    background: var(--color-surface);
    border-radius: var(--radius-2);
    box-shadow: var(--schatten-karte);
    padding: var(--abstand-4);
}

.kalender-reservierung-popup-panel:focus-visible {
    outline: none;
}

.kalender-reservierung-popup-kopf {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--abstand-2);
    margin-bottom: var(--abstand-2);
}

.kalender-reservierung-popup-kopf h2 {
    margin: 0;
}

.kalender-reservierung-popup-kontext {
    margin: 0 0 var(--abstand-3);
    color: var(--color-muted);
    font-size: var(--schriftgroesse-4);
}

/* ---- Board-Spalten (Aufgabe 4.1, PRD 4.4) ------------------------------- */

/*
 * Vier Statusabschnitte. Mobil untereinander (jede Spalte ist innen selbst
 * ein .karten-raster mit eigenen Breakpoints), ab Tablet nebeneinander.
 */
.board-spalten {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--abstand-6);
}

.board-spalte-titel {
    margin: 0 0 var(--abstand-3);
    font-size: var(--schriftgroesse-7);
    font-weight: 600;
}

/*
 * Bugfix 17.08.2026 (Kalt-Review-Fund, Regressions-Screenshot mit
 * realistischen Testdaten): das .karten-raster INNERHALB einer Board-Spalte
 * teilte sich bisher die globale @media(min-width:1024px)-Regel mit /lager
 * (Zeile ~1578) und wurde dort auf 3 Spalten gezwungen — die reagiert auf die
 * FENSTERBREITE, nicht auf die (viel schmalere) Spaltenbreite, bei vier
 * sichtbaren Spalten gemessen nur ~255px. Container Queries lösen genau das:
 * die Spalte selbst wird zum Bezugsrahmen. Das Projekt verlangt an anderer
 * Stelle bereits :has() und color-mix() ohne Fallback (Autorisierung::darf-
 * Selektoren, Belegungsplan) — Container Queries reihen sich in denselben
 * Browser-Anspruch ein, kein neuer Bruch mit dem bisherigen Stand.
 * Die zwei Klassen in .board-spalte .karten-raster wiegen als Selektor mehr
 * als die eine Klasse der globalen Regel und gewinnen deshalb unabhängig von
 * der Reihenfolge in der Kaskade.
 */
.board-spalte {
    container-type: inline-size;
}

.board-spalte .karten-raster {
    grid-template-columns: 1fr;
}

@container (min-width: 480px) {
    .board-spalte .karten-raster {
        grid-template-columns: repeat(2, 1fr);
    }
}

/* ---- Geraetekarte (PRD 7.3: GeraeteKarte) ------------------------------- */

/*
 * Im Muster die .equip-card: Kopf mit Abzeichen, Titel mit Obertitel,
 * Kennwerte in der Mitte, Fusszeile mit Aktion.
 *
 * Der Fuss haengt per margin-top:auto am unteren Rand. Das ist der Punkt, an
 * dem so ein Raster sonst ausfranst: Steht bei einem Geraet eine Zeile mehr
 * Text, rutscht ohne diese Zeile dessen Knopf tiefer als bei den Nachbarn und
 * die Reihe sieht kaputt aus.
 */
/*
 * Hintergrund/Rahmen/Radius/Schatten kommen aus .flaeche — das Markup setzt
 * beide Klassen zusammen (board-screen.js, lager-screen.js, seit 17.08.2026
 * auch docs/design/styleguide.html). Vorher standen dieselben vier
 * Eigenschaften hier dupliziert, obwohl .flaeche ohnehin immer mitgesetzt
 * war — zwei Stellen, die bei jeder Aenderung hätten auseinanderlaufen
 * können. overflow:hidden bleibt hier, .flaeche kennt es nicht.
 */
.geraete-karte {
    display: flex;
    flex-direction: column;
    overflow: hidden;
}

/*
 * Bugfix 20.08.2026 (Playwright-Rundgang nach dem Umbau minimalist-ui): Das
 * Statusabzeichen traegt seit der Versalien+Laufweite-Optik (minimalist-ui
 * §5, "Tags") mehr Breite als vorher ("IM EINSATZ" gesperrt gesetzt ist
 * breiter als "Im Einsatz" normal). Zwei Massnahmen zusammen:
 *
 * 1. flex-wrap: wrap — auf einer schmalen Karte (3-spaltiges Raster ab
 *    1024px) faellt das Abzeichen jetzt in eine eigene Zeile UNTER den
 *    Titel, statt ihn seitlich zu verdraengen. Nur min-width:0 allein hatte
 *    den Titel bei sehr schmalen Karten auf einzelne Woerter je Zeile
 *    gequetscht ("Bautrockner" / "TTK" / "301") — lesbarer ist eine
 *    zweizeilige Kopfzeile als ein zerhackter Titel.
 * 2. min-width:0 am Titel-Block bleibt zusaetzlich bestehen: ohne sie kann
 *    ein Flex-Kind nicht unter seine eigene Inhaltsbreite schrumpfen, und
 *    .geraete-karte (overflow: hidden) haette das Abzeichen weiterhin an der
 *    Kante abgeschnitten, sobald beide NICHT umbrechen (z. B. bei genau
 *    passender Breite kurz vor dem Wrap-Punkt).
 */
.geraete-karte-kopf {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--abstand-2) var(--abstand-3);
    padding: var(--abstand-4) var(--abstand-4) 0;
}

.geraete-karte-kopf > :first-child {
    min-width: 0;
}

.geraete-karte-titel {
    margin: 0;
    font-size: var(--schriftgroesse-8);
    font-weight: 600;
}

/* Die Kategorie ueber dem Namen — im Muster der graue Kleintext. */
.geraete-karte-obertitel {
    display: block;
    margin-bottom: var(--abstand-1);
    color: var(--color-muted);
    font-size: var(--schriftgroesse-4);
}

/* Inventarnummer und Zeitstempel sind echte Daten: Monospace (PRD 12.2). */
.geraete-karte-kennung {
    display: block;
    margin-top: var(--abstand-1);
    color: var(--color-muted);
    font-family: var(--schrift-mono);
    font-size: var(--schriftgroesse-4);
}

.geraete-karte-inhalt {
    padding: var(--abstand-4);
}

/*
 * Bewusst OHNE flex-wrap. Mit Umbruch schiebt eine etwas laengere Meta-Zeile
 * den Knopf in eine zweite Reihe — dann steht er bei einem Geraet unter dem
 * Text und beim Nachbarn daneben, und die Kartenreihe franst aus. Ohne
 * Umbruch bleibt die Zeilenform bei jeder Karte gleich: Text links, Aktion
 * rechts. Ueberlanger Text bricht dann innerhalb seiner eigenen Spalte um.
 */
.geraete-karte-fuss {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--abstand-3);
    margin-top: auto;
    padding: var(--abstand-4);
    border-top: 1px solid var(--flaeche-rahmen);
}

/*
 * Die Meta-Zeile darf schrumpfen, die Aktion nicht. Ohne min-width:0 haelt ein
 * Flex-Kind seine Inhaltsbreite und drueckt den Knopf aus der Karte heraus.
 */
.geraete-karte-fuss > :first-child {
    min-width: 0;
}

.geraete-karte-fuss .knopf-primaer,
.geraete-karte-fuss .knopf-sekundaer {
    flex: none;
}

/* ---- Kennzahlraster (Dashboard, 17.08.2026) ----------------------------- */

/*
 * Eigenes Raster statt .karten-raster: vier gleichrangige Kacheln passen
 * nicht in dessen 3er-Stufe ab Desktop (eine Kachel faellt allein in die
 * naechste Reihe). Mobil 1 Spalte (PRD 12.6 woertlich "einspaltig" fuer die
 * Handystufe), ab 600px 2 Spalten (deckt sich mit PRD "Tablet: zweispaltig"),
 * ab 1024px 4 Spalten in einer Reihe. Breakpoints stehen gebuendelt weiter
 * unten, wie beim Kartenraster.
 */
.kennzahl-raster {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--abstand-4);
}

/* ---- KPI-Kachel (Dashboard, 17.08.2026 · Kartenrhythmus 19.08.2026) ----- */

/*
 * Icon und Zahl+Label nebeneinander statt gestapelt — das haelt die
 * Kachel auch einspaltig auf dem Handy niedrig, ohne von der einspaltigen
 * Mobilstufe aus PRD 12.6 abzuweichen. Icon bleibt nackt auf der Flaeche
 * (PRD 12.5: kein Kachel-/Farbhintergrund hinter einem Icon). Nutzt
 * dieselbe .flaeche-Grundflaeche wie die Gerätekarte, keine eigene Farbe.
 *
 * Die erste Kachel ("Geräte im Einsatz") bekommt ab Desktop mehr Gewicht
 * (siehe :first-child-Regel im 1024px-Breakpoint unten und die groessere
 * Ziffer hier) — vier gleich laute Zahlen nebeneinander sagen nichts
 * darueber, welche gerade zaehlt. Reine Selektor-Regel, kein zusaetzliches
 * Markup und keine zusaetzliche Klasse am Element noetig (DashboardTest
 * zaehlt `class="flaeche kpi-kachel"` woertlich viermal).
 */
.kpi-kachel {
    display: flex;
    align-items: center;
    gap: var(--abstand-3);
    padding: var(--abstand-4);
}

@media (prefers-reduced-motion: no-preference) {
    .kpi-kachel,
    .dashboard-panel {
        transition: transform var(--dauer-ruhig) var(--kurve-ruhig);
    }

    .kpi-kachel:hover,
    .dashboard-panel:hover {
        transform: translateY(-2px);
    }
}

.kpi-kachel-icon {
    width: 28px;
    height: 28px;
    color: var(--color-primary);
    flex: none;
}

.kpi-kachel-wert {
    display: block;
    font-family: var(--schrift-mono);
    font-size: var(--schriftgroesse-12);
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    line-height: var(--zeilenhoehe-2);
}

.kpi-kachel-label {
    color: var(--color-muted);
    font-size: var(--schriftgroesse-4);
}

/* ---- Dashboard-Raster + Panel-Kopf (17.08.2026) ------------------------- */

/*
 * Eigenes Raster statt .karten-raster: zwei Panels fuellen dessen 3er-Stufe
 * ab Desktop nur zu zwei Dritteln. Mobil 1 Spalte, ab 1024px zwei Spalten
 * mit fester Gewichtung (Bewegungsliste breiter als Board-Vorschau).
 * Breakpoint gebuendelt weiter unten.
 */
.dashboard-raster {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--abstand-4);
}

/*
 * Doppelrand-Schale (19.08.2026): eine vertiefte Traegerflaeche UM das
 * bestehende Panel, nicht ANSTELLE davon — .dashboard-panel bleibt
 * unveraendert `.flaeche` (Test zaehlt `class="flaeche dashboard-panel"`
 * woertlich zweimal). Der Radius der Schale ist der Panel-Radius plus ein
 * Abstand-Schritt, damit beide Rundungen konzentrisch zueinander stehen.
 */
.dashboard-panel-schale {
    display: flex;
    flex-direction: column;
    background: var(--flaeche-vertieft);
    border-radius: calc(var(--radius-3) + var(--abstand-1));
    padding: var(--abstand-1);
}

/*
 * Kalt-Review-Fund 20.08.2026, per Browserdurchlauf bestaetigt: im
 * Desktop-3fr/2fr-Raster (.dashboard-raster) ist die Bewegungsliste durch
 * ihre fuenf Eintraege hoeher als die Board-Vorschau — ohne Hoehenbindung
 * endete die kuerzere Karte oberhalb des Schalenbodens, darunter blieb die
 * vertiefte Traegerflaeche als leere graue Flaeche sichtbar. `flex: 1` laesst
 * .dashboard-panel die volle Hoehe der (per Grid-Stretch bereits
 * gleich hohen) Schale ausfuellen.
 */
.dashboard-panel {
    display: flex;
    flex-direction: column;
    flex: 1;
    gap: var(--abstand-3);
    min-height: 8rem;
    padding: var(--abstand-5);
}

.panel-kopf {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--abstand-3);
}

.panel-kopf h2 {
    margin: 0;
    font-family: var(--schrift-display);
    font-size: var(--schriftgroesse-8);
    letter-spacing: var(--spatium-display);
}

.panel-kopf a {
    color: var(--color-primary);
    font-size: var(--schriftgroesse-4);
    white-space: nowrap;
}

/* ---- Bestandsband (Board-Vorschau, 19.08.2026) -------------------------- */

/*
 * Die vier Zahlen (Lager/Einsatz/Reserviert/Wartung) standen bisher nur als
 * Text nebeneinander — das Verhaeltnis zueinander war nirgends ablesbar,
 * obwohl alle Werte laengst da sind. Der Balken macht daraus eine echte
 * Aussage statt Dekoration: die Breite jedes Abschnitts ist der reale
 * Anteil am Gesamtbestand, serverseitig aus $zahlen berechnet
 * (dashboard.blade.php). Eingebettetes SVG statt eines mit style="width"
 * positionierten div: ein style-Attribut waere durch die CSP (PRD 9.2,
 * kein unsafe-inline) blockiert, SVG-Praesentationsattribute (x/width) sind
 * es nicht. Bewusst ohne Rundung: eine Karte mit border-radius auf einem
 * <svg>-Wurzelelement ist browserabhaengig unzuverlaessig zugeschnitten,
 * ein gerader Balken ist die robustere Wahl.
 */
.bestandsband {
    display: block;
    width: 100%;
}

@media (prefers-reduced-motion: no-preference) {
    .bestandsband-segment {
        transition: opacity var(--dauer-ruhig) var(--kurve-ruhig);
    }
}

.bestandsband-segment--frei {
    fill: var(--color-status-frei);
}

.bestandsband-segment--unterwegs {
    fill: var(--color-status-unterwegs);
}

.bestandsband-segment--wartung {
    fill: var(--color-status-wartung);
}

/* Reserviert bekommt bewusst KEINE eigene Statusfarbe, genau wie das
   Abzeichen darunter ohne Zusatzklasse (siehe Statusabzeichen-Kommentar
   oben: "ohne Zusatzklasse ist das Abzeichen neutral"). */
.bestandsband-segment--neutral {
    fill: var(--color-muted);
    opacity: 0.35;
}

/* ---- Statusverteilung (Board-Vorschau, 17.08.2026) ---------------------- */

/*
 * Reine Wiederverwendung von .status-abzeichen — dieselben vier Werte wie
 * im Bestandsband oben, hier als Legende mit Zahl und Text. `reserviert`
 * bekommt bewusst KEINEN Modifikator (siehe Statusabzeichen-Kommentar oben:
 * "ohne Zusatzklasse ist das Abzeichen neutral"), genau wie ueberall sonst
 * in der App.
 */
.status-verteilung {
    display: flex;
    flex-wrap: wrap;
    gap: var(--abstand-2);
}

/* ---- Bewegungsliste (Dashboard, 17.08.2026) ----------------------------- */

/*
 * Zeilenmuster nach dem Vorbild von .kalender-eintrag: Padding, Radius und
 * dezente Flaeche uebernommen, hier aber neutral statt farbig (eine
 * Buchungszeile hat keine Statusfarbe wie ein Kalendertag).
 */
.bewegungs-liste {
    display: flex;
    flex-direction: column;
    gap: var(--abstand-2);
}

.bewegungs-eintrag {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: var(--abstand-1) var(--abstand-2);
    padding: var(--abstand-2) var(--abstand-3);
    border-radius: var(--radius-1);
    background: var(--color-background);
    font-size: var(--schriftgroesse-4);
}

.bewegungs-eintrag-typ {
    font-weight: 600;
}

.bewegungs-eintrag-geraet {
    font-weight: 600;
}

.bewegungs-eintrag-meta {
    color: var(--color-muted);
}

/* ---- Statusabzeichen (PRD 7.3: StatusAbzeichen) ------------------------- */

/*
 * Im Muster das gruene "Sofort Verfuegbar". Drei Punkte, die dort fehlen und
 * hier Pflicht sind:
 *
 * 1. Der Text steht immer da. Farbe allein ist keine Information (PRD 12.7).
 * 2. Die Flaeche ist ein FESTER gedaempfter Pastellton (--flaeche-status-*,
 *    minimalist-ui §4), kein color-mix() mehr aus der Statusfarbe selbst —
 *    der Umbau vom 20.08.2026 ersetzt die variable Mischung durch die
 *    gedaempften Tags/Status-Flaechen des Skills. Die Werte stehen auch in
 *    tests/Feature/KontrastTest.php; wer sie hier aendert, muss sie dort
 *    mitaendern, sonst prueft der Test die falsche Paarung.
 * 3. Jede Kombination haelt WCAG AA, nachgerechnet und nicht angeschaut.
 *
 * Ohne Zusatzklasse ist das Abzeichen neutral — ein Zustand ohne eigene Farbe
 * soll darstellbar bleiben, ohne dass jemand eine erfindet.
 */
.status-abzeichen {
    display: inline-flex;
    flex-shrink: 0;
    align-items: center;
    gap: var(--abstand-2);
    padding: var(--abstand-1) var(--abstand-3);
    border: 1px solid var(--flaeche-rahmen);
    border-radius: var(--radius-2);
    background: var(--color-background);
    color: var(--color-muted);
    font-size: var(--schriftgroesse-1);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: var(--spatium-eyebrow);
    white-space: nowrap;
}

/* Der Punkt ist Beigabe, nie Traeger der Aussage. */
.status-abzeichen::before {
    content: '';
    width: var(--abstand-2);
    height: var(--abstand-2);
    border-radius: 50%;
    background: currentColor;
    flex: none;
}

.status-abzeichen--frei {
    color: var(--color-status-frei);
    background: var(--flaeche-status-frei);
    border-color: color-mix(in srgb, var(--color-status-frei) 30%, var(--color-surface));
}

.status-abzeichen--unterwegs {
    color: var(--color-status-unterwegs);
    background: var(--flaeche-status-unterwegs);
    border-color: color-mix(in srgb, var(--color-status-unterwegs) 30%, var(--color-surface));
}

.status-abzeichen--wartung {
    color: var(--color-status-wartung);
    background: var(--flaeche-status-wartung);
    border-color: color-mix(in srgb, var(--color-status-wartung) 30%, var(--color-surface));
}

/*
 * Ausgesondert (Planhub #11): gesperrt, stillgelegt oder archiviert. Bewusst
 * OHNE eigene Statusfarbe — PRD 12.1 haelt die drei Statusfarben fuer den
 * Verfuegbarkeitsstatus reserviert, und ein viertes Farbsignal daneben
 * verwaessert genau die Bedeutung, die dort geschuetzt werden soll. Dieses
 * Abzeichen traegt seine Aussage im Text und unterscheidet sich in der Form:
 * gedaempfte Flaeche, gestrichelter Rahmen, kein Punkt.
 */
.status-abzeichen--ausgesondert {
    color: var(--color-muted);
    background: var(--color-surface);
    border-style: dashed;
    border-color: var(--flaeche-rahmen);
}

.status-abzeichen--ausgesondert::before {
    display: none;
}

/* ---- Kennwerte (PRD 7.3: ZaehlerFeld) ----------------------------------- */

/*
 * Im Muster gleich zweimal: als Datenblatt-Kacheln am Geraet (Gewicht,
 * Grabtiefe, Antrieb) und als Telemetrie am Einsatz (Betriebsstunden,
 * Tankstand). Genau die Form, die dieses Projekt fuer Zaehlerstaende braucht.
 *
 * auto-fit statt fester Spaltenzahl: In einer schmalen Karte stehen zwei
 * Kacheln nebeneinander, in einer breiten vier, ohne eine einzige
 * Breakpoint-Regel.
 *
 * Die 8rem Mindestbreite sind gemessen, nicht geschaetzt. Mit 7rem passte im
 * Browser ein Datum wie "22.07.2026" nicht mehr in die Kachel: Der Wert wurde
 * an der Kartenkante abgeschnitten, weil die Karte overflow:hidden traegt.
 * Ein abgeschnittener Zaehlerstand ist schlimmer als eine Kachel weniger pro
 * Reihe.
 */
.kennwerte {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(8rem, 1fr));
    gap: var(--abstand-2);
    margin: 0;
    padding: 0;
}

.kennwert {
    padding: var(--abstand-3);
    background: var(--color-background);
    border: 1px solid var(--flaeche-rahmen);
    border-radius: var(--radius-2);
    text-align: center;
}

.kennwert-marke {
    display: block;
    color: var(--color-muted);
    font-size: var(--schriftgroesse-4);
}

/*
 * Der Wert ist eine echte Zahl: Monospace, damit Ziffern untereinander stehen
 * (PRD 12.2).
 *
 * overflow-wrap ist die Rueckfalllinie: Kommt doch einmal ein laengerer Wert
 * als vorgesehen, bricht er um, statt an der Kartenkante abgeschnitten zu
 * werden. Eine umgebrochene Zahl ist haesslich, eine halbe Zahl ist falsch.
 */
.kennwert-wert {
    display: block;
    /*
     * Die 0 links ist kein Schoenheitsfehler, sondern Pflicht: Der Browser
     * gibt jedem <dd> von sich aus 40 px Einzug. Im Browser gemessen blieben
     * dadurch von 127 px Kachelbreite nur 85 px fuer den Wert uebrig, und ein
     * Datum wie 22.07.2026 brach mitten in der Jahreszahl um.
     */
    margin: var(--abstand-1) 0 0;
    font-family: var(--schrift-mono);
    font-weight: 600;
    overflow-wrap: anywhere;
}

/* ---- Filterreihe -------------------------------------------------------- */

/*
 * Im Muster die Kategorie-Knoepfe ueber dem Katalog. Die Umsetzung nutzt
 * echte <button>-Elemente mit aria-pressed statt gefaerbter <div> — sonst
 * erreicht die Tastatur sie nicht und ein Screenreader sagt nicht, welcher
 * Filter gerade steht.
 */
.filterreihe {
    display: flex;
    flex-wrap: wrap;
    gap: var(--abstand-2);
}

.filter-knopf {
    min-height: 44px;
    padding: var(--abstand-2) var(--abstand-4);
    font: inherit;
    color: var(--color-text);
    background: var(--color-surface);
    border: 1px solid var(--flaeche-rahmen);
    border-radius: var(--radius-2);
    cursor: pointer;
}

.filter-knopf:hover {
    border-color: var(--color-accent);
}

.filter-knopf:focus-visible {
    outline: 2px solid var(--color-primary);
    outline-offset: 1px;
}

/*
 * Der gewaehlte Filter wird ueber aria-pressed erkannt, nicht ueber eine
 * zusaetzliche Klasse. So koennen Anzeige und Vorlesetext nicht auseinander
 * laufen: Es gibt nur eine Quelle fuer "ist gewaehlt".
 */
.filter-knopf[aria-pressed='true'] {
    color: var(--auf-flaeche-dunkel);
    background: var(--flaeche-dunkel);
    border-color: var(--flaeche-dunkel);
    font-weight: 600;
}

/* ---- Zweite Aktion ------------------------------------------------------ */

/*
 * Es bleibt bei genau EINER Primaeraktion je Screen (PRD 12.4). Dieser Knopf
 * ist die ruhige Zweitaktion daneben — im Muster der weisse Knopf mit Rahmen.
 */
/* inline-flex aus demselben Grund wie bei .knopf-primaer: Als <a> saesse der
   Text sonst nicht mittig in der Trefflaeche. */
.knopf-sekundaer {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-height: 44px;
    padding: var(--abstand-2) var(--abstand-5);
    font: inherit;
    font-weight: 600;
    text-align: center;
    text-decoration: none;
    color: var(--color-text);
    background: var(--color-surface);
    border: 1px solid var(--flaeche-rahmen);
    border-radius: var(--radius-1);
    cursor: pointer;
}

.knopf-sekundaer:hover {
    border-color: var(--color-accent);
    color: var(--color-accent);
}

.knopf-sekundaer:focus-visible {
    outline: 2px solid var(--color-primary);
    outline-offset: 1px;
}

/*
 * Gesperrt, aber ansprechbar (Planhub #13). Der Knopf bleibt an seinem Platz
 * und sagt beim Antippen, warum er nicht geht — deshalb `aria-disabled` statt
 * `disabled`: ein deaktiviertes <button> empfaengt keinen Klick, und auf einem
 * Handy gibt es kein Ueberfahren, das den Titel zeigen wuerde.
 *
 * Die Farbe ist --color-muted. Achtung bei der Begruendung: KontrastTest.php
 * rechnet --color-muted gegen --color-surface nach, hier liegt die Schrift
 * aber auf --flaeche-vertieft — ein ANDERES Paar, also kein uebertragbarer
 * Beleg. Im Browser gemessen (getComputedStyle + WCAG-Formel, Chrome
 * 25.08.2026): 4,6:1, damit AA erfuellt, aber knapp. Wer --flaeche-vertieft
 * oder --color-muted anfasst, misst diese Stelle neu.
 *
 * Der Hover-Wechsel faellt weg, sonst verspraeche der Knopf eine
 * Bedienbarkeit, die er nicht hat; der Fokusrahmen bleibt, denn angesteuert
 * werden kann er weiterhin.
 */
.knopf-sekundaer[aria-disabled="true"] {
    color: var(--color-muted);
    border-color: var(--flaeche-rahmen);
    background: var(--flaeche-vertieft);
    cursor: default;
}

.knopf-sekundaer[aria-disabled="true"]:hover {
    color: var(--color-muted);
    border-color: var(--flaeche-rahmen);
}

/* ---- Leerzustand (PRD 7.3: LeerZustand) --------------------------------- */

/*
 * Im Muster die leere Miet-Mappe. PRD 7.6 verlangt genau diese Form:
 * erklaerender Text PLUS die naheliegende Aktion, "nie nur keine Daten".
 * Deshalb ist der Platz fuer die Aktion Teil des Bauteils und nicht dem
 * jeweiligen Screen ueberlassen.
 */
.leer-zustand {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--abstand-3);
    /*
     * Dieselbe Hoechstbreite wie .karte. Ohne sie liefe der Leerzustand ueber
     * die volle Spalte, waehrend die Karte darunter bei 44rem endet — zwei
     * gestapelte Floechen mit verschiedenen rechten Kanten sehen nicht nach
     * Absicht aus, sondern nach Versehen.
     */
    max-width: 44rem;
    padding: var(--abstand-7) var(--abstand-5);
    text-align: center;
    border: 1px dashed var(--flaeche-rahmen);
    border-radius: var(--radius-3);
    background: var(--color-surface);
}

.leer-zustand-titel {
    margin: 0;
    font-family: var(--schrift-display);
    font-size: var(--schriftgroesse-8);
    font-weight: 600;
    letter-spacing: var(--spatium-display);
}

.leer-zustand-text {
    margin: 0;
    max-width: 32rem;
    color: var(--color-muted);
}

/* ---- Abschnittskopf ----------------------------------------------------- */

/*
 * Bewusst OHNE das kleine Grossbuchstaben-Kaertchen ueber der Ueberschrift,
 * das im Muster jeden Abschnitt einleitet. Es traegt keine Information, und
 * wenn jeder Abschnitt gleich anfaengt, liest ihn niemand mehr.
 */
.abschnitt-kopf {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--abstand-3);
    margin-bottom: var(--abstand-5);
}

.abschnitt-titel {
    margin: 0;
    font-family: var(--schrift-display);
    font-size: var(--schriftgroesse-8);
    font-weight: 600;
    letter-spacing: var(--spatium-display);
}

/* ---- Tabelle (Layout-Audit 2026-08-06, /admin/preiskatalog) ------------- */

/*
 * Erste <table> des Projekts (Preiskatalog, Feature 15). Das Muster
 * (layoutmuster.html) kennt keine einzige Tabelle — Listen loest es dort als
 * Kartenraster, was fuer eine fuenfspaltige Verwaltungsliste nicht passt.
 * Die Form hier ist deshalb selbst abgeleitet, im Duktus der uebrigen
 * Komponenten: linksbuendige Kopfzelle, feine Trennlinie je Zeile aus
 * --flaeche-rahmen statt der Browser-Werkseinstellung (die vorher weder
 * Abstand noch Trennlinie kannte).
 */
.tabelle-rahmen {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
}

.tabelle {
    width: 100%;
    min-width: 32rem;
    border-collapse: collapse;
    font-size: var(--schriftgroesse-5);
}

.tabelle th {
    padding: var(--abstand-3);
    border-bottom: 2px solid var(--flaeche-rahmen);
    color: var(--color-muted);
    font-size: var(--schriftgroesse-3);
    font-weight: 600;
    text-align: left;
    white-space: nowrap;
}

.tabelle td {
    padding: var(--abstand-3);
    border-bottom: 1px solid var(--flaeche-rahmen);
    vertical-align: middle;
}

.tabelle tbody tr:last-child td {
    border-bottom: none;
}

/*
 * Aktionsknoepfe nebeneinander statt gestapelt. Ohne diese Regel folgt eine
 * Zelle mit zwei <button> deren Blockfluss und stapelt sie untereinander — im
 * Browser gemessen standen „Bearbeiten" und „Loeschen" 44px versetzt in
 * derselben Zelle.
 */
.tabelle-aktionen {
    display: flex;
    flex-wrap: wrap;
    gap: var(--abstand-2);
}

/*
 * Sortierbare Spaltenkoepfe (Planhub #11). Der Schalter ist ein echtes
 * <button> IM <th> — nur so ist er per Tabulator erreichbar (WAI-ARIA APG,
 * „Sortable Table"). Er sieht aus wie die Kopfzeile und verhaelt sich wie ein
 * Knopf; deshalb erbt er Schrift und Farbe statt eigene Werte zu setzen.
 */
.tabelle-sortierknopf {
    display: inline-flex;
    align-items: center;
    gap: var(--abstand-2);
    padding: 0;
    border: 0;
    background: none;
    color: inherit;
    font: inherit;
    letter-spacing: inherit;
    text-align: left;
    cursor: pointer;
}

.tabelle-sortierknopf:hover .tabelle-sortierpfeil,
.tabelle-sortierknopf:focus-visible .tabelle-sortierpfeil {
    opacity: 1;
}

/*
 * Der Pfeil ist Beigabe, nie Traeger der Aussage: welche Spalte sortiert ist,
 * steht in aria-sort am <th>, und daraus faerbt sich der Pfeil. Wer ihn nicht
 * sieht, verliert nichts (dieselbe Linie wie beim Punkt im Statusabzeichen).
 */
.tabelle-sortierpfeil {
    width: 0;
    height: 0;
    border-inline: 4px solid transparent;
    border-block-end: 5px solid currentColor;
    opacity: 0;
}

.tabelle th[aria-sort] .tabelle-sortierpfeil {
    opacity: 1;
}

.tabelle th[aria-sort='descending'] .tabelle-sortierpfeil {
    transform: rotate(180deg);
}

.tabelle th[aria-sort] {
    color: var(--color-primary);
}

/*
 * Abzeichen-Reihe in der Statuszelle: Verfuegbarkeit plus die Gruende, warum
 * die Zeile sonst fehlte. Bewusst ein eigenes Element INNERHALB des <td> und
 * nicht das <td> selbst — `display: flex` auf einer Tabellenzelle nimmt sie
 * aus dem Tabellenraster, ihre Breite passt dann nicht mehr zur Kopfzeile,
 * und die naechste Zelle rutscht darunter statt daneben. Im Browser gemessen
 * am 25.08.2026: zwei solche Zellen nebeneinander ergaben eine 175px hohe
 * Zeile mit gestapelten Spalten statt 53px nebeneinander.
 */
.lager-abzeichen-reihe {
    display: flex;
    flex-wrap: wrap;
    gap: var(--abstand-2);
}

/*
 * Spaltenbreiten der Bestandsliste. Ohne sie verteilt der Browser nach
 * Textlaenge, und ein Geraet mit langem Namen schiebt Standort und Status auf
 * ein Drittel zusammen. Die Bezeichnung darf umbrechen, die uebrigen nicht.
 */
.lager-tabelle-rahmen .tabelle {
    table-layout: fixed;
}

/*
 * Rückfrage vor dem Stilllegen (Planhub #11). Kein <dialog>: das Projekt hat
 * dafür seine eigene Linie (kalender.blade.php:151-156) und arbeitet mit dem
 * hidden-Muster. Der Block hebt sich durch seine Fläche ab statt durch einen
 * Schatten — der wäre hier das einzige Element mit einem, und ein einzelner
 * Schatten in einer schattenlosen Oberfläche liest sich als Versehen.
 */
.lager-stilllegen {
    margin-block: var(--abstand-5);
    padding: var(--abstand-5);
    border: 1px solid var(--flaeche-rahmen);
    border-radius: var(--radius-2);
    background: var(--flaeche-vertieft);
}

.lager-stilllegen-frage {
    margin-block: 0 var(--abstand-4);
    font-weight: 600;
}

/*
 * Filterleiste der Bestandsliste (Planhub #11). Ein Raster statt einer Reihe:
 * fuenf Filter nebeneinander passen auf keinen Handybildschirm, und ein
 * waagerecht scrollender Filterbalken ist schlechter als ein umbrechender.
 * `auto-fit` mit einer Mindestbreite laesst den Browser entscheiden, wie viele
 * Spalten hineinpassen — von einer auf dem Handy bis vier am Schreibtisch.
 */
/*
 * Die Huelle des Filterblocks. Ab 1024px verschwindet der Aufklapp-Griff: dort
 * ist der Block kompakt, und ein Griff, der nie gebraucht wird, ist nur ein
 * weiteres Element zwischen Ueberschrift und Inhalt.
 */
.lager-filter-huelle {
    margin-block-end: var(--abstand-5);
}

.lager-filter-zusammenfassung {
    display: flex;
    align-items: center;
    gap: var(--abstand-2);
    min-height: 44px;
    color: var(--color-muted);
    font-size: var(--schriftgroesse-3);
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    cursor: pointer;
}

/*
 * `display: flex` auf einem <summary> nimmt ihm in Chrome das eingebaute
 * Aufklapp-Dreieck — im Browser gesehen am 25.08.2026: ein Griff ohne jedes
 * Zeichen, dass sich dahinter etwas oeffnen laesst. Der Pfeil hier ersetzt es,
 * in derselben Form wie der Sortierpfeil in der Tabelle darunter.
 */
.lager-filter-zusammenfassung::-webkit-details-marker {
    display: none;
}

.lager-filter-zusammenfassung::after {
    content: '';
    width: 0;
    height: 0;
    border-inline: 5px solid transparent;
    border-block-start: 6px solid currentColor;
}

.lager-filter-huelle[open] > .lager-filter-zusammenfassung::after {
    transform: rotate(180deg);
}

@media (min-width: 1024px) {
    .lager-filter-zusammenfassung {
        display: none;
    }
}

.lager-filter {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--abstand-4);
    margin-block-end: var(--abstand-5);
}

/*
 * Ab 1024px stehen Suche und Standort nebeneinander, die beiden
 * Ankreuzgruppen und der Schalter nehmen die volle Breite. Das ist nicht
 * Geschmack: mit sieben Kategorien in einer schmalen Rasterspalte stapeln sich
 * die Kaestchen untereinander und schieben die Tabelle unter die Falz — im
 * Browser gemessen am 25.08.2026, die Liste begann erst bei 800px Scrolltiefe.
 * Ueber die ganze Breite fliessen dieselben sieben in zwei Reihen.
 */
@media (min-width: 1024px) {
    .lager-filter {
        grid-template-columns: 2fr 1fr;
    }

    .lager-filter-gruppe,
    .lager-filter-schalter {
        grid-column: 1 / -1;
    }
}

/* Ein <fieldset> bringt Browser-Rahmen und -Abstaende mit, die hier stoeren. */
.lager-filter-gruppe {
    margin: 0;
    padding: 0;
    border: 0;
}

.lager-ankreuzreihe {
    display: flex;
    flex-wrap: wrap;
    gap: var(--abstand-2) var(--abstand-4);
}

/*
 * Die vier festen Spalten summieren sich bewusst auf 68 %, nicht auf 100: die
 * fünfte (Aktionen) gibt es nur mit Pflegerecht. Fehlt sie, verteilt der
 * Browser die übrigen 32 % gleichmässig auf die vier — ohne dass hier ein
 * zweiter Satz Breiten für den Fall gepflegt werden müsste.
 */
.lager-tabelle-rahmen th:nth-child(1) { width: 24%; }
.lager-tabelle-rahmen th:nth-child(2) { width: 14%; }
.lager-tabelle-rahmen th:nth-child(3) { width: 14%; }
.lager-tabelle-rahmen th:nth-child(4) { width: 16%; }
.lager-tabelle-rahmen th:nth-child(5) { width: 32%; }

.lager-tabelle-rahmen td {
    vertical-align: top;
    overflow-wrap: anywhere;
}

/*
 * Kompaktere Aktionsknoepfe, NUR in dieser Tabelle. Im Browser gemessen
 * (25.08.2026): mit dem regulaeren Innenabstand von 24px brauchen die drei
 * Knoepfe zusammen 375px, die Spalte hat 319 — jeder Knopf landete in einer
 * eigenen Zeile und die Tabellenzeile wurde 174px hoch. Mit 12px passen sie
 * in eine Zeile (303px) und die Zeile schrumpft auf rund 68px.
 *
 * Die Hoehe bleibt bei 44px: das ist die Groesse, die eine Fingerkuppe
 * braucht (WCAG 2.5.8), und daran wird nicht gespart. Gekuerzt wird nur der
 * waagerechte Abstand, und ausschliesslich hier — die uebrigen Tabellen des
 * Projekts (Preiskatalog, Benutzer, Kategorien) behalten ihre Knopfform.
 */
.lager-tabelle-rahmen .tabelle-aktionen .knopf-sekundaer {
    padding-inline: var(--abstand-3);
    min-height: 44px;
}

/*
 * Gerätebild-Vorschau (Planhub #6, Geräteverwaltungsseite). Gleiche Struktur
 * wie .profil-avatar* in app.css (Bild/Platzhalter/Aktionen nebeneinander),
 * aber eckig statt rund — ein Gerätefoto ist kein Portrait.
 */
.geraet-bild {
    display: flex;
    align-items: center;
    gap: var(--abstand-3);
    margin-bottom: var(--abstand-4);
}

.geraet-bild-vorschau,
.geraet-bild-platzhalter {
    width: 5rem;
    height: 5rem;
    flex-shrink: 0;
    border-radius: var(--radius-2);
}

.geraet-bild-vorschau {
    object-fit: cover;
    border: 1px solid var(--flaeche-rahmen);
}

.geraet-bild-platzhalter {
    display: flex;
    align-items: center;
    justify-content: center;
    border: 1px dashed var(--flaeche-rahmen);
    font-size: var(--schriftgroesse-2);
    color: var(--color-text-muted, currentColor);
    text-align: center;
    padding: var(--abstand-1);
}

.geraet-bild-aktionen {
    display: flex;
    flex-wrap: wrap;
    gap: var(--abstand-2);
}

/* ---- Responsives Verhalten (PRD 12.6) ----------------------------------- */

/*
 * Die drei Stufen aus PRD 12.6, an einer Stelle gebuendelt. Mobile first: was
 * oben steht, gilt fuer das Handy; hier kommt nur dazu, was groessere Geraete
 * anders machen.
 *
 * Vorher hatte das Projekt keine einzige @media-Regel. Das war kein Versaeumnis
 * der bisherigen Screens (eine Anmeldemaske braucht keine), wird aber ab dem
 * Board zur Voraussetzung.
 */

/* Tablet: 600 bis 1023 px — zweispaltig */
@media (min-width: 600px) {
    .inhalt-breite {
        padding-inline: var(--abstand-5);
    }

    .karten-raster {
        grid-template-columns: repeat(2, 1fr);
    }

    /* Kennzahlraster: 2x2 statt vier gestapelter Kacheln — deckt sich mit
       der PRD-Vorgabe "Tablet: zweispaltig". */
    .kennzahl-raster {
        grid-template-columns: repeat(2, 1fr);
    }

    /* Monatsraster: ab hier passen kurze Geraetenamen in die Zelle, die
       Anzahl-Marke weicht ihnen (Entscheidung E2 im Plan). */
    .kalender-tag-kurz {
        display: block;
    }

    .kalender-tag-anzahl {
        display: none;
    }
}

/*
 * Bestandsliste (Planhub #11): dieselben Daten in zwei Darstellungen, die
 * Bildschirmbreite entscheidet. Bis 1023px das Kartenraster — wer einzelne
 * Geraete nacheinander ansieht, ist damit besser bedient. Ab 1024px die
 * Tabelle, weil dort Zeilen miteinander verglichen werden (tenscope.com,
 * „Responsive Table Design"). KEIN Umschalter: die Breite ist die Frage, und
 * die beantwortet CSS, nicht der Anwender.
 *
 * Beide Behaelter sind IMMER gefuellt. `display: none` statt eines leeren
 * Containers ist Absicht — so gibt es keinen Moment, in dem beim Drehen des
 * Geraets die eine Ansicht schon weg und die andere noch nicht da ist.
 */
.lager-tabelle-rahmen {
    display: none;
}

/* Desktop: ab 1024 px — dreispaltig */
@media (min-width: 1024px) {
    .lager-tabelle-rahmen {
        display: block;
    }

    .lager-karten {
        display: none;
    }

    .inhalt-breite {
        padding-inline: var(--abstand-6);
    }

    .karten-raster {
        grid-template-columns: repeat(3, 1fr);
    }

    /*
     * Kennzahlraster (Kartenrhythmus 19.08.2026): drei Spalten statt vier,
     * die erste Kachel spannt die ganze Zeile — "Geräte im Einsatz" ist die
     * Zahl, die im Tagesgeschaeft zuerst interessiert, die uebrigen drei
     * ordnen sich darunter ein. :first-child statt einer eigenen Klasse:
     * DashboardTest.php zaehlt `class="flaeche kpi-kachel"` woertlich
     * viermal, eine zusaetzliche Klasse am Element wuerde das brechen.
     */
    .kennzahl-raster {
        grid-template-columns: repeat(3, 1fr);
    }

    .kennzahl-raster > .kpi-kachel:first-child {
        grid-column: 1 / -1;
        background: color-mix(in srgb, var(--color-primary) 6%, var(--color-surface));
    }

    .kennzahl-raster > .kpi-kachel:first-child .kpi-kachel-icon {
        width: 36px;
        height: 36px;
    }

    .kennzahl-raster > .kpi-kachel:first-child .kpi-kachel-wert {
        font-size: var(--schriftgroesse-13);
    }

    /* Dashboard-Raster: Bewegungsliste breiter als Board-Vorschau. */
    .dashboard-raster {
        grid-template-columns: 3fr 2fr;
    }

    /* Board-Spalten sind ein eigenes Raster (vier Statusabschnitte
       nebeneinander), nicht dasselbe Kartenraster wie ihr Inhalt — erst ab
       Desktop-Breite ist genug Platz für alle vier gleichzeitig.
       auto-fit statt einer festen repeat(4, 1fr) (Bugfix 17.08.2026, mit dem
       .karten-raster-Fix oben zusammen gefunden): board-screen.js blendet bei
       einem aktiven Statusfilter drei Spalten per [hidden] aus, ein fest auf
       vier Spuren stehendes Grid kollabiert ausgeblendete Elemente aber
       NICHT — gemessen blieb die eine sichtbare Spalte bei ~255px stehen,
       drei unsichtbare Spuren daneben ungenutzt. Mit auto-fit ergeben sich
       bei allen vier sichtbaren Spalten weiterhin vier gleich breite Spalten
       (255px bei 1440px Fensterbreite, wie vorher), bei weniger sichtbaren
       nutzt die verbleibende Spalte automatisch die frei gewordene Breite. */
    .board-spalten {
        grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
    }

    /* Kalenderwoche in voller Breite, PRD 12.6 wörtlich: "ab 1024 px in
       voller Wochenbreite" — bleibt wahr, jede der drei Wochengruppen
       (2026-08-17) nutzt weiterhin die volle Breite, nur gestapelt statt
       einzeln. Regel sitzt seitdem auf .kalender-wochengruppe-tage statt
       auf .kalender-woche selbst (das ist jetzt nur noch der Stapel). */
    .kalender-wochengruppe-tage {
        grid-template-columns: repeat(7, 1fr);
    }

    /* Monatsraster: mehr Zeilenhoehe, weil ab hier bis zu zwei Geraetenamen
       plus "+N weitere" in eine Zelle passen sollen. */
    .kalender-monat td {
        height: 6rem;
    }

    /* Seitenleiste (TODO 10.10): ab hier steht sie fest in der Reihe statt
       als Overlay ueber dem Inhalt, standardmaessig ausgeklappt — dieselbe
       [data-zustand]-Steuerung wie mobil, nur mit vertauschter Grund-
       einstellung (Voreinstellung "offen" statt "zu"). Gleiche Spezifitaet
       (eine Klasse) wie die mobile Grundregel oben — hier gewinnt sie durch
       die spaetere Position in der Kaskade; [data-zustand]-Selektoren sind
       in beiden Faellen spezifischer und ueberschreiben unabhaengig von der
       Reihenfolge, siehe .seitenleiste[data-zustand='offen'/'zu'] oben bzw.
       unten. */
    /* Kein align-items hier: die Voreinstellung "stretch" bringt Leiste und
       Inhalt automatisch auf dieselbe Hoehe (vorher stand hier bewusst
       flex-start, das war der Fehler — die Leiste reichte dadurch nur bis
       zu ihrem eigenen Inhalt, nicht bis zum Ende des Hauptbereichs). */
    .shell-rumpf {
        display: flex;
        gap: var(--abstand-5);
    }

    .seitenleiste {
        flex-shrink: 0;
        display: flex;
        flex-direction: column;
        /* TODO 10.13: der Griff steht ab jetzt im Fluss ueber der Liste
           (vorher schwebte er absolut positioniert daneben) — der Abstand
           dazwischen kommt jetzt von hier statt von einem Rand am Griff. */
        gap: var(--abstand-2);
    }

    /* flex:1 laesst die Liste (und ihre dunkle Flaeche) bis zum unteren
       Rand von .seitenleiste durchlaufen — die reicht ihrerseits per
       align-items:stretch auf .shell-rumpf bis zum Ende des Hauptbereichs. */
    /* Die Breite kommt aus der Grundregel oben (width: 15rem) und bleibt in
       JEDEM Zustand gleich — das ist hier wichtiger als mobil: die Leiste
       steht fest in der Reihe, jede Breitenaenderung verschiebt den ganzen
       Hauptinhalt daneben. flex-shrink:0 auf .seitenleiste (oben im selben
       Block) haelt sie zusaetzlich davon ab, unter diese Breite gedrueckt zu
       werden, wenn der Inhalt daneben eng wird. */
    /* Die Breite kommt aus der Grundregel oben (width: 15rem) und bleibt in
       JEDEM Zustand gleich — das ist hier wichtiger als mobil: die Leiste
       steht fest in der Reihe, jede Breitenaenderung verschiebt den ganzen
       Hauptinhalt daneben.

       Die Liste klebt am Fenster statt mitzuscrollen, und ihre Hoehe ist auf
       den sichtbaren Rest begrenzt: mit allen drei Gruppen offen ist sie rund
       940px hoch und fiele bei einem kurzen Fenster (Notebook quer) unten aus
       dem Bild — der letzte Eintrag "Export" war bei 560px Fensterhoehe nicht
       mehr erreichbar. Jetzt scrollt sie stattdessen in sich selbst
       (overflow-y aus der Grundregel oben).

       Die Summe im calc() ist hergeleitet, nicht geraten: Kopfhoehe + der
       Abstand darunter + die 44px Trefflaeche des Griffs + dessen Abstand zur
       Liste. Unten steht --abstand-6 als Reserve, weil --kopf-hoehe die
       MINDEST-Hoehe der Kopfzeile ist und nicht ihre Ist-Hoehe (siehe
       Kommentar am Griff unten) — ohne die Reserve fehlte bei 560px genau ein
       Pixel. Am 24.08.2026 in beiden Zustaenden gemessen, gescrollt wie
       ungescrollt.

       flex:none statt flex:1 — die durchlaufende dunkle Flaeche bis zum
       unteren Rand traegt jetzt .seitenleiste selbst (sie hat denselben
       Hintergrund und streckt sich per align-items:stretch), nicht mehr die
       Liste. Im Browser gegengeprueft: die Flaeche laeuft unveraendert durch. */
    .seitenleiste-liste {
        display: flex;
        flex: none;
        align-self: flex-start;
        position: sticky;
        top: calc(var(--kopf-hoehe) + var(--abstand-4) + 44px + var(--abstand-2));
        max-height: calc(
            100dvh - var(--kopf-hoehe) - var(--abstand-4) - 44px
            - var(--abstand-2) - var(--abstand-6)
        );
        min-width: 0;
        box-shadow: none;
    }

    .seitenleiste[data-zustand='zu'] .seitenleiste-liste {
        display: none;
    }

    /* Schwebender Kreis-Griff statt der eckigen Kachel. TODO 10.13: vorher
       haengte er per position:absolute; top:50% mittig an der GESAMTEN
       Seitenhoehe (.seitenleiste streckt sich ueber den ganzen Inhalt) —
       auf laengeren Seiten lag er dadurch weit unter dem sichtbaren
       Fenster, zugeklappt (Leiste dann 0px breit) sogar halb ausserhalb
       des Bildschirms links. position:sticky bezieht sich stattdessen auf
       das Fenster: der Griff bleibt oben, direkt unter der Kopfzeile,
       gleich in welchem Zustand oder wie weit gescrollt wird. align-self
       haengt ihn an die rechte Kante der Leistenspalte, translateX(50%)
       zentriert ihn AUF der Kante zwischen Leiste und Inhalt (Transform
       aendert die Layoutbreite nicht) — dasselbe Zentrierprinzip wie vorher,
       nur horizontal statt beidachsig.

       Rechnet mit einer EINZEILIGEN Kopfzeile (--kopf-hoehe ist ihre
       Mindesthoehe, nicht ihre garantierte Ist-Hoehe) — bei 1024px und
       1440px im Browser bestaetigt, dass sie das bleibt. Bricht der
       Kopfinhalt (z. B. ein sehr langer Benutzername) doch zweizeilig um,
       waechst die reale Hoehe ueber diesen Abstand hinaus.

       z-index noetig, weil .seitenleiste-liste als Flex-Kind ihr
       z-index:20 aus der mobilen Grundregel BEHAELT (Flexbox-Spezifikation:
       z-index wirkt auf Flex-Kinder unabhaengig vom position-Wert) — ohne
       eigenes z-index hier faerbt sich der Griff beim Ueberscrollen fast
       unsichtbar in die dunkle Liste ein, weil die Liste ihn zudeckt. */
    .seitenleiste-schalter {
        position: sticky;
        top: calc(var(--kopf-hoehe) + var(--abstand-4));
        z-index: 21;
        align-self: flex-end;
        transform: translateX(50%);
        /* 36% statt der 24% der mobilen Grundregel: bei Weiss auf der
           dunklen Flaeche (statt Weiss auf hellem Flaechengrund) haelt die
           mobile Mischung nur 2,20:1 gegen --flaeche-dunkel — unter der
           3:1-Schwelle aus WCAG 2.2 SC 1.4.11. 36% ist der naechste
           Vielfache-von-4-Wert oberhalb des Mindestwerts (~34%), belegt von
           tests/Feature/KontrastTest.php. */
        border: 1px solid color-mix(in srgb, var(--auf-flaeche-dunkel) 36%, transparent);
        border-radius: 50%;
        background: var(--flaeche-dunkel);
        color: var(--auf-flaeche-dunkel);
        box-shadow: var(--schatten-karte);
    }

    .seitenleiste-schalter:hover,
    .seitenleiste-schalter[aria-expanded='true'] {
        border-color: color-mix(in srgb, var(--auf-flaeche-dunkel) 40%, transparent);
    }

    .shell-inhalt {
        flex: 1;
        min-width: 0;
    }
}

/*
 * Wer Bewegung abgeschaltet hat, bekommt keine (PRD 12.7). Seit dem Umbau
 * minimalist-ui (20.08.2026) BEWEGEN sich mehrere dieser Komponenten
 * tatsaechlich (Hover-Anhebung auf .kpi-kachel/.dashboard-panel,
 * Scroll-Einblendung auf .js-einblenden weiter unten) — diese Regel schaltet
 * die Uebergaenge fuer sie ab, statt nur vorsorglich dazustehen.
 */
@media (prefers-reduced-motion: reduce) {
    .filter-knopf,
    .knopf-sekundaer,
    .geraete-karte,
    .kpi-kachel,
    .dashboard-panel,
    .bestandsband-segment,
    .kalender-einstellungen-schalter,
    .seitenleiste-gruppe-pfeil {
        transition: none;
    }
}

/*
 * Scroll-Einblendung (public/assets/js/einblenden.js, minimalist-ui §7).
 * WICHTIG fuer PRD 12.7 ("Inhalt ist immer sichtbar"): opacity:0 steht
 * ausschliesslich hinter .js-einblenden, einer Klasse, die NUR das JS-Modul
 * setzt — ohne JavaScript bleibt jede Karte in ihrem normalen, sichtbaren
 * Zustand. Zusaetzlich doppelt abgesichert per Media-Query: selbst wenn die
 * Klasse gesetzt waere, greift die Anfangs-Transparenz nur, wenn Bewegung
 * ausdruecklich erlaubt ist.
 */
@media (prefers-reduced-motion: no-preference) {
    .js-einblenden {
        opacity: 0;
        transform: translateY(12px);
        transition: opacity 600ms cubic-bezier(0.16, 1, 0.3, 1),
                    transform 600ms cubic-bezier(0.16, 1, 0.3, 1);
    }

    .js-einblenden.js-eingeblendet {
        opacity: 1;
        transform: none;
    }
}

/* Pfeildrehung der Seitenleiste nur, wer Bewegung erlaubt (PRD 12.7) —
   Zustand (auf/zu) bleibt in jedem Fall am aria-expanded/data-zustand
   ablesbar, die Drehung ist reine Zusatzanimation. */
@media (prefers-reduced-motion: no-preference) {
    .seitenleiste-pfeil {
        transition: transform 150ms ease;
    }
}

/*
 * Anzahl-Etiketten-Stepper (Planhub #9, geraet-neu.blade.php +
 * geraete-verwaltung.blade.php): Reihe statt der .feld-Spalte (app.css)
 * aus demselben Grund wie die Wochennavigation im Kalender — drei Elemente
 * nebeneinander, keine Stapelung. Die "-"/"+"-Knoepfe nutzen dasselbe
 * .knopf-sekundaer wie im Rest des Formulars, nur schmaler und ohne
 * seitliches Polster (Zahl statt Text als Inhalt).
 */
.feld-stepper {
    display: flex;
    align-items: stretch;
    gap: var(--abstand-1);
}

.feld-stepper-knopf {
    flex: 0 0 auto;
    width: 44px;
    padding: 0;
    font-size: var(--schriftgroesse-9);
    line-height: var(--zeilenhoehe-1);
}

.feld-stepper .feld-stepper-eingabe {
    width: 4rem;
    text-align: center;
}

/*
 * Zeitauswahl (Planhub #15, Positionen 5–8).
 *
 * Zwei Raster: die Tage eines Monats, darunter die Stunden des gewählten
 * Tages. Beide bestehen aus Knöpfen, und deren Grösse ist hier kein
 * Geschmack, sondern eine Anforderung: WCAG 2.2 Erfolgskriterium 2.5.8
 * verlangt mindestens 24×24 px, die Geräte-Matrix des Projekts 44×44 für
 * Touch. Ohne diese Regeln waren die Tage 23×21 px — am Bildschirm gemessen,
 * nicht geschätzt.
 *
 * Farben kommen ausschliesslich aus den vorhandenen Tokens; der Rahmen nutzt
 * --color-rahmen-bedienelement, den es genau dafür gibt (SC 1.4.11, 3:1
 * gegen die Umgebung).
 */
.zeitauswahl {
    margin-top: var(--abstand-3);
    padding: var(--abstand-3);
    border: 1px solid var(--flaeche-rahmen);
    border-radius: var(--radius-3);
}

.zeitauswahl-kopf {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--abstand-3);
    margin-bottom: var(--abstand-3);
}

.zeitauswahl-titel {
    font-weight: 600;
}

.zeitauswahl-raster {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
    gap: var(--abstand-1);
}

.zeitauswahl-wochentag {
    padding: var(--abstand-1) 0;
    color: var(--color-muted);
    font-size: var(--schriftgroesse-6);
    text-align: center;
}

.zeitauswahl-luecke {
    /* Hält den Platz der Randtage, ohne etwas anzubieten. */
    min-height: 44px;
}

.zeitauswahl-tag,
.zeitauswahl-stunde {
    /* 44px: die Touch-Vorgabe der Geräte-Matrix, nicht nur die 24px aus
       SC 2.5.8. Ein Kalendertag wird mit dem Daumen getroffen. */
    min-width: 44px;
    min-height: 44px;
    padding: var(--abstand-1);
    border: 1px solid var(--color-rahmen-bedienelement);
    border-radius: var(--radius-1);
    background: var(--color-surface);
    color: var(--color-text);
    cursor: pointer;
}

.zeitauswahl-stunden {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(88px, 1fr));
    gap: var(--abstand-1);
    margin-top: var(--abstand-4);
}

/*
 * Gesperrt. Bewusst über [aria-disabled] und nicht über :disabled — die
 * Knöpfe SIND nicht disabled, damit sie ihren Grund sagen können (siehe den
 * Kopf von ui/zeitauswahl.js). Und die Sperre hängt nie an der Farbe allein:
 * das Markup trägt zusätzlich ein × im Text und den Grund im title.
 */
.zeitauswahl-tag[aria-disabled='true'],
.zeitauswahl-stunde[aria-disabled='true'] {
    border-style: dashed;
    color: var(--color-muted);
    cursor: not-allowed;
}

.zeitauswahl-tag[aria-current='date'] {
    border-width: 2px;
    font-weight: 700;
}

/*
 * Bestätigungs-Popup nach dem Anlegen einer Reservierung (Planhub #17).
 *
 * Bewusst NICHT die Klassen des Kalender-Popups wiederverwendet, obwohl das
 * Muster dasselbe ist: dort steht ein ganzes Formular im Panel, hier eine
 * Meldung mit zwei Wegen. Zwei verschiedene Inhalte an einer Klasse hängen zu
 * lassen hätte bedeutet, dass jede Änderung am einen das andere trifft — und
 * Entscheid E2 verlangt ausdrücklich, dass das Kalender-Popup unangetastet
 * bleibt. Die Marken sind dieselben, die Regeln sind eigene.
 *
 * Backdrop aus --color-text statt einem fremden Schwarz, damit er zum
 * Tonsystem der Seite passt (dieselbe Überlegung wie beim Kalender-Popup).
 * Schatten ist --schatten-karte, derselbe wie bei jeder anderen Karte: die
 * Oberfläche kennt genau einen Schatten, und das Popup erfindet keinen
 * zweiten dafür.
 *
 * KEIN `outline: none` auf dem Panel — anders als beim Kalender-Popup. Dort
 * ist es harmlos, weil der sichtbare Fokus auf einem inneren Formularfeld
 * landet. Hier gäbe es kein solches Feld, und ein unsichtbarer Fokus wäre ein
 * Verstoß gegen WCAG 2.2, Kriterium 2.4.7.
 */
.bestaetigung-popup {
    position: fixed;
    inset: 0;
    z-index: 100;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: var(--abstand-4);
    background: color-mix(in srgb, var(--color-text) 55%, transparent);
}

.bestaetigung-popup[hidden] {
    display: none;
}

.bestaetigung-popup-panel {
    width: 100%;
    /*
     * 30rem statt der 26rem des ersten Entwurfs: darunter brach „Neue
     * Reservierung" auf zwei Zeilen, während „Zum Kalender" daneben einzeilig
     * blieb — zwei gleich hohe Knöpfe mit ungleichem Textsatz. Am Bild
     * gesehen, nicht am Code.
     */
    max-width: 30rem;
    max-height: calc(100dvh - var(--abstand-7));
    overflow-y: auto;
    padding: var(--abstand-5);
    background: var(--color-surface);
    border-radius: var(--radius-2);
    box-shadow: var(--schatten-karte);
}

.bestaetigung-popup-kopf {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--abstand-2);
}

.bestaetigung-popup-kopf h2 {
    margin: 0;
}

/*
 * Die Nummer ist der eigentliche Inhalt dieses Fensters — sie wird abgelesen,
 * am Telefon durchgegeben, auf einen Lieferschein geschrieben. Deshalb gross
 * und in der Datenschrift: eine Vorgangsnummer IST Daten, keine Prosa, und
 * eine Monospace-Schrift stellt 0 und O, 1 und l unverwechselbar dar.
 *
 * KEINE zusätzliche Laufweite. Der erste Entwurf setzte hier
 * `--spatium-eyebrow` (0.05em) — gedacht für kleine Versalien, und bei 1,75rem
 * Monospace zerfiel die Nummer im Bild sichtbar in Einzelzeichen
 * (`R – 2 0 2 6 – 0 0 0 3` statt einer zusammenhängenden Nummer). Eine
 * Monospace-Schrift bringt die nötige Luft schon in den Glyphen mit; mehr
 * davon macht sie nicht lesbarer, sondern zerreisst sie.
 */
.bestaetigung-popup-nummer {
    margin: var(--abstand-4) 0;
    font-family: var(--schrift-mono);
    font-size: var(--schriftgroesse-11);
    line-height: var(--zeilenhoehe-2);
    color: var(--color-text);
}

/* Der Offline-Fall: kein Platz für eine Nummer, sondern ein Satz. */
.bestaetigung-popup-nummer--ohne-nummer {
    font-family: var(--schrift);
    font-size: var(--schriftgroesse-standard);
    color: var(--color-muted);
}

.bestaetigung-popup-vorgang {
    display: block;
    margin-top: var(--abstand-1);
    font-family: var(--schrift-mono);
    color: var(--color-text);
}

.bestaetigung-popup-knoepfe {
    display: flex;
    flex-wrap: wrap;
    gap: var(--abstand-2);
    margin-top: var(--abstand-5);
}

/*
 * Ohne Mindestbreite schrumpfen beide Knöpfe auf schmalen Geräten auf ihre
 * Textbreite und stehen ungleich lang nebeneinander; mit ihr brechen sie
 * sauber untereinander um.
 */
.bestaetigung-popup-knoepfe > * {
    flex: 1 1 10rem;
}

/*
 * Sichtbarer Fokus INNERHALB des Popups — an `:focus`, nicht an
 * `:focus-visible`, und das ist der ganze Punkt.
 *
 * Gemessen am 26.08.2026: Wer den Wizard mit der MAUS absendet, bekommt den
 * Fokus programmatisch auf „Neue Reservierung" gesetzt — und Chrome zeichnet
 * dann keinen Ring, weil die letzte Eingabe ein Klick war (`:focus-visible`
 * greift nur nach Tastaturbedienung). `outline-style` stand auf `none`. Der
 * Anwender steht also in einem Fenster, dessen Fokus er nicht sieht; drückt er
 * Tabulator, wandert er weiter, ohne je gesehen zu haben, wo er stand.
 *
 * In einem modalen Fenster ist `:focus` deshalb richtig und nicht zu laut: der
 * Fokus wurde beim Öffnen dorthin GESETZT, nicht vom Anwender hingeklickt.
 * WAI-ARIA APG verlangt für das Dialog-Muster ausdrücklich, dass der Fokus
 * beim Öffnen erkennbar ist.
 *
 * `outline-offset` ist nicht Kosmetik: Der Ring hat die Farbe --color-primary,
 * und der Primärknopf ist mit derselben Farbe gefüllt. Ohne Abstand läge ein
 * schwarzer Ring auf schwarzem Grund. Mit Abstand liegt er auf der
 * Panel-Fläche, und dort trägt er (hell 17,6:1, dunkel ebenso deutlich).
 */
.bestaetigung-popup-panel :focus {
    outline: 2px solid var(--color-primary);
    outline-offset: 2px;
}

/*
 * Der Erde-Link zur Anschrift auf OpenStreetMap (Planhub #19).
 *
 * Er steht ueberall dort, wo ein Standort- oder Baustellenname als Text
 * erscheint, und besteht nur aus einem Zeichen. Daraus folgen drei Dinge:
 *
 * 1. Die Trefferflaeche misst 44 x 44 px. WCAG 2.2 Erfolgskriterium 2.5.8
 *    verlangt 24 px; PRD 12.4 verlangt in diesem Projekt 44 px, weil die
 *    Anwender das Programm mit Arbeitshandschuhen bedienen. 44 erfuellt beides.
 *    Das Zeichen selbst bleibt mit 18 px klein — gross ist die FLAECHE, nicht
 *    das Bild.
 * 2. `currentColor` traegt die Farbe. Der Link erbt damit `--color-accent`,
 *    und der Kontrast stimmt in hell wie dunkel ohne zweiten Satz Regeln.
 * 3. Der Fokusrahmen ist ausgeschrieben. Dieses Projekt hat keine globale
 *    `:focus-visible`-Regel — jedes Bauteil bringt seine eigene mit, und ein
 *    Bauteil ohne eigene haette gar keinen sichtbaren Fokus.
 */
.osm-link {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 44px;
    min-height: 44px;
    vertical-align: middle;
    border-radius: var(--radius-1);
    color: var(--color-accent);
    text-decoration: none;
}

.osm-link-icon {
    width: 18px;
    height: 18px;
}

.osm-link:hover {
    color: var(--color-primary);
}

.osm-link:focus-visible {
    outline: 2px solid var(--color-accent);
    outline-offset: 2px;
}

/*
 * Auswahlfeld und Erde-Link nebeneinander (Planhub #19).
 *
 * `.feld` ist eine Spalte — ein Link, der einfach hinter das Auswahlfeld
 * gehaengt wird, landet darunter statt daneben. Diese Zeile stellt beide auf
 * eine Hoehe. `min-width: 0` ist noetig, damit das Auswahlfeld in einem
 * schmalen Fenster schrumpfen darf, statt die Zeile herauszudruecken.
 */
.feld-mit-kartenlink {
    display: flex;
    align-items: center;
    gap: var(--abstand-2);
}

.feld-mit-kartenlink > .feld-eingabe {
    flex: 1 1 auto;
    min-width: 0;
}

 * Generisches Popup (Planhub #20)
 * ---------------------------------------------------------------------------
 * Overlay und Panel fuer mittige, modale Fenster. Verhalten liegt in
 * public/assets/js/ui/popup.js, das Aussehen hier. Bewusst generisch benannt:
 * die beiden aelteren Popups (.kalender-reservierung-popup*,
 * .bestaetigung-popup*) bleiben vorerst wie sie sind — sie gehoeren zu
 * abgeschlossenen Plaenen und werden von diesem Auftrag nicht angefasst.
 *
 * Unterschied zu jenen, und zwar der einzige beabsichtigte: 100dvh statt
 * 100vh. Auf iOS zaehlt 100vh die eingeblendete Adressleiste mit, das Panel
 * waere dort also hoeher als der wirklich sichtbare Bereich und sein unterer
 * Rand — samt Absende-Knopf — laege unter der Leiste (~/.claude/ui-pruefung.md,
 * „typische Bruchstellen").
 */
.popup {
    position: fixed;
    inset: 0;
    z-index: 100;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: var(--abstand-4);
    background: color-mix(in srgb, var(--color-text) 55%, transparent);
}

.popup-panel {
    width: 100%;
    max-width: 32rem;
    max-height: calc(100dvh - var(--abstand-7));
    overflow-y: auto;
    padding: var(--abstand-5);
    background: var(--color-surface);
    border-radius: var(--radius-2);
    box-shadow: var(--schatten-karte);
}

/*
 * Anders als beim Kalender-Popup wird der Fokusring hier NICHT entfernt. Das
 * Panel bekommt den Fokus, wenn es kein bedienbares Element enthaelt; ohne
 * sichtbaren Ring waere dann nicht erkennbar, wo die Tastatur steht
 * (WCAG 2.2, 2.4.7 Focus Visible).
 */
.popup-kopf {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--abstand-3);
    margin-bottom: var(--abstand-4);
}

.popup-kopf h2 {
    margin: 0;
}

/*
 * 44px statt der 24px aus WCAG 2.5.8: das ist die Fingerkuppen-Groesse aus
 * ~/.claude/ui-pruefung.md und gilt hier, weil das Fenster auf dem Handy
 * genauso bedient wird wie am Rechner. Die Beschriftung bleibt ein echtes
 * Zeichen und kein Bild, damit sie mitskaliert.
 */
.popup-schliessen {
    flex: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 44px;
    min-height: 44px;
    padding: 0;
}

/*
 * Aufklappbare Miethistorie in der Bestandsliste (Planhub #12).
 *
 * Der Schalter ist ein echtes <button> und sieht trotzdem aus wie die
 * Bezeichnung daneben — sonst waere die Tabelle ploetzlich voller Knoepfe.
 * Erkennbar ist er am Pfeil und am Unterstrich beim Ueberfahren; die
 * Zustandsangabe traegt `aria-expanded`, nicht die Farbe (WCAG 1.4.1).
 */
.lager-historie-schalter {
    display: inline-flex;
    align-items: center;
    gap: var(--abstand-2);
    padding: 0;
    border: 0;
    background: none;
    color: inherit;
    font: inherit;
    text-align: left;
    cursor: pointer;
}

.lager-historie-schalter:hover,
.lager-historie-schalter:focus-visible {
    text-decoration: underline;
}

.lager-historie-schalter::after {
    content: '';
    flex: none;
    width: 0.5rem;
    height: 0.5rem;
    border-right: 2px solid currentColor;
    border-bottom: 2px solid currentColor;
    transform: rotate(45deg) translate(-0.1rem, -0.1rem);
    transition: transform 120ms ease;
}

.lager-historie-schalter[aria-expanded='true']::after {
    transform: rotate(-135deg) translate(-0.15rem, -0.15rem);
}

/* In der Karte ist der Schalter ein normaler Knopf und braucht den Pfeil nicht
   als einziges Erkennungsmerkmal — die Klasse knopf-sekundaer traegt dort. */
.geraete-karte-fuss .lager-historie-schalter {
    padding: revert;
    border: revert;
    background: revert;
    color: revert;
}

.lager-historie-zeile > td {
    padding: 0;
}

.lager-historie-panel {
    padding: var(--abstand-4);
    background: var(--flaeche-vertieft);
}

.historie-liste {
    display: flex;
    flex-direction: column;
    gap: var(--abstand-4);
    margin: 0;
    padding: 0;
    list-style: none;
}

.historie-vorgang {
    display: flex;
    flex-direction: column;
    gap: var(--abstand-2);
    padding-bottom: var(--abstand-4);
    border-bottom: 1px solid var(--color-border);
}

.historie-vorgang:last-child {
    padding-bottom: 0;
    border-bottom: 0;
}

.historie-vorgang-kopf {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--abstand-2);
}

.historie-vorgang-zeit {
    font-variant-numeric: tabular-nums;
}

.historie-angaben {
    display: flex;
    flex-wrap: wrap;
    gap: var(--abstand-2) var(--abstand-6);
}

.historie-angabe {
    display: flex;
    flex-direction: column;
}

.historie-angabe-name {
    color: var(--color-muted);
    font-size: var(--schriftgroesse-2);
}

.historie-fotos {
    display: flex;
    flex-wrap: wrap;
    gap: var(--abstand-2);
}

/* Feste Kachelgroesse: die Bilder kommen in ganz unterschiedlichen
   Seitenverhaeltnissen von der Baustelle, und ohne sie springt die Reihe. */
.historie-foto img {
    display: block;
    width: 5rem;
    height: 5rem;
    object-fit: cover;
    border-radius: var(--radius-1);
}

.historie-leer {
    margin: 0;
    color: var(--color-muted);
}
