/* =========================================================================
   NILS-DIGITAL SIGNATURE  ·  nd-base.css
   -------------------------------------------------------------------------
   Das wiedererkennbare Nils-Digital-Designsystem: Layout, Komponenten,
   Typo-Rhythmus (Fredoka + Roboto Mono), Interaktionen, Footer-Credit.
   Über ALLE ND-Projekte identisch -> daran erkennt man "von Nils-Digital".
   Nutzt ausschließlich CSS-Variablen aus theme.css.
   >> KEINE festen Markenfarben hier eintragen! Die gehören in theme.css. <<

   Diese Datei funktioniert für helle UND dunkle Themes, weil jede Fläche,
   jede Linie und jeder Textton über ein Token läuft.
   ========================================================================= */

* { box-sizing: border-box; }
/* Die Grundfläche sitzt am <html>, nicht am <body>. Das ist die Voraussetzung
   dafür, dass die bewegte Hintergrundebene (.page-aurora, weiter unten) mit
   z-index:-1 hinter den Inhalt, aber vor die Grundfläche rutschen kann – hätte
   der Body eine eigene Farbe, läge sie darüber und man sähe nichts davon. */
html {
    scroll-behavior: smooth; background: var(--bg);
    /* Der Header liegt fest über der Seite. Ohne das würde ein
       Ankersprung sein Ziel darunter parken. */
    scroll-padding-top: calc(var(--header-h-compact, 72px) + 12px);
}

/* hidden muss gewinnen. Ohne das überstimmt jede eigene display-Regel
   (etwa .form-row { display: grid }) das Attribut aus dem Browser-Stylesheet –
   das Element bleibt sichtbar, obwohl es versteckt sein soll. */
[hidden] { display: none !important; }

body {
    margin: 0;
    min-height: 100vh;
    display: flex;
    flex-direction: column;
    font-family: var(--font-body);
    color: var(--text);
    background: transparent;   /* siehe <html> oben */
    line-height: 1.6;
    overflow-x: hidden;
    -webkit-font-smoothing: antialiased;
}
/* `overflow-x: clip` hält den waagerechten Scrollbalken von der Seite.

   Es gibt Deko, die absichtlich über ihr Element hinausragt – vor allem den
   farbigen Fleck hinter Bildern und Videos (.photo-wrap::before, `inset` mit
   negativen Werten). Auf breiten Fenstern verschwindet der Überhang im
   Seitenrand. Wird die Spalte schmaler, ragt er über den Rand hinaus, und der
   Browser macht daraus eine waagerechte Bildlaufleiste: gemessen 21 px bei
   820 px Fensterbreite auf der Startseite. Für Menschen, die mit Vergrößerung
   arbeiten, ist seitliches Scrollen beim Lesen ein echtes Hindernis
   (WCAG 1.4.10).

   `clip` und NICHT `hidden`: hidden macht aus dem Element einen Scroll-Container
   und bricht damit `position: sticky` bei allem, was darin liegt. clip klemmt
   nur ab. Und nur die x-Achse – senkrecht muss die Seite scrollen können.

   Am <main> und nicht am <body>: Der Header liegt fixiert über der Seite und
   die bewegte Hintergrundebene dahinter, beide außerhalb von <main>. So bleibt
   sicher, dass hier nichts geklemmt wird, was gar nicht gemeint ist. */
main { flex: 1; overflow-x: clip; }

/* DEUTSCH HAT LANGE WÖRTER, und diese Seite hat schmale Spalten.
   „Pedalverlängerung", „Führerscheinausbildung", „verkehrsmedizinisches" – auf
   einem 320 px breiten Gerät oder bei eingeschalteter Schriftvergrößerung passt
   so ein Wort nicht mehr in seine Spalte, und der Browser macht daraus eine
   waagerechte Bildlaufleiste. Seitliches Scrollen beim Lesen ist genau das, was
   WCGA 1.4.10 verhindern will.

   Zwei verschiedene Mittel, mit Absicht:

   Für FLIESSTEXT `hyphens: auto`. Der Browser trennt nach den Regeln der im
   <html lang="de"> angegebenen Sprache – ohne das Attribut passiert nichts.
   Greift nur, wenn es nötig ist: Passt das Wort in die Zeile, bleibt es ganz.

   Für ÜBERSCHRIFTEN nur `overflow-wrap`. Getrennte Überschriften lesen sich
   gedruckt wie ein Fehler, und bei den kurzen Zeilen einer Überschrift wäre der
   Bindestrich fast immer sichtbar. `overflow-wrap` bricht dagegen ausschließlich
   dann, wenn ein einzelnes Wort länger als die Zeile ist – also im Notfall. */
h1, h2, h3, h4 { overflow-wrap: break-word; }
:is(p, li, dd, dt, figcaption, blockquote, .accordion-teaser) {
    hyphens: auto;
    overflow-wrap: break-word;
}

h1, h2, h3 { font-family: var(--font-head); color: var(--text); line-height: 1.15; font-weight: 600; }
h1 { font-size: var(--fs-4xl); }
h2 { font-size: var(--fs-3xl); }

a { color: var(--accent); text-decoration: none; transition: color .15s; }
a:hover { color: var(--accent-hover); }

img { max-width: 100%; display: block; }

/* Tastaturbedienung muss immer sichtbar sein – Barrierefreiheit ist laut
   Projektvorgabe von Anfang an nicht zu verbauen. */
:focus-visible {
    outline: 3px solid var(--focus-ring);
    outline-offset: 2px;
    border-radius: 4px;
}

/* Die seitliche Einrückung steht als Variable da, weil sie zweimal gebraucht
   wird: hier und beim Markenblock im Header, der sich auf dem Handy aus dem
   Container löst und trotzdem an dessen linker Kante landen muss. */
.container { width: 100%; max-width: var(--container); margin: 0 auto; padding: 0 var(--container-pad, 22px); }
.muted { color: var(--muted); }
.center { text-align: center; }
.ta-right { text-align: right; }
.stack > * + * { margin-top: 1rem; }

/* Nur für Screenreader */
.sr-only {
    position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* Sprunglink: unsichtbar, bis er den Fokus bekommt. Bewusst NICHT `.sr-only` –
   der versteckt dauerhaft, dieser hier muss sichtbar werden, sobald jemand mit
   der Tastatur ankommt (WCAG 2.4.1). Über den fixierten Header hinaus mit
   z-index, sonst springt er dahinter. */
.skip-link {
    position: fixed;
    top: -100px; left: 12px;
    z-index: 100;
    padding: .7rem 1.1rem;
    border-radius: var(--radius-sm);
    background: var(--accent-surface, var(--accent));
    color: var(--on-accent-surface, var(--on-accent));
    font-family: var(--font-head); font-weight: 600; font-size: var(--fs-md);
    box-shadow: var(--shadow-lg);
    transition: top .15s;
}
.skip-link:focus { top: 12px; color: var(--on-accent-surface, var(--on-accent)); }

/* ---------- Buttons ---------- */
.btn {
    display: inline-flex; align-items: center; justify-content: center; gap: .5rem;
    padding: .7rem 1.3rem; border-radius: var(--radius-sm); border: 2px solid transparent;
    font-family: var(--font-head); font-weight: 600; font-size: var(--fs-md); cursor: pointer;
    transition: all .18s ease; text-decoration: none; line-height: 1.2;
}
.btn:hover { text-decoration: none; transform: translateY(-2px); }
.btn svg { width: 20px; height: 20px; flex-shrink: 0; }
.btn:disabled, .btn[aria-disabled="true"] { opacity: .5; cursor: not-allowed; transform: none; }
/* GEFÜLLTE FLÄCHE, EIGENES TOKEN. `--accent-surface` faellt auf `--accent`
   zurueck, wenn ein Projekt es nicht definiert – die ND-Signatur bleibt also
   unveraendert, solange niemand das Token setzt. Warum es bei Sarah gesetzt
   ist und warum der Hover HELLER wird statt dunkler: theme.css, Abschnitt 4. */
.btn-primary { background: var(--accent-surface, var(--accent)); color: var(--on-accent-surface, var(--on-accent)); border-color: var(--accent-surface, var(--accent)); box-shadow: 0 6px 20px var(--shadow-accent); }
.btn-primary:hover { background: var(--accent-surface-hover, var(--accent-hover)); border-color: var(--accent-surface-hover, var(--accent-hover)); color: var(--on-accent-surface, var(--on-accent)); }
.btn-ghost { background: transparent; color: var(--text); border-color: var(--border-strong); }
.btn-ghost:hover { border-color: var(--accent); color: var(--accent); }
.btn-light { background: var(--bg-card); color: var(--text); border-color: var(--bg-card); }
.btn-light:hover { background: var(--bg-card); color: var(--accent); }
.btn-danger { background: transparent; color: var(--red); border-color: var(--red-line); }
.btn-danger:hover { background: var(--red); color: var(--on-danger); border-color: var(--red); }
.btn-lg { padding: .9rem 1.7rem; font-size: var(--fs-lg); }
.btn-sm { padding: .45rem .85rem; font-size: var(--fs-sm); }
.btn-block { width: 100%; }

/* ---------- Header / Nav ----------
   Der Header schrumpft beim Scrollen: oben trägt er die große Marke, danach
   eine kompakte Zeile. main.js setzt dafür `.is-compact`.

   Er ist deshalb `fixed` und NICHT `sticky`, und das ist kein Geschmack:
   Ein sticky Header steht im Textfluss. Ändert er beim Scrollen seine Höhe,
   rutscht der gesamte Inhalt darunter um die Differenz nach oben – ein
   sichtbarer Sprung genau in dem Moment, in dem man zu scrollen anfängt.
   Fixed nimmt ihn aus dem Fluss; den Platz hält stattdessen das
   padding-top am <body>, und das bleibt konstant auf der GROSSEN Höhe.
   Deshalb springt nichts, egal wie oft der Header seine Größe wechselt.

   Die Höhe kommt als Token, weil sie zweimal gebraucht wird (hier und am
   Body) und beide Werte zwingend zusammenpassen müssen. */
.site-header {
    position: fixed; top: 0; left: 0; right: 0; z-index: 50;
    height: var(--header-h, 72px);
    background: var(--header-bg); -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px);
    border-bottom: 1px solid var(--border);
    transition: height .28s cubic-bezier(.22, .61, .36, 1);
}
.site-header.is-compact { height: var(--header-h-compact, 72px); }

/* Der Platz, den der fixierte Header sonst einnehmen würde. Bleibt bewusst
   auf der großen Höhe stehen – siehe oben. Der Admin hat keinen solchen
   Header und muss den Platz deshalb wieder abziehen. */
body { padding-top: var(--header-h, 0px); }
/* Der Kopf ist `fixed` und liegt über dem Inhalt. Ein Sprung zu einem Anker
   setzt das Ziel sonst an die Oberkante des Fensters – also hinter den Kopf.
   Gilt für den Sprung des Browsers wie für scrollIntoView(). */
:target { scroll-margin-top: calc(var(--header-h, 0px) + 1.2rem); }
.admin-body, .admin-auth-body { padding-top: 0; }

.header-inner { display: flex; align-items: center; justify-content: space-between; height: 100%; gap: 1rem; }
/* Marke = die Bildmarke, sonst nichts. Bis SAR-37 stand eine Wortmarke daneben,
   die beim Scrollen einblendete; sie ist entfallen. Das Logo bleibt dekorativ
   (alt=""), den Namen trägt das aria-label am Link. */
/* Der Markenblock spannt sich über die volle Headerhöhe (align-self: stretch).
   Das ist die Voraussetzung dafür, dass die große Marke darunter mit einem
   festen Abstand von OBEN sitzen kann, statt an der wechselnden Mitte zu
   kleben – sonst müsste jede Höhenänderung des Headers nachgerechnet werden. */
/* z-index: 1 ist hier keine Kosmetik. Der Header trägt an seiner Unterkante
   eine Zierlinie als ::after (im Theme der Regenbogenstreifen). Die steht im
   Baum NACH dem Inhalt und wird deshalb ÜBER allem gezeichnet, was aus dem
   Header herausragt – also quer über die große Marke. Mit z-index läuft die
   Linie dahinter durch und das Logo bleibt vollständig sichtbar. */
.brand { position: relative; z-index: 1; align-self: stretch; display: flex; flex-direction: row; align-items: center; gap: .7rem; line-height: 1; text-decoration: none; }
.brand-text { display: flex; flex-direction: column; line-height: 1; }
.brand-logo { width: auto; flex-shrink: 0; display: block; }

/* EIN LOGO, ZWEI GRÖSSEN.
   Oben groß und unter der Leiste HERAUSSTEHEND, beim Scrollen kleiner. Das
   Bild bleibt dabei dasselbe – ein Logo, das sich beim Scrollen verwandelt,
   prägt sich nicht ein.

   Es steht als ganz normales Flex-Kind im Fluss und darf höher sein als der
   Header; `align-self: flex-start` verhindert, dass die Flexbox es auf die
   Headerhöhe zusammenstaucht, und der Überstand fällt unten heraus.

   Daneben stand bis SAR-37 (17.08.2026) eine Wortmarke, die oben unsichtbar
   im Fluss lag und im kompakten Zustand einblendete – sie hielt zugleich die
   Breite des Markenblocks, damit die Navigation beim Umschalten nicht wandert.
   Sie ist entfallen, und mit ihr ihre beiden Regeln (`opacity: 0` hier,
   `opacity: 1` unter `.is-compact`). Die Navigation wandert deswegen nicht:
   Sie sitzt am anderen Ende eines `space-between`-Flex und hängt am rechten
   Rand, nicht an der Breite der Marke.

   Ohne JavaScript kommt `.is-compact` nie: Dann bleibt es beim großen Logo. */
.brand-logo--full {
    align-self: flex-start;
    margin-top: var(--brand-full-top, 4px);
    height: var(--brand-full-h, 54px);
    transition: height .28s cubic-bezier(.22, .61, .36, 1),
                margin-top .28s cubic-bezier(.22, .61, .36, 1);
}

.site-header.is-compact .brand-logo--full {
    height: var(--brand-compact-h, 54px);
    margin-top: var(--brand-compact-top, 0px);
}
.brand-mark { font-family: var(--font-head); color: var(--text); font-weight: 700; font-size: var(--fs-2xl); letter-spacing: .3px; }
.brand-mark::after { content: "."; color: var(--accent); }
.brand-sub { color: var(--accent); font-size: var(--fs-xs); text-transform: uppercase; letter-spacing: 2.5px; margin-top: 2px; }

.main-nav { display: flex; align-items: center; gap: .25rem; }
.nav-link {
    color: var(--muted); padding: .5rem .85rem; border-radius: var(--radius-sm);
    font-weight: 500; font-size: var(--fs-sm); transition: color .15s, background .15s;
}
.nav-link:hover { color: var(--text); text-decoration: none; }
.nav-link.is-active { color: var(--accent); }
/* Abmelden ist ein POST-Formular, sieht aber aus wie ein Navigationslink */
button.nav-link { background: none; border: 0; cursor: pointer; font-family: var(--font-body); font-size: var(--fs-sm); }
.nav-cta { margin-left: .5rem; }
.nav-cta.is-active { color: var(--on-accent); }

/* --- Das Untermenü „Schwerpunkte" (SAR-65) --------------------------------
   Zwei Seiten unter einem Punkt: /fahren-mit-handicap und /neurodivergenz.
   Warum die Gruppe so heißt und warum ihr Kopf ein <button> ist, steht in
   partials/nav.php – hier steht nur, wie sie aussieht.

   KEIN REINES HOVER-MENÜ. Aufgeklappt wird per Klick (`.is-open` aus
   main.js), und das aus einem handfesten Grund: Auf Touchgeräten gibt es
   kein Hover, dort ist der erste Tipp auf einen Hover-Punkt ein Klick auf
   den Elternlink – und der führt bei einer Gruppe ohne eigene Seite
   nirgendwohin. Auf dem Handy ist die Gruppe deshalb gar nicht erst
   zugeklappt (Block bei 1120 px weiter unten).

   DIE ZWEITE REGEL IST DER AUSFALLSCHIRM. Lädt main.js nicht, gibt es kein
   `.is-open` und die beiden Seiten wären über das Menü unerreichbar. Dann
   greifen `:hover` und `:focus-within` – Maus wie Tastatur kommen durch.
   Sie hängen an `html:not(.has-js)`, weil sonst mit JS zwei Mechanismen
   nebeneinander liefen: Der Knopf sagt per `aria-expanded` zu, dass er
   zugeklappt ist, während `:focus-within` ihn beim Antabben schon öffnet.
   Genau diese Lüge gegenüber der Vorlesesoftware soll die Klasse
   verhindern. Sie kommt aus main.js und steht am <html>, nicht am <body>:
   So gilt sie schon, bevor der Körper gezeichnet ist. */
.nav-group { position: relative; }
.nav-group-toggle { display: inline-flex; align-items: center; gap: .4rem; }
/* Das Dreieck ist reine Form (im Markup `aria-hidden`) und deshalb aus
   Rahmen gebaut statt als Zeichen: Ein „▾" aus der Schrift säße je nach
   Font woanders auf der Grundlinie. */
.nav-caret {
    width: 0; height: 0; border-left: 4px solid transparent; border-right: 4px solid transparent;
    border-top: 5px solid currentColor; transition: transform .18s;
}
.nav-group.is-open .nav-caret { transform: rotate(180deg); }
.nav-submenu {
    position: absolute; top: 100%; left: 0; min-width: 232px; z-index: 60;
    display: flex; flex-direction: column; gap: .1rem;
    padding: .4rem; margin-top: .35rem;
    background: var(--bg-card); border: 1px solid var(--border);
    border-radius: var(--radius-sm); box-shadow: var(--shadow-lg);
    /* `visibility` und nicht `display: none`, damit der Wechsel weich läuft –
       und trotzdem kein Tabstopp im zugeklappten Menü hängen bleibt. Genau
       das kann `opacity: 0` allein nicht: Ein durchsichtiges Menü ist für
       die Tastatur weiter da. */
    opacity: 0; visibility: hidden; translate: 0 -4px;
    transition: opacity .15s, translate .15s, visibility .15s;
}
.nav-group.is-open > .nav-submenu,
html:not(.has-js) .nav-group:hover > .nav-submenu,
html:not(.has-js) .nav-group:focus-within > .nav-submenu {
    opacity: 1; visibility: visible; translate: 0 0;
}
.nav-submenu .nav-link { white-space: nowrap; }

.nav-toggle { display: none; flex-direction: column; gap: 5px; background: none; border: 0; cursor: pointer; padding: 8px; }
.nav-toggle span { width: 26px; height: 2px; background: var(--text); border-radius: 2px; transition: .2s; }

/* ---------- Hero ---------- */
.hero {
    position: relative; overflow: hidden; isolation: isolate;
    padding: clamp(3.5rem, 10vw, 7rem) 0;
    border-bottom: 1px solid var(--border);
    background: var(--bg);
}
/* Hintergrundvideo im Hero. Drei Ebenen, und die Reihenfolge ist der ganze
   Trick: Video ganz unten, Schleier darüber, Inhalt zuoberst. `isolation:
   isolate` am `.hero` hält diese Stapelung im Abschnitt, sie kann also mit
   nichts auf der Seite kollidieren.

   `object-fit: cover` füllt den Hero in jedem Seitenverhältnis; das Video ist
   16:9, der Hero auf dem Handy fast quadratisch. Ohne cover stünden Balken.

   DIE FARBE DES SCHLEIERS STEHT IN theme.css, nicht hier: Diese Datei ist die
   ND-Signatur und kennt keine festen Farbwerte. Ohne den Token bleibt der
   Schleier durchsichtig – und dann ist der Kontrast über dem Video nicht mehr
   zusagbar. Wer die Regel in ein anderes Projekt mitnimmt, setzt ihn dort. */
.hero-video {
    position: absolute; inset: 0; z-index: 0;
    width: 100%; height: 100%;
    object-fit: cover;
    /* Rein dekorativ: Klicks gehören den Knöpfen darüber, nicht dem Video. */
    pointer-events: none;
}
.hero-video-scrim {
    position: absolute; inset: 0; z-index: 1;
    background: var(--hero-video-scrim, transparent);
    pointer-events: none;
}
/* Ohne diese Zeile läge der Inhalt im selben Stapel wie der Schleier und
   verschwände darunter. */
.hero-video ~ .hero-inner { position: relative; z-index: 2; }
/* Grundfläche (Bild ODER Farbverlauf – kommt als Token aus theme.css) */
.hero::before {
    content: ""; position: absolute; inset: 0; z-index: -2;
    background: var(--hero-bg, none);
    background-size: cover;
    background-position: var(--hero-focus, center center);
}
/* Lesbarkeits-Overlay der ND-Signatur */
.hero::after {
    content: ""; position: absolute; inset: 0; z-index: -1;
    background: var(--hero-overlay, none);
}
.hero-inner { position: relative; z-index: 1; }
.hero-content { max-width: 660px; }
.hero-eyebrow { display: inline-flex; align-items: center; gap: .6rem; color: var(--accent); text-transform: uppercase; letter-spacing: 3px; font-size: var(--fs-xs); font-weight: 600; margin: 0 0 1.1rem; font-family: var(--font-head); }
.hero-eyebrow::before { content: ""; width: 28px; height: 3px; border-radius: 2px; background: var(--gradient-rainbow, var(--accent)); }
.hero h1 { margin: 0 0 1.1rem; font-size: var(--fs-hero); line-height: 1.06; letter-spacing: -.5px; overflow-wrap: break-word; }
/* `hero-headline` IST MIT SAR-98 ENTFALLEN (30.08.2026). Hier stand
   `white-space: nowrap` für die Überschrift der Startseite: Das Wort
   „Führerscheinausbildung" war unteilbar und erzwang sich seine Spalte
   selbst; ein Bindestrich machte es teilbar, und diese Regel stellte den
   alten Zustand wieder her. Die Überschrift heißt jetzt „Führerschein für
   Menschen mit Handicap" – längstes Wort 12 Zeichen –, also braucht weder
   das eine noch das andere eine Sonderbehandlung. Die Ausnahme unter 500 px
   ist mit entfallen. */
.hero-lead { font-size: var(--fs-lg); color: var(--text-soft); max-width: 560px; margin: 0 0 1.4rem; }
/* Kleine Ortsangabe unter dem Fließtext */
.hero-meta { display: flex; align-items: center; gap: .55rem; margin: 0 0 2rem; color: var(--muted); font-size: var(--fs-sm); }
.hero-meta svg { width: 18px; height: 18px; flex-shrink: 0; color: var(--accent); }
.hero-actions { display: flex; flex-wrap: wrap; gap: .9rem; }
.center-actions { justify-content: center; }
.hero-badges { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem; margin-top: clamp(2.5rem, 6vw, 3.5rem); max-width: 760px; }
.hero-badge { background: var(--glass-card, var(--bg-card)); -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px); border: 1px solid var(--border); border-radius: var(--radius); padding: 1.2rem; transition: border-color .15s, transform .15s; }
.hero-badge:hover { border-color: var(--accent); transform: translateY(-3px); }
.hero-badge strong { display: block; font-family: var(--font-head); color: var(--text); font-size: var(--fs-md); margin-bottom: .25rem; }
.hero-badge span { color: var(--muted); font-size: var(--fs-md); }

/* ---------- Sections ---------- */
.section { padding: clamp(3rem, 6vw, 5rem) 0; }
/* Eigenes Token statt --bg-secondary: Die abgesetzten Sektionen dürfen leicht
   durchscheinen, damit die bewegte Hintergrundebene darunter sichtbar bleibt.
   Alles andere, was --bg-secondary nutzt (Fotorahmen, Admin-Topbar), soll das
   ausdrücklich NICHT – deshalb hier ein eigener Wert mit Rückfall. */
.section--alt { background: var(--section-alt-bg, var(--bg-secondary)); border-top: 1px solid var(--border); border-bottom: 1px solid var(--border); }
.section-head { display: flex; align-items: flex-end; justify-content: space-between; gap: 1rem; margin-bottom: 2.4rem; flex-wrap: wrap; }
.section-head h2 { position: relative; padding-left: 16px; }
/* Schmaler senkrechter Balken -> braucht den senkrechten Verlauf,
   sonst zeigt er nur einen einzelnen Farbausschnitt. */
.section-head h2::before { content: ""; position: absolute; left: 0; top: .15em; bottom: .15em; width: 5px; border-radius: 4px; background: var(--gradient-rainbow-v, var(--accent)); }
.section-head-text { display: flex; flex-direction: column; }
.section-eyebrow { padding-left: 16px; color: var(--accent); text-transform: uppercase; letter-spacing: 3px; font-size: var(--fs-xs); font-weight: 600; font-family: var(--font-head); margin-bottom: .55rem; }
.link-more { font-weight: 600; white-space: nowrap; font-family: var(--font-head); }
.page-head { background: var(--page-head-bg); padding: clamp(2.4rem, 5vw, 3.6rem) 0; border-bottom: 1px solid var(--border); }
.page-head h1 { margin: 0 0 .5rem; }
.page-lead { color: var(--text-soft); font-size: var(--fs-lg); margin: 0; max-width: 640px; }
.page-head + .section { padding-top: 3rem; }

/* ---------- Karten-Raster (generisch) ---------- */
.card-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 1.4rem; }
/* ZWEI SPALTEN, UND ZWAR WIRKLICH ZWEI (SAR-82). Die Klasse hiess schon
   vorher `--2`, hielt das aber nicht: Sie war `auto-fit, minmax(300px, 1fr)`
   und ergab bei 1200 px Containerbreite drei Spalten – mit vier Kacheln also
   3 + 1 und einer allein in der zweiten Zeile, genau die „vergessene Kachel",
   gegen die `--5` weiter unten gebaut ist. Nachgemessen auf
   /fahren-mit-handicap, als Sarahs laengere Texte (SAR-82) das Viererraster
   sprengten und die Kacheln hierher wechselten.

   Die Klasse wurde bis dahin NIRGENDS benutzt, das Umdefinieren aendert also
   keine bestehende Seite – und der Name sagt jetzt, was passiert.

   Die Grenze liegt bei 740 px und nicht bei den 820 px der Nachbarregeln:
   Unterhalb macht das auto-fit der Basis von selbst zwei Spalten und ab rund
   500 px eine. Bei 820 bliebe dazwischen ein schmales Band, in dem die Basis
   wieder drei Spalten stellt und die vierte Kachel allein steht. */
@media (min-width: 740px) {
    .card-grid--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* FÜNF KARTEN GEHEN IN KEINEM RASTER GLATT AUF.
   Mit dem auto-fit oben werden es auf dem Desktop vier Spalten – und die fünfte
   Karte steht allein in der zweiten Zeile, links angeschlagen. Das liest sich
   nicht als Raster, sondern als vergessene Karte.

   Fünf nebeneinander sind die andere naheliegende Lösung und die schlechtere:
   Bei rund 205 px Spaltenbreite bricht jede Überschrift um und die Zeilen
   werden zu Fetzen. Also 3 + 2, mit der zweiten Zeile mittig.

   Gemacht über ein Sechser-Raster, in dem jede Karte zwei Spalten belegt: Die
   vierte setzt bei Spalte 2 ein, die fünfte schließt bei 4 an – zusammen sitzen
   sie mittig unter den drei darüber. Alle Karten behalten dabei DIESELBE
   Breite; zwei breitere Karten in der letzten Zeile würden die Reihe füllen,
   aber die Kacheln wären nicht mehr gleich.

   Die 820 px sind gemessen und nicht gegriffen: Bei 839 px hört das auto-fit
   oben von selbst mit drei Spalten auf (3 × 250 px + 2 × Abstand). Ein Wert
   darüber ließe genau dazwischen ein schmales Band, in dem drei Spalten stehen,
   die zweite Zeile aber wieder links klebt. Darunter greift das auto-fit und
   macht zwei Spalten daraus. */
@media (min-width: 820px) {
    .card-grid--5 { grid-template-columns: repeat(6, minmax(0, 1fr)); }
    .card-grid--5 > * { grid-column: span 2; }
    .card-grid--5 > *:nth-child(4) { grid-column: 2 / span 2; }
}

/* SECHS KARTEN GEHEN GENAUSO WENIG GLATT AUF, und zwar aus demselben Grund
   (SAR-65): Das auto-fit oben macht auf breiten Schirmen VIER Spalten, sechs
   Karten stehen also 4 + 2 – die zweite Zeile halb leer und links angeschlagen.

   Nachgemessen bei 1440 px, nachdem die sechste Kachel dazukam. Die Annahme,
   sechs ergäben von selbst 3 + 3, war falsch: Sie stimmt nur für ein Raster mit
   fester Spaltenzahl, und das hier hat keine.

   Drei feste Spalten statt sechs mit `span 2` wie oben: Bei sechs Karten muss
   nichts zentriert werden, beide Zeilen sind voll. Die 820 px sind dieselbe
   gemessene Grenze wie beim Nachbarn darüber – darunter übernimmt das auto-fit
   und macht zwei Spalten daraus, also 3 × 2. */
@media (min-width: 820px) {
    .card-grid--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
.feature-card {
    position: relative; display: flex; flex-direction: column; overflow: hidden;
    background: var(--bg-card); border: 1px solid var(--border); border-radius: var(--radius);
    padding: 1.7rem 1.5rem; box-shadow: var(--shadow);
    transition: transform .18s, box-shadow .18s, border-color .18s;
}
/* Farbige Kopfkante – die Farbe setzt theme.css über --card-accent */
.feature-card::before {
    content: ""; position: absolute; top: 0; left: 0; right: 0; height: 6px;
    background: var(--card-accent, var(--accent));
}
.feature-card:hover { transform: translateY(-5px); box-shadow: var(--shadow-lg); }
/* Eine Kachel, die als GANZES ein Link ist. Sonst ist `.feature-card` immer
   ein <article> mit Text darin; als <a> würde der Browser die komplette Kachel
   unterstreichen und in Linkfarbe setzen, Überschrift und Absatz inbegriffen.
   Beides hier zurückgenommen: Die Kachel sieht aus wie ihre Nachbarn, sie ist
   nur eben anfassbar. Was sie zum Link macht, steht in der `.feature-meta`
   unten drin. */
.feature-card--link { color: var(--text); text-decoration: none; }
.feature-card--link:hover { text-decoration: none; }
/* DIE ZEILE UNTEN TRÄGT NICHT DIE KACHELFARBE, und das ist eine Kontrast- und
   keine Geschmacksfrage. `.feature-meta strong` steht auf `--card-accent`, und
   die Rotation aus theme.css schickt da auch Gelb durch: #E8B517 kommt auf
   Weiß auf rund 2:1. Gefordert sind 4,5:1 für Text unter 24 px, diese Zeile
   ist 18 px. Auf einer gelben Kachel wäre der Weiterlink also der einzige
   Text der Seite, den man nicht lesen kann.

   Deshalb die Leitfarbe in ihrer textsicheren Fassung, dieselbe wie bei jedem
   anderen Link. Die Kachelfarbe bleibt sichtbar, wo sie keinen Text tragen
   muss: in der Kopfkante und hinter dem Icon. */
.feature-card--link .feature-meta strong { color: var(--accent-text, var(--accent)); }
.feature-icon {
    display: inline-flex; align-items: center; justify-content: center;
    width: 52px; height: 52px; border-radius: 16px; margin-bottom: 1.15rem;
    background: var(--card-accent-soft, var(--accent-soft)); color: var(--card-accent, var(--accent));
    transition: transform .15s;
}
.feature-icon svg { width: 26px; height: 26px; }
.feature-card:hover .feature-icon { transform: translateY(-2px) rotate(-4deg); }
.feature-card h3 { margin: 0 0 .5rem; font-size: var(--fs-lg); }
.feature-card p { margin: 0 0 1.1rem; color: var(--text-soft); font-size: var(--fs-md); line-height: 1.6; }
.feature-card > :last-child { margin-bottom: 0; }
.feature-meta { margin-top: auto; font-size: var(--fs-sm); color: var(--muted); }
.feature-meta strong { color: var(--card-accent, var(--accent)); font-size: var(--fs-lg); font-family: var(--font-head); }

/* ---------- Preistabelle ---------- */
.price-table { display: flex; flex-direction: column; gap: 1rem; }
.price-row { display: grid; grid-template-columns: 1fr auto; align-items: center; gap: 1.5rem; background: var(--bg-card); border: 1px solid var(--border); border-radius: var(--radius); padding: 1.3rem 1.5rem; transition: border-color .15s; }
.price-row:hover { border-color: var(--accent); }
.price-row-main h3 { margin: 0 0 .3rem; font-size: var(--fs-lg); }
.price-row-main p { margin: 0; color: var(--text-soft); font-size: var(--fs-md); }
.price-row-price { text-align: right; white-space: nowrap; }
.price-row-price strong { font-family: var(--font-head); font-size: var(--fs-xl); color: var(--accent); display: block; }
.price-note { font-size: var(--fs-xs); color: var(--muted); text-transform: uppercase; letter-spacing: .5px; }
.price-disclaimer { margin-top: 1.6rem; font-size: var(--fs-sm); }

/* ---------- Chips / Filter ---------- */
.filter-bar { display: flex; flex-wrap: wrap; gap: .6rem; margin-bottom: 2rem; align-items: center; }
.chip { display: inline-flex; align-items: center; gap: .4rem; padding: .48rem 1.05rem; border-radius: 999px; border: 1px solid var(--border-strong); background: var(--bg-card); color: var(--text-soft); font-size: var(--fs-xs); font-weight: 500; font-family: var(--font-head); }
.chip:hover { border-color: var(--accent); color: var(--text); text-decoration: none; }
.chip.is-active { background: var(--accent); color: var(--on-accent); border-color: var(--accent); }

/* ---------- Prozess-Schritte ---------- */
/* Vier Schritte sind der Normalfall, deshalb die 4 als Rückfall. Wer mehr oder
   weniger hat, setzt `--process-cols` am <ol> – so bleibt die Zahl dort stehen,
   wo auch die Schritte stehen, statt hier im gemeinsamen Grundgerüst. */
.process { list-style: none; padding: 0; margin: 0; display: grid; grid-template-columns: repeat(var(--process-cols, 4), minmax(0, 1fr)); gap: 1.6rem; }
.process-step { position: relative; padding-right: 1rem; }
.process-num {
    position: relative; z-index: 1; display: inline-flex; align-items: center; justify-content: center;
    width: 54px; height: 54px; border-radius: 50%; margin-bottom: 1.15rem;
    background: var(--card-accent-soft, var(--accent-soft)); color: var(--card-accent, var(--accent));
    border: 2px solid var(--card-accent, var(--accent));
    font-family: var(--font-head); font-weight: 700; font-size: var(--fs-2xl);
}
.process-step h3 { margin: 0 0 .5rem; font-size: var(--fs-lg); }
/* Silbentrennung braucht dieser Absatz besonders (fünf schmale Spalten,
   „verkehrsmedizinisches"), steht aber seit SAR-34 global für allen Fließtext
   weiter oben – hier nichts mehr zu tun. */
.process-step p { margin: 0; color: var(--text-soft); font-size: var(--fs-md); line-height: 1.6; }
.process-step::after { content: ""; position: absolute; top: 27px; left: 54px; width: calc(100% - 54px + 1.6rem); height: 3px; border-radius: 2px; background: linear-gradient(90deg, var(--card-accent, var(--accent)), transparent); }
.process-step:last-child::after { width: calc(100% - 54px); }

/* --- DER ABLAUF ALS SENKRECHTE LEITER (SAR-83) ----------------------------
   Nebeneinander funktioniert nur, solange die Schritte kurz sind. Sarahs
   zweite Fassung des Ablaufs auf /fahren-mit-handicap hat rund 310 Woerter
   auf fuenf Schritte, einer davon allein 78. Nachgemessen bei 1440 px und
   `--process-cols: 5`: Spalten von 215 px Breite, 30 Zeilen hoch, und Schritt
   2 endete 300 px unter seinen Nachbarn. Die Reihe las sich nicht mehr als
   Abfolge, sondern als fuenf ungleich lange Textsaeulen.

   Dieser Modifier stellt sie untereinander: Nummer, Ueberschrift, Text, und
   ein farbiger Strich links daneben statt des Pfeils nach rechts. Es ist
   dieselbe Form, die das Handy-Layout unter 740 px ohnehin schon herstellt –
   hier gilt sie in jeder Breite.

   DIE BREITE IST BEGRENZT und das ist der eigentliche Gewinn: Ueber die volle
   Containerbreite waeren es rund 130 Zeichen je Zeile, mehr als das Doppelte
   des Lesbaren. Die 800 px sind nachgerechnet und nicht gegriffen: abzueglich
   der 20 px Einzug, den der Strich links braucht, bleiben genau die 780 px
   von `.prose`. Der Ablauf hat damit dieselbe Zeilenlaenge wie der Fliesstext
   im Abschnitt darunter – bei 840 px waren es rund 82 Zeichen und damit
   sichtbar mehr.

   ZUR SPEZIFITAET: Die Regeln stehen bewusst auf zwei Klassen
   (`.process.process--stapel`) beziehungsweise auf drei Stufen bei den
   `::after`. Die Bloecke bei 980 px und 740 px weiter unten fassen dieselben
   Elemente an und stehen SPAETER in der Datei – bei gleicher Spezifitaet
   gewaennen sie und machten daraus auf Tablets wieder zwei Spalten mit
   waagerechtem Strich. */
.process.process--stapel { grid-template-columns: 1fr; gap: 1.7rem; max-width: 800px; }
.process--stapel .process-num { width: 44px; height: 44px; font-size: var(--fs-lg); margin-bottom: .9rem; }
.process--stapel .process-step { padding-left: 1.25rem; padding-right: 0; }
.process--stapel .process-step::after,
.process--stapel .process-step:nth-child(2n)::after,
.process--stapel .process-step:last-child::after {
    top: 6px; bottom: 6px; left: 0; width: 3px; height: auto;
    background: linear-gradient(180deg, var(--card-accent, var(--accent)), transparent);
}

/* ---------- CTA-Band ---------- */
.cta-band { padding: clamp(1.5rem, 4vw, 2.5rem) 0 clamp(3rem, 6vw, 5rem); }
.cta-inner {
    position: relative; overflow: hidden; display: flex; align-items: center; justify-content: space-between;
    gap: 1.4rem 2rem; flex-wrap: wrap; padding: clamp(1.9rem, 4vw, 2.8rem) clamp(1.6rem, 4vw, 3rem);
    border-radius: calc(var(--radius) + 6px); border: 1px solid var(--border);
    background: var(--cta-bg, var(--bg-card)); box-shadow: var(--shadow-lg);
}
.cta-inner::before { content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 6px; background: var(--gradient-rainbow-v, var(--accent)); }
.cta-text { position: relative; z-index: 1; max-width: 640px; }
.cta-band .btn { position: relative; z-index: 1; }
/* Zwei Wege nebeneinander im Abschlussband. Ohne eigenen Container wären es
   zwei Flex-Kinder von .cta-inner (space-between) – die Knöpfe würden dann
   auseinandergerissen, einer links am Text, einer rechts am Rand. */
.cta-actions { position: relative; z-index: 1; display: flex; flex-wrap: wrap; gap: .8rem; }
/* EIN SCHLUSSBAND OHNE KNOPF – seit SAR-101 (31.08.2026) der Normalfall auf
   /fahren-mit-handicap und /neurodivergenz: Dort stehen Sarahs Schlussworte
   allein, der Weg zur Fahrschule ist daneben weggefallen.

   `.cta-inner` ist ein Flex mit `space-between`, und `.cta-text` hört bei
   640 px auf. Bleibt der Knopf weg, klebt der Text links und die halbe Karte
   steht leer – es sieht aus, als fehle etwas. Der Block rückt deshalb in die
   Mitte.

   ZENTRIERT WIRD DER BLOCK, NICHT DER TEXT: Das Band auf /fahren-mit-handicap
   trägt fünf Sätze am Stück, und mittig gesetzter Fließtext ist auf dieser
   Länge kaum lesbar. Das Zeilenmaß von 640 px bleibt aus demselben Grund
   stehen – über die ganze Kartenbreite gezogen wären es zu lange Zeilen
   (dieselbe Überlegung wie in SAR-100).

   `:only-child` statt einer Klasse im Template: So greift die Regel von
   selbst, sobald ein Knopf zurückkommt, und keine View muss etwas dafür tun. */
.cta-inner > .cta-text:only-child { margin-inline: auto; }
.cta-band h2 { margin: 0 0 .4rem; }
.cta-band p { margin: 0; color: var(--text-soft); }

/* ---------- Formulare / Karten ---------- */
.card { background: var(--bg-card); border: 1px solid var(--border); border-radius: var(--radius); padding: 1.9rem; box-shadow: var(--shadow); }
.form label { display: block; margin-bottom: 1.1rem; font-weight: 500; color: var(--text); font-size: var(--fs-sm); font-family: var(--font-head); }
.form input, .form select, .form textarea {
    width: 100%; margin-top: .4rem; padding: .75rem .9rem; border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm); font-size: var(--fs-md); font-family: var(--font-body); color: var(--text);
    background: var(--input-bg); transition: border-color .15s, box-shadow .15s;
}
.form input::placeholder, .form textarea::placeholder { color: var(--placeholder); }
.form input:focus, .form select:focus, .form textarea:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }
.form select option { background: var(--bg-card); }
.form textarea { resize: vertical; }
.form-row { display: grid; grid-template-columns: 1fr 1fr; gap: 0 1.3rem; }
.form-row--3 { grid-template-columns: repeat(3, 1fr); }
.form-hint { display: block; font-weight: 400; color: var(--muted); font-size: var(--fs-xs); margin-top: .4rem; font-family: var(--font-body); }
/* Gruppe zusammengehöriger Felder – der Rahmen sagt "das gehört zusammen",
   ohne dass eine zweite Karte aufgemacht werden muss */
.form-fieldset { border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 1.1rem 1.2rem .2rem; margin: 0 0 1.1rem; }
.form-fieldset legend { font-family: var(--font-head); font-weight: 500; font-size: var(--fs-sm); padding: 0 .4rem; color: var(--text); }
/* In einer Gruppe trägt das Raster den Abstand, nicht jedes Kästchen für
   sich – sonst kommt der Zeilenabstand aus `.form label` noch obendrauf. */
.form-fieldset .choice { margin-bottom: 0; }
.form-actions { margin-top: .6rem; display: flex; gap: .8rem; flex-wrap: wrap; }
.checkbox-label { display: flex; align-items: center; gap: .6rem; }
.checkbox-label input { width: auto; margin: 0; accent-color: var(--accent); }
/* Wochentag-/Uhrzeit-Auswahl als Kästchen-Raster (Serien-Anlage) */
.choice-grid { display: flex; flex-wrap: wrap; gap: .5rem; margin-top: .5rem; }
.choice {
    display: inline-flex; align-items: center; gap: .45rem; cursor: pointer;
    padding: .5rem .9rem; border-radius: 999px; border: 1px solid var(--border-strong);
    background: var(--bg-card); font-family: var(--font-head); font-size: var(--fs-xs); font-weight: 500;
    transition: border-color .15s, background .15s, color .15s;
}
.choice input { width: auto; margin: 0; accent-color: var(--accent); }
.choice:has(input:checked) { border-color: var(--accent); background: var(--accent-soft); color: var(--accent); }
.choice:has(input:focus-visible) { outline: 3px solid var(--focus-ring); outline-offset: 2px; }
/* Wählbar, aber mit ehrlichem Hinweis auf die Folge */
.choice--locked { border-style: dashed; }
.choice-hint { display: block; font-size: var(--fs-xs); font-weight: 400; color: var(--muted); font-family: var(--font-body); }
/* Fehler an einem einzelnen Feld (Kontaktformular). Die Meldung steht UNTER
   dem Feld und nicht nur als roter Rahmen: Farbe allein ist keine Aussage –
   wer sie nicht unterscheidet, sieht sonst gar nichts. Verbunden wird beides
   über aria-describedby am Feld. */
.form-error { display: block; font-family: var(--font-body); font-weight: 400; font-size: var(--fs-xs); color: var(--red-text); margin-top: .4rem; }
/* Bestätigung AN DER STELLE DES FORMULARS statt im Band am Seitenanfang.
   Wer gerade hier unten getippt hat, sieht ein Band ganz oben nicht – und ein
   wieder leeres Formular liest sich, als wäre nichts passiert. Deshalb tritt
   dieser Block an die Stelle des Formulars und nicht darüber. */
.form-erfolg { display: flex; flex-direction: column; align-items: flex-start; gap: .9rem; padding: .5rem 0; }
.form-erfolg-zeichen {
    display: grid; place-items: center; width: 54px; height: 54px; flex-shrink: 0;
    border-radius: 50%; background: var(--green-soft); color: var(--green-text);
}
.form-erfolg-zeichen svg { width: 28px; height: 28px; }
.form-erfolg h3 { margin: 0; font-size: var(--fs-xl); }
.form-erfolg p { margin: 0; color: var(--text-soft); max-width: 42ch; }
.form input[aria-invalid="true"], .form select[aria-invalid="true"], .form textarea[aria-invalid="true"] { border-color: var(--red); }
.form-fieldset[aria-invalid="true"] { border-color: var(--red); }
/* Honigtopf: für Menschen unerreichbar, für Bots ein Feld wie jedes andere.
   Nicht display:none – manche Bots überspringen genau das. */
.hp-field { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }
/* Kontaktformular als Strecke (SAR-95). Die Schritte stehen im HTML alle
   untereinander; erst `initFormSteps()` in main.js setzt `hidden` und blendet
   einen nach dem anderen ein. Ohne JavaScript greift von diesem Block also
   nur der Abstand – und das Formular steht vollständig da.

   Die !important-Regel ist keine Bequemlichkeit: `.form-actions` und
   `.form label` setzen `display`, und das schlägt das `display: none`, das
   der Browser dem hidden-Attribut mitgibt. Ohne sie bleibt der abgeblendete
   Schritt sichtbar. */
.form [hidden] { display: none !important; }
.form-step { margin-bottom: .4rem; }
.form-progress {
    margin: 0 0 1.2rem; font-family: var(--font-head); font-size: var(--fs-xs);
    font-weight: 500; letter-spacing: 2px; text-transform: uppercase; color: var(--accent);
}
/* „Zurück" links, „Weiter" rechts – die Richtung, in die es geht, liegt auf
   der Seite, auf der man sie erwartet. */
.form-nav { justify-content: flex-end; margin-top: 1.4rem; }
/* Solange „Zurück" dasteht, drückt es „Weiter" nach rechts. Ist es
   ausgeblendet (erster Schritt), steht „Weiter" allein am rechten Rand –
   deshalb `flex-end` oben und nicht `space-between`: Das ließe einen
   einzelnen Knopf links kleben. */
.form-nav .btn-ghost:not([hidden]) { margin-right: auto; }

/* ---------- Kontakt / zweispaltige Layouts ---------- */
.split-grid { display: grid; grid-template-columns: 1fr 1.2fr; gap: 2.6rem; align-items: start; }
.split-grid--wide-left { grid-template-columns: 1.4fr 1fr; }
.contact-list { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 1.1rem; }
.contact-list li { display: flex; flex-direction: column; }
/* E-Mail-Adressen, Telefonnummern und Handles sind ein einziges Wort ohne
   Trennstelle – die Silbentrennung weiter oben kann daran nichts machen. Auf
   einem 320 px breiten Gerät passte „info@fahrlehrerinsarah.de" nicht mehr in
   die Spalte und schob die ganze Seite 15 px seitwärts; mit eingeschalteter
   Schriftvergrößerung waren es 52 px im Fuß. Seit SAR-65 heißt die Adresse
   „sarah@fahrlehrerinsarah.de" und ist ein Zeichen länger, die Regel hier ist
   damit eher wichtiger geworden als überflüssig.

   `anywhere` darf notfalls mitten im Wort umbrechen. Bei einer Adresse ist das
   richtig: Sie wird gelesen und nicht gesprochen, ein Umbruch nach dem @ tut
   ihr nichts. Nur an den beiden Stellen, an denen solche Wörter vorkommen, und
   nicht global – im Fließtext wären harte Wortbrüche der schlechtere Tausch
   gegen eine etwas lose Zeile. */
.contact-list a,
.footer-links a { overflow-wrap: anywhere; }
.contact-label { font-size: var(--fs-xs); text-transform: uppercase; letter-spacing: .5px; color: var(--accent); margin-bottom: .2rem; font-family: var(--font-head); }
.steps { list-style: none; counter-reset: step; padding: 0; margin: 0 0 1rem; display: flex; flex-direction: column; gap: 1.1rem; }
.steps li { display: flex; flex-direction: column; padding-left: 2.8rem; position: relative; }
.steps li::before { counter-increment: step; content: counter(step); position: absolute; left: 0; top: 0; width: 2rem; height: 2rem; background: var(--accent-soft); color: var(--accent); border: 1px solid var(--accent); border-radius: 50%; display: flex; align-items: center; justify-content: center; font-weight: 600; font-family: var(--font-head); }
.steps li strong { color: var(--text); font-family: var(--font-head); }
.steps li span { color: var(--text-soft); font-size: var(--fs-md); }

/* =========================================================================
   Terminkalender (Wochenansicht) – generische ND-Komponente
   ========================================================================= */
.week-bar { display: flex; align-items: center; justify-content: space-between; gap: 1rem; flex-wrap: wrap; margin-bottom: 1.8rem; }
.week-bar h2 { margin: 0; font-size: var(--fs-xl); }
.week-nav { display: flex; gap: .5rem; }

/* Eine Woche hat sieben Tage – die stehen nebeneinander, sonst wirkt der
   Kalender wie ein zufälliges Raster. Erst auf schmalen Screens wird gestapelt. */
.week-grid { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: .8rem; align-items: start; }
.week-day {
    background: var(--bg-card); border: 1px solid var(--border); border-radius: var(--radius);
    overflow: hidden; display: flex; flex-direction: column;
}
.week-day--today { border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent-soft); }
.week-day-head {
    padding: .7rem .8rem; border-bottom: 1px solid var(--border);
    background: var(--day-head-bg, var(--bg-secondary));
    font-family: var(--font-head);
}
.week-day-name { display: block; font-weight: 600; color: var(--text); font-size: var(--fs-sm); }
.week-day-date { display: block; font-size: var(--fs-xs); color: var(--muted); }
.week-day-body { padding: .7rem; display: flex; flex-direction: column; gap: .6rem; flex: 1; }
.week-day-empty { color: var(--muted); font-size: var(--fs-xs); padding: .4rem 0; }

/* In einer schmalen Tagesspalte ist nebeneinander kein Platz –
   Uhrzeit, Art und Aktion stehen deshalb untereinander. */
.slot {
    display: flex; flex-direction: column; align-items: stretch; gap: .5rem;
    padding: .6rem .7rem; border-radius: var(--radius-sm);
    border: 1px solid var(--border-strong); background: var(--bg);
    font-size: var(--fs-sm); text-align: left;
}
.slot-time { font-family: var(--font-head); font-weight: 600; color: var(--text); }
.slot-meta { display: block; font-size: var(--fs-xs); color: var(--muted); font-family: var(--font-body); line-height: 1.4; }
.slot form { margin: 0; }
.slot .btn { width: 100%; }
.slot .pill { align-self: flex-start; }
/* Eine Spalte des Wochenrasters ist schmal. Deshalb umbrechen die Aktionen,
   statt aus der Karte zu laufen – die Zuweisen-Auswahl nimmt eine eigene
   Zeile über voller Breite, Sperren und Löschen teilen sich die darunter. */
.slot .cell-actions { display: flex; flex-wrap: wrap; gap: .4rem; white-space: normal; }
.slot .cell-actions .inline-form { margin-left: 0; flex: 1 1 auto; min-width: 0; }
.slot .cell-actions .inline-form:last-child { flex: 0 0 auto; }
.slot .cell-actions .slot-assign { flex: 1 0 100%; }
.slot--frei { border-color: var(--green-line); background: var(--green-soft); }
.slot--gebucht { border-color: var(--border); opacity: .72; }
.slot--eigen { border-color: var(--accent); background: var(--accent-soft); }
.slot--gesperrt { border-color: var(--border); background: var(--bg-secondary); opacity: .6; }
.slot--vergangen { opacity: .45; }

/* Terminliste (Meine Termine / Admin-Buchungen) */
.appointment-list { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 1rem; }
.appointment {
    position: relative; overflow: hidden;
    display: grid; grid-template-columns: auto 1fr auto; align-items: center; gap: 1.2rem;
    background: var(--bg-card); border: 1px solid var(--border); border-radius: var(--radius);
    padding: 1.2rem 1.4rem; box-shadow: var(--shadow);
}
.appointment::before { content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 5px; background: var(--card-accent, var(--accent)); }
.appointment--past { opacity: .6; }
.appointment--cancelled { opacity: .6; }
.appointment--cancelled::before { background: var(--muted); }
.appointment-date {
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    min-width: 74px; padding: .6rem .4rem; border-radius: var(--radius-sm);
    background: var(--accent-soft); font-family: var(--font-head); line-height: 1.1;
}
.appointment-day { font-size: var(--fs-2xl); font-weight: 700; color: var(--accent); }
.appointment-month { font-size: var(--fs-xs); text-transform: uppercase; letter-spacing: 1px; color: var(--accent); }
.appointment-main h3 { margin: 0 0 .25rem; font-size: var(--fs-lg); }
.appointment-main p { margin: 0; color: var(--text-soft); font-size: var(--fs-md); }
.appointment-actions { display: flex; gap: .5rem; flex-wrap: wrap; justify-content: flex-end; }
.appointment-actions form { margin: 0; }

/* =========================================================================
   Bild-Text-Abschnitte
   Wechselnde Seiten statt immer gleicher Kartenreihen – das ist es, was eine
   Seite handgemacht statt zusammengeklickt wirken lässt.
   ========================================================================= */
.duo { display: grid; grid-template-columns: 1fr 1fr; gap: clamp(2rem, 5vw, 4rem); align-items: center; }
.duo--text-first .duo-media { order: 2; }
.duo--narrow-media { grid-template-columns: 1.15fr .85fr; }
/* Dasselbe gespiegelt: schmale Medienspalte LINKS. Kein `order`-Trick,
   sondern das Medium steht im Markup vorn – nur so bekommt es auch die
   schmale Spalte. Über `order` gedreht landete es in der breiten (die
   Spaltenbreiten hängen an der Position im Raster, nicht am Element) und
   ein Hochformat würde die halbe Seite füllen. Nebeneffekt, der hier
   erwünscht ist: Gestapelt steht das Medium dadurch oben. */
.duo--narrow-media-left { grid-template-columns: .85fr 1.15fr; }
/* `.duo--stack-text-first` steht bewusst nicht hier: Nebeneinander ändert der
   Modifier nichts, das Bild bleibt links wie im Markup. Er greift erst
   gestapelt – die Regel steht im 820er-Block ganz unten. */
.duo-text > :first-child { margin-top: 0; }
.duo-text h2 { margin: 0 0 1rem; }
.duo-text p { color: var(--text-soft); }
.duo-actions { display: flex; flex-wrap: wrap; gap: .8rem; margin-top: 1.6rem; }

/* Bild sitzt leicht schräg im Rahmen – wirkt wie eingeklebt, nicht wie eingesetzt */
.photo {
    position: relative; margin: 0;
    border-radius: var(--radius); overflow: hidden;
    background: var(--bg-secondary); box-shadow: var(--shadow-lg);
    transform: rotate(-1.1deg);
}
.photo--right { transform: rotate(1.1deg); }
.photo--straight { transform: none; }
.photo img { width: 100%; height: auto; display: block; }
.photo figcaption {
    position: absolute; left: 0; right: 0; bottom: 0;
    padding: .9rem 1.1rem; font-size: var(--fs-sm); line-height: 1.4;
    color: var(--footer-text); background: linear-gradient(180deg, transparent, rgba(43, 36, 52, .82));
}

/* ZWEI FOTOS IN EINER MEDIENSPALTE  ·  .photo-pair            (SAR-110)
   Das Hochformat oben links, das Querformat darunter nach rechts versetzt und
   leicht darüberliegend.

   WARUM VERSETZT UND NICHT NEBENEINANDER ODER GESTAPELT: Nebeneinander bekäme
   jedes Bild die halbe Spalte, also rund 250 px – auf einem Gruppenfoto ist
   dann kein Gesicht mehr zu erkennen. Sauber gestapelt käme die Spalte auf
   die Summe beider Höhen und wäre doppelt so hoch wie der Text daneben; die
   `.duo` zentriert senkrecht, der Text schwämme dann in der Mitte einer
   langen Bildspalte. Versetzt nutzt jedes Bild rund drei Viertel der Breite,
   und die Überlappung holt die Gesamthöhe wieder herunter.

   DER VERSATZ LÄUFT ÜBER `margin`, NICHT ÜBER `transform`. Auf `.photo` liegt
   schon eine Drehung, und ein `transform` hier würde sie überschreiben statt
   sie zu ergänzen – das Bild stünde gerade. Dieselbe Falle wie beim
   Scroll-Reveal, der aus demselben Grund `translate` statt `transform`
   benutzt (Begründung in der CLAUDE.md).

   `z-index` am zweiten Bild: Ohne ihn liegt das erste oben (späteres Element,
   aber `.photo` bringt `position: relative` mit und beide stehen ohne eigenen
   z-index auf `auto`) – die Überlappung sähe dann nach Fehler aus. Der Schatten
   des oberen auf dem unteren ist das, was die beiden als Stapel lesbar macht.

   Gestapelt (unter 820 px) steht die `.duo` ohnehin einspaltig; der Versatz
   bleibt, wird dort aber kleiner gefahren – zwei stark versetzte Bilder auf
   einer Handybreite lassen für jedes zu wenig übrig. */
.photo-pair { display: grid; }
.photo-pair > .photo { width: 78%; }
.photo-pair > .photo + .photo {
    width: 68%;
    margin-left: auto;
    margin-top: -11%;
    z-index: 1;
}

/* SPEKTRUM-KREIS  ·  .spektrum                                    (SAR-114)
   Ein Kern mit sechs beschrifteten Kreisen darum. Gebaut fuer das
   Neurodivergenz-Schaubild auf /neurodivergenz, aber ohne dessen Begriffe:
   Die Komponente kennt nur „Kern" und „Feld", der Inhalt steht im Template.

   WARUM ALS HTML UND NICHT ALS BILD: Die Vorlage war eine fremde Grafik, in
   der jedes Wort gemalter Text war – nicht skalierbar, nicht markierbar, fuer
   Vorlesesoftware unsichtbar, und die Lizenz ungeklaert. Hier ist es echter
   Text in einer Liste: Er waechst mit der Schriftgroesse des Browsers, laesst
   sich kopieren, wird vorgelesen und traegt in jedem der drei Farbmodi die
   Farben der Seite.

   DIE SECHS PLAETZE STEHEN IM CSS, nicht im Template. Sechs feste Positionen
   im 60-Grad-Abstand – dafuer braucht es keine Inline-Angaben in der View,
   `:nth-child` reicht und haelt das Markup sauber.

   GERECHNET WIRD IN PROZENT DER BREITE, und das geht nur, weil der Kasten
   quadratisch ist (`aspect-ratio: 1`): Ein senkrechter `margin` in Prozent
   bezieht sich in CSS immer auf die BREITE des Elternelements, nie auf die
   Hoehe. Bei einem Quadrat ist beides dasselbe, also stimmen beide Achsen.

   DIE MASSE SIND AN DER VORLAGE NACHGEMESSEN und nicht geschaetzt. Dort
   (872 px breit) ist die graue Scheibe 530 px, ein farbiger Kreis 260 px und
   der Ring-Radius 335 px – in Prozent der Breite also 60,8 / 29,8 / 38,4.
   Daraus folgt das Entscheidende: Der Mittenabstand zweier Nachbarn ist
   genauso gross wie der Radius (38,4 %), ihre Radien summieren sich auf
   29,8 % – die Nachbarn BERUEHREN sich also nicht einmal, sie haben 8,6 %
   Luft. Ueberlappt wird ausschliesslich die Mitte, und zwar von jedem Kreis
   gleich weit (6,9 %).

   Genau das ist der Punkt, an dem eine erste Fassung falsch lag: Sie machte
   die Felder gross (35 %) und den Kern klein (44 %), wodurch sich die
   NACHBARN um 3 % ueberschnitten. Auf dem Handy sah das unruhig aus, weil
   sich Kreise kreuzten, die in der Vorlage nur nebeneinander liegen
   (Nils, 31.08.2026).

   Auf das Quadrat skaliert (Faktor 0,859, damit die Aussenkante hineinpasst):
   Ring-Radius 33 %, davon sin(60°) = 28,6 % zur Seite und cos(60°) = 16,5 %
   nach oben oder unten; Felder 26 %, Kern 52 %.

   AUSSENKANTE: 33 % + 13 % = 46 %, also 4 % Luft zum Rand. Wer am Radius oder
   an der Feldgroesse dreht, rechnet beides nach – die Luft zum Rand UND den
   Abstand der Nachbarn.

   KEINE BEWEGUNG, kein Hover-Effekt, nichts, was sich meldet. Die Seite
   richtet sich an Menschen, die mit vielen Reizen auf einmal schlecht
   zurechtkommen – dieselbe Ueberlegung, aus der sie ohne Hinweiskaesten
   auskommt. Ein Schaubild, das beim Darueberfahren wackelt, waere hier
   falsch.

   Die Schriftgroessen haengen an Container-Einheiten (`cqw`) und nicht am
   Ansichtsfenster: Der Kreis steht in einer Spalte, die je nach Seite anders
   breit ist – `vw` wuerde die Schrift nach der Fensterbreite bemessen und in
   der schmalen Spalte zu gross setzen. Deshalb traegt `.spektrum` selbst
   `container-type`. */
.spektrum {
    position: relative;
    width: 100%;
    max-width: 460px;
    aspect-ratio: 1;
    margin: 0 auto;
    container-type: inline-size;
}
.spektrum-ring { list-style: none; margin: 0; padding: 0; }

.spektrum-kern,
.spektrum-feld {
    position: absolute;
    left: 50%;
    top: 50%;
    translate: -50% -50%;
    aspect-ratio: 1;
    display: grid;
    place-items: center;
    border-radius: 50%;
    text-align: center;
    text-wrap: balance;
    hyphens: auto;
    line-height: 1.15;
}
.spektrum-kern {
    width: 52%;
    /* `margin: 0` ist hier kein Aufraeumen, sondern noetig: Der Kern ist ein
       <p> und traegt sonst den Standardabstand des Browsers (rund 1em oben).
       Bei `position: absolute` verschiebt der ihn nach unten – nachgemessen
       sass die Scheibe dadurch so tief, dass der obere Kreis sie nur 4 px
       ueberlappte und der untere 27 px. Die Ueberlappung MUSS bei allen
       sechs gleich sein, sonst sieht der Ring schief aus. */
    margin: 0;
    font-family: var(--font-head);
    font-weight: 700;
    font-size: clamp(.85rem, 4.6cqw, 1.2rem);
    z-index: 1;
}
.spektrum-feld {
    width: 26%;
    padding: 0 .35em;
    font-weight: 600;
    font-size: clamp(.6rem, 3.1cqw, .82rem);
    border: 2px solid;
    z-index: 2;
}

/* Oben beginnen und im Uhrzeigersinn weiter – dieselbe Reihenfolge, in der
   die Liste vorgelesen wird. */
.spektrum-feld:nth-child(1) { margin-left:     0; margin-top: -33%; }
.spektrum-feld:nth-child(2) { margin-left: 28.6%; margin-top: -16.5%; }
.spektrum-feld:nth-child(3) { margin-left: 28.6%; margin-top:  16.5%; }
.spektrum-feld:nth-child(4) { margin-left:     0; margin-top:  33%; }
.spektrum-feld:nth-child(5) { margin-left:-28.6%; margin-top:  16.5%; }
.spektrum-feld:nth-child(6) { margin-left:-28.6%; margin-top: -16.5%; }

/* IN EINER SCHMALEN SPALTE BLEIBT ES EIN KREIS (SAR-114, zweiter Anlauf).
   Eine erste Fassung baute unter 430 px Spaltenbreite auf eine Reihe von
   Pillen um – verworfen (Nils): Der Kreis IST die Aussage, sechs Dinge, die
   um eine Mitte liegen. Eine Liste sagt „sechs Dinge" und laesst die Mitte
   weg.

   WAS HIER NACHGIBT, IST DIE SCHRIFT – NICHT DIE GEOMETRIE. Ein zweiter
   Versuch liess dafuer die Felder wachsen; damit ueberschnitten sich die
   Nachbarkreise, und genau das sieht unruhig aus (siehe die Rechnung oben).
   Die Anordnung bleibt deshalb in jeder Breite dieselbe, kleiner werden nur
   Schriftgrad und Innenfutter.

   HILFT DABEI: `hyphens: auto` mit `lang="de"` am <html>. „Tourette-Syndrom"
   bricht ohnehin am eigenen Bindestrich, „Hochbegabung" wird zu „Hoch-" und
   „begabung" – genau so steht es in der Vorlage. */
@container (max-width: 430px) {
    .spektrum-feld {
        padding: 0 .2em;
        font-size: clamp(.62rem, 3.1cqw, .8rem);
    }
    .spektrum-kern {
        font-size: clamp(.75rem, 4.2cqw, 1.2rem);
    }
}

/* Freisteller statt Foto: kein Rahmen, kein Schatten, keine Drehung – der
   Gegenstand steht frei auf der Fläche. Die Bildunterschrift rutscht darunter,
   weil es im Bild keine Fläche gibt, auf der sie liegen könnte.
   MUSS nach ".photo figcaption" stehen: gleiche Spezifität, es gewinnt das
   Spätere. Weiter oben eingeordnet greift die Regel nicht. */
.photo--cutout { background: none; box-shadow: none; transform: none; overflow: visible; }
.photo--cutout figcaption {
    position: static; background: none; color: var(--muted);
    padding: .7rem 0 0; text-align: center;
}

/* Hochformat neben Fließtext. Die Fotos in den Duo-Spalten sind sonst quer
   (4:3) und bauen flacher als der Text daneben. Ein Hochformat füllt die
   Spalte dagegen bis zur vollen Breite und wird dadurch anderthalbmal so hoch
   wie der Text: Die Spaltenhöhe richtet sich dann nach dem Bild, und der Text
   steht verloren daneben. Die Begrenzung hält beide auf einer Höhe.

   ZWEI Grenzen, weil eine nicht reicht: Die Breite fängt normale Hochformate
   (3:4), die Höhe die schlanken. Ein Freisteller einer stehenden Person ist
   schnell doppelt so hoch wie breit – bei 360 px Breite wären das 765 px Höhe
   neben 510 px Text. Erst `max-height` bindet auch diesen Fall.

   `width: fit-content` gehört zwingend dazu: Sobald die Höhe greift, wird das
   Bild schmaler als die Spalte. Ohne fit-content behielte die <figure> ihre
   volle Breite – bei `.photo` mit Rahmen stünde der dann als leerer Kasten um
   ein zentriertes Bild herum. */
.photo--portrait {
    max-width: 360px;
    width: fit-content;
    margin-inline: auto;
}
.photo--portrait img { max-height: 560px; width: auto; }

/* Hochkant-Video (Kanal-Format). Nicht gedreht wie die Fotorahmen – ein
   schiefes Video wirkt versehentlich, nicht gestaltet. */
.video-frame {
    margin: 0 auto; max-width: 300px;
    border-radius: var(--radius); overflow: hidden;
    background: var(--video-frame-bg, var(--bg-footer)); box-shadow: var(--shadow-lg);
}
/* Das SCHWARZ bleibt schwarz und ist kein Token: Es ist die Flaeche des
   Videos selbst, also das, was ein Player zeigt, solange kein Bild da ist.
   Faerbt man sie mit ein, blitzt beim Laden eine bunte Kachel auf. */
.video-frame video { display: block; width: 100%; height: auto; background: #000; }
.video-frame figcaption {
    padding: .8rem 1rem; font-size: var(--fs-sm); line-height: 1.45;
    color: var(--video-frame-text, var(--footer-muted));
    background: var(--video-frame-bg, var(--bg-footer));
}

/* Farbiger Fleck hinter dem Bild – die Regenbogenpalette taucht beiläufig auf */
.photo-wrap { position: relative; }
.photo-wrap::before {
    content: ""; position: absolute; inset: -6% -4% -4% -6%;
    border-radius: calc(var(--radius) + 14px);
    background: var(--card-accent, var(--accent)); opacity: .13;
    transform: rotate(2deg);
    /* Rein dekorativ: der Fleck liegt als positioniertes Element über allem, was im
       Fluss steht, und würde sonst Klicks abfangen – z. B. den Play-Knopf des Videos. */
    pointer-events: none;
}
/* Alles, was im Fleck liegt, muss selbst positioniert sein – sonst malt der Browser
   den Fleck darüber und tönt Bild bzw. Video ein. */
.photo-wrap > .photo,
.photo-wrap > .video-frame { position: relative; }

/* Selbstauskunft über dem Fuß. Bewusst leise: Sie soll gefunden werden von
   dem, der sie sucht, und nicht gelesen werden müssen von allen anderen.
   Deshalb schmal, mittig und im ruhigen Ton – lauter gesetzt wäre sie eine
   zweite Überschrift kurz vor dem Fuß der Seite.
   IN DIESEM PROJEKT DERZEIT UNBENUTZT: partials/site-note.php ist am
   22.08.2026 entfallen. Die Regel bleibt wie .footer-credit als Teil der
   ND-Signatur stehen. */
.site-note {
    border-top: 1px solid var(--border);
    padding: 2.2rem 0;
}
.site-note p {
    max-width: 64ch; margin: 0 auto;
    text-align: center; text-wrap: pretty;
    color: var(--text-soft); font-size: var(--fs-md); line-height: 1.7;
}

/* Der eine Satz, der einen Abschnitt trägt: tritt aus dem Fließtext heraus,
   bleibt aber Teil davon. Bewusst KEIN Rahmen und keine Anführungszeichen –
   dafür gibt es `.quote` gleich darunter, und wo beides in einem Abschnitt
   aufeinanderträfe, sähe es nach zwei Zitaten aus. Hier ist es kein Zitat,
   sondern der Text selbst, nur lauter. */
.statement {
    font-family: var(--font-head);
    font-weight: 600;
    font-size: var(--fs-xl);
    line-height: 1.45;
    color: var(--text);
    margin: 1.5rem 0;
}
/* Die Betonung im Satz trägt die Akzentfarbe statt noch mehr Fettung – bei
   600 als Grundgewicht fiele ein <strong> sonst gar nicht mehr auf. */
.statement strong { color: var(--accent-text, var(--accent)); font-weight: 700; }

/* Ein Satz in Sarahs Stimme – bricht den Rhythmus der Textblöcke */
.quote {
    max-width: 780px; margin: 0 auto; padding: 0 0 0 clamp(1.2rem, 3vw, 2rem);
    border-left: 5px solid transparent;
    border-image: var(--gradient-rainbow-v, none) 1;
}
.quote p {
    margin: 0; font-family: var(--font-head); font-weight: 500;
    font-size: var(--fs-2xl); line-height: 1.35; color: var(--text);
}
.quote footer { margin-top: .9rem; color: var(--muted); font-size: var(--fs-sm); }

/* Offene Fragen, die jemand mitbringt – untereinander, jede an einem kurzen
   Strich. Bewusst OHNE Häkchen oder Punkte davor: Ein Häkchen macht aus einer
   Frage ein Merkmal („haben wir"), und das Gegenteil ist gemeint. Hier steht
   das, was jemand noch nicht weiß. */
.question-list { list-style: none; padding: 0; margin: 1.5rem 0; display: grid; gap: .75rem; }
.question-list li {
    padding-left: 1rem;
    border-left: 2px solid var(--card-accent, var(--accent));
    font-family: var(--font-head); font-weight: 500; font-size: var(--fs-md);
    line-height: 1.45; color: var(--text);
}

/* =========================================================================
   ANLESE-ABSCHNITTE  ·  .accordion
   -------------------------------------------------------------------------
   Für lange, gegliederte Texte. Jeder Abschnitt zeigt seine Überschrift und
   seinen ERSTEN ABSATZ; der Rest kommt über „Mehr lesen". Man sieht also
   die Gliederung UND bekommt von jedem Kapitel eine Kostprobe, statt vor
   einer Liste aus Überschriften zu stehen, die nichts über ihren Inhalt
   verrät.

   Der Teaser ist bewusst KEINE geschriebene Zusammenfassung, sondern der
   unveränderte Anfang des Textes. Bei einem Text, der von einer echten
   Person stammt, ist das der Unterschied zwischen Anlesen und Nacherzählen –
   eine Zusammenfassung wäre eine fremde Stimme über ihrer.

   Gebaut auf <details>/<summary> und NICHT auf JavaScript. Das ist die
   Bedingung dafür, dass man Text überhaupt verstecken darf: Der Browser
   bringt Tastaturbedienung, Vorlesesoftware und das Aufklappen bei „Auf
   Seite suchen" (Strg+F) von sich aus mit, und ohne JavaScript steht
   ohnehin alles da. Selbstgebaut wäre jeder dieser vier Punkte ein eigener
   Fehler.

   Die Überschrift steht AUSSERHALB des <summary> und bleibt damit eine
   normale Überschrift. Zöge man sie in die Schaltfläche hinein, läse
   Vorlesesoftware bei jedem Abschnitt die ganze Überschrift als Knopftext
   vor – der Knopf heißt „Mehr lesen", und mehr soll er auch nicht heißen.
   ========================================================================= */
.accordion { max-width: 780px; display: grid; gap: .9rem; }

.accordion-item {
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-left: 4px solid var(--card-accent, var(--accent));
    border-radius: var(--radius);
    box-shadow: var(--shadow);
    padding: clamp(1.1rem, 3vw, 1.5rem);
}

.accordion-title {
    margin: 0 0 .6rem;
    font-size: var(--fs-xl);
    line-height: 1.3;
    text-wrap: balance;
}

.accordion-item p { margin: 0 0 1rem; color: var(--text-soft); }
.accordion-item > details > :last-child,
.accordion-body > :last-child { margin-bottom: 0; }
.accordion-teaser { margin-bottom: 0; }

/* `list-style: none` ist die Fassung für Firefox, das ::-webkit-Pseudoelement
   die für Safari – beide brauchen ihre eigene, sonst steht neben dem eigenen
   Winkel noch das Standard-Dreieck.

   Der Knopf sieht aus wie ein Link und nicht wie eine Schaltfläche: Er sitzt
   am Ende eines Absatzes und ist eine Einladung weiterzulesen, kein
   Formularelement. `width: fit-content` ist dabei wichtig – ein <summary> ist
   von Haus aus so breit wie der Block, und dann läge die Klickfläche über die
   ganze Zeile, ohne dass man das sieht. */
.accordion-more {
    list-style: none;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    gap: .5rem;
    width: fit-content;
    margin-top: .9rem;
    font-family: var(--font-head);
    font-weight: 600;
    font-size: var(--fs-md);
    color: var(--accent-text, var(--accent));
    border-radius: var(--radius-sm);
}
.accordion-more::-webkit-details-marker { display: none; }
.accordion-more:hover { text-decoration: underline; }
.accordion-more:focus-visible {
    outline: 2px solid var(--focus-ring);
    outline-offset: 3px;
}

/* Zwei echte Beschriftungen statt einer, die per CSS ihren Text wechselt:
   Inhalt aus `content:` wird nicht von jeder Vorlesesoftware zuverlässig
   ausgegeben, und ein Knopf ohne Beschriftung ist kein Knopf. */
.accordion-more-close { display: none; }
.accordion-item > details[open] > .accordion-more .accordion-more-open  { display: none; }
.accordion-item > details[open] > .accordion-more .accordion-more-close { display: inline; }

/* Offen wird der Knopf leiser. Er steht dann zwischen dem Anlesetext und dem
   Rest – also mitten im Fließtext –, und in Akzentfarbe unterbricht er dort
   genau das Lesen, das er gerade ermöglicht hat. Zugeklappt ist er eine
   Einladung und darf auffallen, offen ist er nur noch ein Rückweg.

   Er bleibt dabei an seinem Platz und wandert NICHT unter den Text: Er sitzt
   dort, wo eben geklickt wurde. Ein Knopf, der nach dem Klick ans Ende eines
   langen Absatzes springt, ist für den Finger, der ihn gerade getroffen hat,
   verschwunden. */
.accordion-item > details[open] > .accordion-more {
    font-weight: 500;
    font-size: var(--fs-md);
    color: var(--muted);
    margin-top: .4rem;
}

/* Der Winkel ist gezeichnet und kein Zeichen aus einer Schrift: Ein „+" oder
   „›" aus dem Text wandert bei jeder Schriftumstellung mit und lässt sich
   nicht sauber drehen. Zwei Kanten eines Quadrats, um 45° gekippt. */
.accordion-chevron {
    width: 8px; height: 8px;
    flex-shrink: 0;
    margin-top: -4px;
    border-right: 2.5px solid currentColor;
    border-bottom: 2.5px solid currentColor;
    rotate: 45deg;
    transition: rotate .25s ease, margin-top .25s ease;
}
.accordion-item > details[open] > .accordion-more .accordion-chevron {
    rotate: 225deg;
    margin-top: 3px;
}

.accordion-body { padding-top: 1rem; }

/* Der Inhalt gleitet beim Öffnen herein. Bewusst nur der Inhalt und nicht die
   Höhe des Kastens: Eine echte Höhenanimation bräuchte `::details-content`
   und wird längst nicht überall unterstützt – der Kasten springt also auf,
   und der Text kommt eine Spur danach. Das liest sich als Absicht, während
   eine halb funktionierende Höhenanimation nach Fehler aussieht. */
@media (prefers-reduced-motion: no-preference) {
    .accordion-item > details[open] > .accordion-body {
        animation: accordionOpen .3s cubic-bezier(.22, .61, .36, 1);
    }
}
@keyframes accordionOpen {
    from { opacity: 0; translate: 0 -.5rem; }
    to   { opacity: 1; translate: 0 0; }
}

/* Zwei Marken nebeneinander: Kundin und Werkstatt. Steht auf der Referenzseite
   an der Stelle, an der sonst ein Foto stünde.

   Aus den echten Logo-Dateien zusammengesetzt statt als fertige Grafik – so
   bleiben beide scharf, es gibt keine Datei, die bei einer Logo-Änderung
   veraltet, und kein Logo wird dabei verändert.

   Die Höhen sind verschieden und das ist Absicht: Sarahs Logo ist hoch (Bogen
   über Schriftzug), das ND-Logo breit. Auf gleiche Höhe gezwungen wirkt das
   breite Logo winzig; auf gleiche BREITE gezwungen überragt das hohe alles.
   Deshalb bekommt jedes den Wert, bei dem es optisch gleich schwer wiegt. */
.cooperation {
    display: flex; align-items: center; justify-content: center;
    gap: clamp(.8rem, 4vw, 2rem);
    padding: clamp(1.6rem, 5vw, 2.6rem);
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
}
.cooperation-mark { width: auto; height: clamp(88px, 18vw, 130px); display: block; }
/* Das breite Logo über die Breite steuern, nicht über die Höhe – sonst wächst
   es beim Skalieren aus dem Block heraus. */
.cooperation-mark--nd { height: auto; width: clamp(96px, 22vw, 150px); }
.cooperation-x {
    font-family: var(--font-head); font-size: var(--fs-xl);
    color: var(--muted); line-height: 1; flex-shrink: 0;
}

/* Das Wichtigste auf einen Blick – ein abgesetzter Block für alles, was jemand
   sucht, der nicht den ganzen Text liest. Bewusst als Fläche mit Kante und
   nicht als weitere Absatzfolge: Sie soll sich beim Überfliegen vom Fließtext
   abheben, und genau das macht sie überspringbar für alle, die lesen.

   Innen liegt die vorhandene `.facts`-Liste, nur mehrspaltig – ihre Einträge
   sind kurz, untereinander stünde eine schmale Säule in einem breiten Kasten.
   `min(100%, 280px)` statt bloß `280px`: Auf einem schmalen Telefon ist der
   Container keine 280 px breit, und eine Mindestbreite, die größer ist als der
   Platz, sprengt das Raster nach rechts heraus. */
.info-box {
    margin-top: clamp(2rem, 4vw, 2.8rem);
    padding: clamp(1.4rem, 3vw, 2rem);
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
}
.info-box .facts {
    margin: 0;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 280px), 1fr));
    gap: 1.2rem 2rem;
}

/* Kompakte Faktenliste, z.B. „Das bringe ich mit" */
.facts { list-style: none; padding: 0; margin: 1.6rem 0 0; display: grid; gap: .9rem; }
.facts li { display: grid; grid-template-columns: auto 1fr; gap: .9rem; align-items: start; }
.facts svg { width: 22px; height: 22px; margin-top: .2rem; color: var(--card-accent, var(--accent)); }
.facts li:nth-child(7n+1) { --card-accent: var(--c-red); }
.facts li:nth-child(7n+2) { --card-accent: var(--c-orange); }
.facts li:nth-child(7n+3) { --card-accent: var(--c-yellow); }
.facts li:nth-child(7n+4) { --card-accent: var(--c-green); }
.facts li:nth-child(7n+5) { --card-accent: var(--c-teal); }
.facts li:nth-child(7n+6) { --card-accent: var(--c-blue); }
.facts li:nth-child(7n+7) { --card-accent: var(--c-violet); }
.facts strong { display: block; font-family: var(--font-head); }
.facts span { color: var(--text-soft); font-size: var(--fs-md); }

/* Hinweiskasten, der eine Sache klarstellt (z.B. „Anmeldung läuft über die Fahrschule") */
.notice {
    display: grid; grid-template-columns: auto 1fr; gap: 1rem; align-items: start;
    padding: 1.2rem 1.4rem; border-radius: var(--radius);
    background: var(--bg-card); border: 1px solid var(--border);
    border-left: 5px solid var(--card-accent, var(--accent));
}
.notice svg { width: 26px; height: 26px; color: var(--card-accent, var(--accent)); }
.notice h3 { margin: 0 0 .3rem; font-size: var(--fs-md); }
.notice p { margin: 0; color: var(--text-soft); font-size: var(--fs-md); }
.notice p + p { margin-top: .5rem; }

/* Ein Dokument zum Öffnen (SAR-131: Zertifikat auf /neurodivergenz):
   Vorschaubild plus Titel und Linkzeile. Schmal gestapelt, Vorschau oben
   über die volle Breite; ab 600 px steht sie links neben dem Text, auf
   ein Drittel begrenzt. Keine Drehung wie bei `.photo` – ein Dokument
   liegt gerade, sonst liest es sich wie ein Schnappschuss. */
.doc-card {
    display: grid; grid-template-columns: 1fr; gap: 1rem; align-items: center;
    margin-top: 1.6rem; padding: 1.2rem 1.4rem; border-radius: var(--radius);
    background: var(--bg-card); border: 1px solid var(--border);
}
.doc-card-preview {
    display: block; max-width: 420px; border-radius: var(--radius-sm); overflow: hidden;
    border: 1px solid var(--border); box-shadow: var(--shadow);
    transition: box-shadow .15s, translate .15s;
}
.doc-card-preview:hover { box-shadow: var(--shadow-lg); translate: 0 -3px; }
.doc-card-preview img { display: block; width: 100%; height: auto; }
.doc-card h3 { margin: 0 0 .3rem; font-size: var(--fs-md); }
.doc-card p { margin: 0; font-size: var(--fs-md); }
.doc-card p a { font-weight: 700; text-decoration: underline; }
.doc-card-meta { color: var(--muted); font-size: var(--fs-sm); white-space: nowrap; }
@media (min-width: 600px) {
    .doc-card { grid-template-columns: minmax(180px, 280px) 1fr; gap: 1.4rem; }
}

@media (max-width: 820px) {
    .duo, .duo--narrow-media, .duo--narrow-media-left { grid-template-columns: 1fr; }
    .duo--text-first .duo-media { order: 0; }

    /* Gestapelt zuerst der Text, dann das Bild – die Ausnahme von der Regel
       eine Zeile darüber. Der Normalfall ist Bild oben: Es führt in den
       Abschnitt ein, bevor der Text anfängt. Steht direkt darüber aber schon
       eine Abbildung – auf /ueber-mich der Hero, in dem Sarah im Bogen sitzt –,
       dann stoßen genau an dieser Kante zwei Bilder aneinander, ohne eine Zeile
       dazwischen. Der Text muss dann nach oben, nicht das Bild weg. */
    .duo--stack-text-first .duo-media { order: 2; }

    .photo, .photo--right { transform: none; }

    /* Das Bildpaar rückt zusammen (SAR-110). Auf einer Handybreite ist die
       Spalte rund 340 px breit; bei 78 % blieben davon 265 px, und auf einem
       Foto mit zwei Personen ist das wenig. Der Versatz muss trotzdem
       bleiben, sonst stehen zwei Bilder stumpf untereinander – hier oben
       fällt zusätzlich die Drehung weg (Regel eine Zeile darüber), er ist
       also das Einzige, was die beiden noch als Paar zusammenhält. */
    .photo-pair > .photo { width: 86%; }
    .photo-pair > .photo + .photo { width: 78%; margin-top: -7%; }

    /* Gestapelt sitzt das Bild mittig unter dem Text. Linksbündiger Text über
       einem zentrierten Bild liest sich, als wäre eines von beiden verrutscht –
       also rückt der ganze Hero-Block in die Mitte. Nur hier: nebeneinander
       trägt die linke Kante die Komposition. */
    .hero-content { text-align: center; }
    .hero-lead { margin-inline: auto; }
    .hero-eyebrow, .hero-actions { justify-content: center; }
    .hero-badges { margin-inline: auto; }

    /* Der Ortspin gehört zum Text, nicht an den Rand. Als Flex-Zeile über die
       volle Breite klebt er ganz links neben einem zweizeiligen Block und wirkt
       abgehängt. Als mitfließendes Zeichen im Fließtext bricht die Zeile normal
       um und beide Zeilen stehen mittig. */
    .hero-meta { display: block; }
    .hero-meta svg { display: inline-block; vertical-align: -4px; margin-right: .35rem; }

    /* --- Die Hero-Abbildung wird zur Bühne ----------------------------------
       Nebeneinander sind Text und Bild zwei Spalten. Gestapelt wären sie zwei
       Blöcke untereinander: Der Hero würde doppelt so hoch, und man müsste am
       Bild vorbeiscrollen, um zu den Knöpfen zu kommen. Also legt sich die
       Abbildung hinter den Text, statt sich darunter zu stellen.

       Hier steht nur die Mechanik – was in der Medienspalte liegt, wird zur
       Fläche. WIE die Abbildung dabei aussehen muss, ist Sache des Projekts
       und steht in theme.css: Bei Sarah fällt zum Beispiel die Wortmarke aus
       dem Logo weg, weil zwei Textebenen übereinander kein Overlay der Welt
       auseinanderbekommt. */
    .hero .duo-media {
        position: absolute;          /* Bezug ist .hero-inner (position: relative) */
        inset: 0;
        display: flex;
        align-items: center;
        justify-content: center;
        z-index: 0;
        /* Für den Finger nicht vorhanden, sonst fängt die Fläche Tapser ab,
           die den Knöpfen darüber gelten. */
        pointer-events: none;
    }
    /* Die Abbildung darf breiter sein als die Spalte – als Flex-Kind würde sie
       sich sonst brav auf die Spaltenbreite zurückstauchen. `.hero` hat
       overflow: hidden und hält sie im Rahmen. */
    .hero .duo-media > * { flex-shrink: 0; }
    .hero-content { position: relative; z-index: 1; }

    /* Schleier zwischen Bild und Schrift. Sitzt am .duo-media und nicht am
       Bild, weil er darüber liegen muss. Die Farbe kommt als Token, weil sie
       zum Seitenhintergrund passen muss und der je Projekt anders ist. */
    .hero .duo-media::after {
        content: "";
        position: absolute;
        inset: 0;
        background: var(--hero-media-scrim, none);
    }

    /* Die Ortszeile läuft in --muted, und das ist der leiseste Ton im ganzen
       Theme. Auf glattem Creme trägt er gerade so; über einer Abbildung fällt
       er durch – nachgemessen 2,74:1 an der dunkelsten Stelle, gefordert sind
       4,5. Eine Stufe kräftiger sind es 4,8. Gilt nur hier, weil nur hier ein
       Bild dahinterliegt: Sobald die Zeile wieder auf glattem Grund steht,
       darf sie auch wieder zurücktreten. */
    .hero-meta { color: var(--text-soft); }
}

/* ---------- Prose ---------- */
.prose { max-width: 780px; }
/* Der erste Block sitzt am Abschnittsanfang – der Abstand kommt dort schon vom
   Abschnitt selbst. Ohne diese Zeile addiert sich der Vorlauf der Überschrift
   dazu und der Text rutscht sichtbar tiefer als in Abschnitten ohne Prose. */
.prose > :first-child { margin-top: 0; }
.prose h2 { margin: 2.2rem 0 .8rem; font-size: var(--fs-2xl); }
.prose p { margin: 0 0 1rem; color: var(--text-soft); }
.prose a { text-decoration: underline; }
.check-list { list-style: none; padding: 0; margin: 0 0 1rem; }
.check-list li { padding-left: 1.9rem; position: relative; margin-bottom: .6rem; color: var(--text-soft); }
.check-list li::before { content: "✓"; position: absolute; left: 0; color: var(--accent); font-weight: 700; }
/* Zweispaltig, sobald Platz ist (SAR-65). Neun Punkte untereinander sind auf
   einem breiten Schirm eine schmale Kolonne, die den Abschnitt in die Länge
   zieht.

   `columns` und nicht Grid: Der Umbruch soll sich nach der Länge der Punkte
   richten, nicht nach ihrer Anzahl. `break-inside` ist Pflicht – ohne die
   Zeile reißt der Browser einen zweizeiligen Punkt mitten durch und die
   zweite Hälfte steht ohne Haken oben in der nächsten Spalte. */
@media (min-width: 720px) {
    .check-list--two { columns: 2; column-gap: 2.2rem; }
    .check-list--two li { break-inside: avoid; }
}

/* Kurze Sätze, einzeln gesetzt (SAR-65, /neurodivergenz).
   Eine Liste ohne Zeichen davor: Der senkrechte Strich fasst sie zusammen,
   ohne jedem Punkt ein Symbol zu geben. Warum kein Haken, steht in der View
   bei der Liste. */
.beat-list {
    list-style: none; padding: 0 0 0 1.1rem; margin: 0 0 1rem;
    border-left: 3px solid var(--accent);
}
.beat-list li { color: var(--text-soft); margin-bottom: .35rem; }
.beat-list li:last-child { margin-bottom: 0; }

/* Der Weg zur Nachbarseite unter einem Abschnitt (SAR-65). Bewusst
   unauffällig: Er ist eine Fußnote, kein zweiter Aufruf zum Handeln – davon
   hat jede Seite genau einen, und der steht im Schlussband. */
.cross-link { margin: 1.6rem 0 0; color: var(--muted); font-size: var(--fs-sm); }
.cross-link a { font-weight: 500; text-decoration: underline; }

/* Deutlich sichtbarer Platzhalter-Hinweis (Demo-Inhalte) */
.placeholder-note {
    border: 2px dashed var(--border-strong); border-radius: var(--radius);
    padding: 1rem 1.2rem; margin: 0 0 1.6rem; color: var(--text-soft); font-size: var(--fs-md);
    background: var(--bg-secondary);
}
.placeholder-note strong { color: var(--text); font-family: var(--font-head); }

/* ---------- Flash ---------- */
.flash-wrap { margin-top: 1.2rem; }
.flash { padding: .9rem 1.2rem; border-radius: var(--radius-sm); margin-bottom: .8rem; font-weight: 500; border-left: 4px solid; }
.flash--success { background: var(--green-soft); color: var(--green-text); border-color: var(--green); }
.flash--error { background: var(--red-soft); color: var(--red-text); border-color: var(--red); }
.flash--info { background: var(--accent-soft); color: var(--accent-text); border-color: var(--accent); }

/* ---------- Fehler-/Erfolgsseiten ---------- */
.error-code { font-family: var(--font-head); font-size: var(--fs-display); font-weight: 700; margin: 0; background: var(--gradient-rainbow, none); -webkit-background-clip: text; background-clip: text; color: var(--accent); }
.error-code { -webkit-text-fill-color: transparent; }
@supports not (background-clip: text) { .error-code { -webkit-text-fill-color: currentColor; } }

/* =========================================================================
   WEGBEGLEITER  ·  Logo-Reihe unten auf der Startseite + die Unterseiten
   -------------------------------------------------------------------------
   Die Kachel trägt NUR das Logo (Begründung im Partial). Sie ist damit ein
   naher Verwandter der `.feature-card` weiter oben auf der Startseite und
   borgt sich deren Sprache: dieselbe Fläche, derselbe Rahmen, dieselbe
   farbige Kopfkante, dasselbe Anheben beim Überfahren. Bewusst geborgt und
   nicht dieselbe Klasse – eine `.feature-card` bringt Icon-, Überschrift- und
   Absatzregeln mit, von denen hier keine je greift, und die Farbrotation in
   theme.css hängt an `.card-grid > *`. Ein Logo-Kästchen ist keine Karte mit
   Text; es sieht nur so aus.

   Fremde Logos auf einer Seite, die vom Regenbogen lebt: Das Risiko ist, dass
   die Reihe unruhig wird, weil jedes Logo seine eigenen Farben, Proportionen
   und Ränder mitbringt. Dagegen hilft nur eines – jedem Logo denselben
   Rahmen und dieselbe HÖHE geben und die Breite laufen lassen. Ein Logo auf
   feste Breite zu zwingen staucht das nächste, das quadratisch ist.
   ========================================================================= */
/* FLEX UND KEIN GRID, seit hier am 20.08.2026 der zweite Wegbegleiter dazukam.
   Vorher stand hier `grid-template-columns: repeat(auto-fit, minmax(190px, 1fr))`
   und darunter `max-width: 290px` je Kachel. Mit einer Kachel sah das richtig
   aus, mit zweien fiel es auseinander: `auto-fit` legt die leeren Spalten
   zusammen und schenkt die frei gewordene Breite den belegten. Die beiden
   Spalten wurden 548 px breit, die Kacheln blieben bei ihren 290 px und
   standen links darin. Zwischen den Logos klaffte ein Loch von 258 px.

   Flex kennt keine leeren Spalten. Jede Kachel hat dieselbe Breite, darf
   schmaler werden, wenn der Platz nicht reicht, und die nächste steht direkt
   daneben.

   DIE REIHE STEHT MITTIG, seit sie am 20.08.2026 auf sechs Kacheln gewachsen
   ist und damit zum ersten Mal eine unvollständige zweite Zeile hatte. Links
   angeschlagen sah die kurze Zeile aus wie übrig geblieben; mittig sitzt sie
   unter der ersten und die Reihe wirkt gesetzt. Bei sechs Kacheln geht die
   Rechnung seit dem 21.08.2026 ohne Rest auf (3 + 3), die Regel bleibt
   trotzdem stehen – die nächste Anzahl hat den Fall wieder.

   Der linke Anschlag von früher galt einer EINZELNEN Kachel, die sonst in der
   Mitte einer 1100 px breiten Fläche schwamm. Sobald eine Zeile voll wird, ist
   zwischen links und mittig ohnehin kein Unterschied zu sehen. Wer je wieder
   bei einer oder zwei Kacheln landet, schaut noch mal hin. */
.partner-grid {
    list-style: none; padding: 0; margin: 0;
    display: flex; flex-wrap: wrap; justify-content: center;
    gap: 1.2rem;
}
/* ALLE KACHELN GLEICH BREIT, seit dem 21.08.2026 (Ticket SAR-75). Das ist der
   Kern: `flex-grow` steht auf 0, also wächst keine Kachel über ihre 290 px
   hinaus, egal wie viel Platz in ihrer Zeile übrig ist.

   Vorher stand hier `flex: 1 1 240px; max-width: 290px`, und genau die
   Kombination erzeugte zwei verschiedene Breiten auf einer Seite: Eine VOLLE
   Zeile teilt den Restplatz unter ihren Kacheln auf, eine KURZE Zeile hat für
   jede so viel übrig, dass sie an der Klemme von 290 px hängen bleibt.
   Nachgemessen bei sechs Wegbegleitern auf 1116 px Inhaltsbreite: oben vier
   Kacheln à 264,6 px, unten zwei à 290 px. Der Unterschied ist keine Laune des
   Browsers, sondern rechnet sich aus der Anzahl in der Zeile – deshalb hilft
   dagegen nur, das Wachsen ganz abzustellen.

   Der Preis dafür ist die Aufteilung: Bei fester Breite passen drei Kacheln
   nebeneinander und keine vier mehr (drei mal 290 plus Abstände sind 908 px,
   vier wären 1218 px). Aus 4 + 2 wird also 3 + 3 – zwei volle Zeilen, keine
   angebrochene. Für sechs Kacheln ist das der bessere Fall.

   SHRINK BLEIBT ERLAUBT (die 1 in der Mitte): Auf schmalen Geräten schrumpfen
   alle Kacheln gemeinsam und bleiben dabei gleich breit. Nur wachsen dürfen
   sie nicht.

   BEI GENAU VIER WEGBEGLEITERN NOCH EINMAL HINSEHEN: Dann ergibt diese Regel
   3 + 1, also die „vergessene Kachel", die `.card-grid--5` weiter oben abfängt.
   Der Ausweg wäre derselbe wie dort – ein Modifier, der für diese eine Anzahl
   die Basis senkt. Genau so einen gab es hier bis zum 21.08.2026 für fünf
   Kacheln (`.partner-grid--5`); er ist entfallen, weil er dasselbe tat, was
   jetzt für alle gilt. */
.partner-grid > li { flex: 0 1 290px; }

/* VIER STATT DREI PRO ZEILE, für die Anzahlen, bei denen drei nicht aufgehen.
   Ebenfalls SAR-75, und der zweite Teil davon: Gleich groß sind die Kacheln
   nach der Regel darüber IMMER, aber bei 4, 7 oder 10 Wegbegleitern bliebe
   nach vollen Dreierzeilen genau eine übrig – 3 + 1 und 3 + 3 + 1. Sie steht
   dann mittig und in voller Größe da, aber eben allein: dieselbe „vergessene
   Kachel", die `.card-grid--5` weiter oben abfängt.

   260 px ist die Breite, bei der genau vier nebeneinanderpassen und keine
   fünf: vier mal 260 plus drei Abstände sind 1097,6 px bei 1116 px
   Inhaltsbreite, fünf wären 1376,8 px. Damit wird aus 3 + 1 eine volle Zeile
   und aus 3 + 3 + 1 wird 4 + 3.

   NUR DIE BASIS ÄNDERT SICH, `flex-grow` bleibt bei 0 – sonst wäre genau der
   Fehler zurück, den der Block darüber behebt.

   WELCHE ANZAHL DIESEN MODIFIER BEKOMMT, entscheidet `partials/wegbegleiter.php`
   mit einer Zeile. Bei dreizehn Kacheln ginge auch das nicht auf (4 + 4 + 4 + 1);
   so weit ist die Liste nicht, und wer sie so weit füllt, sieht es hier. */
.partner-grid--cols-4 > li { flex: 0 1 260px; }

/* DIE HÖHE IST FEST UND NICHT GEWACHSEN, ebenfalls seit SAR-75.

   Hier stand `height: 100%; min-height: 116px`, und damit bestimmte das
   GRÖSSTE LOGO EINER ZEILE deren Höhe: Alle Kacheln einer Flex-Zeile strecken
   sich auf die höchste. Weil die Logos verschieden hoch sind – 46 px für eine
   Wortmarke, 80 px für eine blockige Marke, dazu 2 × 8 px Polsterung, wenn sie
   auf ihrer eigenen Platte steht –, hing die Zeilenhöhe davon ab, welche Marke
   zufällig in welcher Zeile landete. Nachgemessen: oben 137,6 px, unten
   153,6 px, weil in der zweiten Zeile mit Nils-Digital die einzige Marke
   steht, die blockig IST und eine Platte trägt (96 px).

   `--partner-logo-box` ist genau dieses größte Kästchen. Die Kachelhöhe rechnet
   sich daraus plus ihrer Polsterung, statt als abgeschriebene Zahl daneben zu
   stehen: Wer oben am Padding dreht, verschiebt die Höhe automatisch mit.

   KOMMT EIN LOGO DAZU, das höher als 96 px steht, gehört die Zahl geprüft –
   die Kachel wächst dann nicht mehr mit, sie schneidet ab (`overflow: hidden`
   eine Zeile weiter unten). Das ist der bewusste Tausch: eine Reihe, die
   garantiert gleich aussieht, gegen eine, die sich still verstellt. */
.partner-card {
    --partner-logo-box: 96px;
    position: relative; overflow: hidden;
    display: flex; align-items: center; justify-content: center;
    height: calc(var(--partner-logo-box) + 1.9rem + 1.6rem);
    padding: 1.9rem 1.5rem 1.6rem;
    background: var(--bg-card);
    border: 1px solid var(--border); border-radius: var(--radius);
    box-shadow: var(--shadow);
    text-decoration: none;
    transition: transform .18s, border-color .18s, box-shadow .18s;
}
/* Die farbige Kopfkante – dieselbe Kante, dieselben 6 px wie bei
   `.feature-card`. Die Farbe je Kachel setzt theme.css über --card-accent. */
.partner-card::before {
    content: ""; position: absolute; top: 0; left: 0; right: 0; height: 6px;
    background: var(--card-accent, var(--accent));
}
/* DER SATZ ZUM ÜBERFAHREN, seit dem 21.08.2026.
   Er liegt als Fläche über der ganzen Kachel und blendet ein, während das Logo
   ausblendet. Vier Entscheidungen dahinter:

   ER DECKT DIE KACHEL DECKEND AB (`--bg-card`, nicht halbtransparent): Was
   darunter liegt, ist bei jedem Wegbegleiter ein anderes Logo – mal eine helle
   Wortmarke, mal ein fast schwarzes Quadrat. Über einem durchscheinenden Grund
   wäre der Kontrast des Textes von der Marke abhängig und damit nicht zusagbar.

   DIE FARBIGE KOPFKANTE BLEIBT FREI (`top: 6px`): Sie ist das, was die Reihe
   zusammenhält, und sie zeigt beim Überfahren, welche Kachel gemeint ist.

   NUR AUF GERÄTEN MIT ECHTEM ZEIGER (`hover: hover` und `pointer: fine`). Auf
   einem Touchscreen gibt es kein Überfahren; dort löst `:hover` beim Tippen aus
   und bleibt danach kleben – die Kachel zeigt dann Text, wo eben noch das Logo
   stand, und niemand weiß, warum. Auf dem Handy passiert deshalb gar nichts.

   DER TASTATUR-FOKUS ZEIGT IHN AUCH, und der steht bewusst AUSSERHALB dieser
   Abfrage: Wer mit der Tabulatortaste durch die Reihe geht, tut das auch auf
   einem Gerät ohne Zeiger.

   Das Ein- und Ausblenden hängt am globalen Netz für `prefers-reduced-motion`
   ganz unten in dieser Datei (`transition-duration: .01ms`); es wird dann hart
   umgeschaltet statt geblendet. */
.partner-hint {
    position: absolute; top: 6px; right: 0; bottom: 0; left: 0;
    display: flex; align-items: center; justify-content: center;
    padding: 1rem 1.2rem;
    /* ZWEI LAGEN, und die untere ist der Grund der Kachel. `--card-accent-soft`
       ist bewusst fast durchsichtig – allein gesetzt läge der Text auf dem
       Logo. Der Verlauf aus einer einzigen Farbe ist der übliche Weg, eine
       Farbe als eigene Lage über eine andere zu legen. */
    background:
        linear-gradient(var(--card-accent-soft), var(--card-accent-soft)),
        var(--bg-card);
    /* Fredoka und nicht die Textschrift: Das hier ist ein Etikett, kein Satz,
       und die Seite setzt kurze Etiketten (Kartenzahlen, Fußzeilen-Titel)
       überall in der Überschriftenschrift. Die Farbe bleibt `--text` und nicht
       der Akzent – der ist bei Gelb auf hellem Grund nicht mehr lesbar. */
    color: var(--text);
    font-family: var(--font-head); font-weight: 600;
    font-size: var(--fs-md); line-height: 1.35;
    text-align: center; text-wrap: balance;
    opacity: 0; translate: 0 8px;
    transition: opacity .2s, translate .2s;
    pointer-events: none;
}
/* `scale` und nicht `transform`: Auf der Kachel darüber liegt beim Überfahren
   schon eine Verschiebung, und über `transform` würden die beiden einander
   auslöschen. Dieselbe Regel gilt auf der ganzen Seite. */
.partner-logo { transition: opacity .2s, scale .2s; }

.partner-card:focus-visible .partner-hint { opacity: 1; translate: 0 0; }
.partner-card:focus-visible .partner-logo { opacity: 0; scale: .94; }

@media (hover: hover) and (pointer: fine) {
    .partner-card:hover .partner-hint { opacity: 1; translate: 0 0; }
    .partner-card:hover .partner-logo { opacity: 0; scale: .94; }
}

.partner-card:hover,
.partner-card:focus-visible {
    transform: translateY(-5px);
    border-color: var(--card-accent, var(--accent));
    box-shadow: var(--shadow-lg);
    text-decoration: none;
}

/* Feste Höhe, freie Breite – das ist die Regel, an der die Reihe hängt.
   `object-fit: contain` sorgt dafür, dass ein Logo in seinem Kästchen bleibt,
   auch wenn die Datei quadratisch ist statt quer. */
.partner-logo {
    height: 46px; width: auto; max-width: 100%;
    object-fit: contain; object-position: center;
}

/* BLOCKIGE MARKEN BEKOMMEN MEHR HÖHE, seit am 20.08.2026 mit KE!N EINZELFALL
   e.V. das erste quadratische Logo dazukam.

   Gleiche Höhe ist die richtige Regel für Wortmarken, und bis dahin waren es
   nur Wortmarken. Sie funktioniert, weil ein breites Logo bei gleicher Höhe
   auch viel Fläche bekommt: Die Marke von Ankerliebe ist bei 46 px Höhe
   241 px breit. Ein Quadrat ist bei 46 px Höhe 46 px breit und damit ein
   Fünftel davon. Es stünde nicht ruhig daneben, sondern verloren.

   80 px ist der Ausgleich. Nach Fläche müssten es eher 100 px sein, dann
   sprengt das Quadrat aber die Kachel und zieht die ganze Reihe in die Höhe.
   Bei 80 px wächst die Kachel von 116 auf 136 px, alle gleich, weil sie im
   selben Flex-Container stehen.

   Welche Datei „blockig" ist, entscheidet `Partners::hasBlockLogo()` an ihren
   Maßen, nicht die Seite. Sonst müsste man beim nächsten Logo daran denken.

   Was 80 px NICHT reparieren: Die Schrift im grünen Band bleibt zu klein zum
   Lesen. Das ist die Marke selbst, sie steht auf ihrer eigenen Seite genauso
   klein im Kopf. Deshalb trägt das Logo den Vereinsnamen im alt-Text – dort
   ist er vollständig da, auch wenn das Bild ihn verschluckt. */
.partner-logo--block { height: 80px; }

/* EIN LOGO AUF SEINEM EIGENEN GRUND, seit am 20.08.2026 moooov dazukam.
   Ihre Wortmarke ist ein helles Mintgrün und für den dunklen Hintergrund
   gezeichnet, auf dem sie auf der eigenen Seite steht. Auf der weißen Kachel
   käme sie auf 1,4:1 und wäre ein blasser Schemen; auf der cremefarbenen
   Platte, die a11y.css im dunklen Modus unterlegt, genauso.

   Die Platte ist deshalb nicht hell, sondern trägt die Farbe des Partners
   (`--logo-plate` aus Partners.php). Darauf steht die Marke bei 10:1.

   Dieselbe Idee wie die helle Platte in a11y.css, nur andersherum, und
   dieselbe Regel gilt: so groß wie nötig und keinen Millimeter größer. Die
   Höhe meint weiterhin das LOGO, deshalb `content-box` – die Seite rechnet
   sonst global mit `border-box`, und die Polsterung fräße das Logo von 46 auf
   30 px herunter. */
.partner-logo--plate,
.partner-head-logo--plate {
    box-sizing: content-box;
    background: var(--logo-plate, #1F2B37);
    border-radius: 10px;
    padding: 8px 12px;
}
/* DIE PLATTE MUSS IHRE EIGENE POLSTERUNG MITRECHNEN, seit am 21.08.2026 mit der
   Wortmarke von Johannes Springer das erste breite Logo eine Platte bekam
   (SAR-60). `max-width: 100%` weiter oben begrenzt bei `content-box` nur das
   BILD; die 2 × 12 px der Platte kommen obendrauf. Nachgemessen stand die
   Platte dadurch 12,8 px vom Kachelrand statt der 24 px, die alle anderen
   Logos einhalten – sie sah aus, als wäre sie zu groß geraten.

   Die schmalen Platten (moooov, Nils-Digital) merken davon nichts, sie
   erreichen die Grenze gar nicht. */
.partner-logo--plate { max-width: calc(100% - 24px); }

/* --- Kopf der Unterseite ------------------------------------------------
   Etwas größer als in der Kachel, aber immer noch klein genug, dass die
   Überschrift darunter die Seite trägt und nicht das fremde Logo. */
.partner-head-logo { height: 58px; width: auto; max-width: 100%; object-fit: contain; object-position: left center; display: block; margin-bottom: 1rem; }
/* Dasselbe Verhältnis wie in der Kachel: eine Spur größer als die Wortmarke
   daneben, damit beide gleich schwer wiegen. Die Überschrift trägt die Seite
   trotzdem, sie steht direkt darunter und ist mehr als doppelt so hoch. */
.partner-head-logo--block { height: 96px; margin-bottom: .8rem; }

/* Der Rückweg über dem Seitentitel. Bewusst leise und klein: Er ist eine
   Hilfe, keine Navigation. */
/* `--muted` steht hier nicht, obwohl es nach der leisesten Farbe klingt: Der
   Wert ist auf den Seitengrund gerechnet (4,76:1) und rutscht auf der
   abgesetzten Fläche des Seitenkopfs auf 4,34:1, also unter die geforderten
   4,5:1. `--text-soft` schafft dort 7,09:1 und ist immer noch ruhiger als
   Fließtext. */
.back-link { margin: 0 0 .8rem; font-size: var(--fs-sm); }
.back-link a { color: var(--text-soft); text-decoration: none; }
.back-link a:hover { color: var(--accent); text-decoration: underline; }

/* --- Herkunftsangabe am Fuß einer Wegbegleiter-Seite --------------------
   Auf diesen Seiten stehen fremde Angaben, also gehört dazu, woher sie
   stammen und wie alt sie sind. Der Satz stand bis zum 20.08.2026 als
   `<p class="muted">` mit zwei Inline-Styles in einer eigenen `.section` mit
   `padding-top: 0`. Das ergab genau das, was Kevin an der Ankerliebe-Seite
   aufgefallen ist: ein schmaler grauer Textblock, der oben an der Kante der
   Sektion darüber klebt und unter dem eine halbe Bildschirmhöhe leer bleibt.
   Er sah nicht aus wie eine Fußnote, sondern wie ein vergessener Absatz.

   Zwei Dinge machen daraus eine Fußnote: die Haarlinie darüber, die ihn vom
   Inhalt trennt statt ihn daran zu kleben, und ein Abstand, der nach unten
   kürzer ist als nach oben. Damit endet die Seite mit ihm, statt ihn liegen
   zu lassen.

   DER ABSTAND NACH OBEN STECKT IM ABSATZ, nicht im Abschnitt darum herum
   (Kevin, 20.08.2026). Der Grund ist der Hintergrund: Die Fußnote muss auf
   DERSELBEN Fläche stehen wie das Modul über ihr, sonst sieht sie aus wie
   angeklebt. Auf der Sander-Seite steht davor ein `.cta-band`, und das hat
   gar keinen eigenen Hintergrund – der Seitengrund läuft durch, die Fußnote
   gehört sichtbar dazu. Auf der Ankerliebe-Seite ist das Modul davor ein
   `.section--alt` mit eigener Fläche und Trennlinie; eine Fußnote in einem
   neuen Abschnitt dahinter beginnt hinter dieser Linie und wirkt wie ein
   Nachtrag von jemand anderem. Sie steht deshalb IM `.section--alt` drin.

   Weil der Absatz seinen Abstand selbst mitbringt, funktioniert er an beiden
   Stellen gleich: als letztes Kind eines Moduls oder in `.section--footnote`
   für die Seiten, die ihn allein tragen. */
.section--footnote { padding: 0 0 clamp(2rem, 3vw, 2.6rem); }
.source-note {
    max-width: 64ch; margin: clamp(2.2rem, 4vw, 3rem) 0 0;
    padding-top: 1.3rem;
    border-top: 1px solid var(--border);
    font-size: var(--fs-xs); line-height: 1.75;
    color: var(--muted);
}

/* --- Filialen ----------------------------------------------------------
   Adressblöcke nebeneinander. Kein Kasten drumherum: Acht gerahmte Kärtchen
   sähen aus wie acht Angebote; hier stehen acht Adressen. Der kurze farbige
   Strich links reicht als Trennung und holt die Palette in den Abschnitt. */
.branch-grid {
    list-style: none; padding: 0; margin: 0;
    display: grid; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
    gap: 1.4rem;
}
.branch {
    display: flex; flex-direction: column; gap: .15rem;
    padding-left: .9rem;
    border-left: 3px solid var(--card-accent, var(--accent));
    color: var(--text-soft); font-size: var(--fs-sm); line-height: 1.55;
}
.branch strong { color: var(--text); font-family: var(--font-head); font-size: var(--fs-md); margin-bottom: .2rem; }
.branch a { color: var(--accent-text, var(--accent)); }
/* Dieselbe Rotation wie bei `.facts` – sieben Farben, dann von vorn. */
.branch:nth-child(7n+1) { --card-accent: var(--c-red); }
.branch:nth-child(7n+2) { --card-accent: var(--c-orange); }
.branch:nth-child(7n+3) { --card-accent: var(--c-yellow); }
.branch:nth-child(7n+4) { --card-accent: var(--c-green); }
.branch:nth-child(7n+5) { --card-accent: var(--c-teal); }
.branch:nth-child(7n+6) { --card-accent: var(--c-blue); }
.branch:nth-child(7n+7) { --card-accent: var(--c-violet); }

/* =========================================================================
   ND-CREDIT-BAND
   Sichtbarer Urheberhinweis am Seitenende. Hebt sich vom Kundeninhalt ab,
   ohne ihn zu überstrahlen – deshalb ruhige Fläche statt Akzentfarbe und
   erst NACH dem Abschluss-CTA der Kundin.
   ========================================================================= */
.nd-credit-band { padding: 0 0 clamp(2.5rem, 5vw, 4rem); }
.nd-credit {
    position: relative; overflow: hidden;
    display: grid; grid-template-columns: auto 1fr auto; align-items: center;
    gap: 1.2rem 1.8rem;
    padding: clamp(1.4rem, 3vw, 2rem) clamp(1.4rem, 3vw, 2.2rem);
    border: 1px solid var(--border); border-radius: var(--radius);
    background: var(--bg-secondary);
}
.nd-credit::before {
    content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 5px;
    background: var(--gradient-rainbow-v, var(--accent));
}
.nd-credit-brand { display: flex; align-items: center; gap: .7rem; color: var(--text); text-decoration: none; }
.nd-credit-brand:hover { color: var(--accent); }
.nd-credit-mark { width: 52px; height: auto; flex-shrink: 0; }
.nd-credit-word {
    font-family: var(--font-head); font-weight: 700; font-size: var(--fs-lg);
    letter-spacing: .2px; white-space: nowrap;
}
.nd-credit-word::after { content: "."; color: var(--accent); }
.nd-credit-text h2 { margin: 0 0 .25rem; font-size: var(--fs-md); }
.nd-credit-text p { margin: 0; color: var(--text-soft); font-size: var(--fs-md); line-height: 1.55; max-width: 56ch; }
.nd-credit-actions { display: flex; flex-direction: column; align-items: flex-end; gap: .5rem; white-space: nowrap; }
.nd-credit-actions .link-more { font-size: var(--fs-xs); }

@media (max-width: 860px) {
    .nd-credit { grid-template-columns: auto 1fr; }
    .nd-credit-actions { grid-column: 1 / -1; flex-direction: row; align-items: center; justify-content: flex-start; gap: 1rem; }
}
@media (max-width: 520px) {
    .nd-credit { grid-template-columns: 1fr; }
    .nd-credit-text h2 { font-size: var(--fs-md); }
}

/* ---------- Footer (Stil: Nils-Digital) ---------- */
.site-footer { background: var(--bg-footer); color: var(--footer-muted); padding: 3.2rem 0 1.6rem; margin-top: auto; }
.site-footer h3 { color: var(--footer-text); }
/* Auf dunklem Grund braucht die Unterzeile den hellen Akzent, nicht den dunklen */
.site-footer .brand-sub, .admin-sidebar .brand-sub { color: var(--footer-accent); }
/* DIE SPALTEN RICHTEN SICH NACH DEN KINDERN UND NICHT NACH EINER ZAHL
   (SAR-94, 23.08.2026). Hier stand `1.7fr 1fr 1fr 1fr`, also vier feste
   Spalten – gebaut für Marke, „Über mich", „Fahrstunden" und „Direkt
   erreichen".

   SEIT SAR-54 GIBT ES DIE SPALTE „FAHRSTUNDEN" NUR NOCH BEI EINGESCHALTETER
   TERMINPLANUNG, und die ist aus. Im Fuß standen damit drei Blöcke in einem
   Raster für vier: Die vierte Spalte blieb leer am rechten Rand stehen, und
   die drei anderen drängten sich auf zwei Dritteln der Breite. Die Adresse
   `sarah@fahrlehrerinsarah.de` passte dadurch nicht mehr in ihre Zeile und
   brach mitten im Wort um (`overflow-wrap: anywhere` weiter oben, gedacht als
   Notnagel für schmale Handys). Das Ticket SAR-54 nahm an, das Raster sei
   `auto-fit` und rechne selbst nach – das war es nie.

   `grid-auto-flow: column` MACHT DIE ANNAHME WAHR: Ausgeschrieben ist nur die
   erste Spalte, jedes weitere Kind legt sich seine eigene an. Drei Blöcke
   ergeben drei Spalten, vier ergeben vier, ganz ohne Zählen im Template. Ein
   `repeat(auto-fit, …)` täte dasselbe, verträgt sich aber nicht mit dem `fr`
   der ersten Spalte: Die ganze Regel wäre ungültig und der Fuß einspaltig.

   JEDE SPALTE IST SO BREIT WIE IHR INHALT, verteilt werden sie mit
   `space-between`. Der Weg über Bruchteile (`1fr`) war der erste Versuch und
   sah schlechter aus: Die Marke ist auf 320 px begrenzt (`.footer-brand`) und
   ließ in ihrer Spalte über 100 px leer, während die Linkspalten auf je
   400 px auseinandergezogen wurden. Die Abstände zwischen den drei Gruppen
   waren dadurch verschieden groß. So stehen sie in gleichen Abständen, die
   erste Spalte an der linken Kante des Containers, die letzte an der rechten
   – dieselben Kanten wie in jeder Sektion darüber.

   `minmax(0, max-content)` UND NICHT NUR `max-content`: Der zweite Wert ist
   die Wunschbreite, die Null davor die Erlaubnis, schmaler zu werden. Ohne
   sie stünde die Zeile fest, und käme die vierte Gruppe zurück, liefe der Fuß
   zwischen 880 und 1000 px aus dem Container heraus. So rücken die Spalten
   zusammen und die Adressen brechen notfalls um, statt überzulaufen.

   Der Abstand ist von 2 auf 3 rem gewachsen: Er ist jetzt der einzige Platz
   zwischen zwei Gruppen, vorher lag zusätzlich die leere Hälfte einer
   1fr-Spalte dazwischen. */
.footer-grid {
    display: grid;
    grid-template-columns: minmax(0, max-content);
    grid-auto-flow: column;
    grid-auto-columns: minmax(0, max-content);
    justify-content: space-between;
    gap: 2rem 3rem;
    padding-bottom: 2rem;
    border-bottom: 1px solid var(--footer-border);
}
.footer-grid h3 { font-family: var(--font-head); font-size: var(--fs-sm); text-transform: uppercase; letter-spacing: 1.5px; margin: 0 0 1.1rem; }
.footer-grid .brand-mark { color: var(--footer-text); }
.footer-tagline { font-size: var(--fs-sm); margin-top: 1rem; max-width: 320px; }

/* ---------- Wortmarke im Fuß (SAR-49) ----------
   Der Inhalt von Sarahs Logo als Text, seit die Bilddatei dort raus ist
   (Begründung im Partial). Drei Zeilen in der Reihenfolge des Logos: die
   Berufsbezeichnung klein darüber, der Name groß, die Klassen darunter.

   Alle drei Farben kommen aus den Fuß-Tokens und nicht aus den normalen:
   Der Fuß ist die einzige dunkle Fläche im hellen Schema, `--text` und
   `--muted` sind für den Creme-Grund gerechnet und wären hier unlesbar.
   14,14:1 für den Namen, 6,95:1 für die Berufsbezeichnung, 7,29:1 für die
   Klassenzeile. */
.footer-brand { max-width: 320px; }
.footer-wordmark { margin: 0; display: flex; flex-direction: column; align-items: flex-start; gap: .1rem; }
/* Der Name in einer Zeile, der Regenbogenstrich davor. Er sitzt eine Stufe
   über `.hero-eyebrow`: `--fs-sm` statt `--fs-xs` und die helle Fußfarbe statt
   der leisen. Beides muss so sein, denn hier ist die Zeile nicht die
   Beschriftung von etwas Größerem, sondern das Größte im Block. Bliebe sie auf
   Augenbrauen-Maß, wäre die Klassenzeile darunter der auffälligste Text der
   Spalte, und die Marke stünde kleiner da als ihre eigene Fußnote. */
.footer-wordmark-name {
    display: inline-flex; align-items: center; gap: .6rem;
    color: var(--footer-text);
    font-family: var(--font-head); font-size: var(--fs-sm); font-weight: 600;
    text-transform: uppercase; letter-spacing: 3px;
}
/* Derselbe Regenbogenstrich wie bei `.hero-eyebrow`, gleiche Maße. */
.footer-wordmark-name::before {
    content: ""; width: 28px; height: 3px; border-radius: 2px; flex-shrink: 0;
    background: var(--gradient-rainbow, var(--footer-accent));
}
.footer-wordmark-claims {
    margin-top: .45rem;
    color: var(--footer-accent);
    font-family: var(--font-head); font-size: var(--fs-sm); font-weight: 500;
    line-height: 1.55;
}
.footer-wordmark-claims .sep { color: var(--footer-muted); }
.footer-links { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: .6rem; }
.footer-links a { color: var(--footer-muted); font-size: var(--fs-sm); }
.footer-links a:hover { color: var(--footer-accent); text-decoration: none; }
.footer-bottom { padding-top: 1.6rem; display: flex; flex-direction: column; align-items: center; gap: .8rem; text-align: center; }
.footer-legal { display: flex; flex-wrap: wrap; justify-content: center; gap: .2rem .1rem; font-size: var(--fs-xs); }
.footer-legal a { color: var(--footer-muted); padding: 0 .55rem; }
.footer-legal a:hover { color: var(--footer-accent); text-decoration: none; }
.footer-legal .sep { color: var(--footer-border); }
.footer-copy { font-size: var(--fs-xs); color: var(--footer-muted); margin: 0; }
/* Die Zeile „Gemacht mit ♥ von Nils-Digital" IM Fuß ist am 17.08.2026
   entfallen (SAR-32) – ihre Aufgabe übernimmt der Banner unter dem Fuß, siehe
   unten. Die Regeln bleiben, weil .footer-credit zur ND-Signatur gehört und in
   anderen Kundenprojekten im Fuß steht. */
.footer-credit {
    display: inline-flex; align-items: center; gap: .6rem;
    font-size: var(--fs-xs); margin: 0; color: var(--footer-muted); text-decoration: none;
}
.footer-credit:hover { color: var(--footer-text); }
.footer-credit strong { color: var(--footer-accent); font-family: var(--font-head); font-weight: 600; }
.footer-credit-mark { width: 30px; height: auto; flex-shrink: 0; }
.footer-credit .heart { color: var(--red); }

/* ---------- Nils-Digital-Banner (unter dem Fuß) ----------
   Ein schmaler Streifen ganz am Ende, auf jeder öffentlichen Seite.

   Der HELLE GRUND ist der ganze Trick: Der Fuß darüber ist dunkel, hier wird
   es wieder hell. Diese Kante ist die Aussage – oberhalb endet Sarahs Seite,
   was darunter steht, ist die Signatur dessen, der sie gebaut hat. Ein
   dunkler Streifen unter einem dunklen Fuß sähe dagegen aus wie ein
   abgerutschtes Stück Fuß.

   `margin-top: 0` gegen den Fuß und KEIN `margin-top: auto`: Das steht am
   `.site-footer` und schiebt ihn auf kurzen Seiten nach unten. Stünde es hier
   noch einmal, risse es den Banner vom Fuß los und ließe eine Lücke. */
.nd-banner {
    background: var(--bg);
    border-top: 1px solid var(--border);
    padding: 1.1rem 0;
    font-size: var(--fs-xs);
}
.nd-banner-inner {
    display: flex; align-items: center; justify-content: space-between;
    gap: .7rem 1.6rem; flex-wrap: wrap;
}
.nd-banner-brand {
    display: inline-flex; align-items: center; gap: .6rem;
    color: var(--muted); text-decoration: none;
}
.nd-banner-brand:hover { color: var(--text); }
.nd-banner-brand strong { color: var(--accent-text); font-family: var(--font-head); font-weight: 600; }
.nd-banner-mark { width: 26px; height: auto; flex-shrink: 0; }
.nd-banner-more { color: var(--muted); text-decoration: none; white-space: nowrap; font-family: var(--font-head); }
.nd-banner-more:hover { color: var(--accent-text); }

/* Unter ~520 px stehen die beiden Teile untereinander statt an den Rändern
   auseinandergezogen – „space-between" bei zwei umgebrochenen Zeilen sieht
   aus, als wäre etwas verrutscht. */
@media (max-width: 520px) {
    .nd-banner-inner { flex-direction: column; align-items: flex-start; }
}

/* =========================================================================
   Admin
   ========================================================================= */
.admin-body { display: grid; grid-template-columns: 256px 1fr; min-height: 100vh; background: var(--bg); }
.admin-sidebar { background: var(--bg-footer); color: var(--footer-muted); padding: 1.6rem 1.1rem; display: flex; flex-direction: column; position: sticky; top: 0; height: 100vh; }
.admin-sidebar .brand-mark { color: var(--footer-text); }
/* Das Logo steht mittig über der Navigation, nicht links angeschlagen: es ist
   breiter als die Navigationspunkte und wirkt linksbündig wie verrutscht.
   Die Trennlinie darunter macht aus Marke und Menü zwei ruhige Blöcke. */
.admin-brand {
    display: flex; flex-direction: column; align-items: center; text-align: center;
    gap: .45rem; margin-bottom: 1.5rem; padding: .4rem 0 1.3rem;
    border-bottom: 1px solid var(--footer-border);
}
.admin-brand-logo { width: 172px; max-width: 100%; height: auto; display: block; margin: 0 auto; }
.admin-sidebar .brand-sub { letter-spacing: .04em; }

.admin-nav { display: flex; flex-direction: column; gap: .25rem; flex: 1; }
.admin-nav a { color: var(--footer-muted); padding: .72rem .95rem; border-radius: var(--radius-sm); font-weight: 500; font-family: var(--font-head); display: flex; align-items: center; justify-content: space-between; gap: .6rem; }
.admin-nav a:hover { background: var(--footer-hover); color: var(--footer-text); text-decoration: none; }
.admin-nav a.is-active { background: var(--accent); color: var(--on-accent); }
.nav-badge {
    background: var(--accent); color: var(--on-accent); font-size: var(--fs-xs); font-weight: 700;
    min-width: 1.35rem; height: 1.35rem; padding: 0 .35rem; border-radius: 999px;
    display: inline-flex; align-items: center; justify-content: center; line-height: 1;
}
/* Auf der aktiven, bereits eingefärbten Fläche würde die Plakette verschwinden */
.admin-nav a.is-active .nav-badge { background: var(--on-accent); color: var(--accent-text); }
/* DER GEIST-KNOPF AUF DER DUNKLEN LEISTE.
   `.btn-ghost` nimmt `color: var(--text)` und `border-color: var(--border-strong)`,
   und beide sind für den hellen Seitengrund gerechnet. In der Seitenleiste der
   Schaltzentrale liegen sie auf `--bg-footer`, also auf derselben Farbe wie die
   Schrift: Der Knopf „Abmelden" stand dort unsichtbar, dunkel auf dunkel, samt
   unsichtbarem Rand. Gefunden beim Nachmessen am 19.08.2026; der Fehler ist
   älter und hat mit dem Farbwechsel nichts zu tun.

   Hier deshalb die Fuß-Tokens, die es für genau diesen Fall gibt. Sie färben
   sich in jedem Kontrastmodus mit. */
.admin-sidebar .btn-ghost { color: var(--footer-text); border-color: var(--footer-border); }
.admin-sidebar .btn-ghost:hover { color: var(--footer-accent); border-color: var(--footer-accent); }

.admin-sidebar-foot { display: flex; flex-direction: column; gap: .8rem; border-top: 1px solid var(--footer-border); padding-top: 1.1rem; }
.admin-sidebar-foot a { color: var(--footer-muted); font-size: var(--fs-xs); }
.admin-sidebar-foot a:hover { color: var(--footer-accent); text-decoration: none; }

.admin-main { padding: 0; min-width: 0; }
.admin-topbar { position: sticky; top: 0; z-index: 40; background: var(--bg-secondary); border-bottom: 1px solid var(--border); padding: 1.2rem 2rem; display: flex; align-items: center; justify-content: space-between; gap: 1rem; }
.admin-topbar h1 { font-size: var(--fs-2xl); margin: 0; }
.admin-topbar-left { display: flex; align-items: center; gap: .9rem; min-width: 0; }
.admin-topbar-right { display: flex; align-items: center; gap: 1rem; flex-shrink: 0; }
.admin-user { color: var(--muted); font-size: var(--fs-xs); }

/* Glocke in der Topbar: der Weg zum Posteingang, wenn die Sidebar auf dem
   Handy zugeklappt ist. */
.topbar-bell { position: relative; display: inline-flex; padding: .35rem; border-radius: var(--radius-sm); color: var(--muted); }
.topbar-bell svg { width: 22px; height: 22px; display: block; }
.topbar-bell:hover { color: var(--text); background: var(--row-hover); }
.topbar-bell.has-unread { color: var(--accent-text); }
.bell-dot {
    position: absolute; top: -2px; right: -4px; background: var(--red); color: var(--on-danger);
    font-size: var(--fs-xs); font-weight: 700; font-family: var(--font-head); line-height: 1;
    min-width: 1.05rem; padding: .2rem .25rem; border-radius: 999px; text-align: center;
    border: 2px solid var(--bg-secondary);
}
.admin-nav-toggle { display: none; flex-direction: column; gap: 5px; background: none; border: 0; cursor: pointer; padding: 8px; flex-shrink: 0; }
.admin-nav-toggle span { width: 26px; height: 2px; background: var(--text); border-radius: 2px; transition: .2s; }
.admin-backdrop { display: none; position: fixed; inset: 0; background: var(--backdrop); z-index: 55; border: 0; padding: 0; cursor: pointer; }
.admin-backdrop:not([hidden]) { display: block; }
.admin-content { padding: 2rem; }
.admin-content .flash-wrap { margin-top: 0; margin-bottom: 1rem; }
/* Im Admin füllt der Inhalt die Fläche – kein zusätzlich zentrierter Container */
.admin-content .container { max-width: none; padding: 0; }

.admin-card { background: var(--bg-card); border: 1px solid var(--border); border-radius: var(--radius); padding: 1.6rem; box-shadow: var(--shadow); margin-bottom: 1.5rem; }
.admin-card-head { display: flex; align-items: center; justify-content: space-between; gap: 1rem; margin-bottom: 1.2rem; flex-wrap: wrap; }
.admin-page-head { margin-bottom: 1.5rem; }
.admin-card-head h2, .admin-card h3 { font-size: var(--fs-xl); margin: 0; }

.stat-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); gap: 1.2rem; margin-bottom: 1.5rem; }
.stat-card { position: relative; overflow: hidden; background: var(--bg-card); border: 1px solid var(--border); border-radius: var(--radius); padding: 1.5rem; box-shadow: var(--shadow); display: flex; flex-direction: column; transition: transform .15s, border-color .15s; }
.stat-card::before { content: ""; position: absolute; top: 0; left: 0; right: 0; height: 5px; background: var(--card-accent, var(--accent)); }
.stat-card:hover { transform: translateY(-3px); text-decoration: none; border-color: var(--card-accent, var(--accent)); }
.stat-value { font-family: var(--font-head); font-size: var(--fs-stat); font-weight: 700; color: var(--card-accent, var(--accent)); line-height: 1; }
.stat-label { font-family: var(--font-head); font-weight: 500; color: var(--text); margin-top: .5rem; }
.stat-meta { font-size: var(--fs-xs); color: var(--muted); margin-top: .25rem; }

/* Begrüßungsband über dem Dashboard: Datum, Lage des Tages, Schnellzugriff.
   Steht bewusst vor den Zahlen – wer zwischen zwei Fahrstunden reinschaut,
   will erst wissen, was ansteht, und dann Statistik. */
.admin-hero {
    position: relative; overflow: hidden;
    background: var(--bg-card); border: 1px solid var(--border); border-radius: var(--radius);
    box-shadow: var(--shadow); padding: 1.6rem 1.6rem 1.6rem 2rem; margin-bottom: 1.5rem;
    display: flex; align-items: center; justify-content: space-between; gap: 1.5rem; flex-wrap: wrap;
}
.admin-hero::before { content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 6px; background: var(--gradient-rainbow-v, var(--accent)); }
.admin-hero-text { min-width: 0; }
.admin-hero-date { font-family: var(--font-body); font-size: var(--fs-xs); letter-spacing: .06em; text-transform: uppercase; color: var(--muted); margin: 0 0 .3rem; }
.admin-hero h2 { font-size: var(--fs-2xl); margin: 0 0 .35rem; }
.admin-hero-line { margin: 0; color: var(--text-soft); max-width: 52ch; }
.admin-hero-line strong { color: var(--accent-text); }

/* Zwei gleich starke Spalten (Tagesplan + Ausblick), ab schmal untereinander */
.admin-cols { display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); gap: 1.5rem; align-items: start; margin-bottom: 1.5rem; }
/* Der Abstand kommt vom Raster – die eigene Karten-Marge würde ihn verdoppeln */
.admin-cols > .admin-card { margin-bottom: 0; }

/* Karte, die Aufmerksamkeit will – für ungelesene Meldungen */
.card-alert { border-color: var(--accent); box-shadow: 0 0 0 4px var(--accent-soft), var(--shadow); }

/* -------------------------------------------------------------------------
   Tagesplan – eine Zeile je Zeitfenster
   ------------------------------------------------------------------------- */
.day-plan { list-style: none; margin: 0; padding: 0; }
.day-row {
    display: grid; grid-template-columns: 3.6rem 1fr auto; align-items: center; gap: .9rem;
    padding: .7rem .2rem .7rem .9rem; border-bottom: 1px solid var(--border);
    border-left: 3px solid transparent; border-radius: 2px;
}
.day-row:last-child { border-bottom: 0; }
.day-time { font-family: var(--font-body); font-size: var(--fs-sm); font-weight: 500; color: var(--text); line-height: 1.25; }
.day-time-sub { font-size: var(--fs-xs); color: var(--muted); }
.day-info { display: flex; flex-direction: column; min-width: 0; }
.day-info strong { font-family: var(--font-head); font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.day-info .muted { font-size: var(--fs-xs); }
.day-phone { display: inline-flex; padding: .45rem; border-radius: 999px; color: var(--muted); }
.day-phone svg { width: 18px; height: 18px; }
.day-phone:hover { background: var(--row-hover); color: var(--accent-text); }

/* Der Status färbt nur den Strich links – die Zeile bleibt ruhig lesbar */
.day-row.status-gebucht  { border-left-color: var(--green); }
.day-row.status-frei     { border-left-color: var(--border-strong); }
.day-row.status-gesperrt { border-left-color: var(--red); }
.day-row.status-frei .day-info strong,
.day-row.status-gesperrt .day-info strong { color: var(--muted); font-weight: 400; }
.day-row.is-past { opacity: .5; }

/* -------------------------------------------------------------------------
   Fortschritt (Pflichtfahrten)
   Der Balken zeigt einen Stand, keinen Ladevorgang – deshalb ruhig, ohne
   Animation und mit der Zahl daneben. Die Zahl ist die eigentliche Auskunft,
   der Balken macht sie nur auf einen Blick vergleichbar.
   ------------------------------------------------------------------------- */
.progress-list { list-style: none; margin: 0 0 1.2rem; padding: 0; }
.progress-item + .progress-item { margin-top: 1.1rem; }
.progress-head { display: flex; align-items: baseline; justify-content: space-between; gap: 1rem; margin-bottom: .4rem; }
.progress-head span { font-family: var(--font-head); font-weight: 500; }
.progress-head strong { font-family: var(--font-body); font-size: var(--fs-sm); color: var(--text-soft); display: inline-flex; align-items: center; gap: .35rem; }
.progress-done { display: inline-flex; color: var(--green); }
.progress-done svg { width: 15px; height: 15px; }
.progress-track { height: 10px; border-radius: 999px; background: var(--row-hover); overflow: hidden; }
.progress-bar { height: 100%; border-radius: 999px; background: var(--progress-accent, var(--accent)); }
.progress-meta { margin: .35rem 0 0; font-size: var(--fs-xs); color: var(--muted); }

/* Schlichte Datenliste: Begriff links, Wert rechts */
.detail-list { display: grid; grid-template-columns: auto 1fr; gap: .5rem 1.2rem; margin: 0 0 1.3rem; }
.detail-list dt { font-size: var(--fs-xs); text-transform: uppercase; letter-spacing: .5px; color: var(--muted); font-family: var(--font-head); align-self: center; }
.detail-list dd { margin: 0; align-self: center; }
.pin-dots { letter-spacing: .3em; font-size: var(--fs-lg); color: var(--text); vertical-align: middle; margin-right: .5rem; }

/* Zuweisen-Auswahl direkt im Kalender – schmal, damit sie neben die
   anderen Knöpfe einer Terminzeile passt */
.slot-assign select {
    width: 100%; font-family: var(--font-head); font-size: var(--fs-xs); padding: .4rem .5rem;
    border: 1px solid var(--border-strong); border-radius: var(--radius-sm);
    background: var(--bg-card); color: var(--text-soft);
}
.slot-assign select:hover { border-color: var(--accent); color: var(--text); }

/* -------------------------------------------------------------------------
   Ereignisliste (Posteingang, "Zuletzt passiert")
   ------------------------------------------------------------------------- */
.feed { list-style: none; margin: 0; padding: 0; }
.feed-item {
    display: grid; grid-template-columns: auto 1fr auto; align-items: start; gap: .9rem;
    padding: .95rem .2rem; border-bottom: 1px solid var(--border);
}
.feed-item:last-child { border-bottom: 0; }
.feed-compact .feed-item { padding: .6rem .2rem; align-items: center; }
.feed-dot { width: 10px; height: 10px; border-radius: 999px; margin-top: .42rem; flex-shrink: 0; background: var(--feed-accent, var(--accent)); }
.feed-compact .feed-dot { margin-top: 0; }
.feed-body { min-width: 0; }
.feed-title { margin: 0; font-family: var(--font-head); font-weight: 500; display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; }
.feed-meta { margin: .3rem 0 0; font-size: var(--fs-xs); color: var(--text-soft); display: flex; align-items: center; gap: .45rem; flex-wrap: wrap; }
.feed-meta s { color: var(--muted); }
.feed-foot { margin: .3rem 0 0; font-size: var(--fs-xs); color: var(--muted); }
.feed-time { font-size: var(--fs-xs); color: var(--muted); white-space: nowrap; }
.feed-action { align-self: center; }
/* Ungelesenes wird angehoben, nicht eingefärbt: die Farbe gehört dem Ereignis */
.feed-item.is-new { background: var(--accent-soft); border-radius: var(--radius-sm); padding-left: .8rem; padding-right: .8rem; }

.data-table { width: 100%; border-collapse: collapse; }
.data-table th, .data-table td { text-align: left; padding: .85rem .7rem; border-bottom: 1px solid var(--border); vertical-align: middle; }
.data-table th { font-family: var(--font-head); font-size: var(--fs-xs); text-transform: uppercase; letter-spacing: .5px; color: var(--muted); }
.data-table tbody tr:last-child td { border-bottom: 0; }
.data-table tbody tr:hover { background: var(--row-hover); }
.cell-actions { white-space: nowrap; }
.cell-actions .btn { vertical-align: middle; }
.cell-actions .inline-form { margin-left: .4rem; }

.inline-form { display: inline-block; margin: 0; }

.pill { display: inline-block; padding: .28rem .65rem; border-radius: 999px; font-size: var(--fs-xs); font-weight: 600; font-family: var(--font-head); }
.pill-neutral { background: var(--row-hover); color: var(--muted); }
.pill-accent { background: var(--accent-soft); color: var(--accent-text); }
.pill-success { background: var(--green-soft); color: var(--green-text); }
.pill-warning { background: var(--yellow-soft); color: var(--yellow-text); }
.pill-danger { background: var(--red-soft); color: var(--red-text); }

/* Einmalig angezeigte Zugangsdaten (PIN) */
.credential {
    display: inline-block; font-family: var(--font-body); font-size: var(--fs-2xl); font-weight: 700;
    letter-spacing: .35em; padding: .6rem 1.2rem; border-radius: var(--radius-sm);
    background: var(--accent-soft); color: var(--accent-text); border: 1px dashed var(--accent);
}

.quick-actions { display: flex; gap: .8rem; flex-wrap: wrap; }
.empty-state { text-align: center; padding: 2.5rem 1rem; color: var(--muted); }
.empty-state p { margin: 0 0 1.2rem; }

/* Admin-Login */
.admin-auth-body { background: var(--page-head-bg); }
.admin-auth { min-height: 100vh; display: flex; align-items: center; justify-content: center; padding: 1.5rem; }
.login-card { width: 100%; max-width: 410px; }
.login-head { text-align: center; margin-bottom: 1rem; }
.login-head .brand { flex-direction: column; align-items: center; gap: .4rem; }
.login-head .brand-mark { font-size: var(--fs-2xl); }
.login-logo { width: 190px; height: auto; display: block; }
.login-card h1 { text-align: center; font-size: var(--fs-2xl); margin: 0 0 1.5rem; }
.login-back { display: block; text-align: center; margin-top: 1.3rem; font-size: var(--fs-xs); }

/* =========================================================================
   BEWEGTER HINTERGRUND  ·  .page-aurora
   -------------------------------------------------------------------------
   Eine feststehende Ebene hinter der ganzen Seite: ein paar sehr weiche
   Farbwolken, die langsam ihre Bahn ziehen. Der Inhalt scrollt darüber
   hinweg, der Hintergrund bleibt – dadurch wirkt die Seite in Bewegung,
   ohne dass irgendetwas blinkt oder springt.

   Drei Entscheidungen, die zusammengehören:
   · Die Wolken sind radiale Farbverläufe, KEINE Elemente mit `filter: blur()`.
     Ein Blur über eine halbe Bildschirmdiagonale muss der Browser in jedem
     Animationsschritt neu rechnen; ein Verlauf wird einmal gerastert und
     danach nur noch verschoben.
   · Animiert wird ausschließlich `transform` – das läuft im Compositor und
     kostet kein Layout.
   · Die Farben kommen als Token (--aurora-1 … -4) aus theme.css und sind dort
     bewusst sehr blass. Der Hintergrund ist Stimmung, keine Fläche: Über ihm
     muss Fließtext ohne Nachdenken lesbar bleiben.

   Eingebunden wird die Ebene nur im öffentlichen Layout (app/Views/layout.php).
   Im Admin wäre sie Zierrat vor einem Werkzeug.
   ========================================================================= */
.page-aurora {
    position: fixed;
    inset: 0;
    z-index: -1;
    overflow: hidden;
    pointer-events: none;
    contain: layout paint;
}
.page-aurora span {
    position: absolute;
    display: block;
    width: 62vmax;
    height: 62vmax;
    border-radius: 50%;
    background: radial-gradient(closest-side, var(--aurora-tint, transparent), transparent);
}

/* Vier Wolken, vier unterschiedliche Laufzeiten. Die Zahlen sind absichtlich
   keine Vielfachen voneinander (37/43/53/61 s) – sonst treffen sich alle vier
   regelmäßig in der Ausgangslage und die Schleife wird sichtbar. */
.page-aurora span:nth-child(1) { --aurora-tint: var(--aurora-1); top: -22vmax; left: -14vmax; }
.page-aurora span:nth-child(2) { --aurora-tint: var(--aurora-2); top: 18vmax;  right: -20vmax; }
.page-aurora span:nth-child(3) { --aurora-tint: var(--aurora-3); bottom: -24vmax; left: 12vmax; }
.page-aurora span:nth-child(4) { --aurora-tint: var(--aurora-4); bottom: 4vmax; right: 6vmax; }

@media (prefers-reduced-motion: no-preference) {
    .page-aurora span:nth-child(1) { animation: auroraA 37s ease-in-out infinite; }
    .page-aurora span:nth-child(2) { animation: auroraB 43s ease-in-out infinite; }
    .page-aurora span:nth-child(3) { animation: auroraC 53s ease-in-out infinite; }
    .page-aurora span:nth-child(4) { animation: auroraD 61s ease-in-out infinite; }
}

@keyframes auroraA {
    0%, 100% { transform: translate3d(0, 0, 0) scale(1); }
    50%      { transform: translate3d(7vw, 5vh, 0) scale(1.18); }
}
@keyframes auroraB {
    0%, 100% { transform: translate3d(0, 0, 0) scale(1.1); }
    50%      { transform: translate3d(-6vw, 8vh, 0) scale(.92); }
}
@keyframes auroraC {
    0%, 100% { transform: translate3d(0, 0, 0) scale(.95); }
    50%      { transform: translate3d(9vw, -6vh, 0) scale(1.15); }
}
@keyframes auroraD {
    0%, 100% { transform: translate3d(0, 0, 0) scale(1.05); }
    50%      { transform: translate3d(-8vw, -4vh, 0) scale(.9); }
}

/* =========================================================================
   HERO-AUFTRITT  ·  einmal beim Laden
   -------------------------------------------------------------------------
   Das Bild fällt von oben in den Hero, der Text steigt darunter auf. Beides
   sind reine CSS-Animationen mit `both` als Füllmodus: Sie laufen ohne
   JavaScript, und der Startzustand gilt schon vor der Verzögerung – sonst
   blitzte der Inhalt kurz an seiner Endposition auf.

   Der Hero ist deshalb vom Scroll-Reveal weiter unten ausgenommen (main.js
   überspringt alles innerhalb von .hero). Zwei Systeme auf demselben
   Element würden sich um `opacity` streiten – und eine Überschrift, die
   sich bei jedem Zurückscrollen neu aufbaut, wird schnell zur Zumutung.
   Der Auftritt gehört zum Ankommen auf der Seite, nicht zum Scrollen.
   ========================================================================= */
.hero .duo-media { animation: heroDrop 1s cubic-bezier(.25, .8, .3, 1) both; }
.hero-content    { animation: heroRise .8s cubic-bezier(.22, .61, .36, 1) .18s both; }

/* Der kurze Überschwung bei 62 % ist der Unterschied zwischen „fällt" und
   „wird eingeblendet": Das Bild schießt ein Stück zu tief und federt zurück.
   .hero hat `overflow: hidden`, es fällt also hinter der Oberkante hervor. */
@keyframes heroDrop {
    0%   { opacity: 0; transform: translate3d(0, -90px, 0) scale(.97); }
    62%  { opacity: 1; transform: translate3d(0, 14px, 0)  scale(1.01); }
    100% { opacity: 1; transform: none; }
}
@keyframes heroRise {
    from { opacity: 0; transform: translate3d(0, 24px, 0); }
    to   { opacity: 1; transform: none; }
}

/* --- Getippte Überschrift ------------------------------------------------
   Sichtbar wird sie erst, wenn main.js sie übernimmt (siehe <noscript> im
   Layout). Der Cursor ist ein ::after am <h1> und verschwindet mit der
   Klasse, sobald der Satz steht – ein dauerhaft blinkender Balken neben
   einer fertigen Überschrift sieht aus wie ein hängengebliebenes Skript. */
[data-typewriter] { visibility: hidden; }
.is-typing::after {
    content: "";
    display: inline-block;
    width: .5ch;
    height: .82em;
    margin-left: .1em;
    /* DER CURSOR ZÄHLT NICHT ZUR ZEILENBREITE, seit dem 21.08.2026 (SAR-80).
       Der negative Rand rechts nimmt genau das zurück, was er breit ist –
       gezeichnet wird er weiterhin, er schiebt nur nichts mehr.

       Vorher schob er: Die Überschrift der Startseite steht auf dem Desktop
       in einer Zeile (`white-space: nowrap`) und spannt ihre Rasterspalte
       selbst auf. Reicht der Text bis an den Rand – bei „Führerschein-
       Ausbildung" tut er das –, kommt der Cursor obendrauf, die Spalte wird
       breiter und die Bildspalte daneben schmaler. Im Moment des letzten
       Zeichens verschwindet er, und das Bild springt zurück. Genau das
       „Wackeln", das an einem hohen Hero-Bild auffällt (Kevin, 21.08.2026).

       Das ist der zweite Teil des Fehlers. Der erste sitzt in main.js: Dort
       hält der Typewriter jetzt Höhe UND Breite fest, solange getippt wird.
       Beides zusammen ergibt eine Spalte, die sich während der Animation
       nicht mehr bewegt – eines allein reicht nicht, sie greifen an
       verschiedenen Enden desselben Problems an.

       Der Balken ragt dadurch bis zu einem halben Zeichen über das Zeilenende
       hinaus. Im Hero ist dort die Lücke zur Bildspalte (mindestens 2 rem),
       auf dem Handy der Innenabstand des Containers – nachgemessen entsteht
       nirgends ein waagerechter Scrollbalken. */
    margin-right: calc(-.5ch - .1em);
    vertical-align: -.04em;
    border-radius: 2px;
    background: var(--accent);
    animation: caretBlink .75s steps(1) infinite;
}
@keyframes caretBlink {
    0%, 49%   { opacity: 1; }
    50%, 100% { opacity: 0; }
}

/* =========================================================================
   SCROLL-REVEAL  ·  .reveal
   -------------------------------------------------------------------------
   Inhalte gleiten beim Scrollen herein und wieder hinaus. Die Klassen setzt
   ausschließlich JavaScript (main.js) – und genau deshalb steht hier
   `opacity: 0`, ohne dass jemand ohne JS vor einer leeren Seite sitzt: Wer
   kein JavaScript hat, bekommt die Klasse nie und sieht alles sofort.

   Bewegt wird über die eigenständigen Eigenschaften `translate` und `scale`,
   NICHT über `transform`. Der Grund ist handfest: Auf denselben Elementen
   liegen bereits Transformationen – die Karten heben beim Hover an
   (`.feature-card:hover`), die Fotorahmen stehen schief (`.photo`). Über
   `transform` würde sich beides gegenseitig überschreiben, mit `translate`
   laufen Einblendung und Bestandseffekt unabhängig nebeneinander.

   --reveal-delay staffelt Karten innerhalb eines Rasters. JS setzt den Wert
   beim Hereinkommen und nimmt ihn beim Hinausgehen wieder weg, damit die
   Staffelung nur in eine Richtung wirkt.
   ========================================================================= */
.reveal {
    opacity: 0;
    transition:
        opacity   .75s cubic-bezier(.22, .61, .36, 1) var(--reveal-delay, 0ms),
        translate .75s cubic-bezier(.22, .61, .36, 1) var(--reveal-delay, 0ms),
        scale     .75s cubic-bezier(.22, .61, .36, 1) var(--reveal-delay, 0ms);
}
.reveal--up    { translate: 0 2.2rem; }
.reveal--left  { translate: -3rem 0; }
.reveal--right { translate: 3rem 0; }
.reveal--zoom  { scale: .94; }

.reveal.is-in {
    opacity: 1;
    translate: none;
    scale: none;
}

/* =========================================================================
   Responsive
   ========================================================================= */
@media (max-width: 1080px) {
    .week-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

@media (max-width: 980px) {
    .process { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    /* Die Verbindungslinie eines Schrittes zeigt auf den nächsten und ragt
       dafür in den Spaltenabstand hinein. Am ZEILENENDE gibt es kein Nächstes
       mehr – dort muss sie kurz bleiben, sonst zeigt sie über den Rand hinaus
       auf den Schritt eine Zeile tiefer. Bei zwei Spalten endet jeder GERADE
       Schritt eine Zeile; mit `nth-child(2)` galt das nur für den ersten davon
       und stimmte deshalb nur bei genau vier Schritten. */
    .process-step:nth-child(2n)::after { width: calc(100% - 54px); }
}

@media (max-width: 760px) {
    .week-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 480px) {
    .week-grid { grid-template-columns: 1fr; }
}

@media (max-width: 880px) {
    .split-grid, .split-grid--wide-left { grid-template-columns: 1fr; }
    /* Der Markenblock ist deutlich höher als eine Linkspalte – nebeneinander
       bleibt daneben ein leeres Feld stehen. Deshalb quer über die volle
       Breite, die drei Linkgruppen darunter nebeneinander. */
    /* `grid-auto-flow: row` hebt die Spaltenlogik von oben wieder auf: Die
       Marke liegt hier als eigene Zeile über den Linkgruppen.

       DIE GRUPPEN DARUNTER ZÄHLT `auto-fit` SELBST AB (SAR-94): Drei feste
       Spalten standen hier bis zum 23.08.2026, und zwischen 700 und 880 px
       waren die rund 215 px breit – zu schmal für
       `sarah@fahrlehrerinsarah.de`, die Adresse brach wieder mitten im Wort.
       Mit 230 px Mindestbreite werden daraus von selbst zwei Spalten, sobald
       drei nicht mehr passen. */
    .footer-grid { grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); grid-auto-flow: row; }
    .footer-grid > :first-child { grid-column: 1 / -1; }
    .admin-body { grid-template-columns: 1fr; }
    /* Sidebar wird zum Slide-in-Drawer, per Hamburger im Topbar geöffnet */
    .admin-sidebar {
        position: fixed; top: 0; left: 0; bottom: 0; height: 100%;
        width: 272px; max-width: 82vw; z-index: 60; overflow-y: auto;
        transform: translateX(-100%); transition: transform .25s ease;
        box-shadow: var(--shadow-lg);
    }
    .admin-sidebar.is-open { transform: translateX(0); }
    .admin-nav-toggle { display: flex; }
    body.admin-nav-open { overflow: hidden; }
}

/* --- DIE NAVIGATION KLAPPT BEI 1120 PX ZUSAMMEN, NICHT ERST BEI 740 -------
   Die Grenze ist keine runde Zahl, sondern gemessen an der längsten Fassung
   der Zeile. Die Navigation ist Flex ohne `nowrap`; wird sie zu breit, läuft
   sie nicht quer heraus, sondern „Fahren mit Handicap" bricht um und der
   Header wird zweizeilig. Das sieht nach Fehler aus, ist aber leise – deshalb
   ist es lange niemandem aufgefallen.

   Gerechnet mit Roboto Mono (0.6 em Vorschub) bei --fs-sm 15 px und 13,6 px
   Innenabstand je Seite am Link, dazu Logo (92 px), Header-Gap (16 px) und
   Container-Rand (2 × 22 px):

     abgemeldet   Startseite … Termine          ~730 px  ->  ab  ~882 px frei
     angemeldet   … Meine Stunden, Abmelden,
                  Stunde eintragen              ~952 px  ->  ab ~1104 px frei

   Maßgebend ist die ANGEMELDETE Fassung – sie ist die längere, und wer
   angemeldet ist, ist Fahrschüler:in und damit der häufigere Fall auf dieser
   Seite. 1120 statt 1104, damit ein bisschen Luft bleibt.

   Der angemeldete Umbruch ist übrigens ÄLTER als der Menüpunkt „Startseite"
   (SAR-48). Nachgemessen an der Fassung davor: Bei 1024 px stand die Zeile noch,
   bei 900 px brachen VIER Punkte intern um – „Über mich", „Fahren mit Handicap",
   „Meine Stunden" und „Stunde eintragen" standen zweizeilig. Der neue Punkt hat
   den Umbruch also nicht verursacht, sondern nur so weit nach oben geschoben,
   dass er auffiel.

   WICHTIG: Diese Grenze gilt NUR für den Header. Alles andere am Handy-Layout
   steht weiter bei 740 px im Block darunter. Wer beides zusammenlegt, macht
   das Preisraster und die Schrittfolge schon auf dem Laptop einspaltig.

   Wer hier Menüpunkte umbenennt, verschiebt die Grenze mit – kürzere Namen
   dürfen sie senken, längere müssen sie heben.

   SEIT SAR-54 (21.08.2026) IST DAS MENÜ VIEL KÜRZER: „Anmelden", „Termine"
   und der ganze angemeldete Zustand hängen am Schalter `booking.public` und
   sind derzeit aus. Übrig sind vier Punkte, nachgemessen 526 px; mit Logo,
   Gap und Container-Rand wären ab rund 680 px alle nebeneinander frei – die
   Grenze könnte also auf die 740 des übrigen Handy-Layouts fallen und der
   Header hätte auf Tablets wieder eine echte Zeile statt des Hamburgers.

   ABSICHTLICH NICHT GEMACHT: Diese Grenze weiß nichts von dem Schalter. Wer
   die Terminplanung zurückholt, legt eine Zeile in der `.env` um – und hätte
   dann ein zweizeiliges Menü, ohne zu ahnen, dass es an einem CSS-Breakpoint
   liegt. Die 1120 sind auf die LANGE Fassung gerechnet und bleiben stehen,
   bis entschieden ist, ob die Planung wiederkommt.

   SAR-65 HAT DIE ZEILE VERKÜRZT, nicht verlängert, obwohl eine Seite
   dazugekommen ist: Aus „Fahren mit Handicap" (19 Zeichen) wurde die Gruppe
   „Schwerpunkte" (12), und die zweite Seite hängt im Untermenü statt in der
   Zeile. Nach derselben Rechnung wie oben sind das 7 × 9 px weniger, das
   Dreieck daneben kostet rund 14 px zurück – netto etwa 50 px kürzer, in
   beiden Fassungen gleichermaßen. Die Grenze bleibt trotzdem bei 1120: Sie
   hatte oben schon Luft, und ein Breakpoint, der bei jeder Umbenennung
   nachgeführt wird, ist irgendwann auf nichts mehr gerechnet.

   WAS DAS UNTERMENÜ HIER UNTEN TUT, steht am Ende dieses Blocks: Es klappt
   nicht auf, sondern steht eingerückt offen da. */
@media (max-width: 1120px) {
    .nav-toggle { display: flex; }

    /* --- Die Marke steht oben in der Mitte --------------------------------
       Sobald die Navigation zusammengeklappt ist, trägt der Header nur noch
       Logo und Hamburger. Links in der Ecke sieht das große Logo neben der
       leeren Fläche daneben aus wie verrutscht – also steht es beim Öffnen
       der Seite mittig und wandert beim Scrollen auf seinen Platz nach links,
       während es schrumpft.

       Absolut positioniert und nicht per Flex zentriert, weil die Bewegung
       laufen und nicht springen soll: `left` lässt sich animieren, ein
       `margin: auto` nicht. Bezug ist der Header selbst (er ist `fixed`),
       damit die Mitte die echte Bildschirmmitte ist und nicht die Mitte des
       Restplatzes neben dem Hamburger. Der landet dafür per `flex-end`
       rechts – er ist jetzt das einzige Kind im Fluss.

       Hier steckte bis SAR-37 (17.08.2026) die meiste Mechanik: Die Wortmarke
       durfte oben KEINE Breite halten (anders als auf dem Desktop), sonst saß
       nicht das Logo in der Mitte, sondern das Logo samt unsichtbarem Text
       daneben. Dasselbe galt für den Flex-Gap – der zählt zur Breite, auch
       wenn das Element daneben null breit ist, und schob das Logo nachgemessen
       13 px nach links. Beim Umschalten klappte der Text dann per `max-width`
       auf, mit Verzögerung, damit er sich nicht an die noch große Bildmarke
       drängte.

       Ohne den Text braucht die Marke nichts davon mehr: Sie hat genau ein
       Kind, also gibt es keine fremde Breite und keinen Gap zu kompensieren.
       Übrig bleibt die Bewegung selbst – von der Mitte an den linken Rand,
       während das Logo schrumpft. Genau die sollte bleiben. */
    .header-inner { justify-content: flex-end; }
    .brand {
        position: absolute; top: 0; height: 100%;
        left: 50%; translate: -50% 0;
        transition: left .34s cubic-bezier(.22, .61, .36, 1),
                    translate .34s cubic-bezier(.22, .61, .36, 1);
    }
    .site-header.is-compact .brand { left: var(--container-pad, 22px); translate: 0 0; }

    .main-nav {
        /* 100% statt fester 72px: Der Header wechselt seine Höhe, das Menü
           muss an seiner Unterkante hängen bleiben. */
        position: absolute; top: 100%; left: 0; right: 0; background: var(--bg-card);
        flex-direction: column; align-items: stretch; padding: .8rem 22px 1.2rem; gap: .2rem;
        box-shadow: var(--shadow-lg); border-bottom: 1px solid var(--border); display: none;
    }
    .main-nav.is-open { display: flex; }
    .nav-link { padding: .8rem .5rem; }
    .nav-cta { margin: .5rem 0 0; }

    /* --- „Schwerpunkte" ist hier KEIN Untermenü mehr (SAR-65) --------------
       Im aufgeklappten Hamburger stehen die beiden Seiten einfach eingerückt
       untereinander. Ein Menü im Menü wäre auf dem Handy ein zweiter
       Bedienschritt für zwei Links – und ausgerechnet auf der Seite, die
       davon handelt, Abläufe einfach zu halten.

       Der Knopf bleibt trotzdem stehen und wird zur Überschrift: Er ordnet
       die beiden ein. Er ist per `pointer-events: none` stillgelegt, damit
       niemand auf etwas tippt, das nichts tut – die Ansage bleibt für
       Vorlesesoftware erhalten, weil das Element im Baum bleibt. Sein
       Dreieck ist weg, es verspräche ein Aufklappen, das es hier nicht
       gibt. */
    .nav-group { position: static; }
    .nav-group-toggle {
        pointer-events: none; width: 100%; justify-content: flex-start;
        text-transform: uppercase; letter-spacing: 1.5px; font-size: var(--fs-xs);
        color: var(--muted); padding-bottom: .2rem;
    }
    .nav-group-toggle.is-active { color: var(--muted); }
    .nav-caret { display: none; }
    .nav-submenu {
        position: static; opacity: 1; visibility: visible; translate: 0 0;
        min-width: 0; padding: 0 0 0 .9rem; margin: 0;
        background: none; border: 0; box-shadow: none;
        border-left: 2px solid var(--border);
        transition: none;
    }
}

/* Ab hier das eigentliche Handy-Layout. Es hing nie am Header und bleibt
   deshalb bei 740 px stehen. */
@media (max-width: 740px) {
    .form-row { grid-template-columns: 1fr; }
    .hero-badges { grid-template-columns: 1fr; }
    .price-row { grid-template-columns: 1fr; gap: .8rem; }
    .price-row-price { text-align: left; }
    .section-head { flex-direction: column; align-items: flex-start; gap: .6rem; }
    .process { grid-template-columns: 1fr; gap: 1.7rem; }
    .process-num { width: 44px; height: 44px; font-size: var(--fs-lg); margin-bottom: .9rem; }
    /* Strich wird vertikal und sitzt links neben dem Inhalt.

       DIE AUSNAHMEN MÜSSEN HIER MITGENANNT WERDEN, und das ist kein Schmuck:
       Der 980er-Block darüber kürzt den Strich am Zeilenende über
       `:nth-child(2n)`. Dieser Selektor ist SPEZIFISCHER als `.process-step`
       allein – seine Breite gewinnt also auch hier unten, obwohl dieser Block
       später steht. Wer ihn nicht ausdrücklich überschreibt, bekommt bei jedem
       geraden Schritt ein Element, das die Breite der Karte hat (aus der
       980er-Regel) und ihre Höhe (`top`/`bottom` von hier): ein farbiger
       Balken quer über dem Text.

       Genau das war seit dem Wechsel von `:nth-child(2)` auf `:nth-child(2n)`
       der Fall und fiel als grüne Fläche über Schritt 4 auf (Kevin, SAR-73,
       21.08.2026) – bei Schritt 2 nicht, der stand hier schon namentlich.
       Vier ist grün, weil die Farben in Siebenerschritten rotieren.

       Kurz: Jeder Selektor, der oben die Breite anfasst, gehört hier in die
       Liste. */
    .process-step { padding-left: 1.25rem; padding-right: 0; }
    .process-step::after,
    .process-step:nth-child(2n)::after,
    .process-step:last-child::after { top: 6px; bottom: 6px; left: 0; width: 3px; height: auto; background: linear-gradient(180deg, var(--card-accent, var(--accent)), transparent); }
    .appointment { grid-template-columns: auto 1fr; }
    .appointment-actions { grid-column: 1 / -1; justify-content: flex-start; }
    .admin-content { padding: 1.2rem; }
    /* Begrüßung und Schnellzugriff untereinander, Buttons über die volle Breite */
    .admin-hero { flex-direction: column; align-items: stretch; padding: 1.3rem 1.3rem 1.3rem 1.6rem; }
    .admin-hero h2 { font-size: var(--fs-xl); }
    .quick-actions .btn { flex: 1 1 auto; }
    .day-row { grid-template-columns: 3.2rem 1fr auto; gap: .7rem; padding-left: .7rem; }
    .feed-item { gap: .7rem; }
    /* Der Zeitstempel rutscht unter den Text, sonst quetscht er die Zeile */
    .feed-time { grid-column: 2; grid-row: 2; }
    .admin-topbar { padding: 1rem 1.2rem; }
    .admin-topbar h1 { font-size: var(--fs-lg); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
}

@media (max-width: 560px) {
    .admin-user { display: none; }

    /* Hier passen die Hero-Knöpfe nicht mehr nebeneinander. Untereinander und
       mittig fällt jede unterschiedliche Breite auf – also alle gleich breit.
       Nebenbei sind sie so auch mit dem Daumen sicher zu treffen. */
    .hero-actions { flex-direction: column; align-items: stretch; }
}

/* Schmaler Footer: eine Spalte, alles auf Mitte. Zwei gequetschte Spalten
   nebeneinander lesen sich auf dem Handy wie eine abgerissene Liste – und der
   Fuß der Seite ist ohnehin schon mittig (.footer-bottom). */
@media (max-width: 600px) {
    .footer-grid { grid-template-columns: 1fr; gap: 2.2rem; text-align: center; }
    /* Der Fuß steht hier in einer Spalte und mittig. Die Wortmarke ist ein
       Flex-Stapel, `text-align` allein rückt sie deshalb nicht. */
    .footer-brand { max-width: none; }
    .footer-wordmark { align-items: center; }
    .footer-tagline { max-width: 36ch; margin-inline: auto; }
    .footer-links { align-items: center; }
    .footer-grid h3 { margin-bottom: .8rem; }
}

/* Auf schmalen Screens teilen sich Logo, Wortmarke und Hamburger 100 % Breite –
   das Logo gibt zuerst nach, damit der Name lesbar bleibt. */
@media (max-width: 420px) {
    .brand-mark { font-size: var(--fs-lg); }
    .brand-sub { font-size: var(--fs-xs); letter-spacing: 1.8px; }
}

/* Admin-Tabellen werden auf kleinen Screens zu gestapelten Karten,
   damit nichts (v.a. die Aktions-Buttons) aus dem Container läuft. */
@media (max-width: 680px) {
    .admin-card { padding: 1.1rem; }
    .data-table thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
    .data-table, .data-table tbody, .data-table tr, .data-table td { display: block; width: 100%; }
    .data-table tr {
        border: 1px solid var(--border); border-radius: var(--radius-sm);
        padding: .35rem 1rem; margin-bottom: 1rem; background: var(--bg);
    }
    .data-table tbody tr:last-child { margin-bottom: 0; }
    .data-table tbody tr:hover { background: var(--bg); }
    .data-table td {
        border-bottom: 1px solid var(--border); padding: .7rem 0;
        display: flex; align-items: center; justify-content: space-between; gap: 1rem;
    }
    .data-table tr td:last-child { border-bottom: 0; }
    .data-table td::before {
        content: attr(data-label); flex-shrink: 0;
        font-family: var(--font-head); font-size: var(--fs-xs); font-weight: 600;
        text-transform: uppercase; letter-spacing: .5px; color: var(--muted);
    }
    .data-table td[data-label]:empty { display: none; }
    .cell-actions { flex-direction: column; align-items: stretch; gap: .5rem; white-space: normal; }
    .cell-actions::before { align-self: flex-start; }
    .cell-actions .btn { width: 100%; }
    .cell-actions .inline-form { display: block; margin-left: 0; }
    .data-table td > * { min-width: 0; }
    .data-table td a { overflow-wrap: anywhere; }
}

@media (max-width: 500px) {
    .footer-grid { grid-template-columns: 1fr; }
    .cta-inner { flex-direction: column; align-items: flex-start; }
    .hero h1 { font-size: var(--fs-hero-sm); }
    .hero-content { max-width: 100%; }
    .hero-lead { font-size: var(--fs-md); }
    .appointment { grid-template-columns: 1fr; }
    .appointment-date { flex-direction: row; gap: .5rem; min-width: 0; align-self: flex-start; padding: .4rem .9rem; }
}

/* Bewegung nur, wenn gewünscht */
@media (prefers-reduced-motion: reduce) {
    * { animation-duration: .01ms !important; animation-iteration-count: 1 !important; transition-duration: .01ms !important; scroll-behavior: auto !important; }
    .btn:hover, .feature-card:hover, .hero-badge:hover, .stat-card:hover { transform: none; }

    /* main.js startet das Scroll-Reveal bei dieser Einstellung erst gar nicht.
       Diese Regel ist das Netz darunter: Wer die Einstellung erst nach dem
       Laden umlegt, soll nicht vor halb eingeblendeten Absätzen sitzen. */
    .reveal { opacity: 1 !important; translate: none !important; scale: none !important; }
}

/* =========================================================================
   VIDEO-VORSCHAUEN  ·  .video-feed / .video-grid / .video-card     (SAR-129)
   -------------------------------------------------------------------------
   Hochformat-Kacheln mit Abspielzeichen, jede ein Link auf die Plattform.
   Nur Tokens, keine festen Farben – das Abspielzeichen trägt die Fläche der
   Leitfarbe (`--accent-surface`), weil es ein Bedienelement ist.

   Das Anheben beim Überfahren sitzt am BILD, nicht am <li>: Das <li> ist
   der Block, den das Scroll-Reveal über `translate` bewegt. Beides auf
   demselben Element würde sich gegenseitig überschreiben.
   ========================================================================= */
.video-feed { margin-top: clamp(2.4rem, 5vw, 3.6rem); }
/* DREI KACHELN, IMMER EINE REIHE (Nils, 22.09.2026: höchstens drei Videos).
   Über die volle Containerbreite wäre jede Kachel im Hochformat über 600 px
   hoch – drei Handybildschirme nebeneinander, die den Abschnitt erschlagen.
   Deshalb hält der Block eine eigene Breite und steht mittig.
   Auf dem Handy bleibt es bei drei Spalten statt zwei: Zwei plus eine
   verwaiste dritte Kachel sähe nach Fehler aus. */
.video-feed { max-width: 860px; margin-inline: auto; }
.video-feed-head {
    display: flex; align-items: baseline; justify-content: space-between;
    gap: .6rem 1.2rem; flex-wrap: wrap; margin-bottom: 1.2rem;
}
.video-feed-head h3 { margin: 0; }

.video-grid {
    list-style: none; margin: 0; padding: 0;
    display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: clamp(.9rem, 2vw, 1.5rem);
}
.video-card a { display: flex; flex-direction: column; gap: .45rem; color: var(--text); text-decoration: none; }
.video-card-thumb {
    position: relative; display: block; aspect-ratio: 9 / 16;
    border-radius: var(--radius); overflow: hidden;
    background: var(--bg-secondary); box-shadow: var(--shadow);
    transition: translate .2s ease, box-shadow .2s ease;
}
.video-card-thumb img { display: block; width: 100%; height: 100%; object-fit: cover; }
.video-card-play {
    /* Unten links und nicht in der Mitte: Auf TikTok-Titelbildern steht der
       eingeblendete Text fast immer mittig – ein Knopf dort verdeckt genau
       das Wort, das das Video ankündigt. */
    position: absolute; left: .7rem; bottom: .7rem;
    display: grid; place-items: center;
    width: 44px; height: 44px; border-radius: 50%;
    background: var(--accent-surface); color: var(--on-accent-surface);
    box-shadow: var(--shadow);
    transition: background .2s ease;
}
.video-card-play svg { width: 22px; height: 22px; margin-left: 2px; }
.video-card a:hover .video-card-thumb,
.video-card a:focus-visible .video-card-thumb { translate: 0 -4px; box-shadow: var(--shadow-lg); }
.video-card a:hover .video-card-play { background: var(--accent-surface-hover); }
.video-card a:focus-visible { outline: none; }
.video-card a:focus-visible .video-card-thumb { outline: 3px solid var(--focus-ring); outline-offset: 3px; }
.video-card-title {
    font-family: var(--font-head); font-weight: 600; font-size: var(--fs-sm); line-height: 1.35;
    display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3; line-clamp: 3; overflow: hidden;
}
.video-card-meta { color: var(--muted); font-size: var(--fs-xs); }

@media (max-width: 559px) {
    .video-grid { gap: .6rem; }
    .video-card-title { font-size: var(--fs-xs); -webkit-line-clamp: 2; line-clamp: 2; }
    .video-card-play { left: .45rem; bottom: .45rem; width: 34px; height: 34px; }
    .video-card-play svg { width: 18px; height: 18px; }
}
@media (prefers-reduced-motion: reduce) {
    .video-card-thumb { transition: none; }
    .video-card a:hover .video-card-thumb,
    .video-card a:focus-visible .video-card-thumb { translate: none; }
}
