/* Design-Token liegen in tokens.css und werden davor geladen. */

/* Skip-Link (WCAG 2.4.1): liegt als erstes Element im Body, sitzt aber über dem
   Viewport und kommt nur bei Tastaturfokus herunter. Ziel ist #main.
   Er landet auf dem Navy-Kopfband, deshalb trägt er die eine regelkonforme
   cyane Fläche der App — mit Navy-Schrift, weil Weiß darauf 2,1:1 wäre. */
.skip-link {
  position: absolute; left: var(--space-2); top: calc(-1 * var(--space-16)); z-index: 100;
  padding: var(--space-2) var(--space-4);
  font-family: var(--font-display); font-size: var(--text-sm); font-weight: 600;
  color: var(--color-primary); background: var(--color-accent);
  border-radius: var(--radius-control); text-decoration: none;
  transition: top var(--motion-fast) var(--ease-out);
}
.skip-link:focus { top: var(--space-2); }

/* Schriftfamilien nach CI: DM Sans (humanistisch) traegt den Fliesstext,
   Space Grotesk (technisch) die Ueberschriften, Zahlen und Labels.
   Die Dateien liegen self-hosted, siehe fonts.css. */
body { font-family: var(--font-body); margin: 0; color: var(--color-text); background: var(--color-bg); line-height: var(--leading-normal); }
h1, h2, h3, h4 { font-family: var(--font-display); }
.nav-brand, .unit-index, .flag-severity, .admin-section-label { font-family: var(--font-display); }

/* Grund der Arbeitsseiten: ein 8px-Punktfeld statt einer leeren grauen Fläche.
   Es macht den Grund als Fläche kenntlich, wodurch die weißen Inhaltsflächen
   darauf liegen statt in ihm zu verschwinden — und es ist die einzige
   Dekoration der App. Kein Verlauf, kein Bild, ein Raster. */
.app-page {
  min-height: 100vh;
  background-color: var(--color-bg);
  background-image: radial-gradient(var(--texture-dot) 1px, transparent 1px);
  background-size: var(--texture-step) var(--texture-step);
}
.app-main { max-width: var(--container-app); padding: var(--space-4) var(--space-8) var(--space-8); }

/* Kopf: geschlossenes Navy-Band. Es trägt die Marke, gibt der Seite eine
   Oberkante und ist die Fläche, auf der APV Blue regelkonform Text sein darf
   (7,3:1 auf #002060) — dort sitzt der Aktiv-Zustand der Navigation. */
/* Die Bandhöhe kommt aus Plakette (32px) + Polster und braucht kein min-height
   — das würde sich aufs Polster addieren und den Kopf auf 80px treiben.
   56px mobil, 64px ab Tablet. Jeder Pixel hier kostet den Lernschirm Falz:
   die Tutor-Überschrift muss bei 1440×900 unter ~300px vom Oberrand bleiben. */
nav { margin-bottom: var(--space-4); padding: var(--space-3) var(--space-8); background: var(--color-header-bg); border-bottom: 0; }
.app-nav { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--space-3) var(--space-6); }
@media (min-width: 768px) {
  nav { padding: var(--space-4) var(--space-8); }
}
/* Die Bildmarke auf einer weißen Plakette, nicht direkt auf dem Band: das „A"
   der Marke ist #192e47 und würde auf Navy verschwinden, und der CI-Guide will
   das Logo ohnehin auf weißem Grund. So bleibt es unverändert wie geliefert —
   inklusive Hundekopf und cyaner Schale — und die Plakette gibt dem Kopf
   gleichzeitig ein Formmotiv (gerundetes Quadrat), das Zähler und Badges
   wieder aufnehmen.
   background-image statt <img>: test_nav_consistency vergleicht den Anker in
   nav.html literal, dort darf kein Markup dazukommen. Und background statt
   mask-image, weil eine Maske nur den Alphakanal nutzt — die Marke fiele zu
   einer einfarbigen Silhouette zusammen. */
.nav-brand {
  display: inline-flex; align-items: center; gap: var(--space-3);
  font-weight: 700; color: var(--color-on-header); text-decoration: none;
  border-radius: var(--radius-badge);
}
.nav-brand::before {
  content: ""; flex: none;
  width: 2rem; height: 2rem;
  border-radius: var(--radius-badge);
  background: var(--color-surface) url('/static/brand/apv-mark.svg') center / 1.4rem auto no-repeat;
}
a.nav-brand:hover { text-decoration: underline; }
.nav-links { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3) var(--space-4); }
.nav-links a { color: var(--color-on-header); text-decoration: none; font-weight: 500; padding: 0.25rem 0; border-bottom: 2px solid transparent; }
.nav-links a:hover { text-decoration: underline; }
.nav-links a.active { color: var(--color-accent-on-primary); font-weight: 700; border-bottom-color: var(--color-accent); text-decoration: none; }
.nav-user { color: var(--color-accent-on-primary); font-size: var(--text-sm); }
/* Auf dem Navy-Band dreht sich der Fokusring: Navy wäre dort unsichtbar,
   der Akzent trägt 7,3:1. Gilt für alles im Kopf, auch den Sprachumschalter. */
nav :where(a, button):focus-visible { outline-color: var(--focus-color-on-primary); }
a.nav-brand:focus-visible { outline: var(--focus-width) solid var(--focus-color-on-primary); outline-offset: 3px; }
.lang-switch { display: inline-flex; }
/* line-height: 0 gegen den Baseline-Spalt, den Inline-SVG sonst unter sich lässt. */
.lang-flag { display: inline-flex; padding: 0; margin: 0; background: none; border: 1px solid var(--color-border-strong); border-radius: var(--radius-badge); line-height: 0; cursor: pointer; overflow: hidden; }
.lang-flag:hover { border-color: var(--color-accent); box-shadow: 0 0 0 2px var(--color-accent-tint); }
.lang-flag:focus-visible { outline: var(--focus-width) solid var(--focus-color); outline-offset: var(--focus-offset); }
/* Umschalter-Kopf für die zwei Seiten ohne nav.html (Landing, Login). */
.page-topbar { display: flex; justify-content: flex-end; max-width: var(--container-narrow); margin: 0 auto; padding: var(--space-4) var(--space-6) 0; }
.lang-corner { position: absolute; top: 0.9rem; right: 0.9rem; }
/* Zentrieren gilt fuer jeden <main>; Breite und Polster setzt die Seite. */
main { margin: 0 auto; }
.public-main { max-width: var(--container-narrow); padding: var(--space-8) var(--space-6) var(--space-12); }
/* Ohne eigenen Rahmen: der Lerntext soll im Panel wohnen, nicht darin
   eingerahmt sitzen. Die Kante kommt vom umgebenden .learning-text-panel. */
.content-frame { width: 100%; min-height: 320px; border: 0; border-radius: var(--radius-control); background: var(--color-surface); }
/* Tabellen ohne Gitter: `td, th { border: 1px solid }` war der Excel-Effekt.
   Jetzt trennt nur eine Haarlinie die Zeilen, der Kopf ist ein Label statt
   einer grauen Fläche, und Zahlen stehen in Tabellenziffern untereinander.
   Die Fläche ist weiß und gerundet, damit eine Tabelle auch dort eine eigene
   Fläche ist, wo sie ohne Karte direkt auf dem Punktraster steht (Review-Queue,
   Evidenz-Register). Innerhalb einer Karte fällt sie mit ihr zusammen und
   bleibt unsichtbar. `border-collapse: separate` ist dafür Pflicht — bei
   `collapse` greift die Rundung nicht. */
table {
  border-collapse: separate; border-spacing: 0; width: 100%;
  background: var(--color-surface);
  border-radius: var(--radius-card); overflow: hidden;
}
td, th { border: 0; border-bottom: 1px solid var(--color-border); padding: var(--space-3) var(--space-4); text-align: left; }
th {
  background: transparent; color: var(--color-text-muted);
  font-family: var(--font-display); font-size: var(--label-size); font-weight: var(--label-weight);
  letter-spacing: var(--label-tracking); text-transform: uppercase;
  border-bottom: 2px solid var(--color-primary);
}
td { height: var(--row-h); font-variant-numeric: tabular-nums; }
td:first-child { font-weight: 500; }
tr:last-child td { border-bottom: 0; }
tbody tr:hover { background: var(--color-bg); }
/* Dringlichkeit als Regel, nicht als Fläche: eine rosa Zeile schreit über die
   ganze Breite und färbt dabei auch Daten ein, die nichts damit zu tun haben
   (Datum, Aktionsknopf). Die 3px-Kante am Zeilenanfang sagt dasselbe an der
   Stelle, an der das Auge die Zeile betritt. Das Statuswort trägt die Aussage
   ohnehin doppelt. */
tr.overdue { background: transparent; }
tr.overdue td:first-child { box-shadow: inset 3px 0 0 var(--color-error); }

/* Karten sind weiße Inseln auf dem Raster — ohne 1px-Umrandung. Der Rahmen war
   Teil der Default-Panel-Signatur; die Kante entsteht jetzt aus dem Kontrast
   zur Textur. Der transparente Rahmen bleibt stehen, damit Modifier wie
   .danger-zone und .review-gate weiterhin eine Kante setzen können. */
.card {
  border: 1px solid transparent; border-radius: var(--radius-card);
  padding: var(--card-pad); margin-bottom: var(--space-6);
  background: var(--color-surface); box-shadow: var(--shadow-card);
}
/* Sektionsüberschriften im Label-Stil — die identitätstragendste Regel des CI.
   Sie ordnet die Fläche ein, ohne mit dem Seitentitel zu konkurrieren.
   Zwei Ausnahmen weiter unten: das Tutor-Panel und die Landing-Karten tragen
   echte Überschriften, weil dort der Text der Inhalt ist. */
.card > h2 {
  font-size: var(--label-size); font-weight: var(--label-weight);
  letter-spacing: var(--label-tracking); text-transform: uppercase;
  color: var(--color-text-muted); margin: 0 0 var(--space-4);
}
.hint { color: var(--color-text-muted); font-size: 0.95rem; }
/* Leerzustand als Fläche, nicht als grauer Satz im Nichts. Sechs Wörter in
   grauer Schrift lassen eine leere Liste wie einen Fehler aussehen; der
   gestrichelte Rahmen sagt „hier ist ein Platz, er ist nur gerade leer".
   Kein Kursiv: es gibt keinen italic-Schnitt, und synthetisch geneigte
   DM Sans sieht schlecht aus. */
.empty {
  display: block; margin: 0;
  padding: var(--space-6);
  color: var(--color-text-muted); text-align: center;
  border: 1px dashed var(--color-border-strong); border-radius: var(--radius-control);
}
.ok { color: var(--color-success-ink); }
.error { color: var(--color-error-ink); }
.warn { color: var(--color-warning-ink); background: var(--color-warning-bg); border: 1px solid var(--color-warning-border); border-radius: var(--radius-control); padding: 0.75rem 1rem; }
/* Zwei Register für H1: Display (40–72px) nur für Marke und Eintritt, hier die
   Arbeitsseiten-Größe. 48px würden auf dem Lernschirm den Tutor unter die Falz
   schieben. balance verhindert die einzelne Restsilbe in Zeile zwei. */
.app-main h1 { font-size: var(--text-3xl); letter-spacing: -0.02em; line-height: var(--leading-tight); }
h1 { margin-top: 0; text-wrap: balance; }
h2, h3 { text-wrap: balance; }
.hint, .landing-lead { text-wrap: pretty; }
/* Der Lead unter dem Seitentitel ist Prosa und läuft auf dem Satzspiegel, nicht
   über die volle Containerbreite — 1100px sind bei 16px rund 140 Zeichen. */
.app-main h1 + .hint { max-width: var(--measure); }
h2 { font-size: 1.15rem; margin: 0 0 0.75rem; }

/* Affordanz, Fokus, Bewegung
   -------------------------------------------------------------------------
   Mit der APV-Palette tragen Links Navy — dieselbe Farbe wie der Fliesstext.
   Vorher war es ein mittleres Blau, das allein durch die Farbe „anklickbar"
   gesagt hat. Diese Aussage uebernimmt jetzt der Akzent als Unterstreichung:
   APV Blue darf keine Textfarbe sein, als 2px-Linie unter dem Text ist es
   dagegen genau richtig. */
a {
  color: var(--color-link);
  text-decoration-color: var(--color-link-underline);
  text-decoration-thickness: 2px;
  text-underline-offset: 2px;
}
a:hover { color: var(--color-link-hover); }
/* Navigation traegt keine Dauer-Unterstreichung (sie hat den Aktiv-Zustand),
   aber der Hover muss als Akzent lesbar sein und nicht als Textfarbe. */
.nav-links a:hover, a.nav-brand:hover, .preview-back:hover {
  text-decoration-color: var(--color-link-underline);
}

/* Ein Fokusmuster fuer alles statt drei Einzelselektoren. :where() hat
   Spezifitaet 0 — bestehende Sonderregeln gewinnen weiterhin. */
:where(a, button, summary, input, select, textarea, [tabindex]):focus-visible {
  outline: var(--focus-width) solid var(--focus-color);
  outline-offset: var(--focus-offset);
}

/* Die Motion-Token schalten sich in tokens.css selbst ab. Dieser Block fasst
   zusaetzlich alles, was nicht ueber die Token laeuft — fremde Keyframes,
   scroll-behavior, scrollIntoView-Animationen. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 1ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 1ms !important;
    scroll-behavior: auto !important;
  }
}

/* Forms */
/* Formularelemente erben die App-Schrift — sonst rendern Browser textarea/input in Monospace. */
button, input, select, textarea, .button { font-family: inherit; }
button, input[type="submit"] { padding: 0.55rem 1rem; border-radius: var(--radius-control); border: 1px solid var(--color-field-border); background: var(--color-surface); cursor: pointer; font-size: 0.95rem; }
button:hover, input[type="submit"]:hover { background: var(--color-bg); }
.stack-form { display: flex; flex-direction: column; gap: 0.75rem; }
/* Ein Knopf ist so breit wie sein Label. In einer Spaltenform streckt
   align-items: stretch ihn sonst über die ganze Formularbreite — 500px
   „Suchen" liest als Fläche, nicht als Aktion. Die Login-Karte ist bewusst
   ausgenommen: dort ist der Knopf der einzige Weg und darf die Karte füllen. */
.stack-form > .btn, .stack-form > button, .stack-form > input[type="submit"] { align-self: start; }
.stack-form label.field, .field { display: flex; flex-direction: column; gap: 0.35rem; font-size: 0.9rem; }
.stack-form input, .stack-form select, .stack-form textarea,
.filter-form input, .filter-form select { padding: 0.55rem 0.75rem; border: 1px solid var(--color-field-border); border-radius: var(--radius-control); font-size: 1rem; }
.filter-form { display: grid; gap: 0.75rem; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); align-items: end; }
.inline-form { display: inline-block; margin-right: 0.35rem; }
.actions { white-space: nowrap; }
/* Zählwerte in der Review-Queue: "6 Flags · 6 blockierend" darf nicht mitten
   im Label brechen, sonst steht "blockierend" allein auf Zeile zwei. */
.findings-open-btn, .flag-open-btn { white-space: nowrap; }
.checkbox-label, .radio-label { display: flex; align-items: center; gap: 0.5rem; margin: 0.35rem 0; }
fieldset { border: 1px solid var(--color-border); border-radius: var(--radius-control); padding: 0.75rem 1rem; margin-bottom: 0.75rem; }
legend { font-weight: 600; padding: 0 0.25rem; }
.batch-actions { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 0.75rem; margin-bottom: 0.75rem; }
.appeal-details { margin-top: 1rem; }

/* Training layout + tutor (tutor-first) */
.training-layout { display: grid; gap: 1rem; grid-template-columns: 1fr; }
@media (min-width: 900px) {
  /* Eine Spalte, immer: der Lerntext klappt unter dem Tutor auf, wo er auch
     zugeklappt sitzt. Ein Nebeneinander würde beiden Bereichen Breite nehmen. */
  .training-layout.tutor-first { grid-template-columns: minmax(0, 1fr); align-items: start; }
}
.progress-summary { font-weight: 600; color: var(--color-primary); margin: 0.25rem 0 0.75rem; }
.unit-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.35rem; }
.unit-list-item a { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem 0.75rem; text-decoration: none; color: inherit; padding: 0.5rem 0.65rem; border-radius: var(--radius-control); border: 1px solid transparent; }
.unit-list-item.is-current a { background: var(--color-accent-tint); border-color: var(--color-accent-tint-border); }
.unit-list-item.is-done a { opacity: 0.85; }
.unit-index { display: inline-flex; align-items: center; justify-content: center; width: 1.6rem; height: 1.6rem; border-radius: var(--radius-badge); background: var(--color-bg); font-weight: 700; font-size: 0.85rem; }
/* nowrap ist hier Pflicht: bricht ein Inline-Element mit Rahmen um, reißen
   Hintergrund und Rahmen in zwei Hälften auseinander. */
.badge { display: inline-flex; align-items: center; gap: 0.45em; white-space: nowrap; font-size: 0.75rem; font-weight: 600; padding: 0.2rem 0.55rem; border-radius: var(--radius-badge); }
.badge-open { background: var(--color-warning-bg); color: var(--color-warning-ink); border: 1px solid var(--color-warning-border); }
.badge-done { background: var(--color-success-bg); color: var(--color-success-ink); border: 1px solid var(--color-success-border); }

/* Status badges (localized assignment/skill state). */
.status-badge { display: inline-flex; align-items: center; gap: 0.45em; font-size: 0.75rem; font-weight: 600; padding: 0.2rem 0.6rem; border-radius: var(--radius-badge); white-space: nowrap; border: 1px solid transparent; }

/* Ein 6px-Punkt vor jedem Badge — die Icon-Strategie für Zustände, ganz ohne
   Assets: eine Form, die pro Rolle nur die Farbe wechselt. Er trägt keine
   eigene Information (das Wort daneben sagt alles) und ist deshalb dekorativ;
   currentColor garantiert, dass er dieselbe geprüfte Tinte hat wie der Text
   und nicht die durchgefallene Guide-Sättigung. */
.status-badge::before, .badge::before {
  content: ""; flex: none;
  width: 6px; height: 6px; border-radius: var(--radius-round);
  background: currentColor;
}
.status-open { background: var(--color-bg); color: var(--color-text-muted); border-color: var(--color-field-border); }
.status-in_progress { background: var(--color-info-bg); color: var(--color-info-ink); border-color: var(--color-info-border); }
.status-overdue { background: var(--color-error-bg); color: var(--color-error-ink); border-color: var(--color-error-border); }
.status-completed { background: var(--color-success-bg); color: var(--color-success-ink); border-color: var(--color-success-border); }
.status-update { background: var(--color-warning-bg); color: var(--color-warning-ink); border-color: var(--color-warning-border); }
/* Inhaltsstatus im Admin-Katalog (publiziert / Draft in Review / retired). */
.status-draft { background: var(--color-warning-bg); color: var(--color-warning-ink); border-color: var(--color-warning-border); }
.status-retired { background: var(--color-bg); color: var(--color-text-muted); border-color: var(--color-border); }
.unit-nav-controls { margin-top: 0.85rem; padding-top: 0.75rem; border-top: 1px solid var(--color-border); }
.unit-nav-links { display: flex; flex-wrap: wrap; gap: 0.5rem; }
.tutor-panel h2 { margin: 0 0 0.35rem; font-size: 1.15rem; }
.tutor-main {
  display: flex;
  flex-direction: column;
  /* Fallback ohne JS; das Skript streckt die Höhe exakt bis zum Fold. Nicht sticky. */
  height: min(62vh, 560px);
  min-height: 20rem;
  padding: 0 !important;
  overflow: hidden;
  align-self: start;
}
.tutor-panel-header {
  flex-shrink: 0;
  padding: 0.85rem 1rem 0.5rem;
  border-bottom: 1px solid var(--color-border);
}
.tutor-disclaimer { font-size: 0.85rem; border-left: 3px solid var(--color-primary); padding-left: 0.75rem; margin: 0.35rem 0 0; }
.tutor-messages {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: 0.75rem 1rem;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  scroll-behavior: smooth;
  /* Volle Panel-Breite für Höhe und Ruhe, aber ein lesbarer Satzspiegel —
     über ~80 Zeichen Zeilenlänge wird Fließtext schlechter lesbar, nicht besser. */
  width: 100%;
  max-width: 78ch;
  margin-inline: auto;
}
.tutor-msg {
  padding: 0.6rem 0.85rem;
  border-radius: var(--radius-control);
  font-size: 1rem;
  line-height: var(--leading-relaxed);
}
/* Die Tutor-Antwort ist der eigentliche Lerninhalt — sie bekommt die ganze Spalte. */
.tutor-msg-user { max-width: 95%; }
.tutor-msg p { margin: 0 0 0.45rem; }
.tutor-msg p:last-child { margin-bottom: 0; }
.tutor-msg ul, .tutor-msg ol { margin: 0.25rem 0 0.45rem; padding-left: 1.2rem; }
.tutor-msg li { margin: 0.15rem 0; }
.tutor-msg-user {
  background: var(--color-accent-tint);
  align-self: flex-end;
  white-space: pre-wrap;
}
.tutor-msg-assistant {
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  align-self: stretch;
}
.tutor-msg-error { background: var(--color-error-bg); color: var(--color-error-ink); align-self: stretch; }

/* Wartezustand
   -------------------------------------------------------------------------
   Die Antwort braucht 3–7 Sekunden. Drei pulsende Punkte stehen an genau der
   Stelle, an der sie erscheinen wird — das ist die ganze Botschaft.
   Ausdrücklich keine Sparkles, kein Gradient-Avatar-Ring, kein „KI denkt
   nach"-Hirn: die Identität des Tutors ist die Akzentfarbe und der Label-Stil,
   nicht ein Klischee. */
.tutor-msg-pending {
  display: inline-flex; align-items: center; gap: 0.4rem;
  align-self: flex-start;
  background: var(--color-bg); border: 1px solid var(--color-border);
  padding: 0.75rem 0.9rem;
}
.tutor-msg-pending span {
  width: 6px; height: 6px; border-radius: var(--radius-round);
  background: var(--color-accent); opacity: 0.4;
  animation: apv-tutor-dot 1.1s var(--ease-out) infinite;
}
.tutor-msg-pending span:nth-child(2) { animation-delay: 0.16s; }
.tutor-msg-pending span:nth-child(3) { animation-delay: 0.32s; }
@keyframes apv-tutor-dot {
  0%, 100% { opacity: 0.4; }
  40% { opacity: 1; }
}
/* Der Statustext liegt in einer eigenen Live-Region (siehe Template). Leer
   nimmt er keine Höhe — aber er bleibt im Renderbaum: ein `display: none`
   erst beim Füllen sichtbar zu machen, hindert manche Screenreader daran,
   die Änderung überhaupt anzukündigen. Genau darum geht es hier. */
.tutor-status {
  flex-shrink: 0; margin: 0; padding: 0 1rem 0.5rem;
  font-family: var(--font-display); font-size: var(--text-sm);
  color: var(--color-text-muted);
}
.tutor-status:empty { padding: 0; height: 0; overflow: hidden; }
.tutor-form {
  flex-shrink: 0;
  display: flex;
  gap: 0.5rem;
  align-items: flex-end;
  padding: 0.65rem 1rem 0.85rem;
  border-top: 1px solid var(--color-border);
  background: var(--color-surface);
}
.tutor-input-field { flex: 1; margin: 0; }
.tutor-form textarea {
  resize: none;
  min-height: 2.6rem;
  max-height: 6rem;
  margin: 0;
  width: 100%;
}
.tutor-form .btn { flex-shrink: 0; }
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* Abschlusstest modal — replaces tutor, no chatting during audit check */
.mastery-dialog {
  border: none;
  border-radius: var(--radius-card);
  padding: 0;
  max-width: min(720px, calc(100vw - 2rem));
  width: 100%;
  max-height: calc(100vh - 2rem);
  overflow: auto;
  box-shadow: var(--shadow-overlay);
}
.mastery-dialog::backdrop {
  background: var(--scrim);
}
.mastery-dialog-inner {
  margin: 0;
  padding: 1.25rem 1.35rem 1.5rem;
  border: none;
  box-shadow: none;
}
.mastery-dialog-header {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  justify-content: space-between;
  gap: 0.75rem 1rem;
  margin-bottom: 1rem;
  padding-bottom: 0.75rem;
  border-bottom: 1px solid var(--color-border);
}
.mastery-dialog-header h2 { margin: 0; flex: 1 1 12rem; }
.mastery-dialog-header .hint { flex: 1 1 100%; margin: 0; }
.mastery-dialog-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-top: 0.5rem;
}
.training-layout.mastery-active {
  grid-template-columns: 1fr;
}
.learning-text-panel { margin-bottom: 1rem; }
.learning-text-panel summary { cursor: pointer; font-weight: 600; }
.learning-text-panel .content-frame { margin-top: 0.75rem; min-height: 240px; }

/* Autoren-Vorschau (/admin/preview) — Rückweg und Freigabe-Aktionen am Inhalt */
.preview-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem 1rem;
  margin-bottom: 1.25rem;
  padding: 0.75rem 1rem;
  background: var(--color-bg);
  border: 1px solid var(--color-field-border);
  border-radius: var(--radius-control);
}
.preview-back { color: var(--color-primary); font-weight: 600; text-decoration: none; white-space: nowrap; }
.preview-back:hover { text-decoration: underline; }
.preview-meta { flex: 1 1 14rem; margin: 0; color: var(--color-text-muted); font-size: 0.95rem; }
.preview-result-list { margin: 0.5rem 0 1rem; padding-left: 1.1rem; }
.preview-result-list li { margin: 0.25rem 0; }

/* Reviewer-Arbeitsplatz */
.review-head { margin-bottom: 1rem; }
.review-head h1 { margin: 0.35rem 0 0.25rem; }
.review-gate { border-left: 4px solid var(--color-primary); }
.review-gate-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem; margin: 0.75rem 0 0.5rem; }
.review-gate-actions .inline-form { margin: 0; }
.review-unit { margin-bottom: 1.25rem; }
.review-unit-head h3 { margin: 0 0 0.35rem; }
.review-text summary, .review-text-edit summary, .review-add-question summary { cursor: pointer; font-weight: 600; padding: 0.35rem 0; }
.review-text .content-frame { margin-top: 0.75rem; min-height: 320px; }
.review-question { border: 1px solid var(--color-border); border-radius: var(--radius-control); padding: 0.85rem 1rem; margin: 0.75rem 0; }
.review-question.has-finding { border-color: var(--color-error-ink); background: var(--color-error-bg); }
.review-finding { margin: 0 0 0.5rem; font-size: 0.95rem; }
.review-question-form .field { margin-bottom: 0.5rem; }
.review-options { margin: 0.5rem 0; }
.review-option { display: flex; align-items: center; gap: 0.6rem; margin: 0.3rem 0; }
.review-option input[type="text"] { flex: 1; min-width: 0; }
.review-question-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem; }
.findings-list { margin: 0; padding: 1rem 1.25rem 1rem 2.4rem; max-height: 50vh; overflow-y: auto; }
.findings-list li { margin: 0.4rem 0; }

/* Verify-flags modal (Review-Queue) */
.verify-flags-dialog { border: none; border-radius: var(--radius-card); padding: 0; max-width: 720px; width: calc(100% - 2rem); box-shadow: var(--shadow-overlay); }
.verify-flags-dialog::backdrop { background: var(--scrim); }
.verify-flags-dialog-inner { margin: 0; padding: 0; }
.verify-flags-dialog-header { display: flex; align-items: flex-start; justify-content: space-between; gap: 1rem; padding: 1.25rem 1.25rem 0.5rem; border-bottom: 1px solid var(--color-border); }
.verify-flags-dialog-header h2 { margin: 0; font-size: 1.15rem; }
.dialog-close-btn { padding: 0.35rem 0.65rem; min-width: 2rem; }
.verify-flags-dialog-inner > .hint { padding: 0 1.25rem; margin: 0.5rem 0 0; }
.verify-flags-list { padding: 1rem 1.25rem; max-height: 50vh; overflow-y: auto; display: flex; flex-direction: column; gap: 0.75rem; }
.verify-flags-dialog-footer { padding: 0.75rem 1.25rem 1.25rem; border-top: 1px solid var(--color-border); display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 0.75rem; }
.verify-flag-card { border: 1px solid var(--color-border); border-radius: var(--radius-control); padding: 0.85rem 1rem; background: var(--color-bg); }
.verify-flag-card-head { display: flex; align-items: center; justify-content: space-between; gap: 0.5rem; margin-bottom: 0.5rem; }
.flag-severity { font-size: 0.75rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em; padding: 0.2rem 0.5rem; border-radius: var(--radius-badge); }
.flag-severity-high { background: var(--color-error-bg); color: var(--color-error-ink); border: 1px solid var(--color-error-border); }
.flag-severity-medium { background: var(--color-warning-bg); color: var(--color-warning-ink); border: 1px solid var(--color-warning-border); }
.flag-severity-low { background: var(--color-bg); color: var(--color-text-muted); border: 1px solid var(--color-border); }
.verify-flag-message { margin: 0; font-size: 0.95rem; }
.verify-flag-claim,
.verify-flag-context { margin-top: 0.65rem; font-size: 0.9rem; }
.verify-flag-claim summary,
.verify-flag-context summary { cursor: pointer; color: var(--color-primary); font-weight: 500; }
.verify-flag-claim p,
.verify-flag-context p { margin: 0.35rem 0 0; color: var(--color-text-muted); white-space: pre-wrap; }
.verify-flag-preview { margin: 0.75rem 0 0; font-size: 0.9rem; }
.verify-flag-preview a { color: var(--color-primary); font-weight: 500; }

/* Danger zone + retire modal */
.admin-cols { display: grid; gap: 1rem; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); align-items: start; }
.admin-section-label { color: var(--color-text-muted); font-size: 0.8rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em; margin: 2rem 0 0.5rem; }
.danger-zone { border-color: var(--color-error-border); background: var(--color-error-bg); margin-top: 1.5rem; }
.danger-zone h2 { color: var(--color-error-ink); }
.danger-zone-warning { background: var(--color-error-bg); border: 1px solid var(--color-error-border); border-radius: var(--radius-control); padding: 0.85rem 1rem; color: var(--color-error-ink); margin: 0 0 1rem; }
.danger-zone-warning p { margin: 0.35rem 0 0; }
.btn-danger { background: var(--color-error-ink); color: var(--color-surface); }
.btn-danger:hover { background: var(--color-error-ink); }
.btn-danger:disabled { background: var(--color-error-border); cursor: not-allowed; }
.retire-modal { border: none; border-radius: var(--radius-card); padding: 0; max-width: 720px; width: calc(100% - 2rem); box-shadow: var(--shadow-overlay); }
.retire-modal::backdrop { background: var(--scrim); }
.retire-modal-inner { padding: 0; }
.retire-modal-header { display: flex; align-items: flex-start; justify-content: space-between; gap: 1rem; padding: 1.25rem 1.25rem 0.75rem; border-bottom: 1px solid var(--color-border); }
.retire-modal-header h2 { margin: 0; font-size: 1.15rem; }
.retire-step { padding: 1rem 1.25rem 1.25rem; }
.retire-select-list { max-height: 45vh; overflow: auto; margin: 0.75rem 0; border: 1px solid var(--color-border); border-radius: var(--radius-control); }
.retire-select-list table { margin: 0; }
.retire-modal-footer { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 0.75rem; margin-top: 1rem; padding-top: 1rem; border-top: 1px solid var(--color-border); }
.retire-final-warning { margin-bottom: 1rem; }
.retire-confirm-list { margin: 0 0 1rem; padding-left: 1.25rem; max-height: 30vh; overflow-y: auto; }
.import-hint { margin-bottom: 1rem; }
.import-result-banner { font-size: 1rem; }
.import-running-notice { margin: 0 0 1rem; }

/* Buttons */
.btn { display: inline-block; padding: 0.65rem 1.25rem; border-radius: var(--radius-control); font-weight: 600; text-decoration: none; border: none; cursor: pointer; font-size: 1rem; }
.btn-primary { background: var(--color-primary); color: var(--color-surface); }
.btn-primary:hover { background: var(--color-primary-hover); }
.btn-secondary { background: var(--color-bg); color: var(--color-text); border: 1px solid var(--color-field-border); }
.btn-secondary:hover { background: var(--color-hover-bg); }
.btn-slack { background: var(--color-slack); color: var(--color-surface); width: 100%; text-align: center; box-sizing: border-box; }
.btn-slack:hover { background: var(--color-slack-hover); }
.btn-lg { padding: 0.85rem 1.75rem; font-size: 1.05rem; }
.btn-sm { padding: 0.35rem 0.85rem; font-size: 0.85rem; }
a.btn { text-align: center; }

/* Aktionsgruppe: die wahrscheinlichste nächste Aktion steht als Knopf da,
   alles Weitere liegt im Kontextmenü — vorhanden, auffindbar, aber nicht im Weg. */
/* nowrap: Primäraktion und Umschalter gehören optisch zusammen und sollen in einer
   Tabellenzelle nicht übereinander rutschen — die Spalte darf dafür breiter werden. */
.action-group { display: inline-flex; flex-wrap: nowrap; align-items: center; gap: 0.5rem; }
.action-menu { position: relative; display: inline-block; }
.action-menu > summary { list-style: none; display: inline-flex; align-items: center; gap: 0.4rem; user-select: none; }
.action-menu > summary::-webkit-details-marker { display: none; }
.action-menu > summary::after { content: '\25BE'; font-size: 0.8em; line-height: 1; }
.action-menu[open] > summary { background: var(--color-hover-bg); }
.action-menu-list {
  position: absolute;
  right: 0;
  top: calc(100% + 0.35rem);
  z-index: 30;
  min-width: 15rem;
  padding: 0.35rem;
  background: var(--color-surface);
  border: 1px solid var(--color-field-border);
  border-radius: var(--radius-control);
  box-shadow: var(--shadow-overlay);
  text-align: left;
  white-space: normal;
}
.action-menu-list form { margin: 0; display: block; }
.action-menu-item {
  display: block;
  width: 100%;
  box-sizing: border-box;
  text-align: left;
  padding: 0.5rem 0.65rem;
  border: none;
  /* Eine Stufe kleiner als das Menü selbst: Innenradius = Außenradius minus
     Polster, sonst laufen die Kanten nicht parallel. */
  border-radius: var(--radius-badge);
  background: none;
  font: inherit;
  font-size: 0.95rem;
  color: var(--color-text);
  text-decoration: none;
  cursor: pointer;
}
.action-menu-item:hover,
.action-menu-item:focus-visible { background: var(--color-accent-tint); }
.action-menu-item.is-danger { color: var(--color-error-ink); }
.action-menu-item.is-danger:hover,
.action-menu-item.is-danger:focus-visible { background: var(--color-error-bg); }
.action-menu-note { margin: 0 0.65rem 0.35rem; color: var(--color-text-muted); font-size: 0.8rem; line-height: 1.35; }
.action-menu-sep { border: 0; border-top: 1px solid var(--color-border); margin: 0.3rem 0; }
/* Eintritt (Landing + Login) — hier trägt die Navy-Fläche
   -------------------------------------------------------------------------
   Die beiden anonymen Seiten sind der einzige Ort, an dem die Marke ganz
   auftreten darf: volle Navy-Seite, dasselbe Punktraster wie die Arbeitsseiten
   (dort in Cyan, weil Grau auf Navy unsichtbar wäre), und das Logo auf weißer
   Plakette. Auf den Arbeitsseiten bleibt der Grund hell — der Lerntext-iframe
   ist immer weiß und würde aus einer dunklen Seite herausleuchten. */
.landing-page, .auth-page {
  min-height: 100vh;
  background-color: var(--color-primary);
  background-image: radial-gradient(var(--texture-dot-on-primary) 1px, transparent 1px);
  background-size: var(--texture-step) var(--texture-step);
  color: var(--color-on-primary);
}
.landing { max-width: var(--container-narrow); padding: var(--space-12) var(--space-6) var(--space-16); }
.landing-hero { margin-bottom: var(--space-12); }
/* Das vollständige Lockup, nicht nur die Bildmarke: hier steht kein
   Navigationskopf daneben, der den Namen schon nennt. Auf Weiß, wie der
   CI-Guide es verlangt — und wie im Kopf als gerundete Plakette. */
.landing-hero::before {
  content: ""; display: block;
  width: 9.5rem; height: 7rem;
  margin-bottom: var(--space-6);
  border-radius: var(--radius-card);
  background: var(--color-surface) url('/static/brand/apv-logo.svg') center / 7rem auto no-repeat;
}
.landing-hero h1 { font-size: var(--text-display); line-height: var(--leading-tight); letter-spacing: -0.02em; margin: var(--space-2) 0 var(--space-4); }
.landing-lead { font-size: var(--text-lg); color: var(--color-accent-on-primary); max-width: var(--measure); margin: 0 0 var(--space-8); }
.landing-page .auth-kicker { color: var(--color-accent-on-primary); letter-spacing: var(--label-tracking); }
/* Der einzige gefüllte cyane Knopf der App — und er trägt ein Navy-Label,
   weil Weiß darauf 2,1:1 wäre. Genau die Stelle, an der der Guide Cyan als
   Fläche zulässt: groß, einmal pro Viewport, mit geprüfter Schriftfarbe. */
.landing-page .btn-primary { background: var(--color-accent); color: var(--color-primary); }
.landing-page .btn-primary:hover { background: var(--color-accent-hover); }
.landing-grid { display: grid; gap: var(--space-4); grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); }
/* Die drei Nutzenkarten sind weiße Inseln — dort ist der Text der Inhalt,
   also tragen sie echte Überschriften statt Mikro-Labels. */
.landing-grid .card { color: var(--color-text); }
.landing-grid .card > h2 {
  font-size: var(--text-lg); font-weight: 600; text-transform: none;
  letter-spacing: normal; color: var(--color-text); margin: 0 0 var(--space-2);
}

/* Auth / Login */
.auth-page { display: flex; align-items: center; justify-content: center; padding: var(--space-6); }
.auth-card { position: relative; width: 100%; max-width: var(--container-form); background: var(--color-surface); color: var(--color-text); border: 0; border-radius: var(--radius-card); padding: var(--space-8); box-shadow: var(--shadow-overlay); }
.auth-header { text-align: center; margin-bottom: 1.5rem; }
/* Die Bildmarke über dem Login — hier ohne Plakette, weil die Karte schon weiß
   ist. Nur die Marke und nicht das Lockup: „AlphaPet" steht im Kicker
   darunter schon, ein zweiter Schriftzug wäre eine Wiederholung. */
.auth-header::before {
  content: ""; display: block;
  width: 2.75rem; height: 2.75rem; margin: 0 auto var(--space-4);
  background: url('/static/brand/apv-mark.svg') center / contain no-repeat;
}
.auth-header h1 { margin: 0.25rem 0 0.5rem; font-size: 1.75rem; }
.auth-kicker { text-transform: uppercase; letter-spacing: var(--label-tracking); font-size: var(--label-size); color: var(--color-text-muted); margin: 0; font-weight: var(--label-weight); }
.auth-section { margin-top: 1.25rem; }
.auth-section h2 { font-size: 1rem; margin: 0 0 0.5rem; }
.auth-section-divider { border-top: 1px solid var(--color-border); padding-top: 1.25rem; }
.auth-form { display: flex; flex-direction: column; gap: 0.75rem; }
.field { display: flex; flex-direction: column; gap: 0.35rem; font-size: 0.9rem; }
.field input { padding: 0.6rem 0.75rem; border: 1px solid var(--color-field-border); border-radius: var(--radius-control); font-size: 1rem; }
.field input:focus { outline: var(--focus-width) solid var(--focus-color); border-color: var(--color-primary); }
.auth-footer { margin-top: 1.5rem; text-align: center; font-size: 0.9rem; }
.auth-footer a { color: var(--color-primary); }
.alert { border-radius: var(--radius-control); padding: 0.85rem 1rem; margin-bottom: 1rem; }
.alert-error { background: var(--color-error-bg); border: 1px solid var(--color-error-border); color: var(--color-error-ink); }
.alert-error p { margin: 0.35rem 0 0; font-size: 0.95rem; }

/* Success Mirror + checkpoint formats */
.success-mirror-cols { display: grid; gap: 1rem; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); }
.success-mirror-cols h3 { margin: 0 0 0.5rem; font-size: 0.95rem; }
.success-mirror-cols ul { margin: 0; padding-left: 1.1rem; }

/* Einheiten-Titel und „Wie läuft das ab?"-Disclosure */
.unit-title {
  flex: 1;
  min-width: 10rem;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.how-it-works { margin: 0 0 1rem; }
.how-it-works summary { cursor: pointer; font-weight: 500; color: var(--color-text-muted); }
.how-it-works-list { margin: 0.75rem 0 0; padding-left: 1.25rem; }

/* Fokuszone des Lernschirms: aktuelle Einheit zuerst, Stepper nachgeordnet */
.training-head h1 { margin-bottom: 0.25rem; }
.unit-focus-head { margin: 0.25rem 0 1rem; }
.unit-progress-meta { color: var(--color-text-muted); font-size: 0.9rem; font-weight: 600; margin: 0 0 0.35rem; }
.current-unit-heading { font-size: 1.4rem; line-height: 1.3; margin: 0; }
.unit-stepper { margin: var(--space-6) 0 var(--space-4); }
.unit-stepper-title { font-size: 1rem; color: var(--color-text-muted); margin: 0 0 0.75rem; }

/* ==========================================================================
   Mikro-Interaktionen
   --------------------------------------------------------------------------
   Ein Muster für alles Bedienbare statt Einzelfällen: 120 ms auf Farbe, 1 px
   Versatz beim Druck. Bewusst NICHT auf transform/box-shadow im Hover — das
   erzeugt Layout-Flimmern in Tabellenzeilen. Alles hängt an den Motion-Token,
   die sich unter prefers-reduced-motion selbst auf 1 ms setzen.
   ========================================================================== */
.btn, button, input[type="submit"], .action-menu-item, .unit-list-item a,
.lang-flag, .nav-links a, summary, tbody tr {
  transition:
    background-color var(--motion-fast) var(--ease-out),
    border-color var(--motion-fast) var(--ease-out),
    color var(--motion-fast) var(--ease-out);
}
.btn:active, button:active, input[type="submit"]:active { transform: translateY(1px); }

/* ==========================================================================
   Seiteneintritt
   --------------------------------------------------------------------------
   240 ms Einblenden mit 40 ms Versatz je Block, einmalig, nichts
   scroll-getriggert.

   AUSSCHLIESSLICH opacity, kein translateY — und das ist kein Geschmack:
   learner_training.html misst beim Laden per getBoundingClientRect().top, wie
   viel Platz bis zur Falz bleibt, und schreibt daraus die Panelhöhe des
   Tutors. Ein Transform während dieser Messung verschiebt die gemessene
   Position und verfälscht die Höhe. Opacity berührt die Geometrie nicht.
   ========================================================================== */
@keyframes apv-reveal { from { opacity: 0; } to { opacity: 1; } }
main > * { animation: apv-reveal var(--motion-base) var(--ease-out) both; }
main > *:nth-child(2) { animation-delay: var(--reveal-step); }
main > *:nth-child(3) { animation-delay: calc(var(--reveal-step) * 2); }
main > *:nth-child(4) { animation-delay: calc(var(--reveal-step) * 3); }
main > *:nth-child(n + 5) { animation-delay: calc(var(--reveal-step) * 4); }

/* ==========================================================================
   Breakpoints
   --------------------------------------------------------------------------
   Vorher gab es genau eine Media Query im Projekt, und die war faktisch ein
   No-op. Die Stufen folgen dem Guide: 576 / 768 / 1024 / 1200.
   ========================================================================== */
@media (max-width: 575px) {
  nav { padding: var(--space-3) var(--space-4); }
  .app-main { padding: var(--space-4) var(--space-4) var(--space-8); }
  .public-main { padding: var(--space-6) var(--space-4) var(--space-8); }
  .landing { padding: var(--space-8) var(--space-4) var(--space-12); }
  .page-topbar { padding: var(--space-3) var(--space-4) 0; }
  .auth-card { padding: var(--space-6); }
  /* Auf 390px kostet die Plakette zu viel Breite für den Schriftzug daneben. */
  .nav-brand::before { width: 1.75rem; height: 1.75rem; background-size: 1.25rem auto; }
  .landing-hero::before { width: 7rem; height: 5.25rem; background-size: 5.25rem auto; }
  /* Zellpolster zusammenziehen, sonst bleibt für den Inhalt nichts übrig. */
  td, th { padding: var(--space-2) var(--space-3); }
}

/* Trefferflächen: 44px ist WCAG 2.5.5 (AAA) und nur dort erzwungen, wo mit dem
   Finger gezielt wird. Auf dem Desktop würde es die Listenseiten um rund ein
   Drittel höher machen, und die heutigen 34px erfüllen 2.5.8 (AA, 24px)
   bereits. Bewusster Trade-off. */
@media (pointer: coarse), (max-width: 767px) {
  .btn-sm, .nav-links a, .action-menu > summary, .action-menu-item, .lang-flag {
    min-height: var(--control-min);
    display: inline-flex;
    align-items: center;
  }
  .action-menu-item { display: flex; }
  .learning-text-panel summary, .how-it-works summary { min-height: var(--control-min); display: flex; align-items: center; }
}
