/* tonecoach — design tokens per the validated dataviz reference palette.
   Light and dark are both *selected* token sets, not automatic inversions. */

:root {
  /* Redesign V7.7 "Salbei Global" (Auftrag 271, Owner-Entscheid 25.09.2026,
     Figma 4lJUPJ00neASmi6h7voRGK, Rahmen 536:7224): Farbrollen aus dem
     Handoff, Werte verbindlich. Kontrast gemessen mit derselben Formel wie
     die bisherigen Kommentare (spike/a11y_kontrast.py-Methode). */
  --page: #ECF3F0;
  --surface: #FBFDFC;
  --ink: #102722;           /* auf --page 13,96:1 · auf --surface 15,40:1 */
  --ink-2: #506963;         /* = --muted, siehe dort */
  /* K1 des Audits 09/2026 (E1, Owner-Entscheid 05.09.): der Nebentext lag
     hell bei 3,50:1 auf der Karte und 3,41:1 auf der Seite und riss damit
     auf JEDEM Screen 4,5:1. Eine Stufe dunkler in derselben Farbfamilie
     traegt beide Grundflaechen (4,68:1 / 4,56:1). Der dunkle Satz bleibt
     bei #898781 — dort steht derselbe Ton bereits bei 4,85:1 / 5,41:1. */
  --muted: #506963;         /* Salbei "muted": auf --page 5,26:1 · auf --surface 5,80:1 */
  --grid: #e1e0d9;
  /* Die Grundlinie ist eine Spur, kein Dekor: gestrichelte Ablagefelder,
     die Nulllinie im Kurvenbild, die Unterkante der Zeichen-Kacheln. Sie
     lag hell bei 1,75:1 und dunkel bei 1,48:1 gegen die Karte (K5) und war
     damit auf einem hellen Bildschirm im Sonnenlicht schlicht weg. Beide
     Werte stehen jetzt ueber 3:1 (hell 3,51:1, dunkel 3,61:1). --grid
     bleibt, wo es ist: es ist keine Spur, sondern die ruhige FLAECHE des
     Sekundaerknopfs — angehoben wuerde daraus ein grauer Klotz. */
  --baseline: #89877f;
  --midline: #a36207;        /* speaker midline (f0) — amber, distinct from the blues */
  --border: #8DB5AD;        /* Handoff-Wert; dekorative Kante/Trenner (nicht 3:1-pflichtig) */
  /* Die Kante, die etwas TRAEGT (Auftrag 53, Punkt 3). --border trennt die
     Karte vom Grund; dort ist eine Haarlinie richtig, und das Audit fuehrt
     den Kartenrand ausdruecklich nicht als Befund. Ein leeres Eingabefeld
     hat dagegen nur seine Kante — sie ist der einzige Traeger und muss
     3:1 gegen die Flaeche halten (K4/E12). Deshalb ein zweites Token statt
     einer Anhebung von --border: sonst wuerde jede Karte im Blatt hart.
     Halbtransparent wie --border, damit es ueber Karte UND Seite traegt
     (hell 3,44:1 / 3,42:1). */
  --border-strong: #5A8079;  /* traegt (Eingabefeld-Kante): 4,28:1 auf --surface, 3,89:1 auf --page */
  /* Marke = Tuerkis des Figma-Entwurfs (#00a896). Als Flaeche hinter weisser
     Schrift reisst dieser Ton 4,5:1 (nur 2,98:1), deshalb ist --brand die
     naechstdunklere Stufe; das Entwurfs-Tuerkis lebt als --brand-bright weiter
     und traegt nur Dekor (Ringbogen, Verlaeufe) — nie Text. */
  --brand: #126B60;          /* weiss darauf 6,37:1 · als Text auf --page 5,65:1 */
  --brand-bright: #1E8577;   /* hellere Salbei-Stufe, rein dekorativ */
  --brand-ink: #0C4E47;      /* "brand-strong"/Aktiv (Handoff): weiss darauf 9,56:1, als Text auf --page 8,49:1 */
  --brand-soft: #CFE6E0;     /* "soft"/Hover (Handoff) */
  --global-accent: #D9A441;  /* Gold, sparsam: Region/Marke/Meridian/Sprachpille — auf --page nur ~2:1, NIE als Fokus oder Fliesstext */
  --on-brand: #ffffff;       /* Schrift auf --brand */
  /* Schrift auf Signalflaeche (Auftrag 53, Punkt 2). Das Gegenstueck zu
     --on-brand, aber ZWEI Tokens statt einem: keine einzige Farbe erreicht
     4,5:1 gleichzeitig auf dem hellen Gruen (#0ca30c) und dem hellen Rot
     (#d03b3b). Rechnerisch — Gruen hat die Leuchtdichte 0,263, Rot 0,169;
     eine dunkle Schrift muesste dafuer dunkler als Schwarz sein, eine helle
     heller als Weiss. Deshalb traegt Gruen dunkle, Rot weisse Schrift, und
     jede Signalflaeche nennt ihr Gegenstueck beim Namen. */
  --on-good: #ffffff;        /* weisse Schrift auf --good (Erfolg, Handoff-Gruen): 6,56:1 */
  --on-critical: #ffffff;    /* weisse Schrift auf --critical: 4,80:1 */
  --brand-glow: rgba(0, 122, 106, 0.30);
  --band-fill: #d9f2ee;
  --band-edge: #63bfb2;
  /* Die Gegenfarbe zu --brand fuer die Saeule „Hoeren": --brand traegt
     „Sprechen" (Balken, Ringe), --listen traegt „Hoeren". Sie stand
     dreimal als Rohwert #1baf7a im Blatt (Balken, Farbtupfer, Abzeichen)
     und war damit kein Token, sondern eine Wiederholung. Wert unveraendert
     — es ist eine Umbenennung, keine Umfaerbung. */
  --listen: #158a61;         /* hell: gegen die Balkenspur 3,28:1 */
  --good: #176B3A;           /* Erfolg (Handoff): weiss darauf 6,56:1 */
  --good-text: #176B3A;      /* als Text auf --page 5,83:1 */
  --critical: #B42318;       /* Fehler (Handoff): weiss darauf 6,57:1, als Text auf --page 5,84:1 */
  /* Die vierte Urteilsstufe (Owner-Entscheid 06.09.2026, Auftrag 75):
     orange fuer eine Ablehnung, die dicht am Ziel lag. Zwei Tokens wie bei
     Gruen, aus demselben Grund: die FLAECHE (Abzeichen, Balkensegment) und
     die SCHRIFT brauchen verschiedene Helligkeiten.
       --near      Flaeche; dunkle Schrift darauf (--on-near) 5,02:1
       --near-text Schrift; auf --page 4,79:1, auf --surface 4,92:1
     Der Entwurfswert der Variante C (#E8701A) lebt als Flaeche weiter; als
     Schrift auf hellem Grund traegt er nur 3,10:1 und ist deshalb NICHT die
     Textfarbe. */
  --near: #E8701A;
  --near-text: #b35309;
  --on-near: #3d1a00;        /* dunkle Schrift auf --near: 5,02:1 */
  --warning: #8A5A00;        /* Warnung (Handoff): als Text auf --page 5,26:1 */
  --cls-mid: #1c5cab;    /* consonant classes: blue / amber / green */
  --cls-high: #a36207;
  --cls-low: #1f7a4d;
  --cls-mid-bg: rgba(42, 120, 214, 0.12);
  --cls-high-bg: rgba(250, 178, 25, 0.16);
  --cls-low-bg: rgba(31, 122, 77, 0.13);
  /* Die Schrift des gefuellten Pruefen-Chips (Owner 06.09.2026). Auf der
     Fuellung --cls-high-bg traegt --cls-high selbst nur 4,32:1; eine Stufe
     dunkler sind es 5,65:1. Im Dunkeln reicht --cls-high (7,78:1). */
  --strict-ink: #8a5206;
  /* Pastelltoene der Entwurfs-Kacheln (Frame 6:4). Die Flaechen sind
     Dekor, die Konturfarbe traegt nur Raender — Text steht in --ink. */
  --cat-tone-bg: #f6eeff;   --cat-tone-line: rgba(131, 56, 236, 0.14);
  --cat-listen-bg: #ebfdf5; --cat-listen-line: rgba(15, 157, 88, 0.14);
  --cat-apply-bg: #fff8ec;  --cat-apply-line: rgba(212, 122, 0, 0.14);
  --cat-vocab-bg: #fff0f6;  --cat-vocab-line: rgba(216, 27, 96, 0.14);
  --fy-weak-bg: #fff1ee;    --fy-weak-ink: #a83c29;   /* 5,70:1 */
  --fy-nb-bg: #ecf3ff;      --fy-nb-ink: #1a5fd6;     /* 5,16:1 */
  --shadow: 0 1px 2px rgba(11,11,11,.05), 0 6px 24px rgba(11,11,11,.06);
  /* Fund F3 (01.09.): `--shadow` ist im Dunkelmodus `none`, und das
     bleibt so — ein schwarzer Schatten auf fast schwarzem Grund traegt
     dort keine Tiefe. Wo ein Rand die Tiefe traegt (`.card`, `.k-card`,
     `.k-modal`, `.msg-row`, `.letter-tile`), ist das folgenlos. Wo der
     Schatten der EINZIGE Traeger war — die aktive Pille auf ihrer
     Schiene —, steht jetzt `--lift`: hell derselbe Schatten, dunkel ein
     1-px-Ring. Damit hat jeder Zustand in BEIDEN Modi einen Traeger. */
  --lift: 0 1px 2px rgba(11,11,11,.05), 0 6px 24px rgba(11,11,11,.06);
  /* Schwebend (Auftrag 53, Punkt 4). --shadow hebt eine Karte um einen
     Millimeter; ein Nutzermenue, ein Rueckgaengig-Streifen und eine
     Kurzmeldung liegen dagegen UEBER der Seite und muessen sich von dem
     abheben, was sie verdecken. Zwei Tokens, weil die beiden Modi es
     verschieden loesen: hell traegt der kraeftigere Schatten, dunkel ein
     1-px-Ring (ein schwarzer Schatten auf fast schwarzem Grund traegt
     dort nichts — Fund F3). Beide werden IMMER zusammen gesetzt:
     `box-shadow: var(--elevated-ring), var(--elevated-shadow);`. Das
     jeweils stumme Token ist ein gueltiger, unsichtbarer Schatten, damit
     die Reihenfolge in beiden Modi dieselbe bleibt. */
  --elevated-shadow: 0 4px 12px rgba(11,11,11,.08), 0 16px 48px rgba(11,11,11,.14);
  --elevated-ring: 0 0 0 0 transparent;

  /* ---- KIT Welle 2: die fuenf Tonfarben ------------------------------
     Diese fuenf Tokens sind die EINZIGE Quelle der Tonfarben: `charts.js`
     liest sie fuer die Leinwand ueber `toneColor()` aus, damit der
     Ton-Chip und die Kurve daneben dieselbe Farbe tragen — auch im
     Dunkelmodus, wo alle fuenf andere Werte haben (bis 05.09.2026 stand
     im Skript eine feste Kopie des hellen Satzes). Vier davon sind bereits
     vorhandene Tokens; nur `rising` ist ein eigener Wert (das Violett
     der Familie --cat-tone-line). Die Farbe traegt NIE
     allein: jeder Chip hat Kurve und Namen dazu. */
  /* Fingerfarben der Tastaturschule — dezent und nur als schmaler
     Streifen an der Taste. Linke Hand kuehl, rechte Hand warm; die
     Legende benutzt dieselben acht Werte. */
  --finger-l5: #8a7fbf; --finger-l4: #5b8ec9; --finger-l3: #3f9e8f;
  --finger-l2: #6a9e46; --finger-r2: #c08a2e; --finger-r3: #c2703f;
  --finger-r4: #bf5b73; --finger-r5: #96709b;
  --tone-mid: var(--cls-mid);
  --tone-low: var(--cls-low);
  --tone-falling: var(--critical);
  --tone-high: var(--cls-high);
  --tone-rising: #7b3fbf;

  /* ---- KIT: Abstands-Raster (4/8) ----------------------------------
     Jeder Abstand im Kit kommt aus dieser Leiter. Zwischenwerte sind
     kein Geschmack, sondern ein Fehler: sie sind der Grund, warum
     dieselbe Karte heute in sieben Bauformen existiert. */
  --sp-1: 4px;  --sp-2: 8px;  --sp-3: 12px; --sp-4: 16px;
  --sp-5: 24px; --sp-6: 32px; --sp-7: 48px; --sp-8: 64px;
  /* Radien und Rahmen — ebenfalls endlich viele */
  --r-1: 8px; --r-2: 12px; --r-3: 16px; --r-pill: 999px;
  /* ---- KIT: Breite und Dehnung ----
     --wrap  = die aeussere Grenze des Inhalts (Owner 01.09.: fluide bis
               ~1200 px, danach zentriert)
     --measure = Lesebreite fuer linearen Text/Assistenten (.k-flow)
     --col-min = Mindestbreite einer Karte im Kit-Raster; darunter bricht
               das Raster auf eine Spalte um */
  /* ---- KIT: Fokusring ----------------------------------------------
     Der sichtbare Fokus ist EIN Token, keine zwanzig Wiederholungen von
     `outline: 3px solid var(--focus)`. Er zeigt auf `--brand`, weil der
     Ring in beiden Modi gegen Seite und Karte traegt (gemessen mit
     spike/a11y_kontrast.py: hell 4,99 / 5,12, dunkel 10,56 / 9,46 — der
     Massstab fuer eine Bedienelement-Kante ist 3:1). Ein eigenes Token
     statt des Markentokens, damit eine spaetere Fokusfarbe an EINER
     Stelle entsteht und nicht die Marke mitnimmt. */
  --focus: var(--brand-ink); /* Fable-Entscheid 271: brand-strong #0C4E47, NICHT Gold (erreicht auf --page nur ~2:1) */
  --wrap: 1200px;
  --measure: 660px;
  --col-min: 240px;
  /* Fusshoehe der mobilen Reiterleiste (`.mainnav`, @media max-width:760px)
     -- EIN Token, das die Leiste selbst UND jeder feste Fuss-Abstand
     darueber (`.k-blattfest`, `.k-thumbbar`, Bewertungsleiste, Kapitel-
     Leiste) gemeinsam nutzen (259-Audit-Fund/Fable-Review: vorher rechneten
     alle mit `58px`, waehrend die Leiste tatsaechlich 63px hoch war -- ein
     durchgehender ~5px-Ueberhang der festen Blaetter UEBER die Leiste).
     Gemessen: 50px `.tab`-Mindesthoehe + 2x4px `.mainnav-inner`-Innenabstand
     + 1px oberer Rand = 59px reichte nicht, echte Schrift/Icon-Masse
     brauchen 63px -- die Leiste setzt jetzt exakt `--k-fuss-h`, statt sich
     organisch einzupendeln, damit beide Seiten garantiert dieselbe Zahl
     verwenden. */
  --k-fuss-h: 63px;
  /* Breite der linken Seitenleiste ab 761px (`.mainnav`) -- `#app` und
     jedes feste Blatt, das in diesem Bereich noch `position: fixed`
     bleibt (`.k-blattfest`, 761-899px-Band), brauchen dieselbe Zahl fuer
     ihr `left`, sonst liegt ihr Inhalt UNTER der Leiste (259-Audit-Fund,
     Fable-Review 25.09.: Knopfreihe/Text links um 232px abgeschnitten). */
  --k-seitenleiste-w: 232px;
  /* ---- KIT: Typo-Stufen (clamp = fluide zwischen 390 und 1280 px) ---- */
  --fs-1: clamp(0.72rem, 0.70rem + 0.10vw, 0.78rem);   /* Marginalie */
  --fs-2: clamp(0.80rem, 0.78rem + 0.12vw, 0.88rem);   /* Sekundaertext */
  --fs-3: clamp(0.94rem, 0.92rem + 0.14vw, 1.02rem);   /* Fliesstext */
  --fs-4: clamp(1.05rem, 1.00rem + 0.35vw, 1.25rem);   /* Kartentitel */
  --fs-5: clamp(1.25rem, 1.10rem + 0.80vw, 1.60rem);   /* Seitentitel */
  --fs-6: clamp(1.70rem, 1.40rem + 1.60vw, 2.60rem);   /* Kennzahl */
  /* Auftrag 271: feste Typo-Stufen der Vorlage (Handoff), zusaetzlich zu den
     fluiden --fs-*-Stufen oben. H1 22/29 700, Modultitel 16/22 700,
     Body 13/20, Meta 10/15. */
  --k-h1: 700 22px/29px var(--k-font);
  --k-modul: 700 16px/22px var(--k-font);
  --k-body: 400 13px/20px var(--k-font);
  --k-meta: 400 10px/15px var(--k-font);
  --k-font: "Noto Sans", system-ui, -apple-system, "Segoe UI", sans-serif;
  --k-font-thai: "Noto Sans Thai", "Thonburi", system-ui, sans-serif;
  /* Fund 41 (05.09.2026): ohne diese Ansage rendert der Browser JEDES
     Systemsteuerelement hell — im Dunkelmodus waren die Kaestchen der
     Einwilligungen weisse Quadrate und ein Knopf ohne Klasse stand als
     weisse Schrift auf der hellen Standardflaeche. `color-scheme` ist
     kein Token, sondern die Ansage an den Browser, welchen Satz seiner
     eigenen Farben er nehmen soll (Kaestchen, Auswahlfelder, Rollbalken,
     Textmarken). */
  color-scheme: light;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --page: #0F1A17;
    --surface: #16241F;
    --ink: #EAF3F0;         /* auf --page 15,74:1 * auf --surface 14,22:1 */
    --ink-2: #9FB8B0;       /* = --muted, siehe dort */
    --muted: #9FB8B0;       /* auf --page 8,44:1 * auf --surface 7,62:1 */
    --grid: #2c2c2a;
    --baseline: #73726c;
    --midline: #e0a63c;
    --border: #2A423C;      /* dekorative Kante/Trenner (nicht 3:1-pflichtig) */
    --border-strong: #6FA89D; /* traegt: 6,58:1 auf --page, 5,94:1 auf --surface */
    /* dunkel: helles Tuerkis wie im Entwurf; Schrift DARAUF wird dunkel
       (--on-brand), sonst reisst die Flaeche den Textkontrast */
    --brand: #4FD8BE;        /* als Text auf --page 10,08:1 * auf --surface 9,11:1 */
    --brand-bright: #7BE8D3;
    --brand-ink: #2FE0C0;   /* "brand-strong"/Aktiv dunkel: 10,63:1 auf --page, 9,60:1 auf --surface */
    --brand-soft: #16332D;  /* "soft"/Hover dunkel */
    --global-accent: #E8BE6E;  /* Gold dunkel: 10,18:1 auf --page, 9,20:1 auf --surface -- sparsam wie hell */
    --listen: #1baf7a;       /* dunkel: der Entwurfswert traegt hier (4,97:1) */
    --on-brand: #06231f;     /* dunkle Schrift auf --brand: 9,38:1 */
    /* dunkel: --good ist derselbe Ton wie hell (4,96:1 mit --on-good),
       --critical wird zum helleren #e66767 — Weiss faellt darauf auf
       3,23:1, eine tiefrote Schrift traegt 5,92:1. */
    --on-good: #04240a;      /* dunkle Schrift auf --good: 9,24:1 */
    --on-critical: #210707;  /* dunkle Schrift auf --critical: 8,33:1 */
    --brand-glow: rgba(20, 214, 192, 0.24);
    --band-fill: rgba(20, 214, 192, 0.16);
    --band-edge: #0e8d7f;
    --good: #59D98A;         /* Erfolg dunkel: 9,93:1 auf --page, 8,97:1 auf --surface */
    --good-text: #59D98A;
    --critical: #FF8A75;     /* Fehler dunkel: 7,75:1 auf --page, 7,00:1 auf --surface */
    --warning: #F0B429;      /* Warnung dunkel: 9,54:1 auf --page, 8,62:1 auf --surface */
    /* dunkel: die Flaeche bleibt derselbe Ton wie hell (die dunkle
       Schrift darauf traegt 5,02:1), und weil der Ton auf dunklem Grund von
       selbst leuchtet, ist er dort AUCH die Textfarbe: auf --surface
       (#1a1a19) 5,61:1, auf --page (#0d0d0d) 6,26:1. Genau die Bauweise von
       --good / --good-text. */
    --near: #E8701A;
    --near-text: #E8701A;
    --on-near: #3d1a00;
    --cls-mid: #86b6ef;
    --cls-high: #f3c04c;
    --cls-low: #6fce9e;
    --cls-mid-bg: rgba(57, 135, 229, 0.20);
    --cls-high-bg: rgba(250, 178, 25, 0.14);
    --cls-low-bg: rgba(31, 122, 77, 0.22);
    --strict-ink: #f3c04c;   /* auf --cls-high-bg 7,78:1 */
    --cat-tone-bg: #211b3d;   --cat-tone-line: rgba(160, 120, 240, 0.22);
    --cat-listen-bg: #132c24; --cat-listen-line: rgba(90, 210, 150, 0.20);
    --cat-apply-bg: #2d2318;  --cat-apply-line: rgba(240, 180, 90, 0.20);
    --cat-vocab-bg: #2d1b28;  --cat-vocab-line: rgba(240, 120, 170, 0.20);
    --fy-weak-bg: #2c1a1a;    --fy-weak-ink: #ff9d8a;  /* 8,22:1 */
    --fy-nb-bg: #1a243d;      --fy-nb-ink: #8fb8ff;    /* 7,68:1 */
    --finger-l5: #b1a6e6; --finger-l4: #8ab6e8; --finger-l3: #6ec7b7;
    --finger-l2: #97c471; --finger-r2: #e5b45a; --finger-r3: #e2996b;
    --finger-r4: #e28b9f; --finger-r5: #bf9cc4;
    --tone-mid: var(--cls-mid);
    --tone-low: var(--cls-low);
    --tone-falling: var(--critical);
    --tone-high: var(--cls-high);
    --tone-rising: #b18cf0;
    --shadow: none;
    --lift: 0 0 0 1px var(--border);
    --elevated-shadow: 0 0 0 0 transparent;
    --elevated-ring: 0 0 0 1px var(--border-strong);
    color-scheme: dark;
  }
}
:root[data-theme="dark"] {
    --cls-mid: #86b6ef;
    --cls-high: #f3c04c;
    --cls-low: #6fce9e;
    --cls-mid-bg: rgba(57, 135, 229, 0.20);
    --cls-high-bg: rgba(250, 178, 25, 0.14);
    --cls-low-bg: rgba(31, 122, 77, 0.22);
    --strict-ink: #f3c04c;   /* auf --cls-high-bg 7,78:1 */
    --page: #0F1A17;
    --surface: #16241F;
    --ink: #EAF3F0;         /* auf --page 15,74:1 * auf --surface 14,22:1 */
    --ink-2: #9FB8B0;       /* = --muted, siehe dort */
    --muted: #9FB8B0;       /* auf --page 8,44:1 * auf --surface 7,62:1 */
    --grid: #2c2c2a;
    --baseline: #73726c;
    --midline: #e0a63c;
    --border: #2A423C;      /* dekorative Kante/Trenner (nicht 3:1-pflichtig) */
    --border-strong: #6FA89D; /* traegt: 6,58:1 auf --page, 5,94:1 auf --surface */
    --brand: #4FD8BE;       /* als Text auf --page 10,08:1 * auf --surface 9,11:1 */
    --brand-bright: #7BE8D3;
    --brand-ink: #2FE0C0;   /* "brand-strong"/Aktiv dunkel: 10,63:1 auf --page, 9,60:1 auf --surface */
    --brand-soft: #16332D;  /* "soft"/Hover dunkel */
    --global-accent: #E8BE6E;  /* Gold dunkel: 10,18:1 auf --page, 9,20:1 auf --surface -- sparsam wie hell */
    --listen: #1baf7a;       /* dunkel: der Entwurfswert traegt hier (4,97:1) */
    --on-brand: #06231f;     /* dunkle Schrift auf --brand: 9,38:1 */
    --on-good: #04240a;      /* dunkle Schrift auf --good: 9,24:1 */
    --on-critical: #210707;  /* dunkle Schrift auf --critical: 8,33:1 */
    --brand-glow: rgba(20, 214, 192, 0.24);
    --band-fill: rgba(20, 214, 192, 0.16);
    --band-edge: #0e8d7f;
    --good: #59D98A;         /* Erfolg dunkel: 9,93:1 auf --page, 8,97:1 auf --surface */
    --good-text: #59D98A;
    --critical: #FF8A75;     /* Fehler dunkel: 7,75:1 auf --page, 7,00:1 auf --surface */
    --warning: #F0B429;      /* Warnung dunkel: 9,54:1 auf --page, 8,62:1 auf --surface */
    /* dunkel: die Flaeche bleibt derselbe Ton wie hell (die dunkle
       Schrift darauf traegt 5,02:1), und weil der Ton auf dunklem Grund von
       selbst leuchtet, ist er dort AUCH die Textfarbe: auf --surface
       (#1a1a19) 5,61:1, auf --page (#0d0d0d) 6,26:1. Genau die Bauweise von
       --good / --good-text. */
    --near: #E8701A;
    --near-text: #E8701A;
    --on-near: #3d1a00;
    --cat-tone-bg: #211b3d;   --cat-tone-line: rgba(160, 120, 240, 0.22);
    --cat-listen-bg: #132c24; --cat-listen-line: rgba(90, 210, 150, 0.20);
    --cat-apply-bg: #2d2318;  --cat-apply-line: rgba(240, 180, 90, 0.20);
    --cat-vocab-bg: #2d1b28;  --cat-vocab-line: rgba(240, 120, 170, 0.20);
    --fy-weak-bg: #2c1a1a;    --fy-weak-ink: #ff9d8a;  /* 8,22:1 */
    --fy-nb-bg: #1a243d;      --fy-nb-ink: #8fb8ff;    /* 7,68:1 */
    --finger-l5: #b1a6e6; --finger-l4: #8ab6e8; --finger-l3: #6ec7b7;
    --finger-l2: #97c471; --finger-r2: #e5b45a; --finger-r3: #e2996b;
    --finger-r4: #e28b9f; --finger-r5: #bf9cc4;
    --tone-mid: var(--cls-mid);
    --tone-low: var(--cls-low);
    --tone-falling: var(--critical);
    --tone-high: var(--cls-high);
    --tone-rising: #b18cf0;
    --shadow: none;
    --lift: 0 0 0 1px var(--border);
    --elevated-shadow: 0 0 0 0 transparent;
    --elevated-ring: 0 0 0 1px var(--border-strong);
    color-scheme: dark;
}

/* Auftrag 271: Noto Sans (Latin + Latin-ext) und Noto Sans Thai gebuendelt
   unter webapp/fonts/ (OFL.txt daneben) — kein Nachladen beim Deployment.
   font-display: swap haelt den Text sichtbar, waehrend die Datei laedt;
   ohne Internet beim Erstaufruf bleibt system-ui stehen (Fallback-Kette). */
@font-face {
  font-family: "Noto Sans";
  src: url("fonts/NotoSans-Regular.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Noto Sans";
  src: url("fonts/NotoSans-Bold.woff2") format("woff2");
  font-weight: 700; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Noto Sans Thai";
  src: url("fonts/NotoSansThai-Regular.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Noto Sans Thai";
  src: url("fonts/NotoSansThai-Bold.woff2") format("woff2");
  font-weight: 700; font-style: normal; font-display: swap;
}
* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
body {
  background:
    radial-gradient(120% 40% at 50% 0%, var(--brand-glow), transparent 70%),
    var(--page);
  color: var(--ink);
  font-family: var(--k-font);
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  min-height: 100vh;
}
/* buttons neither inherit font nor color by default — force both once */
button { font: inherit; color: inherit; }
/* Ein sichtbarer Fokus fuer ALLES, was Tastaturfokus bekommen kann
   (WCAG 2.4.7). Bis 05.09.2026 gab es rund zwanzig punktuelle
   `:focus-visible`-Regeln — und rund dreissig klickbare Formen ohne eine
   (.btn, .seg-btn, .lesson-card, .discrim-option, .goal-opt, .duel-chip,
   .path-step, .k-badgestrip-item …), die auf dem Standardring des
   Browsers sassen. Diese Grundregel steht in `:where(...)` und hat damit
   Spezifitaet NULL: jede bestehende Regel gewinnt weiter gegen sie, und
   keine Ansicht muss angefasst werden. */
:where(a[href], area, button, input, select, textarea, summary,
       [tabindex]:not([tabindex="-1"])):focus-visible {
  outline: 3px solid var(--focus);
  outline-offset: 2px;
}
#app { margin: 0 auto; padding: 0; }
.view { max-width: var(--wrap); margin: 0 auto; padding: 0 clamp(16px, 4vw, 32px) 56px; }
/* Seitenboden unter festen Blaettern (Auftrag 264, Owner-Befund 25.09.2026
   20:0x): die beiden bisherigen Werte (56px hier, 96px unten fuer
   <=760px) reservierten nur Kopf-/Fusszeile, nie die Hoehe eines
   kBlattFest-Stapels am Fuss (Baustein 9b2, `--k-blattfest-boden`, kit.js)
   -- ein Nutzer konnte die unteren Kacheln NICHT hinter dem Blatt
   hervorscrollen. Gilt fuer 761-899px hier (Blaetter dort noch `position:
   fixed`, siehe die 761-899.98px-Regel bei `.k-blattfest`); die
   <=760px-Regel steht unten bei `.view` selbst, die Ausnahme fuer
   `.has-thumbbar`-Seiten (Daumenleiste, siehe dort) in beiden Faellen.
   Ab 900px bleibt es beim Grundwert (Blaetter dort statisch, R7). */
@media (min-width: 761px) and (max-width: 899.98px) {
  /* Fable-Review 25.09.2026 (vor dem Commit): ohne die Ausnahme wuchs die
     Uebungsseite auf 768x1024 nach dem Urteil um die Stapelhoehe und wurde
     scrollbar -- dieselbe Regressionsklasse, die bei <=760px schon einmal
     zurueckgenommen wurde (siehe die `.view:not(:has(.has-thumbbar))`-
     Regel dort). `.has-thumbbar` bleibt in diesem Bruchband, weil der
     Stapel dort NOCH `position: fixed` ist (Regel bei 761-899.98px oben,
     `.k-blattfest`) -- ihre eigene Reservierung will die Uebungsseite auch
     hier nicht per Boden-Zuschlag verdoppeln, siehe dort. */
  .view:not(:has(.has-thumbbar)) {
    padding-bottom: calc(56px + var(--k-blattfest-boden, 0px));
  }
}
/* Der programmatische Fokus nach jedem Ansichtsaufbau (chrome.js
   fokusAufTitel: h1/.k-pagehead-title, sonst der .view selbst, alle mit
   tabindex -1) ist fuer Vorleser da, nicht fuers Auge: die Grundregel oben
   schliesst tabindex -1 zwar aus, der BROWSER zeichnet seinen eigenen Ring
   aber trotzdem (Safari immer, Chrome nach Tastaturbedienung) -- der
   hellblaue Rahmen um den ganzen Inhalt (Owner 25.09.2026, Auftrag 251). */
.view:focus, .view:focus-visible,
[tabindex="-1"]:is(h1, .k-pagehead-title):focus { outline: none; }
/* ---- KIT-Responsivitaet (Owner-Befund 01.09.) ----------------------
   Die alte Regel „jede Sektion ist 640 px breit, ausser diesen fuenf"
   ist gestorben. Sie war der Grund, warum die App auf dem Desktop eine
   Handy-Saeule in der Mitte blieb und warum jede neue Ansicht eine
   Ausnahme in die Liste schreiben musste.

   Neue Regel, zweiteilig:
   1. Jede Sektion ist FLUIDE bis --wrap. Das ist die Voreinstellung.
   2. Wer eine Lesebreite braucht (linearer Text, Assistent, Uebungs-
      karte), traegt `k-flow` — dann gilt --measure, und der Inhalt
      bleibt zentriert.
   Das Dehnverhalten steht damit am Baustein, nicht in einer zentralen
   Ausnahmeliste. */
.view > section { width: 100%; margin-inline-start: auto; margin-inline-end: auto; }
.view > section.k-flow { max-width: var(--measure); }
.view > section.k-flow-wide { max-width: 860px; }
.view > section { animation: fadein .22s ease; }
@keyframes fadein { from { opacity: 0; transform: translateY(4px); } }

/* ---- chrome ---- */
.topbar {
  position: sticky; top: 0; z-index: 50;
  background: color-mix(in srgb, var(--page) 86%, transparent);
  backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px);
  border-bottom: 1px solid var(--border);
  padding: 0 clamp(16px, 4vw, 32px);
}
.topbar-inner {
  max-width: 1140px; margin: 0 auto; height: 56px;
  display: flex; align-items: center; justify-content: space-between;
}
.brand { font-weight: 750; font-size: 1.05rem; letter-spacing: -0.01em;
  display: inline-flex; align-items: center; gap: 8px; }
.brand span { color: var(--brand); }
/* Der App-Name ist ein <button> (Auftrag 102, Punkt 3): eigene Reset-Regeln
   statt der Knopf-Vorgaben des Browsers, plus ein sichtbarer Fokusrahmen
   fuer die Tastatur — `.brand` selbst bleibt fuer BEIDE Traeger gueltig
   (Kopfzeile als Knopf, Anmeldeseite `.brand-big` weiter als reine Marke). */
#brand-home { background: none; border: 0; padding: 0; margin: 0;
  color: inherit; cursor: pointer; text-align: start; }
#brand-home:focus-visible { outline: 3px solid var(--focus);
  outline-offset: 3px; border-radius: var(--r-1); }
.brand-big { font-size: 1.7rem; justify-content: center; width: 100%; }
.logo {
  width: 30px; height: 30px; border-radius: 9px; flex: none;
  background: linear-gradient(135deg, var(--brand), var(--brand-ink));
  display: grid; place-items: center; color: var(--on-brand);
  box-shadow: 0 2px 8px var(--brand-glow);
}
.brand-big .logo { width: 44px; height: 44px; border-radius: 13px; }
.logo svg { display: block; }
.topbar-right { display: flex; align-items: center; gap: 10px; }
/* WCAG 2.2 AA Ziel 44 px (Auftrag 271, Punkt 7): Kopfzeilen-Knoepfe waren
   knapp (`.btn.small` = 6px/12px Innenabstand). */
.topbar-right .btn { min-width: 44px; min-height: 44px;
  display: inline-flex; align-items: center; justify-content: center; }
/* Sprachpille "◎ DE" (Auftrag 271, Owner-Entscheid 2): rund wie im Handoff
   ("Pillen rund"), Symbol + Sprachcode. */
.lang-pill { border-radius: var(--r-pill); gap: 4px; padding-inline: 12px; font-weight: 650; }
.lang-pill svg { width: 18px; height: 18px; flex: none; }
#nav-more svg { width: 20px; height: 20px; }
.user-email { color: var(--muted); font-size: 0.8rem; max-width: 160px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.h1 { font-size: clamp(1.25rem, 1.1rem + 0.8vw, 1.6rem); margin: 26px 2px 14px;
  letter-spacing: -0.015em; }

.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 16px;
  box-shadow: var(--shadow);
  padding: 20px;
}

/* ---- buttons ---- */
.btn {
  font: inherit; border-radius: 12px; border: 1px solid var(--border);
  background: var(--surface); color: var(--ink);
  padding: 10px 18px; cursor: pointer;
  transition: transform .06s ease, background .15s ease, opacity .15s ease;
  touch-action: manipulation;
}
.btn:active { transform: scale(0.97); }
.btn.primary { background: var(--brand); border-color: var(--brand); color: var(--on-brand); font-weight: 600; }
.btn.primary:hover { filter: brightness(1.06); }
/* Derselbe Befund wie F1 am sekundaeren Kit-Knopf (ad76a22): `.btn.ghost`
   war `transparent` mit Hover auf `--surface` — der Farbe der Karte, auf
   der solche Knoepfe fast immer stehen; beim Ueberfahren passierte
   sichtbar nichts. Gleiche Behandlung wie dort: Ruhe `--grid`, Hover
   `--brand-soft` mit mitwechselndem Rand und mitwechselnder Schrift,
   weil die zwei Flaechen im Dunkelmodus dicht beieinander liegen. */
.btn.ghost { background: var(--grid); }
.btn.ghost:hover {
  background: var(--brand-soft); border-color: var(--brand); color: var(--brand-ink);
}
.btn.small { padding: 6px 12px; font-size: 0.85rem; border-radius: 10px; }
.btn.block { width: 100%; }
.btn:disabled { opacity: 0.5; cursor: default; }

/* ---- auth ---- */
.auth-card { margin-top: 12vh; display: flex; flex-direction: column; gap: 14px; }
.tagline { color: var(--ink-2); text-align: center; margin: 0 0 6px; }
.seg { display: flex; background: var(--page); border-radius: 12px; padding: 4px; gap: 4px; }
.seg-btn {
  flex: 1; font: inherit; border: 0; background: transparent; color: var(--ink-2);
  padding: 8px; border-radius: 9px; cursor: pointer; font-weight: 550;
}
.seg-btn.active { background: var(--surface); color: var(--ink); box-shadow: var(--lift); }
.field { display: flex; flex-direction: column; gap: 6px; margin-bottom: 12px; }
.field span { font-size: 0.85rem; color: var(--ink-2); font-weight: 550; }
.field input {
  font: inherit; padding: 11px 12px; border-radius: 11px;
  border: 1px solid var(--border-strong); background: var(--surface); color: var(--ink);
}
.field input:focus { outline: 3px solid var(--focus); outline-offset: 1px; border-color: transparent; }
.hint { color: var(--muted); }
.form-error { color: var(--critical); font-size: 0.88rem; margin: 10px 2px 0; }

/* ---- home ---- */
.banner {
  border-radius: 14px; padding: 14px 16px; margin-top: 16px;
  display: flex; gap: 12px; align-items: flex-start;
  background: var(--band-fill); color: var(--ink);
  border: 1px solid var(--band-edge);
}
.banner .emoji { font-size: 1.3rem; line-height: 1.2; }
/* Auftrag 271c: Gast-Banner-Symbol als Linien-SVG statt Emoji. */
.banner .emoji svg { width: 1.3rem; height: 1.3rem; display: block; }
.banner p { margin: 0; font-size: 0.9rem; }
.banner strong { display: block; margin-bottom: 2px; }
/* Stimmprofil-Karte im Konto (03.09.2026): die beiden Verwaltungs-
   knoepfe standen bei 390 px in derselben Zeile wie der Text und
   quetschten ihn auf ~90 px — sechs Zeilen fuer zwei Saetze. Jetzt
   nimmt der Text die Zeile, die Knoepfe stehen darunter, gleich breit.
   Ab 560 px ist Platz, dann stehen sie wieder rechts neben dem Text.
   Dehnung: der Text waechst, die Knopfzeile bricht um und teilt sich
   haelftig; ueber 560 px wachsen die Knoepfe nicht mehr. */
.banner-manage { flex-wrap: wrap; }
.banner-manage p { flex: 1 1 12rem; }
.banner-actions { display: flex; gap: var(--sp-2); flex: 1 1 100%; }
.banner-actions .btn { flex: 1 1 0; min-width: 0; }
@media (min-width: 560px) {
  .banner-actions { flex: 0 0 auto; margin-inline-start: auto; }
  .banner-actions .btn { flex: 0 0 auto; }
}
/* Begruessungsbereich (Vorlage V7.7 Salbei Global, Auftrag 271): Meta-Zeile
   klein/leise ueber der H1, die H1 selbst auf die feste Typo-Stufe --k-h1
   (22/29 700) -- vorher trug .greeting nur die Meta-Groesse (0,85rem),
   das war vor der Vorlage keine eigene Ueberschriftenstufe. */
.home-hero { position: relative; margin: 18px 0 0; padding-inline-end: 56px; }
.home-meta { color: var(--muted); font-size: var(--fs-1); margin: 0 2px 4px; }
.greeting { color: var(--ink); font: var(--k-h1); margin: 0 2px; }
/* Meridian-Dekor: markante, rein dekorative Linie rechts oben im
   Begruessungsbereich (Handoff), Gold sparsam als Region-/Markenakzent. */
.home-meridian { position: absolute; top: -6px; inset-inline-end: 0;
  width: 48px; height: 48px; opacity: 0.55; }
.lesson-list {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
  gap: 14px; align-items: stretch; }
.lesson-list > .cat-head { grid-column: 1 / -1; }
.lesson-card { display: flex; flex-direction: column; }
.lesson-card .lesson-meta { margin-top: auto; }
.lesson-card {
  display: block; width: 100%; text-align: start; cursor: pointer;
  position: relative; overflow: hidden; padding-inline-start: 24px;
  transition: transform .12s ease, box-shadow .12s ease;
}
.lesson-card::before {
  content: ""; position: absolute; inset-inline-start: 0; top: 0; bottom: 0; width: 5px;
  background: linear-gradient(180deg, var(--brand), var(--band-edge));
}
.lesson-card:hover { transform: translateY(-1px); box-shadow: var(--lift); }
.lesson-card h2 { margin: 0 0 4px; font-size: 1.02rem; color: var(--ink); }
.lesson-card p { margin: 0; color: var(--ink-2); font-size: 0.88rem; }
/* Modulkarte (Auftrag 275): das Symbol, das bis dahin als fuehrende Emoji-
   Glyphe im Titeltext stand, sitzt jetzt in einer eigenen `.k-icon-cell`
   VOR der Ueberschrift -- dieselbe Zelle wie kBtn/kListRow/kBanner. */
.module-head { display: flex; align-items: center; gap: 8px; margin-bottom: 4px; }
.module-head h2 { margin: 0; }
.module-head .k-icon-cell { width: 26px; height: 26px; color: var(--brand); }
.module-head .k-icon-cell svg { width: 20px; height: 20px; }
.lesson-meta { margin-top: 12px; color: var(--muted); font-size: 0.8rem;
  display: flex; justify-content: space-between; align-items: center; }
.lesson-meta .go { color: var(--brand); font-weight: 600; }
.lesson-progress { display: inline-flex; align-items: center; gap: 6px; }
.lesson-progress .bar {
  width: 74px; height: 5px; border-radius: 3px; background: var(--grid);
  overflow: hidden; display: inline-block;
}
.lesson-progress .bar i { display: block; height: 100%; background: var(--good-text); border-radius: 3px; }

/* ---- breadcrumbs ---- */
/* small, muted trail above the content: "Start › Bereich › Seite".
   Every link but the last navigates (navGo); the last one is the page you
   are on and stays a plain span. */
.crumbs {
  display: flex; flex-wrap: wrap; align-items: center; gap: 3px;
  font-size: 0.82rem; line-height: 1.4; color: var(--muted); margin: 0;
}
.crumbs .crumb-sep { color: var(--grid); padding: 0 1px; }
.crumbs .crumb {
  font: inherit; padding: 2px 3px; border-radius: 6px;
  max-width: 24ch; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.crumbs button.crumb {
  border: 0; background: transparent; color: var(--ink-2); cursor: pointer;
}
.crumbs button.crumb:hover { color: var(--brand); text-decoration: underline; }
.crumbs button.crumb:focus-visible { outline: 3px solid var(--focus); outline-offset: 1px; }
.crumbs .crumb.current { color: var(--ink); font-weight: 600; }
/* standalone (summary card): needs its own breathing room */
section > .crumbs { margin: 12px 2px 6px; }
.lesson-list > .crumbs, .lesson-list > .k-pagehead { grid-column: 1 / -1; }

/* ---- practice ---- */
.progress-dots { display: flex; gap: 6px; }
.progress-dots i {
  width: 8px; height: 8px; border-radius: 50%; background: var(--grid); display: block;
  transition: background .2s ease, transform .2s ease;
}
.progress-dots i.done { background: var(--good-text); }
.progress-dots i.now { background: var(--brand); transform: scale(1.35); }
/* E6 (Auftrag 112): ab neun Schritten (l5: zwoelf, l6: dreizehn) schnitt
   ,← Lektion' im Kopf ab (,- Lektio') — die Punkte nahmen dem
   Zurueck-Knopf die Breite. Ab neun Schritten werden sie kleiner und
   enger; die Kurzform ,Lektion' bleibt lesbar. */
.progress-dots.many { gap: 3px; }
.progress-dots.many i { width: 5px; height: 5px; }
.progress-dots.many i.now { transform: scale(1.4); }

.item-card { text-align: center; }
.thai { font-size: 3.4rem; line-height: 1.25; font-weight: 500;
  font-family: "Noto Sans Thai", "Thonburi", system-ui, sans-serif; }
.gloss { color: var(--ink-2); margin-top: 6px; }
/* Auftrag 271c: Platzhalter-Symbol im Wort-Feld (Diskriminationsmodus
   "nur hoeren", statt des Emoji 👂 vorher). */
.thai-icon-symbol { width: 44px; height: 44px; flex: none; }
/* Auftrag 271c: Zeilen-Symbol vor einem Textknopf (Lernheft/Merken/Melden,
   Schreib-Chip, Abschluss-Knopf) -- inline in der Schrift, keine eigene
   32-px-Zelle noetig, weil der Knopf schon seinen eigenen Rand traegt. */
.item-row-icon { width: 1em; height: 1em; vertical-align: -0.2em; flex: none;
  margin-inline-end: 3px; }
.item-row-icon.is-active { fill: currentColor; }

.record-zone { display: flex; flex-direction: column; align-items: center; gap: 12px; margin: 26px 0; }
/* Auftrag 246b: die Aufnahmezone bleibt beim Urteil-Blatt bedienbar,
   solange dessen Stufe "klein"/"halb" ist (Owner-Punkt "Mikro oberhalb",
   seit 246 dreimal vertagt) -- practice.js setzt diese Klasse nur bei
   Stufe "voll" (dort deckt das Blatt sie ohnehin ab, 85vh). Ab 900px bleibt
   sie IMMER verborgen, sobald ein Ergebnis steht (unveraendert wie vor
   diesem Auftrag): die Seitenleiste zeigt dort schon den vollen Inhalt,
   kein Skript fragt das Sichtfeld ab (:has() statt matchMedia). */
#record-zone.record-zone-blatt-voll { display: none; }
@media (min-width: 900px) {
  .practice:has(#result-panel:not([hidden])) #record-zone { display: none; }
}
@keyframes pulse {
  0% { transform: scale(1); opacity: .6; }
  100% { transform: scale(1.25); opacity: 0; }
}
.spinner {
  width: 22px; height: 22px; border-radius: 50%;
  border: 3px solid rgba(255,255,255,.4); border-top-color: #fff;
  animation: spin .8s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* ---- result ---- */
.result-panel { margin-top: 8px; display: flex; flex-direction: column; gap: 16px; }
.result-head { display: flex; align-items: center; gap: 16px; }
/* Der Score 0-100 ist gestrichen (Owner-Entscheid 01.09.). An seiner
   Stelle steht die Kit-Kennzahl-Kachel mit der Ton-Trefferquote; sie
   braucht hier nur ihre Breite im Ergebniskopf. */
.result-head .k-stat { flex: 0 0 auto; min-width: 116px; }
.verdict { display: flex; align-items: center; flex-wrap: wrap; gap: 4px 8px;
  font-weight: 650; }
/* Untertitel: Diagnose und Aussprache in EINER leisen Zeile unter dem
   Urteil. Sie faengt hinter dem Abzeichen an, damit die Zeile nicht wie
   ein zweites Urteil aussieht.
   Dehnung: volle Breite der Urteilszeile, bricht in sich um. */
.verdict-sub { flex: 1 1 100%; display: flex; flex-wrap: wrap;
  align-items: baseline; gap: 2px 10px; margin-inline-start: 32px;
  font-size: var(--fs-1); font-weight: 500; color: var(--ink-2); }
.verdict-diag { color: var(--ink-2); }
.verdict-schreib { border: 1px solid var(--border); background: var(--surface); color: var(--ink-2);
  border-radius: 999px; padding: 3px 10px; font: inherit; font-size: 12px; cursor: pointer; }
.verdict-schreib:hover { border-color: var(--border-strong); color: var(--ink); }
.verdict-diag.cue-tip { color: var(--muted); }
.verdict.ok { color: var(--good-text); }
.verdict.fail { color: var(--critical); }
/* 24 px, und der Glyph sitzt mittig: als Flexkind der Urteilszeile
   wurde der Kreis vorher mitgedehnt, und `place-items` allein hielt das
   Zeichen nicht in der Mitte (Auftrag 77b, Punkt 2). `flex: none` und
   `line-height: 1` sind die beiden Zeilen, die es festhalten. */
.verdict .badge {
  flex: none; width: 24px; height: 24px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  line-height: 1; text-align: center; font-size: 0.78rem; font-weight: 700;
  padding: 0;
}
.verdict.ok .badge { background: var(--good); color: var(--on-good); }
.verdict.fail .badge { background: var(--critical); color: var(--on-critical); }

.chart-block { background: var(--surface); border: 1px solid var(--border);
  border-radius: 14px; padding: 14px; }
.legend { display: flex; flex-wrap: wrap; gap: 6px 16px; font-size: 0.8rem;
  color: var(--ink-2); margin-bottom: 8px; }
.legend-item { display: inline-flex; align-items: center; gap: 6px; white-space: nowrap; }
.swatch { width: 14px; height: 10px; border-radius: 3px; display: inline-block; }
.swatch.band { background: var(--band-fill); border: 1px solid var(--band-edge); }
.swatch.line { background: var(--brand); height: 3px; border-radius: 2px; }
.swatch.midline { background: var(--midline); height: 3px; border-radius: 0; }
.swatch.turn-target, .swatch.turn-you {
  width: 10px; height: 10px; border-radius: 50%;
}
.swatch.turn-target { background: var(--surface); border: 2px solid var(--band-edge); }
.swatch.turn-you { background: var(--brand); }
.chart-caption { color: var(--muted); font-size: 0.78rem; margin-top: 8px; text-align: center; }

.cues { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.cues li {
  background: var(--surface); border: 1px solid var(--border); border-radius: 12px;
  padding: 10px 14px; font-size: 0.92rem; display: flex; gap: 10px; align-items: flex-start;
}
.cues li::before { content: "💡"; }
/* §4.1: Diagnose als TIPP auf einem bestandenen (oder gelben) Versuch —
   gedaempft, damit sie nicht wie eine Ruege aussieht. */
.cues li.cue-tip { color: var(--muted); background: transparent; }
.cues li.cue-tip::before { content: "→"; }
/* Die uebrigen Tipps: EINE leise Zeile unter der Kurvenkarte statt
   dreier Karten (Auftrag 77b, Punkt 3). Das Gluehbirnen-Zeichen steht
   einmal davor, die Saetze trennt ein Mittelpunkt.
   Dehnung: volle Breite, bricht um; keine Kartenflaeche, kein Rahmen. */
.cues li.cue-line { display: block; background: transparent; border: 0;
  padding: 2px 2px 0; font-size: 0.82rem; color: var(--ink-2); }
.cues li.cue-line::before { content: "💡 "; }
.drift-note { color: var(--muted); font-size: 0.8rem; margin: -6px 2px 0; }

/* verdict withheld: the take was not clear enough to judge */
.unclear-card {
  background: var(--surface); border: 1px solid var(--border); border-radius: 14px;
  padding: 18px; display: flex; flex-direction: column; gap: 10px; align-items: flex-start;
}
.unclear-title { font-weight: 650; font-size: 1.05rem; }
.unclear-text { margin: 0; color: var(--ink-2); font-size: 0.92rem; }
.unclear-card .btn { align-self: stretch; padding: 13px; }

/* Die Knopfzeile bricht um, statt aus der Karte zu laufen: die
   Kontext-Leiter haengt an ihre Zusammenfassung bis zu drei eigene
   Knoepfe (Ausweg, nochmal, naechstes Wort) — mit "Zur Uebersicht" sind
   das vier, und vier nebeneinander passen mobil nicht mehr. Bei zwei
   Knoepfen aendert sich nichts, sie fuellen die Zeile weiter je zur
   Haelfte. */
.result-actions { display: flex; flex-wrap: wrap; gap: 10px; }
.result-actions .btn { flex: 1 1 8.5rem; padding: 13px; }

/* Item-Zeile im Urteil-Blatt (Auftrag 246, Figma 482:742): drei kurze
   Knoepfe -- Lernheft, Merken, Melden -- ab Stufe "halb" (Klasse
   `k-blattfest-nur-halb` regelt die Sichtbarkeit, siehe Baustein 44). */
.item-row-urteil { display: flex; gap: 8px; flex-wrap: wrap; }
.item-row-urteil .btn { flex: 1 1 8rem; }
.item-row-urteil .btn.is-on { border-color: var(--brand); color: var(--brand); }

/* Werte-Tabelle (Stufe "voll"): die schon berechneten Kennzahlen des
   Versuchs (Naehe, Registerabweichung, Aussprache-Score) als Paare statt
   als verstreute Saetze -- keine neue Messung, nur eine zweite Ansicht auf
   dieselben Felder. */
.k-wertetabelle { display: flex; flex-direction: column; gap: 4px;
  border: 1px solid var(--border); border-radius: var(--r-3); padding: 10px 14px;
  font-size: 0.86rem; }
.k-wertetabelle-zeile { display: flex; justify-content: space-between; gap: 12px; }
.k-wertetabelle-label { color: var(--ink-2); }
.k-wertetabelle-value { font-weight: 650; }

/* ---- toast ---- */
/* Der Toast weicht der Daumenleiste aus: mobil steigt er ueber sie,
   auf breiten Schirmen geht er nach links unten — die Aktionszeile
   steht dort in der rechten Spalte (Auftrag 77b, Punkt 8). Ohne Leiste
   bleibt alles, wie es war. */
body:has(.k-thumbbar:not([hidden])) .toast {
  bottom: calc(164px + env(safe-area-inset-bottom, 0px)); }
@media (min-width: 761px) {
  body:has(.k-thumbbar:not([hidden])) .toast {
    /* rechts NEBEN der Seitenleiste (232 px), nicht darunter */
    inset-inline-start: 256px; inset-inline-end: auto; bottom: 26px; transform: none; animation: none; }
}
.toast {
  position: fixed; inset-inline-start: 50%; bottom: 26px; transform: translateX(-50%);
  background: var(--ink); color: var(--page);
  border-radius: 12px; padding: 11px 18px; font-size: 0.9rem;
  box-shadow: var(--elevated-ring), var(--elevated-shadow);
  max-width: min(90vw, 440px); text-align: center;
  animation: rise .25s ease;
}
@keyframes rise { from { opacity: 0; transform: translate(-50%, 8px); } }

/* ---- Versionshinweis ----------------------------------------------
   Der Hinweis steht ZWISCHEN Kopfzeile und Inhalt, nicht im Inhalt: der
   Inhaltsbereich wird bei jedem Ansichtswechsel ausgetauscht, der
   Hinweis soll ihn ueberdauern. Polsterung und Breite sind dieselben wie
   bei `.view`, damit er mit dem Inhalt darunter buendig steht. Eigene
   Farben hat er keine — die traegt `k-banner`. */
.version-banner {
  max-width: var(--wrap); margin: 0 auto;
  padding: var(--sp-3) clamp(16px, 4vw, 32px) 0;
}

@media (prefers-reduced-motion: reduce) {
  * { animation: none !important; transition: none !important; }
}

/* ---- model audio ---- */
.play-model {
  margin-top: 14px; display: inline-flex; align-items: center; gap: 8px;
  border-radius: 999px; padding: 8px 16px; font-size: 0.9rem; color: var(--brand);
  border-color: var(--band-edge);
}

/* ---- calibration ---- */
.cal-title { margin: 0 0 6px; font-size: 1.1rem; }
.cal-text { color: var(--ink-2); margin: 0; }
.passage {
  margin-top: 14px; padding: 14px 16px; border-radius: 12px;
  background: var(--page); border: 1px solid var(--border);
  font-family: "Noto Sans Thai", "Thonburi", system-ui, sans-serif;
  font-size: 1.02rem; line-height: 1.8; max-height: 240px; overflow-y: auto;
}
.cal-card .record-zone { margin: 22px 0 6px; }
.take-ok { color: var(--good-text); font-weight: 650; }

/* tone room (calibration task T): the learner echoes one syllable per take */
.cal-tone { text-align: center; margin-top: 18px; }
.cal-tone .thai { font-size: 4.2rem; }
.cal-tone-name { color: var(--muted); font-size: 0.85rem; margin-top: 4px; }
.cal-tone .k-playback { margin-top: 12px; }
.cal-refresh { margin-top: 10px; }
.cal-refresh .banner { align-items: flex-start; }
.vp-meta { display: flex; flex-direction: column; gap: 6px; font-size: 0.88rem;
  color: var(--ink-2); margin-bottom: 14px; }
.vp-meta .vp-row { display: flex; justify-content: space-between; gap: 12px; }
.vp-meta .vp-row b { color: var(--ink); font-weight: 600; }
.vp-empty { color: var(--muted); font-size: 0.88rem; margin: 0; }

/* ---- tone discrimination ---- */
/* Auftrag 112 (E1): die vier/fuenf Antwortkacheln mit dem handgezeichneten,
   immer markenfarbenen SVG (`.discrim-option`) sind weg — die Antwort ist
   jetzt `kHoerfrage` (siehe „Hoerfrage" im Kit-Abschnitt), dieselbe
   Bauform wie die Wahrnehmungsfrage vor dem Sprechen. Die Zone bleibt nur
   noch der Rahmen, der sie traegt. */
.discrim-zone { margin: 20px 0; display: flex; flex-direction: column; gap: 14px; }

.correction-btn { align-self: center; }

/* ---- perception before production (ideen.md 7) ---- */
.perception-zone { margin: 18px 0; display: flex; flex-direction: column; gap: 12px; }
.perception-zone .con-feedback { margin-bottom: 0; text-align: center; }
.perception-toggle { display: block; margin: 8px auto 0; }
/* E3 (Auftrag 112): der Weg unter den Optionen, der die (ausgeblendete)
   Aufnahmeleiste vorzeitig freigibt. */
.perception-skip { display: block; margin: 4px auto 0; }

.translit { font-size: 1.35rem; color: var(--ink); font-weight: 550;
  letter-spacing: 0.01em; margin-top: 2px; }

/* ---- account footer ---- */
.account-footer { margin-top: 40px; text-align: center; font-size: 0.8rem;
  color: var(--muted); display: flex; gap: 10px; justify-content: center; }
.account-footer a { color: var(--muted); }
.linklike { background: none; border: 0; padding: 0; font: inherit;
  color: var(--muted); text-decoration: underline; cursor: pointer; }
.linklike:hover, .account-footer a:hover { color: var(--critical); }

/* ---- progress ---- */
.stat-row { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; margin-bottom: 14px; }
.stat-tile { background: var(--surface); border: 1px solid var(--border);
  border-radius: 14px; padding: 12px 8px; text-align: center; }
.stat-tile .v { font-size: 1.7rem; font-weight: 700; letter-spacing: -0.02em; }
.stat-tile .l { font-size: 0.72rem; color: var(--muted); margin-top: 2px; }
.chart-card { margin-bottom: 14px; }
.card-title { font-size: 0.95rem; margin: 0 0 10px; }
/* Die Tonbalken der Fortschrittsseite (.tone-bars/.tone-row/.hbar) und
   ihre Legendenmarke sind am 04.09.2026 in den Kit-Baustein
   `kToneProfile` (.k-toneprofile) gewandert. */
.activity-chart { display: flex; align-items: flex-end; gap: 4px; height: 90px; padding-top: 8px; }
.activity-chart .day { flex: 1; display: flex; flex-direction: column;
  align-items: center; gap: 4px; height: 100%; justify-content: flex-end; }
.activity-chart .bar { width: 100%; max-width: 18px; border-radius: 4px 4px 0 0;
  background: var(--brand); min-height: 2px; }
.activity-chart .bar.zero { background: var(--grid); }
/* Beschriftet wird nur jeder dritte Tag; die anderen Tage tragen einen
   LEEREN Knoten. Ohne reservierte Hoehe ist der leer — und dann steht der
   Balken eines unbeschrifteten Tages 15 px tiefer als der seines
   beschrifteten Nachbarn (gemessen an den Screenshots vom 01.09.2026).
   `min-height` gibt jedem Tag dieselbe Grundlinie, ohne den Text zu
   aendern. */
.activity-chart .d-label { font-size: 0.62rem; color: var(--muted);
  line-height: 1.4; min-height: 1.4em; }
/* Die drei Schulen im Fortschritt (HVPT, Tastatur, Handschrift).
   Dehnung: die Karte ist eine k-card und dehnt sich wie jede andere; die
   Klasse setzt nur den Abstand zur naechsten und den Luftraum ueber den
   Zwischenzeilen („Je Ton", „Je Stufe"), damit die Raster darunter nicht
   an der vorigen Zahlenreihe kleben. */
.skill-card { margin-bottom: var(--sp-3); }
.skill-card .k-card-text.k-muted { margin-top: var(--sp-3); margin-bottom: var(--sp-1); }
.skill-card .activity-chart { height: 72px; }
/* Vergleichsraster (fuenf Toene, vier Stimmen): der Kit-Standard
   `--col-min: 240px` laesst innerhalb einer Karte auf dem Telefon nur EINE
   Spalte zu — neun gleich aussehende Kacheln stehen dann untereinander und
   die Seite wird unnoetig lang. Die Kacheln tragen hier nur eine kurze
   Beschriftung (ein Tonname, ein Stimmname), also darf die Spalte schmaler
   sein. Die Stufenraster
   behalten den Standard, weil ihre Beschriftung zwei Werte traegt. */
.skill-card .skill-raster { --col-min: 140px; }
/* Das TON-Raster ist seit Auftrag 58 (Punkt 6, 05.09.2026) keine dehnbare
   Flaeche mehr, sondern eine Reihe: fuenf Kacheln nebeneinander in jeder
   Breite (`k-grid-kompakt`). `grid-auto-rows: 1fr` und `align-items:
   stretch` halten alle fuenf gleich hoch, auch wenn eine Zahl dreistellig
   ist. Das Stimmen-Raster darunter bleibt das dehnbare — seine Namen sind
   Eigennamen und lassen sich nicht kuerzen. */
.skill-card .skill-raster-toene { align-items: stretch; grid-auto-rows: 1fr; }
/* Halbierte Polsterung, NUR hier (Owner-Entscheid 05.09.2026): die fuenf
   Kacheln sollen die vollen Woerter tragen, nicht Abkuerzungen. `--sp-1`
   statt `--sp-2` gibt jeder Kachel rund 8 px Textbreite zurueck — bei
   360 px sind das 36 -> 44 px, und genau daran haengt, ob ,steigend' noch
   hineinpasst. Global an `.k-stat-compact` waere die Regel falsch: die
   Zwei- und Drei-Kachel-Reihen am Kartenkopf haben Platz und sollen ihre
   Luft behalten. */
.skill-card .skill-raster-toene .k-stat-compact { padding: var(--sp-1); }

.weak-card::before { background: linear-gradient(180deg, var(--warning), var(--critical)); }

/* ---- tone imitation ---- */
.target-preview { margin-top: 14px; }

/* ---- level check ---- */
.level-check { display: flex; flex-direction: column; gap: 10px; margin-top: 14px; }
.level-track { height: 10px; border-radius: 5px; background: var(--grid); overflow: hidden; }
.level-fill { height: 100%; width: 0; border-radius: 5px; background: var(--good-text);
  transition: width .08s linear, background .15s ease; }
.level-fill.quiet { background: var(--baseline); }
.level-fill.hot { background: var(--critical); }
.level-hint { font-size: 0.85rem; color: var(--ink-2); min-height: 1.3em; text-align: center; }
/* Verweigertes Mikrofon: der Fehler steht am Balken, nicht in einem
   Toast — der Kalibrier-Vorspann laeuft weiter (Ziel C). */
.level-track.is-error { background: color-mix(in srgb, var(--critical) 18%, var(--grid)); }
.level-track.is-error .level-fill { background: var(--critical); }
.level-hint.is-error { color: var(--critical); }
.level-rate { font-size: 0.8rem; color: var(--ink-2); text-align: center; }
.level-rate.warn { color: var(--critical); font-weight: 600; }

/* ---- home toolbar ---- */
.home-toolbar { display: flex; gap: 8px; margin: 16px 0 4px; align-items: center; }
.search-input {
  flex: 1; font: inherit; padding: 9px 14px; border-radius: 999px;
  border: 1px solid var(--border-strong); background: var(--surface); color: var(--ink);
}
.search-input:focus { outline: 3px solid var(--focus); border-color: transparent; }
.cat-head { font-size: 0.78rem; font-weight: 700; letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--muted); margin: 20px 2px 8px; }

/* ---- Kachel-Navigation der Sprechen-Startseite ---- */
.area-grid {
  grid-column: 1 / -1;
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px;
}
/* Auftrag 103 Q0c/1+2: die kompakte Zeilenkachel, nur <= 480 px sichtbar
   (Start A2, Lernen C). Beide Raster stehen im DOM, das Stylesheet
   entscheidet — dieselbe Regel wie `kurzLangText` (Auftrag 102), damit
   ein Drehen des Geraets nichts nachmisst. */
.area-grid-compact { display: none; }
@media (max-width: 480px) {
  .area-grid { display: none; }
  .area-grid-compact {
    grid-column: 1 / -1;
    display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 6px;
  }
}
/* Bereichsseite (§3.4): Kopf, Koennens-Zeile, „Heute hier", dann die
   Module in Abschnitten. Alles aus Kit-Bausteinen — hier steht nur der
   Abstand, den die Seite als Ganzes braucht. */
.area-view { display: flex; flex-direction: column; gap: 14px; }
.area-can:empty, .area-view #area-today:empty { display: none; }
.area-view .area-today { margin: 0; }

/* Ein Anker, auf den jemand gesprungen ist, leuchtet kurz auf — danach
   sieht die Karte wieder aus wie jede andere (§3.5, §3.6). */
.anchor-hit { animation: anchor-hit 1.6s ease-out; }
@keyframes anchor-hit {
  0%, 60% { box-shadow: 0 0 0 3px var(--brand-glow); }
  100% { box-shadow: none; }
}
@media (prefers-reduced-motion: reduce) {
  .anchor-hit { animation: none; box-shadow: 0 0 0 3px var(--brand-glow); }
}

.word-hits { display: flex; flex-direction: column; gap: 8px; margin-top: 8px; }
.word-hit { display: flex; align-items: center; gap: 12px; text-align: start;
  padding: 10px 14px; cursor: pointer; }
.word-hit .w-thai { font-family: "Noto Sans Thai", "Thonburi", system-ui, sans-serif;
  font-size: 1.3rem; min-width: 64px; }
.word-hit .w-tr { font-weight: 600; }
.word-hit .w-gloss { color: var(--ink-2); font-size: 0.85rem; margin-inline-start: auto; }

/* ---- lesson composition badges ---- */
.comp-badges { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 10px; }
.comp-badge {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: 0.72rem; font-weight: 600; color: var(--ink-2);
  border: 1px solid var(--border); border-radius: 999px; padding: 2px 10px 2px 7px;
  background: var(--page);
}
.comp-badge .ci { font-size: 0.7rem; opacity: 0.85; }
.comp-badge .ci svg { width: 12px; height: 12px; display: block; }
.comp-sentence { border-color: var(--band-edge); color: var(--brand-ink); }
.comp-listening { border-color: var(--listen); }
/* difficulty / prerequisite tags — deliberately quiet: they classify the
   card, they are not the message */
.comp-badge.tag-beginner { color: var(--cls-low); border-color: var(--cls-low); }
.comp-badge.tag-advanced { color: var(--cls-mid); border-color: var(--cls-mid); }
.comp-badge.tag-reading { color: var(--cls-high); background: var(--cls-high-bg);
  border-color: var(--warning); }
/* the compact 🧭 tiles carry the same badges, just tighter */

/* ---- lesson summary ---- */
.summary-card { text-align: center; margin-top: 24px; }
.summary-emoji { font-size: 2.6rem; }
.summary-title { margin: 8px 0 2px; }
.summary-sub { color: var(--ink-2); margin: 0 0 16px; }
.summary-list { display: flex; flex-direction: column; gap: 6px; text-align: start; margin-bottom: 18px; }
.summary-row { display: flex; align-items: center; gap: 10px; padding: 8px 12px;
  border-radius: 10px; background: var(--page); font-size: 0.92rem; }
.summary-row .s-mark { font-weight: 700; }
.summary-row.ok .s-mark { color: var(--good-text); }
.summary-row.fail .s-mark { color: var(--critical); }
.summary-row .s-thai { font-family: "Noto Sans Thai", "Thonburi", system-ui, sans-serif; }
.summary-row .s-label { color: var(--ink-2); margin-inline-start: auto; font-size: 0.8rem; }
.summary-actions { margin-top: 4px; }
.auth-forgot { margin-top: 12px; display: block; width: 100%; text-align: center; font-size: 0.85rem; }
/* Auftrag 271c: Sprachumschalter-Symbol als Linien-SVG statt Emoji. */
#auth-lang svg { width: 1em; height: 1em; vertical-align: -0.15em; }

/* ============ reading module (M4) ============ */

.rs-intro { color: var(--ink-2); margin: 4px 0 18px; max-width: 60ch; }
.letter-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(92px, 1fr));
  gap: 10px; margin-bottom: 22px; }
.letter-tile { position: relative; display: flex; flex-direction: column; align-items: center;
  gap: 4px; padding: 12px 6px 10px; background: var(--surface); border: 1px solid var(--border);
  border-radius: 14px; cursor: pointer; box-shadow: var(--shadow);
  transition: transform 0.08s ease; }
.letter-tile:not(.static):hover { transform: translateY(-2px); }
.letter-tile.static { cursor: default; }
.letter-tile .lt-char { font-size: 2.1rem; line-height: 1.25; }
.letter-tile .lt-sign { font-size: 2.9rem; line-height: 1.35; }
.letter-tile .lt-example { font-size: 1.5rem; line-height: 1.3; margin-top: 2px; }
.letter-tile .lt-thai-name { font-size: 0.95rem; color: var(--ink); }
.letter-tile .lt-name { font-size: 0.72rem; color: var(--ink-2); text-align: center; }
.letter-tile.mastered { border-color: var(--good); }
.letter-tile .lt-check { position: absolute; top: 6px; inset-inline-end: 8px; color: var(--good-text);
  font-size: 0.8rem; }
/* Namensclip-Knopf an Buchstaben-/Ziffern-/Vokalkacheln (Auftrag 152, Q41;
   Buchstaben seit Auftrag 181, Q55a P1). Bis Auftrag 181 stand der Knopf
   `position: absolute` in der unteren rechten Ecke — auf 360 px ueberlappte
   das die Umschrift-Beschriftung darunter und schnitt sie ab (Befund B12,
   `lesen-s1-zeichen.png`: "gaa" hinter dem ▶). Jetzt steht der Knopf im
   normalen Fluss der Spalte (`.letter-tile` ist `flex-direction: column`)
   UNTER der Umschrift, statt sie zu ueberlagern. */
.letter-tile .lt-audio { min-height: 26px; padding: 2px 6px; margin-top: 2px; }

.cls-chip { display: inline-flex; align-items: center; gap: 4px; font-size: 0.68rem;
  font-weight: 600; padding: 2px 8px; border-radius: 999px; }
.cls-chip.cls-mid { color: var(--cls-mid); background: var(--cls-mid-bg); }
.cls-chip.cls-high { color: var(--cls-high); background: var(--cls-high-bg); }
.cls-chip.cls-low { color: var(--cls-low); background: var(--cls-low-bg); }

.rs-gate-hint { color: var(--ink-2); font-size: 0.85rem; margin: 0 0 10px; }
#rs-practice { margin-top: 8px; }

/* exercise loop */
.rq-question { color: var(--ink-2); text-align: center; margin: 4px 0 10px; }
.rq-prompt { font-size: 3.6rem; text-align: center; line-height: 1.3; min-height: 1.3em; }
.rq-sub { text-align: center; color: var(--muted); margin-bottom: 8px; min-height: 1.2em; }
.rq-sound { font-family: ui-monospace, monospace; font-weight: 700; }
.rq-options { display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px; margin: 16px 0 6px; }
.rq-opt { padding: 14px 10px; font-size: 1.05rem; background: var(--page);
  border: 1.5px solid var(--border); border-radius: 12px; cursor: pointer; text-align: center; }
.rq-opt .rq-thai { font-size: 1.9rem; }
.rq-opt:not(:disabled):hover { border-color: var(--brand); }
.rq-opt.correct { border-color: var(--good); background: var(--cls-low-bg); }
.rq-opt.wrong { border-color: var(--critical); }
.rq-opt:disabled { cursor: default; }
.rq-feedback { text-align: center; margin: 10px 0 4px; }
.rq-feedback .fb-ok { color: var(--good-text); font-weight: 600; }
.rq-feedback .fb-bad { color: var(--critical); font-weight: 600; }
.rq-feedback .fb-explain { display: block; margin-top: 6px; color: var(--ink-2);
  font-size: 0.92rem; }

/* letter modal. z-index 60 = dieselbe Ebene wie .k-modal-overlay/.k-sheet-overlay:
   die vier festen Dialoge (flag/judge/beta/letter) lagen mit 40 UNTER der
   Desktop-Seitennavigation (.mainnav 55), der Kopfzeile (50) und den festen
   Blaettern/Leisten (40, spaeter im DOM) — Befund Owner 26.09.2026 (Bewertungs-
   Melden verdeckt, Queue 277). */
.modal-overlay { position: fixed; inset: 0; background: rgba(0, 0, 0, 0.45);
  display: flex; align-items: center; justify-content: center; z-index: 60;
  padding: 16px; }
.modal-overlay[hidden] { display: none; }
/* The hidden attribute must always win, even against display:flex/grid
   rules (this bug class hit the modal overlay and the home toolbar). */
[hidden] { display: none !important; }
.modal { position: relative; max-width: 340px; width: 100%; padding: 20px; }
/* Ein Dialog, der laenger ist als der Bildschirm, muss scrollen — sonst
   liegen seine Schliess-Knoepfe ausserhalb und es gibt keinen Ausweg mehr
   (Nutzerfehler 05.09.2026 am Erklaerdialog zur Bewertungsregel, 360x640).
   Die Regel gilt fuer ALLE alten Ueberlagerungen, nicht nur fuer die eine,
   die einmal einzeln repariert wurde; die Formel ist dieselbe wie bei
   `.k-modal`. */
.modal-overlay > .modal { max-height: min(82vh, 100% - 32px); overflow-y: auto; }
/* Der Ausweg bleibt beim Rollen oben stehen (Auftrag 58, Punkt 3,
   05.09.2026). Seit `.modal-overlay > .modal` innen rollt, rollte das
   absolut gesetzte Kreuz mit dem Text nach oben aus dem Bild: auf 360x640
   stand der Erklaerdialog zur Bewertungsregel gerollt ohne sichtbares
   Kreuz da — also mit halb behobenem Nutzerfehler statt behobenem.
   `position: sticky` verlangt den Fluss, deshalb `float: inline-end` statt
   `absolute`: der Knopf steht weiter in der oberen rechten Ecke, die
   Ueberschrift laeuft um ihn herum, und sobald gerollt wird, klebt er am
   oberen Rand des rollenden Kastens. Die Regel gilt fuer ALLE vier alten
   Ueberlagerungen — Melden, Bewertungsregel, Beta-Hinweis, Buchstabe —,
   weil alle vier ihr Kreuz als erstes Kind des rollenden `.modal`
   tragen; ein Kopfbereich ausserhalb des rollenden Teils haette in vier
   Vorlagen je eine neue Verschachtelung gekostet. */
.modal-close { position: sticky; top: 0; inset-inline-end: auto; float: inline-end;
  z-index: 1; margin-inline-start: var(--sp-2); }
/* learn section */
.learn-card { margin-bottom: 22px; }
.learn-card p { color: var(--ink-2); line-height: 1.65; margin: 0 0 12px; }
.learn-sub { font-size: 0.95rem; margin: 18px 0 10px; }
.rule-table-scroll { overflow-x: auto; }
.rule-table { border-collapse: collapse; font-size: 0.82rem; min-width: 560px; }
.rule-table th, .rule-table td { padding: 7px 10px; text-align: start;
  border-bottom: 1px solid var(--grid); white-space: nowrap; }
.rule-table thead th { color: var(--muted); font-weight: 500; font-size: 0.75rem; }
.rule-table tbody th { font-weight: 500; }
.ex-gloss { color: var(--muted); }

/* reading articles (intro, rule book) */
.read-article .article-body { max-width: 64ch; }
.article-body .card-title { margin: 26px 0 8px; }
.article-body p { color: var(--ink-2); line-height: 1.7; margin: 0 0 12px; }
.article-body .ref-hint { color: var(--muted); font-size: 0.85rem; }
.article-body .thai-line { font-size: 1.3rem; }
#ri-done { margin-top: 24px; max-width: 64ch; }
.info-cards { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-bottom: 14px; }
.info-card { text-align: start; padding: 16px; cursor: pointer; }
.info-card h2 { font-size: 0.95rem; margin: 0 0 6px; }
.info-card p { color: var(--ink-2); font-size: 0.82rem; margin: 0; line-height: 1.5; }
@media (max-width: 560px) { .info-cards { grid-template-columns: 1fr; } }

/* ======= Regelheft neu (Auftrag 180, Q54, Figma-Vorlage A) =============
   Die alte flache Regelheft-Seite ist mit Auftrag 180 komplett ersetzt
   (showReadingReference in reading.js) — ihre Klassen ref-class-row,
   ref-letter und ref-line sind darum weg, nicht nur ergaenzt; rule-table
   bleibt stehen (die Lernkarte der Stufen benutzt sie weiter). Alles
   unten ist neu, ueber Tokens (--cls-*/--tone-*/--sp-*), keine
   Pixelfarben in dieser Ansicht. */
.ref-page { max-width: 64ch; }
.ref-page .ref-intro { margin-top: 0; }
.ref-jumpnav { display: flex; flex-wrap: wrap; gap: var(--sp-1); margin: 0 0 var(--sp-5); }
.ref-jump-pill { background: var(--surface); border: 1px solid var(--border);
  border-radius: 999px; padding: 4px 10px; font-size: 0.75rem; font-weight: 500;
  color: var(--ink-2); text-decoration: none; white-space: nowrap; }
.ref-jump-pill:hover, .ref-jump-pill:focus-visible { border-color: var(--brand); color: var(--brand); }

.ref-section { margin: 0 0 var(--sp-7); scroll-margin-top: var(--sp-4); }
.ref-sec-head { display: flex; align-items: center; gap: var(--sp-2); margin: 0 0 var(--sp-3); }
.ref-sec-num { background: var(--brand); color: var(--on-brand); border-radius: 999px;
  font-size: 0.72rem; font-weight: 600; line-height: 1; padding: 4px 8px; flex: none; }
.ref-sec-title { font-size: 1.05rem; font-weight: 600; margin: 0; color: var(--ink); }

.ref-class-card { margin: 0 0 var(--sp-2); }
.ref-class-chiphead { font-size: 0.8rem; color: var(--ink-2); margin: 0 0 var(--sp-2); }
.ref-letters { display: flex; flex-wrap: wrap; gap: 6px; }
.ref-letter-tile { min-width: 36px; padding: 4px 8px; border: 1px solid var(--border);
  background: var(--surface); border-radius: 8px; font-size: 1.25rem; color: var(--ink); }
.ref-letter-tile.muted { color: var(--muted); }
.ref-letter-tile:hover, .ref-letter-tile:focus-visible { border-color: var(--brand); }

.ref-two-col { display: flex; gap: var(--sp-2); margin: 0 0 var(--sp-3); }
.ref-livedead { flex: 1 1 0; min-width: 0; }
.ref-livedead-title { font-weight: 600; margin: 0 0 6px; }
.ref-livedead.is-live .ref-livedead-title { color: var(--good); }
.ref-livedead.is-tot .ref-livedead-title { color: var(--critical); }
.ref-livedead-line { color: var(--ink-2); font-size: 0.85rem; line-height: 1.4; margin: 0 0 4px; }
.ref-livedead-line.strong { color: var(--ink); font-weight: 600; }
@media (max-width: 420px) { .ref-two-col { flex-direction: column; } }

.ref-finals { display: flex; flex-direction: column; }
.ref-finals-row { display: flex; align-items: baseline; gap: var(--sp-3);
  padding: 6px 0; border-bottom: 1px dashed var(--grid); }
.ref-finals-row:last-child { border-bottom: 0; }
.ref-finals-sound { width: 34px; flex: none; font-weight: 600; font-size: 0.9rem; }
.ref-finals-sound.is-live { color: var(--good); }
.ref-finals-sound.is-tot { color: var(--critical); }
.ref-finals-letters { font-size: 1.1rem; }

.ref-tonemarks { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--sp-2); }
.ref-tonemark-card { text-align: center; padding: var(--sp-2); }
/* Tonzeichen als SVG aus dem Font (kMarkSvg): gross, Traegerkreis blass daneben
   (Owner 14.09.: vorher winzig und vom Platzhalter verdeckt) */
.k-mark { display: block; height: 4.6rem; width: auto; max-width: 100%; margin: 0 auto; color: var(--ink); }
.k-mark-zeichen { fill: currentColor; }
.k-mark-traeger { fill: none; stroke: currentColor; stroke-width: 22; stroke-dasharray: 30 26; opacity: 0.32; }
.k-mark-text { display: block; font-size: 2.4rem; line-height: 1.2; }
.ref-tonemark-char { margin: 0 auto var(--sp-1); }
.ref-tonemark-name { font-size: 0.72rem; color: var(--ink-2); margin: 2px 0 0; }
@media (max-width: 420px) { .ref-tonemarks { grid-template-columns: repeat(2, 1fr); } }

.ref-table-scroll { overflow-x: auto; }
.ref-rule-table, .ref-vowel-table { border-collapse: collapse; font-size: 0.82rem; width: 100%; }
.ref-rule-table th, .ref-rule-table td,
.ref-vowel-table th, .ref-vowel-table td { padding: 6px 6px; text-align: start;
  border-bottom: 1px solid var(--grid); }
.ref-rt-corner { width: 92px; }
.ref-rt-rowhead { font-size: 0.75rem; color: var(--ink-2); font-weight: 500; width: 92px; }
.ref-rt-cell { display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start;
  gap: 1px; min-height: 0; background: none; border: 0; border-radius: 0; padding: 0;
  font: inherit; color: inherit; cursor: default; width: 100%; }
.ref-rt-cell.is-playable { cursor: pointer; }
.ref-rt-cell.is-playable:hover, .ref-rt-cell.is-playable:focus-visible { text-decoration: underline; }
.ref-rt-cell .k-playback-icon, .ref-rt-cell .k-playback-label:empty { display: none; }
td:has(> .ref-rt-cell) { padding: 6px 4px; }
.ref-tone-dot { width: 8px; height: 8px; border-radius: 999px; display: inline-block; }
.ref-tone-mid { background: var(--tone-mid); }
.ref-tone-low { background: var(--tone-low); }
.ref-tone-falling { background: var(--tone-falling); }
.ref-tone-high { background: var(--tone-high); }
.ref-tone-rising { background: var(--tone-rising); }
.ref-tone-name { font-size: 0.7rem; color: var(--ink-2); }
.ref-tone-syllable { font-size: 1.05rem; color: var(--ink); }
.ref-rt-cell.is-empty, td.is-empty { color: var(--muted); text-align: center; }
@media (max-width: 380px) {
  .ref-rt-corner, .ref-rt-rowhead { width: 76px; }
  .ref-rule-table { font-size: 0.74rem; }
}

.ref-finger-rule { display: flex; flex-direction: column; gap: var(--sp-3); }
.ref-finger-classpick { display: flex; gap: 6px; }
.ref-finger-classbtn.is-active { border-color: var(--brand); background: var(--brand-soft); }
.ref-finger-bars { display: flex; align-items: flex-end; justify-content: space-between;
  gap: 6px; padding: 0 var(--sp-1); }
.ref-finger { display: flex; flex-direction: column; align-items: center; gap: 2px; flex: 1 1 0; min-width: 0; }
.ref-finger-stem { width: 100%; max-width: 40px; border-radius: 8px 8px 2px 2px; background: var(--muted); }
.ref-finger.ref-tone-mid .ref-finger-stem { background: var(--tone-mid); }
.ref-finger.ref-tone-low .ref-finger-stem { background: var(--tone-low); }
.ref-finger.ref-tone-falling .ref-finger-stem { background: var(--tone-falling); }
.ref-finger.ref-tone-high .ref-finger-stem { background: var(--tone-high); }
.ref-finger.ref-tone-rising .ref-finger-stem { background: var(--tone-rising); }
.ref-finger.is-off { opacity: 0.4; }
.ref-finger-live .ref-finger-stem { height: 56px; }
.ref-finger-ek .ref-finger-stem { height: 84px; }
.ref-finger-tho .ref-finger-stem { height: 96px; }
.ref-finger-tri .ref-finger-stem { height: 88px; }
.ref-finger-jattawa .ref-finger-stem { height: 66px; }
.ref-finger-sign { font-size: 1.1rem; }
.ref-finger-thai { font-size: 0.72rem; color: var(--ink-2); }
.ref-finger-tone { font-size: 0.68rem; color: var(--ink); font-weight: 600; text-align: center; }
.ref-finger-explain { min-height: 2.6em; }

.ref-word-row { display: flex; align-items: center; gap: var(--sp-2); padding: 6px 0;
  border-bottom: 1px dashed var(--grid); }
.ref-word-row:last-child { border-bottom: 0; }
.ref-word-thai { font-size: 1.25rem; color: var(--ink); flex: none; }
.ref-word-info { display: flex; flex-direction: column; flex: 1 1 0; min-width: 0; }
.ref-word-translit { font-size: 0.85rem; color: var(--ink-2); }
.ref-word-gloss { font-size: 0.75rem; color: var(--muted); }

.ref-vowel-empty { color: var(--muted); }
.ref-vowel-pattern { font-size: 1.05rem; }
.ref-vowel-specials { display: flex; flex-direction: column; margin-top: var(--sp-2); }
/* Auftrag 189b (Q55e-Nachzug): Stellung je Zelle statt eigener Spalte
   (Brief 189 §3) — kleine Zeile unter dem Muster. */
.ref-vowel-cell { display: flex; flex-direction: column; gap: 1px; }
.ref-vowel-cell-pos { font-size: 0.68rem; color: var(--muted); }

/* Auftrag 189b (Q55e-Nachzug): Regelheft Abschnitt 0 "Umschrift lesen". */
.ref-translit-table { border-collapse: collapse; font-size: 0.82rem; width: 100%; }
.ref-translit-table th, .ref-translit-table td { padding: 6px 6px; text-align: start;
  border-bottom: 1px solid var(--grid); }
.ref-translit-key { font-weight: 600; }
.ref-translit-thai { font-size: 1.1rem; }
.ref-translit-note { font-size: 0.75rem; color: var(--muted); }
.ref-word-list { display: flex; flex-direction: column; margin-top: var(--sp-2); }

.ref-signs { display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-2); }
.ref-sign-card { text-align: center; }
.ref-sign-char { height: 4rem; margin: 0 auto var(--sp-1); }
.ref-sign-name { font-size: 0.8rem; font-weight: 600; margin: 2px 0 0; }
.ref-sign-role { font-size: 0.72rem; color: var(--muted); margin: 0; }

.ref-numerals-card { margin-top: var(--sp-3); }
.ref-numerals { display: flex; flex-wrap: wrap; gap: 6px; }
.ref-numeral-tile { display: flex; flex-direction: column; align-items: center; gap: 2px;
  border: 1px solid var(--border); border-radius: 8px; padding: 6px 8px; min-width: 44px; }
.ref-numeral-char { font-size: 1.3rem; }
.ref-numeral-value { font-size: 0.7rem; color: var(--muted); }
.rule-box { background: var(--band-fill); border-inline-start: 3px solid var(--band-edge);
  padding: 10px 14px; border-radius: 0 10px 10px 0; color: var(--ink) !important;
  font-weight: 500; }

/* learn-section blocks */
.watch-list, .can-list { margin: 4px 0 12px; padding-inline-start: 20px; }
.watch-list li, .can-list li { color: var(--ink-2); line-height: 1.6; margin-bottom: 6px; }
.watch-list li::marker { content: "⚠ "; }
.can-list li::marker { content: "✓ "; color: var(--good-text); }
.rule-box.locked-note { background: var(--page); border-inline-start-color: var(--muted);
  font-weight: 400; color: var(--ink-2) !important; }
.rq-glottal { font-size: 0.95rem; font-weight: 500; }
.rq-prompt .rq-glottal { font-size: 1.4rem; }

/* stage tabs + polish pass */
.stage-tabs { display: flex; gap: 6px; background: var(--page); border-radius: 14px;
  padding: 5px; margin: 4px 0 18px; position: sticky; top: 8px; z-index: 5; }
.stage-tabs button { flex: 1; border: 0; background: transparent; padding: 10px 8px;
  border-radius: 10px; font-size: 0.9rem; font-weight: 600; color: var(--ink-2);
  cursor: pointer; }
.stage-tabs button.active { background: var(--surface); color: var(--ink);
  box-shadow: var(--lift); }
.stage-tab > .btn.block { margin-top: 18px; }
.rs-progress { display: flex; align-items: center; gap: 8px; flex: 1;
  justify-content: flex-end; }
.rs-progress .bar { width: 90px; height: 6px; background: var(--grid);
  border-radius: 999px; overflow: hidden; display: inline-block; }
.rs-progress .bar i { display: block; height: 100%; background: var(--good-text);
  border-radius: 999px; transition: width 0.4s ease; }
.rs-progress-label { font-size: 0.75rem; color: var(--muted); white-space: nowrap; }
.rs-locked-note { color: var(--ink-2); background: var(--page); border-radius: 12px;
  padding: 14px 16px; line-height: 1.6; }
.bu-explain-toggle { border: 0; background: transparent; color: var(--brand);
  cursor: pointer; font-size: 0.85rem; padding: 2px 0; text-align: start; }
.bu-explain { color: var(--ink-2); font-size: 0.88rem; padding: 2px 8px 10px 8px;
  border-bottom: 1px dashed var(--grid); }
.summary-row { display: flex; align-items: center; gap: 10px; padding: 8px 4px;
  border-bottom: 1px solid var(--grid); }
.summary-row:last-child { border-bottom: 0; }
.sr-kind { color: var(--muted); font-size: 0.75rem; min-width: 110px; }
.sr-item { flex: 1; font-size: 1.25rem; }
.sr-mark.ok { color: var(--good-text); font-weight: 700; }
.sr-mark.fail { color: var(--critical); font-weight: 700; }
/* focus visibility for keyboard users on the new controls */
.stage-tabs button:focus-visible, .letter-tile:focus-visible, .rq-opt:focus-visible,
.bu-explain-toggle:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; }
/* consistent Thai rendering */
.lt-char, .rq-prompt, .w-thai, .sr-item,
.lt-thai-name, .thai-line {
  font-family: "Noto Sans Thai", "Sathu", "Thonburi", system-ui, sans-serif; }
.verdict-note { display: block; font-size: 0.75rem; color: var(--muted); font-weight: 400; }

/* gelbes Band + Silbenleiste (docs/grader-gui-decisions.md §3.4/§3.5):
   weder Häkchen noch Kreuz — die Prüfungen waren sich selbst nicht sicher. */
.verdict.knapp { color: var(--warning); }
.verdict.knapp .badge { background: var(--warning); }

/* Orange „Nah dran — zählt" (Owner-Entscheid 06.09.2026, Auftrag 75):
   eine Ablehnung, die dicht an der Schwelle lag. Sie ZÄHLT als Fehlversuch
   — anders als das gelbe Band — und sagt es im Untertitel; die Farbe steht
   deshalb zwischen Gelb und Rot und nicht auf einer der beiden. */
.verdict.near { color: var(--near-text); }
.verdict.near .badge { background: var(--near); color: var(--on-near); }

/* Satzmodus-Silbenleiste (docs/grader-gui-decisions.md §3.4): eine Zelle je
   Silbe statt Note und Banner. Die Zelle traegt Lautschrift, Zielton-Symbol
   und die Farbe der Rahmung; die Veto-Silbe bekommt einen roten Rahmen und
   sagt inline, wonach sie klang — auch wenn der Satz mit 0,7 besteht. */
.verdict.neutral { color: var(--ink); font-weight: 650; }
.syl-hint { font-size: 0.72rem; color: var(--muted); margin: 0 2px 8px; }
.cues li.syl-diag-head { font-weight: 650; background: transparent; border: 0;
  padding: 2px 2px 0; font-size: 0.86rem; color: var(--ink-2); }
.cues li.syl-diag-head::before { content: "🔎"; }
.cues li.syl-diag-none::before { content: "✓"; }
.jm-row .setting-lbl { display: block; margin-bottom: 4px; font-size: 0.9rem; }
.jm-row .setting-hint { display: block; color: var(--ink-2); font-size: 0.82rem; }
.jm-sub { font-size: 0.9rem; margin: 12px 0 4px; }
.jm-numbers { font-size: 0.85rem; color: var(--muted); }
.model-range-toggle { display: block; margin: 4px auto 0; font-size: 0.75rem;
  color: var(--muted); }

/* reference mode -- Sprachregel Auftrag 267: die Oberflaechensprache
   (.consent-native) liest sich normal, Englisch/Thai folgen kompakt und
   gedaempft direkt danach (negativer Abstand ruecke sie an den
   vorangehenden Absatz statt an einen eigenen). .consent-subtitle
   ueberschreibt Block B/C mit ihrem eigenen (kleinen) Titel. */
.consent-card { max-width: 64ch; }
.consent-card .consent-native { font-size: 1.02rem; }
.consent-card .consent-en, .consent-card .consent-th {
  color: var(--ink-2); font-size: 0.88rem; margin-top: -6px; }
.consent-subtitle { font-size: 0.8rem; font-weight: 650; color: var(--ink-2);
  margin: 14px 0 2px; text-transform: uppercase; letter-spacing: 0.02em; }
.consent-check { display: flex; gap: 10px; align-items: flex-start; margin: 12px 0;
  line-height: 1.5; cursor: pointer; }
.consent-check input { margin-top: 4px; }
/* Drei Checkbox-Texte je Block, mehrsprachig UNTEREINANDER (eine Checkbox,
   Beschriftung gestapelt) statt eines geklebten Strings. */
.consent-lbl-line { display: block; }
.consent-lbl-line.consent-en, .consent-lbl-line.consent-th {
  color: var(--ink-2); font-size: 0.88em; margin-top: 2px; }
/* Lerner-Opt-in (progress.js): dieselbe Regel, kompakt als zweiter/dritter
   Absatz unter dem Kaestchen statt eigener Bloecke. */
.setting-hint.consent-en, .setting-hint.consent-th { font-style: italic; }

/* compare row + flag -- beide Knoten haengen IMMER als direkte Kinder von
   `.k-blattfest-body` (Urteil-Blatt, halb), dessen Flex-`gap: var(--sp-3)`
   (12px) schon den Abstand zu Nachbarn setzt. Die frueheren eigenen Raender
   (12px oben/6px unten bzw. 10px unten) addierten sich dazu -- 18px statt
   eines Rastermasses (259-Audit-Fund, I13, wie zuvor bei `.schreib-hinweis`). */
.compare-caption { font-size: 0.78rem; color: var(--muted); }
.compare-row { display: flex; gap: 8px; flex-wrap: wrap; }
#fm-comment { width: 100%; font: inherit; padding: 10px; border-radius: 10px;
  border: 1px solid var(--border); background: var(--page); color: var(--ink);
  margin: 10px 0 4px; resize: vertical; }
.fm-text { color: var(--ink-2); font-size: 0.88rem; line-height: 1.6; }

.srs-card { border-inline-start: 3px solid var(--brand); }
.goal-row { color: var(--ink-2); font-size: 0.9rem; margin: 4px 0 18px; }
.goal-opt { border: 1px solid var(--border); background: var(--surface); color: var(--ink);
  border-radius: 999px; padding: 4px 12px; cursor: pointer; margin-inline-start: 4px; }
.goal-opt.active { background: var(--brand); border-color: var(--brand); color: var(--on-brand); }
.setting-row { margin: 0 0 18px; font-size: 0.9rem; }
.setting-row .setting-hint { display: block; color: var(--ink-2); font-size: 0.82rem;
  margin-top: 2px; }
/* C9: der Hinweis, dass der zugestimmte Text veraltet ist — auffaellig,
   aber keine Fehlermeldung: es ist nichts kaputt, es ist nur neu. */
.consent-check.consent-missing { outline: 2px solid var(--warning); outline-offset: 4px; border-radius: 6px; }
.consent-check .consent-stale { color: var(--cls-high); font-weight: 600; margin-top: 4px; }

/* duel */
.duel-card { border-inline-start: 3px solid var(--warning); }
/* Die Verwechslungsmatrix ist Kit-Baustein 20 (.k-matrix). */

/* word of the day + flash reading */
.wod-card { cursor: pointer; position: relative; display: flex; flex-direction: column; align-items: flex-start; gap: 2px;
  text-align: start; padding: 14px 18px; cursor: pointer;
  border-inline-start: 3px solid var(--good); }
.wod-label { font-size: 0.72rem; color: var(--muted); letter-spacing: 0.04em; }
.wod-thai { font-size: 1.9rem; }
.wod-tr { color: var(--ink-2); font-size: 0.9rem; }
.wod-derive { color: var(--muted); font-size: 0.8rem; }
.flash-hidden { color: var(--muted); }
.flash-pending { pointer-events: none; opacity: 0.35; }
.guest-try { margin-top: 10px; }

/* ---------- messaging ---------- */
.msg-badge {
  position: absolute; top: -4px; inset-inline-end: -4px; min-width: 16px; height: 16px;
  padding: 0 4px; border-radius: 8px; background: var(--critical);
  color: var(--on-critical);
  font-size: 10px; line-height: 16px; font-weight: 700; text-align: center; }
.msg-list { display: flex; flex-direction: column; gap: 8px; margin: 16px 0; }
.msg-row { max-width: 78%; padding: 8px 12px; border-radius: 12px;
  border: 1px solid var(--border); background: var(--surface);
  box-shadow: var(--shadow); white-space: pre-wrap; word-break: break-word; }
.msg-row.mine { align-self: flex-end; background: var(--band-fill);
  border-color: var(--band-edge); }
.msg-row .msg-meta { display: block; font-size: 11px; color: var(--muted); margin-top: 4px; }
.msg-empty { color: var(--muted); text-align: center; padding: 24px 0; }
.msg-form { display: flex; gap: 8px; align-items: flex-end; }
.msg-form textarea { flex: 1; resize: vertical; min-height: 56px; padding: 10px 12px;
  border: 1px solid var(--border-strong); border-radius: 10px; background: var(--surface);
  color: var(--ink); font: inherit; }

/* ---------- admin ---------- */
.adm-table { width: 100%; border-collapse: collapse; font-size: 14px; }
.adm-table th { text-align: start; color: var(--muted); font-weight: 600;
  padding: 6px 8px; border-bottom: 1px solid var(--border); }
.adm-table td { padding: 8px; border-bottom: 1px solid var(--border); vertical-align: top; }
.adm-table tr.clickable { cursor: pointer; }
.adm-table tr.clickable:hover td { background: var(--band-fill); }
.adm-wrap { overflow-x: auto; }
.adm-pill { display: inline-block; padding: 1px 8px; border-radius: 9px; font-size: 12px;
  border: 1px solid var(--border); color: var(--ink-2); }
.adm-pill.open { border-color: var(--warning); color: var(--warning); }
.adm-pill.reviewed, .adm-pill.closed { border-color: var(--good); color: var(--good-text); }
.adm-pill.unread { background: var(--critical); border-color: var(--critical);
  color: var(--on-critical); }
.adm-detail .card { margin-top: 12px; }
.adm-actions { display: flex; flex-wrap: wrap; gap: 8px; margin: 10px 0; }
.adm-pre { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12px;
  background: var(--page); border: 1px solid var(--border); border-radius: 8px;
  padding: 10px; overflow-x: auto; white-space: pre-wrap; word-break: break-word; }
.adm-note { width: 100%; min-height: 48px; padding: 8px 10px; margin-top: 8px;
  border: 1px solid var(--border); border-radius: 8px; background: var(--surface);
  color: var(--ink); font: inherit; resize: vertical; }

/* ---------- topbar nav + user menu ---------- */
.topbar-right { gap: 6px; }
.topbar .btn.ghost { border-color: transparent; }
.topbar .btn.ghost:hover { border-color: var(--border); background: var(--surface); }
.avatar-btn {
  width: 34px; height: 34px; border-radius: 50%; border: 0; cursor: pointer;
  background: linear-gradient(135deg, var(--brand), var(--brand-ink));
  color: var(--on-brand); font-weight: 700; font-size: 0.95rem; display: grid;
  place-items: center; box-shadow: 0 2px 8px var(--brand-glow);
  transition: transform .12s ease; }
.avatar-btn:hover { transform: scale(1.06); }
.user-menu { position: relative; margin-inline-start: 4px; }
.menu-pop {
  position: absolute; inset-inline-end: 0; top: calc(100% + 10px); min-width: 250px;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: 14px; box-shadow: var(--elevated-ring), var(--elevated-shadow);
  padding: 6px; z-index: 60; animation: menupop .14s ease; }
@keyframes menupop { from { opacity: 0; transform: translateY(-4px); } }
.menu-head { display: flex; align-items: center; gap: 10px; padding: 10px 10px 8px; }
.avatar-mini { width: 28px; height: 28px; border-radius: 50%; flex: none;
  background: linear-gradient(135deg, var(--brand), var(--brand-ink));
  color: var(--on-brand); font-weight: 700; font-size: 0.8rem; display: grid;
  place-items: center; }
.menu-head .user-email { max-width: 190px; }
.menu-sep { height: 1px; background: var(--border); margin: 5px 8px; }
.menu-row {
  display: flex; align-items: center; gap: 10px; width: 100%;
  padding: 9px 10px; border: 0; border-radius: 9px; background: none;
  font-size: 0.9rem; color: var(--ink); text-align: start; cursor: pointer;
  text-decoration: none; }
.menu-row:hover { background: var(--band-fill); }
.menu-row.danger { color: var(--critical); }
.menu-row.danger:hover { background: color-mix(in srgb, var(--critical) 10%, transparent); }
.menu-select { justify-content: space-between; cursor: default; }
.menu-select select {
  font: inherit; color: var(--ink); background: var(--page);
  border: 1px solid var(--border-strong); border-radius: 8px; padding: 4px 8px; }
.msg-badge { z-index: 1; }

/* ---------- responsive refinements ---------- */
.home-toolbar .search-input { flex: 1; min-width: 200px; }
@media (min-width: 900px) {
  .progress-view { display: grid; grid-template-columns: 1fr 1fr;
    gap: 0 16px; align-items: start; }
  .progress-view > .k-pagehead, .progress-view > .h1 { grid-column: 1 / -1; }
}
/* Eine Gruppe der Fortschrittsseite (Auftrag 117, Muster B) ist selbst EIN
   Gitterkind (`kGruppe` traegt Kopf und Inhalt in einem Knoten) — sonst
   staende der Kopf „Schulen" in der linken und sein Inhalt in der rechten
   Spalte (Bild vom 04.09.2026, desktop 1280).
   Dehnung: die Gruppe dehnt sich wie jedes Gitterkind; `min-width: 0`
   erlaubt ihren Karten, unter die Inhaltsbreite zu schrumpfen. */
.progress-view > .k-gruppe { min-width: 0; }
@media (max-width: 640px) {
  .menu-head .user-email { max-width: 150px; }
}

/* ---------- concept lessons ---------- */
.concept { max-width: 760px; }
.con-section { margin-bottom: 14px; }
.con-section h2 { margin: 0 0 8px; font-size: 1.05rem; }
.con-section p { margin: 0 0 12px; color: var(--ink-2); }
.con-examples { display: grid; gap: 6px; }
.con-example { display: flex; align-items: center; gap: 12px; padding: 8px 10px;
  border: 1px solid var(--border); border-radius: 10px; background: var(--page); }
.con-example .ex-thai { font-family: "Noto Sans Thai", "Thonburi", system-ui, sans-serif;
  font-size: 1.25rem; min-width: 90px; }
.con-example .ex-translit { color: var(--brand-ink); min-width: 90px; font-size: 0.9rem; }
.con-example .ex-gloss { color: var(--ink-2); font-size: 0.9rem; flex: 1; }
.con-example .play-ex { border: 0; background: none; cursor: pointer; font-size: 1.1rem; }
.con-question { font-size: 1.05rem; font-weight: 600; }
.con-choices { display: grid; gap: 8px; margin: 12px 0; }
.con-choice { display: flex; align-items: baseline; gap: 12px; text-align: start;
  padding: 12px 14px; border: 1px solid var(--border); border-radius: 12px;
  background: var(--page); cursor: pointer; font: inherit;
  transition: border-color .12s ease, transform .12s ease; }
.con-choice:hover { border-color: var(--band-edge); transform: translateY(-1px); }
.con-choice .ch-thai { font-family: "Noto Sans Thai", "Thonburi", system-ui, sans-serif;
  font-size: 1.3rem; }
.con-choice .ch-label { color: var(--ink-2); font-size: 0.9rem; }
.con-choice.right { border-color: var(--good); background: color-mix(in srgb, var(--good) 8%, transparent); }
.con-choice.wrong { border-color: var(--critical); background: color-mix(in srgb, var(--critical) 8%, transparent); }
.con-feedback { padding: 10px 12px; border-radius: 10px; margin-bottom: 10px;
  background: var(--band-fill); color: var(--ink); }
.con-feedback.ok { background: color-mix(in srgb, var(--good) 10%, transparent); }
.con-feedback.err { background: color-mix(in srgb, var(--critical) 8%, transparent); }
.concept .result-actions { margin-top: 16px; display: flex; gap: 10px; }
.comp-badge.comp-concept { color: var(--cls-high); background: var(--cls-high-bg); }

/* ---------- admin users ---------- */
.adm-search { width: 100%; max-width: 380px; margin-bottom: 10px; padding: 8px 12px;
  border: 1px solid var(--border-strong); border-radius: 10px; background: var(--surface);
  color: var(--ink); font: inherit; }
.adm-table .btn.small { padding: 3px 10px; font-size: 0.8rem; }
/* E3: die Geraetezeile haengt UNTER ihrem Take — eine eigene Zeile ueber
   die volle Breite statt einer sechsten Spalte, die auf 390 px keiner
   mehr sieht. Sie traegt keine eigene Trennlinie: sie gehoert zur Zeile
   darueber, nicht zur naechsten. */
.adm-table tr.row-device td { padding-top: 0; border-bottom: 1px solid var(--border); }
.adm-table tr:not(.row-device) + tr.row-device td { border-top: 0; }
.adm-table tr.row-device + tr td { border-top: 0; }

/* ---------- listening duel v2 ---------- */
.duel { max-width: 700px; }
.duel-chip { padding: 9px 14px; border-radius: 999px; border: 1px solid var(--border);
  background: var(--page); cursor: pointer; font: inherit; color: var(--ink);
  transition: border-color .12s ease; }
.duel-chip:hover { border-color: var(--band-edge); }
.duel-chip.active { background: var(--brand); border-color: var(--brand); color: var(--on-brand); }
.duel-stage { text-align: center; }
/* Auftrag 271c: Trefferhaken im Fortschrittstext als Linien-SVG statt Emoji. */
#duel-progress svg { width: 1em; height: 1em; vertical-align: -0.15em; }
.k-tonechips .row-label { flex-basis: 100%; color: var(--muted); font-size: 0.8rem;
  font-weight: 700; letter-spacing: 0.05em; text-transform: uppercase; }
.duel-reveal { margin: 14px auto 4px; padding: 12px; border-radius: 12px;
  background: var(--page); border: 1px solid var(--border); max-width: 420px; }
.duel-reveal .rv-thai { font-family: "Noto Sans Thai", "Thonburi", system-ui, sans-serif;
  font-size: 1.8rem; }
.duel-reveal .rv-translit { color: var(--brand-ink); }
.duel-reveal .rv-gloss { color: var(--ink-2); }
.duel-reveal .rv-verdict { font-weight: 700; margin-top: 6px; }

/* ---------- vocab trainer ---------- */
.vocab { max-width: 700px; }
.voc-thai { margin-top: 6px; }

/* ---------- Lernheft ---------- */
.bookmark-btn { border: 0; background: none; cursor: pointer; font-size: 1.15rem;
  padding: 2px 6px; vertical-align: middle; opacity: 0.75; transition: transform .12s ease; }
.bookmark-btn:hover { transform: scale(1.15); opacity: 1; }
.bookmark-btn.suggest { animation: nbpulse 1.2s ease-in-out 2; }
/* Auftrag 271c: Lernheft-Symbol als Linien-SVG statt Emoji; „gemerkt"
   zeigt sich per Fuellung statt zweitem Zeichen. */
.bookmark-btn svg { width: 1.15em; height: 1.15em; display: block; }
.bookmark-btn.active svg { fill: currentColor; }
@keyframes nbpulse { 50% { transform: scale(1.35); } }
.notebook { max-width: 860px; }
.nb-review { display: flex; align-items: center; justify-content: space-between;
  gap: 14px; margin-bottom: 14px; }
.nb-list { display: flex; flex-direction: column; gap: 10px; }
.nb-entry { text-align: start; }
.nb-entry .nb-head { display: flex; align-items: baseline; gap: 12px; cursor: pointer; }
.nb-entry .nb-thai { font-family: "Noto Sans Thai", "Thonburi", system-ui, sans-serif;
  font-size: 1.35rem; }
.nb-entry .nb-translit { color: var(--brand-ink); }
.nb-entry .nb-gloss { color: var(--ink-2); flex: 1; }
.nb-entry .adm-pill { flex: none; }
.nb-detail { margin-top: 12px; display: none; }
.nb-entry.open .nb-detail { display: block; }
.nb-actions { display: flex; flex-wrap: wrap; gap: 8px; margin: 10px 0; }
.nb-note { width: 100%; min-height: 44px; padding: 8px 10px; border: 1px solid var(--border-strong);
  border-radius: 8px; background: var(--page); color: var(--ink); font: inherit; resize: vertical; }
.anchor-zone { margin-top: 8px; display: flex; flex-wrap: wrap; gap: 6px;
  align-items: center; justify-content: center; }
.anchor-zone .duel-chip.small { padding: 5px 10px; font-size: 0.82rem; }
.wod-card .bookmark-btn { position: absolute; top: 10px; inset-inline-end: 10px; }

/* ---------- favorites (⭐ topbar menu + star overlay on cards) ---------- */
.fav-btn {
  position: absolute; top: 8px; inset-inline-end: 10px; z-index: 2;
  border: 0; background: none; padding: 2px 4px; line-height: 1; cursor: pointer;
  font-size: 1.05rem; color: var(--muted); opacity: 0;
  transition: opacity .12s ease, transform .12s ease; }
.card:hover > .fav-btn, .card:focus-within > .fav-btn,
.k-card:hover > .fav-btn, .k-card:focus-within > .fav-btn,
.fav-btn:focus-visible, .fav-btn.on { opacity: 1; }
.fav-btn:hover { transform: scale(1.18); }
.fav-btn.on { color: var(--warning); }
.fav-btn svg { width: 18px; height: 18px; display: block; }
.fav-btn.on svg { fill: currentColor; }
.fav-btn:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px;
  border-radius: 6px; }
/* no hover on touch: the star stays visible there */
@media (hover: none) { .fav-btn { opacity: 0.85; } }
/* keep the title clear of the star */
.lesson-card h2 { padding-inline-end: 22px; }

.fav-menu { position: relative; }
.fav-pop { min-width: 240px; max-width: 320px;
  max-height: 70vh; overflow-y: auto; }
.fav-list { display: flex; flex-direction: column; gap: 2px; padding: 2px; }
.fav-row { display: flex; align-items: center; gap: 4px; border-radius: 9px;
  transition: transform .12s ease; }
.fav-row.dragging { position: relative; z-index: 3; transition: none;
  background: var(--band-fill); box-shadow: var(--shadow); }
.fav-grip { display: none; flex: none; width: 20px; text-align: center;
  color: var(--muted); cursor: grab; touch-action: none; user-select: none;
  -webkit-user-select: none; font-size: 1rem; }
.fav-list.editing .fav-grip { display: block; }
.fav-go { flex: 1; min-width: 0; display: flex; align-items: center; gap: 10px;
  padding: 9px 10px; border: 0; border-radius: 9px; background: none;
  font: inherit; font-size: 0.9rem; color: var(--ink); text-align: start;
  cursor: pointer; }
.fav-go:hover { background: var(--band-fill); }
.fav-go:focus-visible { outline: 3px solid var(--focus); outline-offset: -2px; }
.fav-list.editing .fav-go { cursor: default; }
.fav-list.editing .fav-go:hover { background: none; }
.fav-icon { flex: none; font-size: 1.05rem; display: inline-flex; align-items: center; }
.fav-icon svg { width: 18px; height: 18px; }
.fav-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.fav-del { display: none; flex: none; border: 0; background: none; cursor: pointer;
  color: var(--critical); font-size: 0.9rem; padding: 6px 8px; border-radius: 8px; }
.fav-del svg { width: 16px; height: 16px; display: block; }
.fav-list.editing .fav-del { display: block; }
.fav-del:hover { background: color-mix(in srgb, var(--critical) 10%, transparent); }
.fav-empty { margin: 0; padding: 14px 12px; color: var(--ink-2); font-size: 0.85rem; }
.fav-foot { display: flex; justify-content: flex-end; padding: 4px 4px 2px;
  margin-top: 4px; border-top: 1px solid var(--border); }

/* ---------- segmental line ---------- */
.seg-line { text-align: center; font-size: 0.9rem; margin-top: 4px; }
.seg-line.ok { color: var(--good-text); }
.seg-line.fail { color: var(--critical); }

/* ---------- admin: user creation ---------- */
.adm-create { display: flex; flex-wrap: wrap; gap: 8px; align-items: center;
  padding: 12px; margin-bottom: 10px; }
.adm-create input { flex: 1; min-width: 170px; padding: 7px 10px;
  border: 1px solid var(--border); border-radius: 8px; background: var(--page);
  color: var(--ink); font: inherit; }
.adm-pw-out { margin: 0 0 10px; padding: 10px 12px; border-radius: 10px;
  background: var(--cls-high-bg); border: 1px solid var(--warning); }
.adm-pw-out code { font-family: ui-monospace, Menlo, monospace; font-weight: 700; }

/* ---------- Wortsalat (sentence builder) ---------- */
.bld-prompt { font-size: 1.05rem; margin: 6px 0 12px; color: var(--ink); }
.bld-answer { min-height: 58px; display: flex; flex-wrap: wrap; gap: 8px; justify-content: center;
  align-items: center; padding: 8px; border: 2px dashed var(--grid); border-radius: 14px;
  margin: 0 0 14px; }
.bld-answer:empty::before { content: attr(data-empty); color: var(--muted); font-size: 0.85rem; }
.bld-pool { display: flex; flex-wrap: wrap; gap: 8px; justify-content: center; margin-bottom: 14px;
  min-height: 44px; }
.bld-tile { font-family: "Noto Sans Thai", "Thonburi", system-ui, sans-serif; font-size: 1.25rem;
  padding: 8px 14px; border-radius: 12px; border: 1px solid var(--border);
  background: var(--page); color: var(--ink); cursor: pointer; font-weight: 500;
  transition: transform .12s ease, border-color .12s ease; }
.bld-tile:hover { border-color: var(--band-edge); transform: translateY(-1px); }
.bld-answer .bld-tile { background: var(--band-fill); border-color: var(--band-edge); }
.bld-tile.right { border-color: var(--good); background: color-mix(in srgb, var(--good) 12%, transparent); }
.bld-tile.wrong { border-color: var(--critical); background: color-mix(in srgb, var(--critical) 12%, transparent); }
.bld-tile:disabled { cursor: default; transform: none; }
.bld-solution { font-family: "Noto Sans Thai", "Thonburi", system-ui, sans-serif; font-size: 1.3rem;
  display: block; margin-top: 4px; }


/* ---------- badges ---------- */
.badge-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(96px, 1fr)); gap: 10px; }
.badge { display: flex; flex-direction: column; align-items: center; text-align: center; gap: 2px;
  padding: 12px 6px; border-radius: 14px; border: 1px solid var(--border); background: var(--page); }
.badge .b-emoji { font-size: 1.8rem; line-height: 1.2; }
.badge .b-name { font-size: 0.78rem; font-weight: 600; }
.badge .b-sub { font-size: 0.66rem; color: var(--muted); }
.badge.earned { border-color: var(--band-edge); background: var(--band-fill); }
.badge.locked { opacity: 0.55; filter: grayscale(1); }
.sbx-actions { justify-content: center; margin-top: 8px; }
.sbx-privacy { text-align: center; font-size: 0.75rem; margin: 10px 0 0; }
/* Auftrag 250: das Vorbild fuer den gewaehlten Ton, ueber dem Aufnahme-/
   Karaoke-Start -- Knopf + Stimmetikett (Code+Symbol, TTS in Warnfarbe
   wie im Stimmen-Blatt, s. .voice-sheet-sub.is-warn). */
.sbx-vorbild { display: flex; flex-direction: column; align-items: center;
  gap: 4px; margin: 0 0 8px; }
.sbx-vorbild-tag { font-size: var(--fs-1); color: var(--muted); }
.sbx-vorbild-tag.is-warn { color: var(--cls-high); }
.sbx-game-vorbild { justify-content: center; margin-top: 8px; }
/* pitch karaoke: trace the target contour with your voice */
.sbx-game { margin-top: 16px; padding-top: 12px; border-top: 1px solid var(--border); }
.sbx-game-hint { margin: 2px 0 10px; }
.sbx-game-status { margin-top: 12px; text-align: center; font-weight: 700;
  font-size: 1.05rem; color: var(--ink-2); }
.sbx-game-status.ok { color: var(--good-text); }
.sbx-game-status.close { color: var(--midline); }
.sbx-game-status.miss { color: var(--critical); }
.sbx-game-actions { justify-content: center; margin-top: 8px; }
/* register warmup: three guided phases, own section under the karaoke */
.sbx-warm { margin-top: 16px; padding-top: 12px; border-top: 1px solid var(--border); }
.sbx-warm-actions { justify-content: center; margin-top: 10px; }
.sbx-warm-status { min-height: 1.2em; }

/* ---------- onboarding tour (spotlight overlay) ---------- */
.tour { position: fixed; inset: 0; z-index: 70; overflow: hidden; }
/* steps without a spotlight target dim through the backdrop itself */
.tour.tour-dim { background: rgba(10, 10, 10, 0.62); }
.tour-cutout { position: fixed; border-radius: 14px; border: 2px solid var(--brand);
  box-shadow: 0 0 0 9999px rgba(10, 10, 10, 0.62); pointer-events: none;
  transition: top .18s ease, left .18s ease, width .18s ease, height .18s ease; }
.tour-card { position: fixed; inset-inline-start: 50%; transform: translateX(-50%);
  width: min(92vw, 380px); padding: 16px 18px; }
.tour-step { display: block; color: var(--muted); font-size: 0.72rem; font-weight: 700;
  letter-spacing: 0.08em; text-transform: uppercase; }
.tour-title { margin: 4px 0 6px; font-size: 1.05rem; }
.tour-text { margin: 0; color: var(--ink-2); font-size: 0.9rem; }
.tour-actions { display: flex; align-items: center; justify-content: space-between;
  gap: 10px; margin-top: 14px; }
/* B4: ein Schritt darf auf einen bestehenden Dialog verweisen */
.tour-link { margin-top: 10px; }
/* Ein aus der Tour geoeffneter Dialog liegt ueber deren Blocker (.tour: 70) —
   sonst waere er sichtbar, aber nicht bedienbar. */
.modal-overlay.over-tour { z-index: 80; }

/* ---------- Beta-Hinweis (B6) ---------- */
/* Hoehe und Ueberlauf stehen jetzt allgemein bei `.modal-overlay > .modal`. */
#beta-modal .modal { max-width: 400px; }
#beta-modal p { margin: 0 0 10px; color: var(--ink-2); font-size: 0.9rem;
  line-height: 1.5; }


/* ---------- pronunciation detail ---------- */
.seg-hints { margin: 6px 0 0; padding-inline-start: 18px; color: var(--ink-2); font-size: 0.84rem; }
.seg-hints li { margin: 2px 0; }
.pron-rows { display: grid; gap: 8px; }
.pron-row { display: flex; align-items: center; gap: 10px; padding: 8px 10px;
  border: 1px solid var(--border); border-radius: 12px; background: var(--page); }
.pron-pair { font-size: 1.15rem; font-weight: 600;
  font-family: "Noto Sans Thai", "Thonburi", system-ui, sans-serif; }
.pron-arrow { color: var(--critical); margin: 0 6px; font-size: 0.9rem; }
.pron-count { color: var(--muted); font-size: 0.8rem; margin-inline-end: auto; }

/* ---------- mini dialogs ---------- */
/* Owner 24.09.2026: das Skript war ein Grid, die Blasen setzten aber
   align-self (im Grid die SENKRECHTE Achse) -- alle Blasen klebten links,
   jede 86 % breit, die eigene Zeile mit rechtsbuendigem Text in einer
   linksstehenden Blase. Jetzt eine Flex-Spalte: Vorbild links, eigene
   Zeile rechts, Blase nur so breit wie ihr Text. Fruehere Zeilen
   (.dlg-past) schrumpfen auf Thai + Bedeutung in einer Reihe, nur die
   aktuelle Zeile bleibt gross. */
.dlg-script { display: flex; flex-direction: column; gap: 10px; margin-bottom: 16px; }
.dlg-turn { position: relative; display: flex; flex-direction: column; gap: 2px;
  width: fit-content; max-width: 86%; padding: 10px 14px; border-radius: 16px;
  border: 1px solid var(--border); }
.dlg-app { align-self: flex-start; background: var(--surface); border-end-start-radius: 4px; }
.dlg-user { align-self: flex-end; background: var(--band-fill); border-end-end-radius: 4px;
  text-align: end; align-items: flex-end; }
.dlg-current { border-color: var(--brand); box-shadow: 0 0 0 2px var(--brand-glow); }
.dlg-thai { font-family: "Noto Sans Thai", "Thonburi", system-ui, sans-serif; font-size: 1.2rem; }
.dlg-tr { font-size: 0.82rem; color: var(--ink-2); }
.dlg-gloss { font-size: 0.85rem; color: var(--muted); }
/* Knopf und Stimmenetikett in EINER Reihe statt untereinander. */
.dlg-foot { display: flex; align-items: center; gap: 10px; margin-top: 6px; }
.dlg-play, .dlg-reveal { align-self: flex-start; }
.dlg-reveal { margin-top: 6px; }
.dlg-user .dlg-reveal { align-self: flex-end; }
/* Kompakte Vorzeile: Thai, Bedeutung und (beim Vorbild) ein runder
   Abspielknopf in einer Reihe; Umschrift und Stimmenetikett entfallen. */
.dlg-past { flex-direction: row; align-items: center; gap: 10px; padding: 6px 12px; }
.dlg-past .dlg-thai { font-size: 1.05rem; }
.dlg-past .dlg-gloss { font-size: 0.8rem; }
.dlg-past .dlg-play { align-self: center; }
.dlg-past .dlg-mark { position: static; width: 20px; height: 20px; font-size: 0.85rem; }
/* Wortsalat an der Zeile (C8): der Kasten haengt UNTER der Sprechblase und
   nimmt ihre volle Breite — die Kacheln lesen sich links nach rechts, auch
   wenn die Blase rechtsbuendig steht. */
.dlg-prep { display: flex; flex-direction: column; align-items: flex-end; gap: 6px;
  margin-top: 6px; width: 100%; }
.dlg-prep-box { width: 100%; text-align: start; display: grid; gap: 8px; }
.dlg-prep-box[hidden] { display: none; }
.dlg-prep-btn { align-self: flex-end; }
.dlg-mark { position: absolute; top: 8px; inset-inline-end: -6px; font-size: 0.9rem; font-weight: 700;
  background: var(--page); border-radius: 50%; width: 22px; height: 22px; display: grid;
  place-items: center; border: 1px solid var(--border); }
.dlg-mark.ok { color: var(--good-text); }
.dlg-mark.miss { color: var(--critical); }
.summary-row { display: flex; align-items: baseline; gap: 10px; padding: 6px 0;
  border-bottom: 1px solid var(--grid); }
.summary-row .s-mark.ok { color: var(--good-text); }
.summary-row .s-mark.miss { color: var(--critical); }
.summary-row .s-thai { font-family: "Noto Sans Thai", "Thonburi", system-ui, sans-serif; }
.summary-row .s-gloss { color: var(--muted); font-size: 0.82rem; margin-inline-start: auto; }

/* ---------- Hör-Diktat ---------- */
.dict-wrap { text-align: center; }
/* the reading loop lays options out in a 2-column grid — the tile board
   is one wide board, not a grid cell */
.rq-options > .dict-wrap { grid-column: 1 / -1; }
.dict-slots { min-height: 56px; }
.dict-tile { font-size: 1.5rem; min-width: 44px; padding: 6px 10px; }
.dict-actions { display: flex; gap: 10px; justify-content: center; align-items: center; }

/* ---------- reference work queue ---------- */
.rw-summary { margin-bottom: 14px; }
.rw-bar { height: 10px; border-radius: 6px; background: var(--grid); overflow: hidden; }
.rw-bar i { display: block; height: 100%; background: var(--good-text); border-radius: 6px;
  transition: width .3s ease; }
.rw-summary .hint { margin: 8px 0 0; }
.rw-group .rw-start { margin-top: 12px; }
.adm-approved { color: var(--good-text); font-size: 0.8rem; white-space: nowrap; }

.adm-bar { display: inline-block; width: 90px; height: 8px; vertical-align: middle; }
.adm-bar-lbl { font-size: 0.75rem; color: var(--muted); margin-inline-start: 6px; }
.adm-cov { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin: 0 0 12px; }

/* ---------- native studio (SPEC 11 / Welle 6; Studio A, Auftrag 116) ----
   Ein Bildschirm statt 2,6 (Soll, Auftrag 116): Anleitung, Angaben und
   die Nummernliste liegen im Blatt (kSheet, siehe dort), hier bleibt nur
   die immer sichtbare Aufnahmekarte, die Set-Chips mit ihrem eigenen
   Fortschrittsbalken und die eingeklappte Zeile zur Liste. */
.ns-keyhint { margin: -6px 0 0; font-size: 0.8rem; }
.ns-meta { display: flex; flex-direction: column; gap: 8px; }
.ns-meta-title { margin: 0; font-weight: 700; font-size: var(--fs-3); }
.ns-meta .hint { margin: 0; }
.ns-meta-form { display: grid; gap: 10px; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); }
.ns-meta-field { display: flex; flex-direction: column; gap: 4px; font-size: 0.8rem;
  color: var(--muted); }
.ns-meta-field input, .ns-meta-field select {
  padding: 7px 10px; border-radius: 9px; border: 1px solid var(--border-strong);
  background: var(--page); color: var(--ink); font: inherit; font-size: 0.9rem; }
.ns-meta-actions { display: flex; align-items: center; gap: 10px; margin-top: 4px; }

/* Nachbesserung Auftrag 116 (Fable-Review): eine Chip-ZEILE statt drei
   Karten (die brauchten zwei nebeneinander + C in voller Breite, macht
   ~145 px Hoehe). Die Chips selbst sind kChip (aktiver Chip hervorgehoben
   wie im Optionen-Blatt); sie bleiben in EINER Reihe, die noetigenfalls
   waagerecht scrollt statt umzubrechen (Muster wie .k-badgestrip). */
.ns-tabs { display: flex; gap: 8px; margin-bottom: 8px; flex-wrap: nowrap;
  overflow-x: auto; scrollbar-width: thin; padding-bottom: 2px; }
.ns-tabs .k-chip { flex: none; }
/* Ein einziger Balken fuer das AKTIVE Set statt eines je Chip. */
.ns-set-bar { width: 100%; margin-bottom: 12px; height: 4px;
  border-radius: 3px; background: var(--grid); overflow: hidden; }
.ns-set-bar i { display: block; height: 100%; width: 0; background: var(--brand);
  border-radius: 3px; transition: width 0.2s ease; }

/* Nachbesserung Auftrag 116: Innenabstand oben/unten halbiert (20px -> 10px),
   die seitlichen 20px der .card bleiben unangetastet. */
.ns-stage { text-align: center; padding-top: 10px; padding-bottom: 10px; }
.ns-stage-head { display: flex; justify-content: space-between; align-items: center;
  margin-bottom: 6px; }
.ns-stage .thai { font-size: 3.6rem; }
.ns-word { margin-top: 4px; font-size: 1.05rem; color: var(--ink-2); }
.ns-sub { margin-top: 4px; color: var(--muted); font-size: 0.85rem; }
/* Auftrag 128, Q38: der Ton steht seither als kToneChip (Beispiel +
   Vorhoeren) statt als Wort in `.ns-sub` — das Tonraum-Studio ist genau
   die Stelle, die die กา-Reihe aufnimmt. */
.ns-tone-row { display: flex; flex-wrap: wrap; justify-content: center;
  gap: var(--sp-2); margin-top: var(--sp-2); }
.ns-done-pill { font-size: 0.75rem; color: var(--good-text); }
.ns-stage .k-playback { margin-top: 12px; }
/* Nachbesserung Auftrag 116: Abstand Vorbild-Knopf -> Mikrofon von
   ~110 auf ~48 px (Muster wie .practice .record-zone / .dialog-play
   .record-zone, die schon 12px statt der vollen 26px setzen). */
.ns-stage .record-zone { margin: 12px 0; }

/* Die eingeklappte Zeile zur Liste (Soll, Auftrag 116): oeffnet das
   Listen-Blatt, sie traegt selbst keine Zahlen ausser dem Textinhalt. */
.ns-list-toggle { display: flex; align-items: center; justify-content: space-between;
  width: 100%; margin-top: var(--sp-2); padding: 8px 12px; border-radius: var(--r-2);
  border: 1px solid var(--border); background: var(--surface); color: var(--ink);
  font: inherit; font-size: 0.82rem; text-align: start; cursor: pointer;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ns-list-toggle::after { content: "\203a"; color: var(--muted); margin-inline-start: 8px; }

/* Inhalt des Listen-Blatts: Suchfeld, Sprung-Hinweis, das Chip-Raster
   darunter (unveraendert gegenueber der alten Dauerliste). */
.ns-sheet-list { display: flex; flex-direction: column; gap: 10px; }
.ns-search { padding: 9px 12px; border-radius: 10px; border: 1px solid var(--border-strong);
  background: var(--page); color: var(--ink); font: inherit; font-size: 0.9rem; width: 100%; }
.ns-list { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 0; }
.ns-chip { display: inline-flex; align-items: center; gap: 6px; padding: 5px 10px;
  border-radius: 10px; border: 1px solid var(--border); background: var(--surface);
  color: var(--ink); font: inherit; cursor: pointer; }
.ns-chip i { font-style: normal; font-size: 0.7rem; color: var(--muted); }
/* Auftrag 271c: erledigt-Haken als Linien-SVG statt Emoji. */
.ns-tick svg { width: 0.9em; height: 0.9em; vertical-align: -0.1em; display: inline-block; }
.ns-chip.done { color: var(--muted); background: var(--page); }
.ns-chip.done .ns-tick { color: var(--good-text); }
.ns-chip.current { border-color: var(--brand); box-shadow: 0 0 0 2px var(--band-fill); }

.ns-result { margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--border); }
.ns-curve { max-width: 280px; }
.ns-curve polyline { fill: none; stroke: var(--brand); stroke-width: 2.4;
  stroke-linecap: round; stroke-linejoin: round; }
.ns-curve .ns-zero { stroke: var(--baseline); stroke-width: 1; stroke-dasharray: 3 3; }
.ns-metrics { display: flex; flex-wrap: wrap; gap: 10px; justify-content: center;
  font-size: 0.78rem; color: var(--muted); margin-top: 6px; }
.ns-warn { color: var(--warning); font-size: 0.85rem; margin: 8px 0 0; }
.ns-result-actions { display: flex; gap: 10px; justify-content: center; margin-top: 12px; }

/* Native-Studio take status.
   Bis 05.09.2026 standen hier fuenf Rohwerte (#0ca30c, #e08a00, #b3261e,
   #9aa0a6 und ein halbdurchsichtiges #e08a0055) ohne Dunkelfassung: das
   Rot lag im Dunkeln bei 2,15:1 gegen die Karte, Orange und Grau rissen
   im Hellen ihre Schwelle. Jetzt tragen sie die Tokens, die dieselbe
   Bedeutung schon anderswo tragen — Signalgruen, Bernstein als SCHRIFT
   (--cls-high) und als KANTE (--warning), Rot und Nebenton. */
.ns-chip.kept_clean { border-color: var(--good); }
.ns-chip.kept_clean .ns-tick { color: var(--good-text); }
.ns-chip.kept_shaky { border-color: var(--warning); }
.ns-chip.kept_shaky .ns-tick { color: var(--cls-high); }
.ns-chip.recorded { border-color: var(--critical); border-style: dashed; }
.btn.has-warn { box-shadow: 0 0 0 2px color-mix(in srgb, var(--warning) 33%, transparent); }

.ns-status-label { font-size: .85em; color: var(--ink-2); }
.ns-legend { font-size: .85em; color: var(--ink-2); margin: .3em 0 .6em; }

.ns-status { font-size: 1.1em; line-height: 1; }
.ns-status.st-green { color: var(--good-text); }
.ns-status.st-orange { color: var(--cls-high); }
.ns-status.st-red { color: var(--critical); }
.ns-status.st-grey { color: var(--muted); }

/* ============ Ausfallzustand (§8by(h)) ============
   Der Hinweis, dass der gelernte Hoerrichter fehlt. Er ist ein
   `k-banner` in der Vorgabe-Auspraegung („Hinweis", kein Alarm) — hier
   steht NUR seine Einordnung im Uebungs-Screen, keine eigene Farbe.
   Dehnung: volle Breite des Screens, Abstand nach unten wie zwischen
   zwei Karten. */
.ausfall-banner { margin-bottom: var(--sp-3); }
/* Der Hinweis auf die fehlenden Satzkacheln steht im Lernbereich unter der
   Anlaufstellen-Reihe, wo die Kachel stuende.
   Dehnung: volle Breite, Abstand oben und unten wie zwischen zwei Karten. */
.ausfall-satz { margin-top: var(--sp-3); }

/* ============ Zustand "wartet" (§11, Auftrag 94) ============
   Ausfall ist eine Stoerung, kein Bewertungsmodus mehr: ohne faehigen
   Worker nimmt der Server den Versuch trotzdem an. Derselbe `k-banner`
   im Hinweiston "warn" wie ein Formfehler — hier steht nur die
   Einordnung im Uebungs-Screen, keine eigene Farbe. */
.wartet-banner { margin-bottom: var(--sp-3); }

/* SRS-Ende (Auftrag 147, Q33 Stufe 3; Q42 Punkt 2, Auftrag 148): der
   Hinweis am Bestaetigungspunkt, derselbe `k-banner` wie oben — hier nur
   die Einordnung im Uebungs-Screen. */
.srs-confirm-banner { margin-bottom: var(--sp-3); }

/* ============ context ladder (ideen #3) ============ */
.ladder-card { border-inline-start: 3px solid var(--brand); }
.ladder-banner { padding: 10px 14px; margin-bottom: 12px; }
.ladder-banner .ladder-rung { font-weight: 600; font-size: 0.9rem; }
.ladder-banner .hint { margin: 4px 0 0; }
.ladder-verdict h2 { margin: 0 0 8px; font-size: 1.05rem; }
.ladder-verdict .s-thai { font-family: "Noto Sans Thai", "Thonburi", system-ui, sans-serif; }
.ladder-verdict .s-label { color: var(--ink-2); margin-inline-start: auto; font-size: 0.8rem; }

/* ============ vowel length drill (ideen #4) ============ */
.length-card { border-inline-start: 3px solid var(--brand); }
.length-verdict h2 { margin: 0 0 8px; font-size: 1.05rem; }
.length-verdict .s-thai { font-family: "Noto Sans Thai", "Thonburi", system-ui, sans-serif;
  font-size: 1.15rem; }
.length-verdict .s-label { color: var(--ink-2); margin-inline-start: auto; font-size: 0.8rem; }
.length-verdict .length-ratio { font-weight: 600; margin: 10px 0 4px; }

/* ============ weakness drill (ideen #6) ============ */
.weakness-card { border-inline-start: 3px solid var(--brand); }

/* ============ placement wizard + learning paths ============ */
.placement-view .pl-step, .paths-view .pl-step { color: var(--muted); font-size: 0.8rem;
  margin-inline-start: auto; }
.pl-choices { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; }
.pl-choices.column { flex-direction: column; }
.pl-chip { border: 1px solid var(--border); background: var(--surface); color: var(--ink);
  border-radius: 999px; padding: 8px 14px; font: inherit; font-size: 0.9rem; cursor: pointer;
  text-align: start; display: inline-flex; align-items: center; gap: 6px; }
.pl-chip.wide { border-radius: 12px; }
.pl-chip.active { background: var(--brand); border-color: var(--brand); color: var(--on-brand); }
/* Zielauswahl (Auftrag 275): pl_goal_* verlor seine fuehrende Emoji-Glyphe,
   das Symbol sitzt jetzt klein in einer eigenen `.k-icon-cell` vor dem
   Text -- nur diese Chips tragen ueberhaupt eine (Minuten/Vorwissen bauen
   dieselbe Klasse ohne Symbol, die Zelle bleibt dann einfach leer/fehlt). */
.pl-chip .k-icon-cell { width: 18px; height: 18px; }
.pl-chip .k-icon-cell svg { width: 16px; height: 16px; }
.pl-actions { display: flex; flex-direction: column; align-items: center; gap: 10px;
  margin-top: 16px; }
.pl-actions .btn { width: 100%; }
.pl-play { margin-bottom: 10px; }
.pl-measured { margin: 8px 0 0; padding-inline-start: 18px; color: var(--ink-2); font-size: 0.88rem; }
.pl-measured li { margin: 3px 0; }
.pl-gap { font-weight: 600; }
/* Die Alternative steht unter der Empfehlung und bleibt leiser als sie:
   kein eigener Farbwert, nur weniger Kontrast und ein schmalerer Rahmen. */
.pl-alt { margin-top: 14px; }
.pl-alt-lead { margin: 0 0 6px; }
.pl-alt-card { border-style: dashed; background: transparent; }
.pl-alt-card .card-title { font-size: 0.98rem; }
.pl-alt-pick { margin-top: 8px; }
.placement-view .rq-opt { display: flex; flex-direction: column; gap: 3px; align-items: center; }
.placement-view .rq-opt small { color: var(--muted); font-size: 0.75rem; }

.path-card { margin-bottom: 12px; }
.path-card .path-emoji { margin-inline-end: 4px; }
/* Auftrag 271c: `path.emoji` wird ueber PFAD_SYMBOL/kIcon zu einem
   Linien-Symbol -- dieselbe Zelle wie das Emoji zuvor, textgross statt
   32 px, weil sie inline in Ueberschrift/Zeile steht. */
.path-emoji { display: inline-flex; align-items: center; vertical-align: -0.15em; }
.path-emoji svg, .pl-unlock-icon svg { width: 1.1em; height: 1.1em; flex: none; }
.pl-unlock { display: flex; align-items: center; gap: 6px; }
.pl-unlock-icon { display: inline-flex; }
.pa-skill-icon { width: 1em; height: 1em; vertical-align: -0.2em; flex: none;
  margin-inline-end: 3px; }
/* Auftrag 271c (Batch A: reading/vocab/admin/tasks/home/dialogs/hvpt):
   generisches Linien-Symbol inline vor Text, wo kein eigener, schon
   gestylter Baustein (kListRow-Icon, kBtn-Icon, k-icon-cell, ...) greift --
   Knopf-Vorsilben in innerHTML-Vorlagen, Tab-Chips, Uebungs-Prompts,
   Tabellenzellen. Groesse relativ (1.1em), damit sie sich dem jeweiligen
   Textkontext anpasst, statt eine feste Pixelzahl je Stelle zu erfinden --
   dasselbe Muster wie `.pa-skill-icon`/`.path-emoji svg` oben. */
.q271c-inline-icon { width: 1.1em; height: 1.1em; vertical-align: -0.15em; flex: none; }
.path-steps { margin-top: 12px; display: flex; flex-direction: column; gap: 6px; }
.path-step { display: flex; align-items: center; gap: 10px; width: 100%; text-align: start;
  border: 1px solid var(--border); background: var(--page); color: var(--ink);
  border-radius: 10px; padding: 10px 12px; font: inherit; cursor: pointer; }
.path-step:hover { border-color: var(--brand); }
.path-step .go { margin-inline-start: auto; color: var(--muted); }
.path-step.done { opacity: 0.72; }
.path-step.current { border-color: var(--brand); box-shadow: 0 0 0 2px var(--cls-low-bg) inset; }

/* ---- content review (Owner-Spezifikation 31.08.2026) ----
   Der Prüfer arbeitet am Telefon: ein Clip, große Knöpfe, kein Scrollen
   zwischen Hören und Urteilen. */
.review .rv-card { text-align: center; padding-bottom: 18px; }
/* Neutrales Kennzeichen der Vorsortierung: eine Bitte um Aufmerksamkeit,
   KEIN Urteil der Maschine — deshalb in der Hinweisfarbe, nicht in
   --critical, und ohne jede Zahl (§8bl(e)2). */
.review .rv-flag {
  color: var(--ink-2); background: var(--page);
  border: 1px solid var(--border); border-radius: 999px;
  display: inline-block; padding: 4px 12px; margin: 0 0 10px;
  font-size: 0.85rem;
}
.review .rv-translit { color: var(--ink-2); margin: 6px 0 0; font-size: 1.05rem; }
.review .rv-tone { color: var(--muted); margin: 6px 0 14px; font-size: 0.9rem; }
/* Zielton-Kacheln, Stimmwahl, gehoerter Ton, Ohr-Eichung (03.09.2026) */
.review .rv-tonechips { justify-content: center; margin: -8px 0 14px; }
.review .rv-speakers { margin: 0 0 12px; gap: var(--sp-1); }
.review .rv-heard { display: flex; flex-direction: column; gap: 6px; margin-bottom: 4px; }
.review .rv-heard-label { color: var(--muted); font-size: 0.85rem; margin: 0; }
.review .rv-heard-rows { display: flex; flex-direction: column; gap: var(--sp-2); }
.review .rv-heard-rows .k-tonechips { align-items: center; }
.review .rv-ear { margin-top: 12px; display: flex; flex-direction: column; align-items: center; gap: var(--sp-2); }
.review .rv-ear-panel {
  width: 100%; border-top: 1px solid var(--border); padding-top: 10px;
  display: flex; flex-direction: column; align-items: center; gap: var(--sp-2);
}
.review .rv-ear-title { color: var(--muted); font-size: 0.85rem; margin: 0; }
.review .rv-ear-panel .k-tonechips { justify-content: center; }
.review .rv-audio { width: 100%; max-width: 420px; margin: 0 auto; display: block; }
.review .rv-comment { display: flex; flex-direction: column; gap: 6px; margin: 16px 0 12px; }
.review .rv-comment label { color: var(--muted); font-size: 0.85rem; }
.review .rv-comment textarea {
  width: 100%; border: 1px solid var(--border-strong); border-radius: 12px;
  padding: 10px 12px; font: inherit; background: var(--surface); color: inherit;
}
/* Anleitungstext + Info-Knopf (Auftrag 102): auf dem Desktop steht der
   Absatz einfach da, der Knopf bleibt weg; ab 760 px dreht die Abfrage
   unten das Verhaeltnis um. */
.rv-intro-row { display: flex; align-items: flex-start; gap: var(--sp-2); }
.rv-intro-row .rs-intro { flex: 1 1 auto; margin: 0; }
.rv-info-btn { display: none; flex: 0 0 auto; width: 32px; height: 32px;
  border-radius: 50%; border: 1px solid var(--border-strong);
  background: var(--surface); font-size: 1rem; cursor: pointer; }
/* Stoergeraeusch-Kennzeichen (Auftrag 95, Punkt 1; als zwei Chips in EINER
   Zeile seit Auftrag 102): unabhaengig vom Urteil, direkt vor den drei
   Urteilsknoepfen. Der Zustand (gefuellt/leer) faerbt den Chip ueber
   `:has(:checked)` — dieselbe Regel, die schon den Toast an der
   Daumenleiste erkennt (Zeile 633). */
.rv-noise { display: flex; flex-wrap: wrap; gap: var(--sp-2); margin: var(--sp-2) 0; }
.rv-noise-chip { display: inline-flex; align-items: center; gap: var(--sp-1);
  padding: 6px 12px; border-radius: var(--r-pill);
  border: 1px solid var(--border-strong); background: var(--surface);
  cursor: pointer; font-size: var(--fs-2); font-weight: 600; }
.rv-noise-chip input { margin: 0; }
.rv-noise-chip:has(input:checked) { background: var(--brand-soft);
  border-color: var(--brand); color: var(--brand-ink); }
/* Die feste Urteilsleiste (Auftrag 102, Punkt 2): der Traeger bleibt auf
   dem Desktop im Fluss, `#rv-prev` steht darin als Textlink direkt ueber
   den drei Knoepfen. Erst die Abfrage unten macht ihn auf dem Handy fest. */
.rv-judgebar { display: flex; flex-direction: column; gap: var(--sp-1); }
.rv-prev-link { align-self: center; font-size: 0.9rem; }
.rv-buttons { display: grid; gap: 10px; grid-template-columns: 1fr; }
.rv-big {
  min-height: 62px; font-size: 1.12rem; font-weight: 650; border-radius: 16px;
  justify-content: center;
}
.rv-correct { background: var(--good); border-color: var(--good); color: var(--on-good); }
.rv-unsure { background: var(--surface); }
.rv-wrong { background: var(--critical); border-color: var(--critical); color: var(--on-critical); }
@media (min-width: 620px) { .rv-buttons { grid-template-columns: repeat(3, 1fr); } }
.rv-big[disabled] { opacity: .45; filter: saturate(.35); cursor: default; }

/* --- Ton-Zustand (B2): ohne geladenen Ton kein Urteil --- */
.review .rv-audio-state { color: var(--muted); font-size: 0.9rem; margin: 8px 0 0; }
.review .rv-audio-state.is-error { color: var(--critical); }
.review .rv-audio-actions { display: flex; gap: 8px; justify-content: center;
  margin-top: 8px; flex-wrap: wrap; }
/* Zustand „spielt gerade" (S4): die grosse Taste ist die EINZIGE
   Abspielbedienung, also muss sie ihn selbst zeigen. */
.play-model.is-playing {
  border-color: var(--brand); color: var(--brand);
  animation: rv-pulse 1.4s ease-in-out infinite;
}
@keyframes rv-pulse {
  0%, 100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--brand) 40%, transparent); }
  50% { box-shadow: 0 0 0 6px color-mix(in srgb, var(--brand) 0%, transparent); }
}
@media (prefers-reduced-motion: reduce) { .play-model.is-playing { animation: none; } }

/* --- Ausfallzustand der Karte (B1) und Speicher-Ausfall (S3) --- */
.review .rv-offline { text-align: center; }
.review .rv-offline-text { margin: 4px 0 14px; color: var(--ink-2); }
.review .rv-save-error, .review .rv-mark {
  border-radius: 12px; padding: 8px 12px; margin: 10px 0; font-size: 0.92rem;
}
.review .rv-save-error { color: var(--critical);
  border: 1px solid var(--critical); background: var(--surface); }
.review .rv-mark { color: var(--ink-2);
  border: 1px solid var(--border); background: var(--surface); }
/* Die Frage-Zeile (Stufen-Entscheid 02.09.2026): klein, ruhig, direkt
   ueber den Urteilsknoepfen — sie sagt, WONACH gefragt wird, und darf den
   drei Knoepfen nicht die Aufmerksamkeit nehmen. */
.review .rv-stage {
  margin: 12px 0 6px; font-size: 0.86rem; color: var(--ink-2); text-align: center;
}

/* „Gespeichert — rueckgaengig" (B3): liegt UEBER der Tab-Leiste, damit die
   drei Urteilsknoepfe von ihr nicht verschoben werden. */
.rv-undo {
  position: fixed; inset-inline-start: 12px; inset-inline-end: 12px; bottom: calc(84px + env(safe-area-inset-bottom, 0px));
  z-index: 30; display: flex; align-items: center; justify-content: space-between;
  gap: 12px; padding: 10px 14px; border-radius: 14px;
  background: var(--surface); border: 1px solid var(--border);
  box-shadow: var(--elevated-ring), var(--elevated-shadow); font-size: 0.92rem;
}
@media (min-width: 761px) { .rv-undo { inset-inline-start: auto; inset-inline-end: 24px; bottom: 24px; max-width: 420px; } }

/* Mobil zaehlt jeder Pixel: die drei Urteilsknoepfe muessen auf 390x844
   ohne Scrollen stehen (S2). Gemessen im Durchlauf vom 02.09.; Auftrag 102
   (07.09.) nimmt Kruemelpfad, Anleitungstext und Fussnote ganz weg (hinter
   dem Info-Knopf bzw. dem Zurueck-Knopf) und macht die Urteilsleiste fest. */
@media (max-width: 760px) {
  .review .k-pagehead-crumbs { display: none; }
  /* „🎧 Vorbild-Aufnahmen prüfen" bricht bei voller Groesse zweizeilig;
     kleiner (mit Auslassungspunkten als Sicherheitsnetz fuer laengere
     Uebersetzungen) bleibt sie eine Zeile neben dem Zurueck-Knopf. */
  .review .k-pagehead-title { font-size: 1.05rem; white-space: nowrap;
    overflow: hidden; text-overflow: ellipsis; }
  .review .rs-intro, .review .hint { display: none; }
  .rv-info-btn { display: inline-flex; align-items: center; justify-content: center; }
  /* Die zwei Stoergeraeusch-Chips bleiben in EINER Zeile (Auftrag 102,
     Punkt 2): statt zu brechen, teilen sie sich die Breite haelftig und
     ihre eigene Beschriftung wird klein genug, um darin zu bleiben —
     „Störgeräusch am Anfang" bricht dann hoechstens INNERHALB des Chips,
     nicht zwischen den Chips. */
  .review .rv-noise { flex-wrap: nowrap; }
  .review .rv-noise-chip { flex: 1 1 0; justify-content: center;
    text-align: center; font-size: 0.74rem; padding: 6px 8px; }
  .review .rv-card { padding: 12px 14px 14px; }
  .review .thai { font-size: 2.6rem; margin: 2px 0 0; }
  .review .rv-tone { margin: 4px 0 8px; }
  .review .k-playback { margin-top: 6px; }
  /* Die feste Leiste (S2, Auftrag 102): drei Knoepfe in EINER Zeile statt
     gestapelt, ueber der Reiterleiste wie die Daumenleiste der Uebung
     (dieselbe Hoehe: 58 px + Geraete-Sicherheitszone). */
  .review .rv-judgebar {
    position: fixed; inset-inline-start: 0; inset-inline-end: 0;
    bottom: calc(var(--k-fuss-h) + env(safe-area-inset-bottom, 0px));
    z-index: 40; gap: 4px; padding: var(--sp-1) var(--sp-3) var(--sp-2);
    background: var(--surface); border-top: 1px solid var(--border);
  }
  .review .rv-buttons { grid-template-columns: repeat(3, 1fr); gap: 8px; }
  .review .rv-big { min-height: 56px; font-size: 0.92rem; padding: 0 6px; }
  /* Platz fuer die feste Leiste, sonst verdeckt sie Kommentarfeld,
     Fussnote-Ersatz und die „Gespeichert"-Zeile. */
  .review { padding-bottom: 156px; }
  /* „Gespeichert — rueckgaengig" liegt jetzt UEBER der festen Urteilsleiste
     (die naechste Aufnahme zeigt ihre Knoepfe schon, waehrend die Meldung
     noch steht) und nicht mehr nur ueber der Reiterleiste. */
  .review .rv-undo {
    bottom: calc(var(--k-fuss-h) + env(safe-area-inset-bottom, 0px) + 100px);
  }
}
.adm-review-log { font-size: 0.82rem; }
.adm-review-codes { display: flex; flex-wrap: wrap; gap: 6px; }

/* ======================================================================
   Entwurfs-Umbau 31.08.2026 — Navigation, Start-Dashboard, Konto
   Ein Navigationsknoten, zwei Auspraegungen: unten als Tab-Leiste
   (mobil), links als Sidebar (Desktop). Kein Wert ist hier hart
   verdrahtet — alles laeuft ueber die Tokens oben.
   ====================================================================== */

.mainnav { position: fixed; z-index: 55; }
.mainnav-inner { display: flex; }
.tab {
  flex: 1 1 0; display: flex; flex-direction: column; align-items: center;
  justify-content: center; gap: 3px; min-height: 50px; padding: 6px 4px;
  border: 0; background: none; cursor: pointer; color: var(--muted);
  font-size: 0.69rem; font-weight: 600; border-radius: 12px;
  transition: color .14s ease, background .14s ease;
}
.tab .tab-ic { position: relative; display: grid; place-items: center; }
.tab:hover { color: var(--ink-2); }
.tab.active { color: var(--brand); font-weight: 750; }
.tab:focus-visible { outline: 3px solid var(--focus); outline-offset: -2px; }
.tab-dot {
  position: absolute; top: -2px; inset-inline-end: -4px; min-width: 8px; height: 8px;
  border-radius: 999px; background: var(--critical);
  font-size: 0; line-height: 0;
}
/* Baustein 29 (kTabCount, 04.09.2026): derselbe Punkt, aber mit lesbarer
   Zahl — der Studio-Reiter nennt die Menge der offenen Punkte der
   Rollenarbeit. Marke statt --critical: Rollenarbeit ist eine Aufgabe,
   keine Stoerung. Die Hoehe ist an die Zeilenhoehe gebunden, damit die
   Zahl in beiden Themen mittig sitzt.
   Dehnung: die Breite folgt der Zahl (min-width + waagerechtes Polster),
   der Zaehler haengt rechts oben am Zeichen und verschiebt die
   Reiterbeschriftung nicht. */
.tab-count {
  top: -6px; inset-inline-end: -8px;
  min-width: 16px; height: 16px; padding: 0 4px;
  background: var(--brand); color: var(--on-brand);
  font-size: 0.62rem; line-height: 16px; font-weight: 750;
  letter-spacing: 0; text-align: center;
}

/* --- mobil: untere Leiste --- */
@media (max-width: 760px) {
  .mainnav {
    inset-inline-start: 0; inset-inline-end: 0; bottom: 0;
    background: color-mix(in srgb, var(--surface) 92%, transparent);
    backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px);
    border-top: 1px solid var(--border);
    padding-bottom: env(safe-area-inset-bottom, 0);
    /* Feste Hoehe aus DEMSELBEN Token wie die Fuss-Abstaende der festen
       Blaetter darueber (--k-fuss-h, siehe :root) -- vorher liess die
       Leiste ihre Hoehe vom Inhalt bestimmen (organisch ~63px), waehrend
       jede Fuss-Rechnung anderswo 58px annahm (259-Audit-Fund). */
    height: var(--k-fuss-h);
    display: flex; align-items: center;
  }
  .mainnav-inner { padding: var(--sp-1) var(--sp-1); width: 100%; }
  /* Seit dem Studio-Reiter (03.09.2026) stehen bis zu FUENF Ziele in der
     Leiste. Auf 360 px bleibt je Reiter rund ein Fuenftel; die laengste
     Beschriftung ist „Fortschritt". Sie darf weder umbrechen noch
     abgeschnitten werden — deshalb faellt das seitliche Innenpolster weg
     und die Buchstaben ruecken enger zusammen, waehrend die Schriftgroesse
     bei 0.69rem bleibt (darunter wird die Leiste unleserlich). */
  .tab { padding-inline-start: 0; padding-inline-end: 0; letter-spacing: -0.015em; }
  .tab-lbl { white-space: normal; overflow-wrap: break-word; line-height: 1.15; }
  .view { padding-bottom: 96px; }
  /* Auftrag 264: `--k-blattfest-boden` reserviert den Stapel am Fuss --
     ausser die Ansicht traegt `.has-thumbbar` (Daumenleiste, Baustein 32):
     die zieht den Stapel schon in IHRE eigene Reservierung (siehe dort),
     ein zweites Mal ueber `.view` waere Doppel-Addieren. Die 96px oben
     bleiben fuer JEDE Ansicht stehen (Fussleiste), nur die Zugabe faellt
     bei `.has-thumbbar` weg. */
  .view:not(:has(.has-thumbbar)) {
    padding-bottom: calc(96px + var(--k-blattfest-boden, 0px));
  }
}

/* --- Desktop: linke Sidebar --- */
@media (min-width: 761px) {
  .mainnav {
    top: 56px; inset-inline-start: 0; bottom: 0; width: var(--k-seitenleiste-w);
    background: var(--surface); border-inline-end: 1px solid var(--border);
    padding: 24px 16px;
  }
  .mainnav-inner { flex-direction: column; gap: 6px; }
  .tab {
    flex: none; flex-direction: row; justify-content: flex-start;
    gap: 14px; padding: 13px 16px; font-size: 0.94rem; border-radius: 14px;
    min-height: 48px;
  }
  .tab.active { background: var(--brand-soft); }
  .tab .tab-lbl { line-height: 1.2; }
  /* die Sidebar liegt unter der Kopfzeile — nur der Inhalt rueckt ein */
  #app { padding-inline-start: var(--k-seitenleiste-w); }
  .topbar-inner { max-width: none; }
}

/* ---------- Start-Dashboard ---------- */
.home-dash { display: flex; flex-direction: column; gap: 22px; padding-top: 8px; }
.home-dash > *, .home-main > * { margin: 0; }
/* Hauptspalte: unter 900px nur ein durchsichtiger Rahmen (die Kinder sind
   weiter Flex-Kinder von .home-dash), ab 900px die linke Gitterspalte. */
.home-main { display: contents; }

.home-dash .greeting { font-size: 1.55rem; font-weight: 800; letter-spacing: -0.02em;
  margin: 0; line-height: 1.15; color: var(--ink); }
#home-extras:empty { display: none; }
#home-extras { display: flex; flex-direction: column; gap: 12px; }

/* Pfadzeile (Auftrag 253, Owner-Rueckbau von 248): Start behaelt nur EINE
   `kListRow` zum gewaehlten Pfad -- die volle Pfadkarte (`.path-card`
   unten) zog zurueck auf die Lernpfade-Seite. */
.path-row { margin: 0 0 var(--sp-2); }
.path-row[hidden] { display: none; }

/* Pfadkarte (`renderPathCard`, urspruenglich Auftrag 248): die Abschnitte
   des gewaehlten Lernpfads als eigene Zeilen -- die Kopfzeile ist eine
   gewoehnliche `kListRow` (`.k-listrow`), je Abschnitt darunter ein
   Mini-Balken. Seit Auftrag 253 auf der Lernpfade-Seite gemountet (vorher
   hier auf Start). */
.path-card { display: flex; flex-direction: column; gap: 6px; }
.path-card[hidden] { display: none; }
.pfk-list { display: flex; flex-direction: column; }
.pfk-row { padding: 4px 0; border-bottom: 1px solid var(--border); }
.pfk-row:last-child { border-bottom: 0; }
.pfk-bar { height: 4px; border-radius: 2px; background: var(--border);
  margin: 0 var(--sp-3) 2px; overflow: hidden; }
.pfk-bar-fill { height: 100%; background: var(--brand); border-radius: 2px; }
.pfk-gain { margin: 0 var(--sp-3) 4px; }

/* "Heute" (Auftrag 246 als "Dein Stand", Auftrag 248 umgebaut, kBlattFest-
   Schluessel "heute"): die drei Ebenen bekommen nur einen Namen, ihr
   Aussehen erben sie von `.k-list`/`.k-stack`. */
.heute-blatt-kopf { display: flex; flex-direction: column; gap: 2px; }
.heute-blatt-titel { font-weight: 700; }
.heute-blatt-stand { margin: 0; }
.heute-blatt-halb, .heute-blatt-voll { display: flex; flex-direction: column; }
.heute-plan-liste { margin-bottom: var(--sp-2); }
.heute-kennzahlen { display: flex; gap: var(--sp-3); }
.heute-kennzahlen > * { flex: 1 1 0; }
.heute-blatt-links { margin-top: var(--sp-2); }
/* Ab 900px wird "Heute" eine Seitenleiste (Auftrag 246, derselbe
   Bruch wie `.progress-view`): eigene Spalte ueber die volle Hoehe der
   Startseite -- die `@media(min-width:900px)`-Regel auf `.k-blattfest`
   selbst (Baustein 44) macht daraus ein gewoehnliches Gitterkind. Muss
   NACH der Grundregel (`display: flex` oben) stehen, sonst gewinnt die
   unbedingte Regel per Quellreihenfolge. */
@media (min-width: 900px) {
  .home-dash { display: grid; grid-template-columns: minmax(0, 1fr) 340px;
    gap: 0 var(--sp-5); align-items: start; }
  .home-main { display: flex; flex-direction: column; gap: 28px; grid-column: 1; grid-row: 1; }
  .home-dash > .account-footer { grid-column: 1; }
  .home-dash > #heute-blatt { grid-column: 2; grid-row: 1; }
}

/* --- Desktop: Start breiter (Frame 7:273) --- */
@media (min-width: 761px) {
  .home-dash { gap: 28px; }
  .home-dash .greeting { font-size: 2rem; }
}

/* ---------- Konto-Tab ---------- */
.profile-view { display: flex; flex-direction: column; gap: 14px; padding-top: 8px; }
.profile-view .h1 { margin: 8px 0 0; }
.pr-id { display: flex; align-items: center; gap: 14px; padding: 16px; }
.pr-avatar {
  width: 46px; height: 46px; border-radius: 50%; flex: none; display: grid;
  place-items: center; font-weight: 700; font-size: 1.1rem;
  background: linear-gradient(135deg, var(--brand), var(--brand-ink));
  color: var(--on-brand);
}
.pr-id-meta { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.pr-id-meta strong { overflow-wrap: anywhere; }
/* Seit Muster B (Auftrag 117) baut nur noch die Sprachzeile (ein <select>
   in der Zeile, das `kListRow` nicht kann) auf `.pr-row`; jede andere
   Konto-Zeile steht auf `kListRow`. */
.pr-row {
  display: flex; align-items: center; gap: 12px; width: 100%;
  padding: 13px 16px; min-height: 52px; text-align: start; cursor: pointer;
  color: inherit; text-decoration: none;
}
.pr-row:hover { border-color: var(--band-edge); }
.pr-row:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; }
.pr-ic { font-size: 1.15rem; flex: none; width: 24px; text-align: center; }
.pr-ic svg { width: 20px; height: 20px; display: inline-block; vertical-align: middle; }
.pr-meta { display: flex; flex-direction: column; gap: 1px; flex: 1 1 auto; min-width: 0; }
.pr-label { font-weight: 650; }
.pr-select { cursor: default; }
.pr-select select {
  border: 1px solid var(--border-strong); border-radius: 9px; background: var(--surface);
  color: var(--ink); font: inherit; padding: 6px 8px;
}

/* Daueranlaufstellen im Lernbereich (Pfad, Lernheft, Favoriten) */
.entry-row { display: flex; flex-wrap: wrap; gap: 8px; margin: 4px 0 14px; }
.entry-chip { display: inline-flex; align-items: center; gap: 7px; min-height: 40px; }

/* ==================================================================
   UI-KIT (Nacht B, 01.09.2026)
   ------------------------------------------------------------------
   Zwoelf Bausteine. Jeder hat eine Klasse mit Praefix `k-`, einen
   Render-Helfer in `webapp/js/kit.js` und ein DOKUMENTIERTES
   DEHNVERHALTEN (die Zeile „Dehnung:" am Baustein). Die Wildwuechse
   aus `docs/ia-audit-analyse.md` §2.2 (7 Karten-, 6 Knopf-, 6 Chip-,
   5 Seitenkopf-, 4 Listenzeilen-Formen) ziehen hier zusammen.

   Regel fuer alles hier drin: Abstaende aus --sp-*, Radien aus --r-*,
   Groessen aus --fs-*, Farben aus Tokens. Kein Zwischenwert.
   ================================================================== */

/* ---- Raster ------------------------------------------------------
   Dehnung: eine Spalte auf schmalen Geraeten, danach so viele Spalten,
   wie mit --col-min hineinpassen. Kein Breakpoint noetig — das Raster
   rechnet selbst. `k-grid-2` deckelt bei zwei Spalten (Kachelpaare),
   `k-grid-fix2` bleibt auch mobil zweispaltig (Kategorie-Kacheln). */
.k-grid { display: grid; gap: var(--sp-3);
  grid-template-columns: repeat(auto-fill, minmax(min(100%, var(--col-min)), 1fr)); }
.k-grid-2 { --col-min: 320px; }
.k-grid-wide { --col-min: 360px; }
.k-grid-fix2 { display: grid; gap: var(--sp-3);
  grid-template-columns: repeat(2, minmax(0, 1fr)); }
/* `k-grid-kompakt` (Auftrag 9, 04.09.2026): alle Kinder in EINER Reihe,
   gleich breit, auch bei 360 px. Gedacht fuer die zwei bis drei
   Kennzahl-Kacheln am Kopf einer Karte: untereinander gestapelt kosten
   sie dort rund 300 px und schieben den Startknopf aus dem Bild.
   Kein Breakpoint — die Reihe gilt in jeder Breite; was sie traegt,
   muss deshalb `k-stat-compact` sein. */
.k-grid-kompakt { display: grid; gap: var(--sp-2);
  grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr);
  grid-template-columns: none; }
.k-stack { display: flex; flex-direction: column; gap: var(--sp-3); }
.k-stack-tight { gap: var(--sp-2); }
.k-row { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2); }

/* ---- 1. Karte ----------------------------------------------------
   Drei Auspraegungen statt sieben: neutral · getoent (Hinweis) ·
   Kategorie (Pastellflaeche). Dehnung: fuellt immer die Zelle, in der
   sie steht (width 100%); Hoehe waechst mit dem Inhalt, Aktionen
   sitzen unten (margin-top:auto in .k-card-actions). */
.k-card {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-3); padding: var(--sp-4); width: 100%;
  box-shadow: var(--shadow); display: flex; flex-direction: column;
  gap: var(--sp-2); text-align: start; color: var(--ink); min-width: 0;
}
button.k-card { font: inherit; cursor: pointer;
  transition: transform .12s ease, box-shadow .12s ease, border-color .12s ease; }
button.k-card:hover { transform: translateY(-1px); border-color: var(--band-edge); }
button.k-card:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; }
.k-card.k-card-toned { background: var(--band-fill); border-color: var(--band-edge);
  box-shadow: none; }
.k-card.k-card-cat { box-shadow: none; }
/* Fuenfte Auspraegung „Marken-Ton" (04.09.2026): der Wochenbericht der
   Fortschrittsseite steht auf --brand-soft. „toned" waere das Bandgruen
   des Konturbildes — dieselbe Familie, aber die Bedeutung „Hinweis". */
.k-card.k-card-brand { background: var(--brand-soft); border-color: var(--brand-soft);
  box-shadow: none; }
/* Ein Abschnittskopf als erstes Kind einer Karte braucht seinen
   Aussenabstand nach oben nicht — die Karte hat schon Polster. */
.k-card > .k-secthead:first-child { margin-top: 0; }
/* Vierte Auspraegung „Akzent" (Variante 5 A, Owner 01.09.): eine
   farbige Kante links, 4 px, Farbe ueber ein Token. Sie ersetzt die drei
   handgesetzten `border-left-color`-Regeln, die .hvpt-card, .typ-card
   und .hw-card ausserhalb des Kits fuehrten (Fund F2). Sie greift
   bewusst OHNE `.k-card` davor, damit auch die noch nicht umgestellten
   `.card`-Formen dieselbe Kante bekommen statt einer eigenen. */
.k-card-accent { border-inline-start: 4px solid var(--k-card-accent, var(--brand)); }
.k-card-title { font-size: var(--fs-4); font-weight: 650; margin: 0; }
.k-card-text { font-size: var(--fs-2); color: var(--ink-2); margin: 0; }
.k-card-actions { display: flex; flex-wrap: wrap; gap: var(--sp-2);
  margin-top: auto; padding-top: var(--sp-2); }

/* ---- 2. Knopf ----------------------------------------------------
   Drei Raenge statt sechs: primaer · sekundaer · Textaktion.
   Regel (aus der Analyse, Teil 5 Punkt 2): eine Karte traegt einen
   VOLLBREITKNOPF, wenn sie eine Sitzung startet; sonst eine
   Textaktion. Dehnung: von Haus aus inhaltsbreit; `k-btn-block`
   fuellt die Zeile, im Kartenfuss auf Mobil ohnehin voll. */
.k-btn {
  display: inline-flex; align-items: center; justify-content: center;
  gap: var(--sp-2); min-height: 44px; padding: var(--sp-2) var(--sp-4);
  border-radius: var(--r-2); border: 1px solid var(--border);
  background: var(--surface); color: var(--ink); font: inherit;
  font-weight: 600; font-size: var(--fs-3); cursor: pointer;
  transition: filter .12s ease, background .12s ease;
}
/* Auftrag 271c: ein Knopf mit `icon:` bekommt eine kleinere Symbol-Zelle
   als die 32-px-Standardzelle -- der Knopf hat selbst schon einen Rand. */
.k-btn .k-icon-cell { width: 20px; height: 20px; }
.k-btn .k-icon-cell svg { width: 18px; height: 18px; }
.k-btn:active { transform: scale(0.97); }
.k-btn:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; }
.k-btn:disabled { opacity: 0.5; cursor: default; }
.k-btn-primary { background: var(--brand); border-color: var(--brand); color: var(--on-brand); }
.k-btn-primary:hover:not(:disabled) { filter: brightness(1.06); }
/* Sekundaer hat eine EIGENE Ruheflaeche (Variante 6 A, Owner 01.09.,
   Fund F1). Vorher: `transparent` mit Hover auf `--surface` — und weil
   sekundaere Knoepfe fast immer auf einer `--surface`-Karte stehen,
   passierte beim Ueberfahren sichtbar nichts. `--grid` ist in beiden
   Modi von `--page` UND `--surface` unterscheidbar (hell 1,26 / 1,29 ·
   dunkel 1,39 / 1,24 — die Kante traegt die Form, die Flaeche nur den
   Rang), Schrift darauf: hell 14,87:1, dunkel 13,99:1.
   Ueberfahren wird `--brand-soft`; damit der Wechsel auch dunkel
   sichtbar ist (Flaeche gegen Flaeche nur 1,03), wechseln Rand und
   Schrift mit — Farbe ist hier nicht der alleinige Traeger.
   Schrift auf der Hoverflaeche: hell 7,12:1, dunkel 9,94:1. */
.k-btn-secondary { background: var(--grid); }
.k-btn-secondary:hover:not(:disabled) { background: var(--brand-soft);
  border-color: var(--brand); color: var(--brand-ink); }
.k-btn-text {
  background: none; border: 0; padding: var(--sp-1) var(--sp-2);
  color: var(--brand); font-weight: 600; min-height: 32px;
}
.k-btn-text:hover:not(:disabled) { text-decoration: underline; }
.k-btn-danger { color: var(--critical); border-color: var(--critical); background: transparent; }
.k-btn-block { width: 100%; }
.k-btn-small { min-height: 32px; padding: var(--sp-1) var(--sp-3); font-size: var(--fs-2); }

/* ---- 3. Listenzeile ---------------------------------------------
   Eine Form statt vier (Vorbild: die beste vorhandene, `pr-row`).
   Dehnung: immer volle Breite, das Label schrumpft (min-width:0),
   das Beiwerk rechts nie. */
.k-list { display: flex; flex-direction: column; gap: var(--sp-1); width: 100%; }
.k-listrow {
  display: flex; align-items: center; gap: var(--sp-3); width: 100%;
  padding: var(--sp-3) var(--sp-4); border-radius: var(--r-2);
  background: var(--surface); border: 1px solid var(--border);
  font: inherit; color: var(--ink); text-align: start;
}
button.k-listrow, a.k-listrow { cursor: pointer; text-decoration: none; }
button.k-listrow:hover, a.k-listrow:hover { border-color: var(--band-edge); }
button.k-listrow:focus-visible, a.k-listrow:focus-visible {
  outline: 3px solid var(--focus); outline-offset: 2px; }
.k-listrow[disabled] { opacity: 0.6; cursor: default; }
.k-listrow-icon { flex: none; font-size: 1.15rem; line-height: 1; }
.k-listrow-icon svg { width: 22px; height: 22px; display: block; }
.k-listrow-body { min-width: 0; flex: 1; }
.k-listrow-label { font-size: var(--fs-3); font-weight: 550;
  overflow: hidden; text-overflow: ellipsis; }
.k-listrow-sub { font-size: var(--fs-1); color: var(--muted); }
.k-listrow-tags { display: flex; flex-wrap: wrap; gap: var(--sp-1); margin-top: var(--sp-1); }
.k-listrow-end { flex: none; color: var(--muted); font-size: var(--fs-2); }
.k-listrow-end svg { width: 18px; height: 18px; display: block; }
/* `danger` (Auftrag 117): eine Zeile, die etwas UNWIDERRUFLICHES tut
   (Konto loeschen) — derselbe rote Text wie vorher `.pr-row.danger`. */
.k-listrow-danger .k-listrow-label { color: var(--critical); }
/* Konto-Zeile im Werkzeuge-Blatt (Auftrag 246): eine `k-listrow`-Zeile
   OHNE Klick-Navigation -- sie zeigt Name/E-Mail und traegt einen
   eigenen Abmelden-Knopf statt eines Pfeils. */
.tools-konto-row { cursor: default; }
.tools-konto-row .k-listrow-sub { overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap; }
/* Zahlenspalte der Zeile (04.09.2026): Koennens-Zahl laut, Nebenzahl leise,
   uebereinander — nebeneinander waere die Zeile auf 360 px zu voll.
   Dehnung: die Spalte bleibt so breit wie ihr laengster Text. */
.k-listrow-num { flex: none; display: flex; flex-direction: column;
  align-items: flex-end; gap: 1px; text-align: end; }
.k-listrow-value { font-size: var(--fs-2); font-weight: 700; color: var(--ink);
  font-variant-numeric: tabular-nums; white-space: nowrap; }
.k-listrow-note { font-size: var(--fs-1); color: var(--muted); white-space: nowrap; }

/* ---- 4. Chip -----------------------------------------------------
   Zwei Auspraegungen statt sechs: Auswahl (klickbar, aktiv/inaktiv)
   und Status (nicht klickbar, faerbt nach Bedeutung).
   Dehnung: inhaltsbreit, die Reihe umbricht (.k-row). */
.k-chip {
  display: inline-flex; align-items: center; gap: var(--sp-1);
  padding: var(--sp-1) var(--sp-3); min-height: 32px;
  border-radius: var(--r-pill); border: 1px solid var(--border);
  background: var(--page); color: var(--ink-2);
  font: inherit; font-size: var(--fs-2); font-weight: 600; white-space: normal;
  overflow-wrap: break-word; text-align: center;
}
button.k-chip { cursor: pointer; min-height: 40px; }
button.k-chip:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; }
.k-chip.is-active { background: var(--brand-soft); border-color: var(--band-edge);
  color: var(--brand-ink); }
.k-chip-status { pointer-events: none; }
.k-chip-good { color: var(--good-text); border-color: var(--good); }
.k-chip-warn { color: var(--cls-high); border-color: var(--warning); }
.k-chip-bad { color: var(--critical); border-color: var(--critical); }

/* ---- 5. Banner ---------------------------------------------------
   Dehnung: volle Breite des Containers; Text umbricht, die Aktion
   rutscht unter den Text, sobald es eng wird. */
.k-banner {
  display: flex; flex-wrap: wrap; align-items: flex-start; gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-4); border-radius: var(--r-2);
  background: var(--band-fill); border: 1px solid var(--band-edge);
  width: 100%;
}
.k-banner-warn { background: var(--cls-high-bg); border-color: var(--warning); }
.k-banner-bad { background: var(--fy-weak-bg); border-color: var(--critical); }
/* tone="next" (Variante H, Auftrag 89, 07.09.2026): die „Jetzt"-Zeile der
   Aufgabenkarte nennt den naechsten Handgriff. Dieselbe warme, gelbliche
   Flaeche wie k-banner-warn — Gelb ist hier ein Wink, kein Alarm; der
   Unterschied steht im Symbol (Zeigefinger) und im Satz, nicht in einer
   zweiten Farbe. */
.k-banner-next { background: var(--cls-high-bg); border-color: var(--warning); }
.k-banner-icon { font-size: 1.25rem; line-height: 1.2; flex: none; }
.k-banner-body { min-width: 200px; flex: 1; }
.k-banner-title { font-weight: 650; font-size: var(--fs-3); }
.k-banner-text { font-size: var(--fs-2); color: var(--ink-2); margin: 0; }

/* Leise Zeile „Pfad-Voreinstellung: ... · Anpassen ›" (Auftrag 233,
   Punkt 11): bewusst ohne Kasten/Rahmen -- ein Hinweis, keine Warnung. */
.k-preset-note {
  font-size: var(--fs-2); color: var(--ink-2); margin: 0 0 var(--sp-3);
}
.k-preset-adjust {
  background: none; border: none; padding: 0; color: var(--brand);
  font-size: var(--fs-2); cursor: pointer;
}

/* ---- 6. Fortschrittsring ----------------------------------------
   EIN Ring pro Screen (Analyse Teil 5, Punkt 6). Dehnung: feste
   Kantenlaenge, weil ein Kreis nichts gewinnt, wenn er waechst;
   Groesse ueber --k-ring-size steuerbar. */
.k-ring { --k-ring-size: 96px; position: relative; flex: none;
  width: var(--k-ring-size); height: var(--k-ring-size); }
.k-ring svg { display: block; width: 100%; height: 100%; transform: rotate(-90deg); }
.k-ring circle { fill: none; stroke-width: 8; stroke-linecap: round; }
.k-ring .k-ring-track { stroke: var(--grid); }
/* Der Bogen traegt die Aussage des Rings — er muss sich von der Spur
   abheben (K3/E13). Das Entwurfs-Tuerkis --brand-bright kam hell nur auf
   2,25:1 gegen --grid; die naechstdunklere Stufe der Marke traegt 3,97:1
   (dunkel 7,60:1). Dieselbe Entscheidung wie bei der Schrift auf der
   Markenflaeche: das Tuerkis bleibt Dekor. */
.k-ring .k-ring-arc { stroke: var(--brand);
  transition: stroke-dashoffset .5s ease; }
.k-ring.is-reached .k-ring-arc { stroke: var(--good-text); }
/* Variante 7 A (Owner 01.09.): die Marke sitzt AUF dem Ring und zeigt,
   wo das Tagesziel liegt — ein Strich quer durch die Spur, keine zweite
   Farbe im Bogen. */
.k-ring .k-ring-goal { stroke: var(--ink); stroke-width: 3; }
.k-ring-center { position: absolute; inset: 0; display: grid; place-items: center;
  align-content: center; gap: 1px;
  text-align: center; line-height: 1.05; }
/* Zahl gross, Einheit klein: die Zahl traegt die Aussage, die Einheit
   sagt nur, worin gezaehlt wird. */
.k-ring-value { font-size: var(--fs-5); font-weight: 750; color: var(--brand-ink);
  line-height: 1; }
.k-ring-unit { font-size: 10px; font-weight: 600; color: var(--muted);
  max-width: calc(var(--k-ring-size) - 26px); }
.k-ring-label { font-size: var(--fs-1); color: var(--muted); }

/* ---- 7. Reiter-/Segmentleiste -----------------------------------
   Dehnung: umbricht, statt ueberzulaufen — das ist die Reparatur des
   Admin-Befunds (fuenf Reiter auf 390 px, Seite 938 px breit).
   Zusaetzlich waagerecht scrollbar, damit auch acht Reiter passen. */
.k-tabs {
  display: flex; flex-wrap: wrap; gap: var(--sp-1);
  background: var(--page); border-radius: var(--r-2); padding: var(--sp-1);
  max-width: 100%; overflow-x: auto; scrollbar-width: thin;
}
.k-tab {
  border: 0; background: transparent; font: inherit; font-size: var(--fs-2);
  font-weight: 600; color: var(--ink-2); cursor: pointer;
  padding: var(--sp-2) var(--sp-3); border-radius: var(--r-1); min-height: 40px;
  white-space: normal; overflow-wrap: break-word;
}
.k-tab.is-active { background: var(--surface); color: var(--ink); box-shadow: var(--lift); }
.k-tab:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; }
/* Variante „gleichbreit": zwei bis vier Modi teilen sich die Zeile */
.k-tabs-even .k-tab { flex: 1 1 auto; text-align: center; }

/* ---- 8. Formularfeld --------------------------------------------
   Dehnung: volle Breite des Formulars; Beschriftung ueber dem Feld,
   Fehlertext darunter (umbricht). */
.k-field { display: flex; flex-direction: column; gap: var(--sp-1); width: 100%; }
.k-field-label { font-size: var(--fs-2); color: var(--ink-2); font-weight: 550; }
.k-field input, .k-field select, .k-field textarea {
  width: 100%; font: inherit; font-size: var(--fs-3);
  padding: var(--sp-3); border-radius: var(--r-2);
  border: 1px solid var(--border-strong); background: var(--surface); color: var(--ink);
}
.k-field input:focus, .k-field select:focus, .k-field textarea:focus {
  outline: 3px solid var(--focus); outline-offset: 1px; border-color: transparent; }
.k-field-hint { font-size: var(--fs-1); color: var(--muted); }
.k-field-error { font-size: var(--fs-2); color: var(--critical); min-height: 1.2em; }

/* ---- 9. Modal / Formular-Dialog ---------------------------------
   Ersetzt prompt()/confirm(). Dehnung: bis 420 px breit, danach
   zentriert; Hoehe auf 80 % des Sichtfelds begrenzt und INNEN
   scrollend (Befund: die Richterhilfe lief unter die Tab-Leiste).
   Der Fussabstand haelt die mobile Tab-Leiste frei. */
.k-modal-overlay {
  position: fixed; inset: 0; z-index: 60; padding: var(--sp-4);
  background: rgba(0, 0, 0, 0.45); display: flex;
  align-items: center; justify-content: center;
}
.k-modal {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-3); box-shadow: var(--shadow);
  width: 100%; max-width: 420px; padding: var(--sp-5);
  max-height: min(80vh, 100% - 96px); overflow-y: auto;
  display: flex; flex-direction: column; gap: var(--sp-3);
}
.k-modal-title { font-size: var(--fs-4); font-weight: 700; margin: 0; }
.k-modal-text { font-size: var(--fs-2); color: var(--ink-2); margin: 0; }
.k-modal-actions { display: flex; flex-wrap: wrap; gap: var(--sp-2);
  justify-content: flex-end; margin-top: var(--sp-2); }
.k-modal-actions .k-btn { flex: 1 1 140px; }

/* ---- 9b. Blatt (kSheet, Auftrag 116) ------------------------------
   Wie kModal, aber am Fuss der Seite verankert statt in der Mitte: ein
   Griff nach unten statt nach der Bildschirmmitte. EIN Codepfad fuer
   alle Breiten (Owner-Entscheid Auftrag 116) — kein zweiter Aufbau fuer
   den Desktop.

   Dehnung: volle Breite bis 560 px, danach zentriert und auf 560 px
   begrenzt; Hoehe bis 85 % des Sichtfelds, innen scrollend. */
.k-sheet-overlay {
  position: fixed; inset: 0; z-index: 60; padding: 0;
  background: rgba(0, 0, 0, 0.45); display: flex;
  align-items: flex-end; justify-content: center;
}
.k-sheet {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-3) var(--r-3) 0 0; box-shadow: var(--shadow);
  width: 100%; max-width: 560px; padding: var(--sp-5);
  padding-bottom: calc(var(--sp-5) + env(safe-area-inset-bottom, 0px));
  max-height: 85vh; overflow-y: auto;
  display: flex; flex-direction: column; gap: var(--sp-3);
}
.k-sheet-title { font-size: var(--fs-4); font-weight: 700; margin: 0; }
.k-sheet-text { font-size: var(--fs-2); color: var(--ink-2); margin: 0; }
.k-sheet-actions { display: flex; flex-wrap: wrap; gap: var(--sp-2);
  justify-content: flex-end; margin-top: var(--sp-2); }
.k-sheet-actions .k-btn { flex: 1 1 140px; }

/* ---- 44. Persistentes Blatt (kBlattFest, Auftrag 246) --------------
   V07 Blatt-Architektur (Owner-Entscheid 25.09.2026): anders als
   `.k-sheet` (Dialog mit Overlay) haengt dieses Blatt OHNE Overlay
   dauerhaft am Fuss der Ansicht — die Ansicht dahinter bleibt bedienbar.
   Drei Rastpunkte per `data-level`: klein (eine Zeile), halb (45vh),
   voll (85vh, innen scrollend). Was erst ab einer Stufe erscheint,
   traegt `.k-blattfest-nur-halb`/`.k-blattfest-nur-voll` — GENAU diese
   zwei Regeln blenden es aus, solange die Stufe niedriger ist.

   Ab 900px (derselbe Bruch wie `.practice`/`.progress-view`) macht ALLEIN
   diese CSS-Regel daraus eine gewoehnliche Spalte (kein Skript fragt das
   Sichtfeld ab, Baustein-Regel `test_die_matrix_schaltet_ohne_javascript_um`):
   Griff weg, Stufen-Klassen wirken nicht mehr (immer der volle Inhalt), die
   Platzierung als Spalte macht die Seite selbst (Gitter oder eigene Regel)
   — dieser Baustein setzt hier nur `position: static`, damit er ein
   gewoehnliches Gitterkind werden kann.

   Dehnung: volle Breite unter 900px, feste Hoehe je Stufe (`transition`
   ausser waehrend des Ziehens); ab 900px folgt die Hoehe dem Inhalt
   (`height: auto`), die Seite bestimmt Breite und Spalte. */
.k-blattfest { position: fixed; inset-inline-start: 0; inset-inline-end: 0; z-index: 40;
  bottom: calc(var(--k-fuss-h) + env(safe-area-inset-bottom, 0px) + var(--k-blattfest-versatz, 0px));
  background: var(--surface); border: 1px solid var(--border); border-bottom: 0;
  border-radius: var(--r-3) var(--r-3) 0 0; box-shadow: var(--shadow);
  display: flex; flex-direction: column; overflow: hidden;
  transition: height 0.18s ease, bottom 0.18s ease; }
.k-blattfest[hidden] { display: none; }
.k-blattfest.is-dragging { transition: none; }
.k-blattfest[data-level="klein"] { height: auto; }
/* Mehrere Blaetter am selben Fuss (Auftrag 247/258, Stapel "uebung"):
   `--k-blattfest-versatz` traegt kit.js je Knoten ein (Summe der Hoehen
   der Blaetter darunter im Stapel, Baustein 9b2) und setzt die Fussposition
   (oben). Fuer die eigene Hoehe eines gehobenen Blatts (R3) reicht das
   NICHT: ein Blatt mit niedrigem Rang (z. B. Urteil, ganz unten im Stapel)
   haette sonst nichts abzuziehen und bliebe bei "voll" unbegrenzt hoch,
   waehrend Blaetter mit hoeherem Rang (Versuche/Schreib) darueber aus dem
   Sichtfeld geschoben wuerden (259-Audit-Fund). `--k-blattfest-versatz-hoehe`
   ist deshalb die Summe ALLER anderen Blaetter im Stapel (unter UND ueber
   dem eigenen Rang) -- da R2 ohnehin nur ein gehobenes Blatt erlaubt, sind
   das immer deren klein-Hoehen. Ohne Stapel bleiben beide Variablen 0px,
   Verhalten unveraendert (Heute-Blatt). */
/* Zwei weitere 259-Audit-Funde, beide auf 360x640/th (kleinstes Sichtfeld,
   laengste Sprache):
   (1) 85vh + Kopfzeile (56px Innenhoehe + 1px Rand, `.topbar-inner`) +
   Fussleiste (--k-fuss-h, siehe `bottom` oben) sind zusammen groesser als die
   Bildschirmhoehe -- das oberste Blatt im Stapel ragt dann UNTER die
   Kopfzeile, egal wie klein die anderen Blaetter sind (der Versatz-Abzug
   haelt die SUMME aller Blaetter konstant bei 85vh, das allein reicht auf
   640px nicht mehr). `min()` deckelt zusaetzlich auf die tatsaechlich
   vorhandene Resthoehe.
   (2) mit sehr hohen "klein"-Nachbarn (laengere
   Thai-Diagnosezeile) zog der Versatz-Abzug "halb" (nur 45vh) fast auf 0 --
   Koerper-Innenhoehe 24px, das letzte Kind blieb selbst nach dem Scrollen
   ans Ende 27px unten abgeschnitten (I6). "voll" (85vh) hatte auf keinem
   geprueften Sichtfeld dasselbe Problem -- ein Mindestmass NUR bei "halb"
   haelt eine benutzbare, scrollbare Innenhoehe ein, auch wenn das den
   Versatz nicht mehr auf den letzten Pixel ausgleicht (ein knapp zu
   kleines, aber benutzbares Blatt schlaegt ein unbenutzbares 24px-Fenster);
   ein Mindestmass bei "voll" brauchte es nicht und schob dort nur unnoetig
   die Nachbarn ueber die Kopfzeile hinaus (I1-Ruecksprung, wieder entfernt). */
.k-blattfest[data-level="halb"] {
  height: max(200px,
    calc(min(45vh, 100vh - 57px - var(--k-fuss-h)) - var(--k-blattfest-versatz-hoehe, 0px))); }
.k-blattfest[data-level="voll"] {
  height: calc(min(85vh, 100vh - 57px - var(--k-fuss-h)) - var(--k-blattfest-versatz-hoehe, 0px)); }
/* 761-899px -- Randzone zwischen den zwei Umbruchpunkten des Auftritts:
   `.mainnav` ist ab 761px schon die linke Seitenleiste (kein Balken mehr
   am unteren Rand), die zwei-Spalten-Ansicht (`.practice`, Grid) und
   `position: static` fuer dieses Blatt beginnen aber laut R7 erst bei
   900px -- bis dahin bleibt es ein fester Boden-Zettel. Ohne diese Regel
   reservierte `bottom`/die Stufenhoehen weiter `--k-fuss-h` fuer eine
   Fussleiste, die es in dieser Breite gar nicht mehr gibt: das Blatt
   schwebte zu hoch, mit einer Luecke bis zur echten Unterkante (259-Audit-
   Fund, I1 bei 768x1024, Fable-Review 25.09. -- "muss alle Invarianten
   erfuellen", keine Randzonen-Ausnahme). Zweiter Fund derselben Review:
   `inset-inline-start: 0` (Grundregel) liegt in diesem Band UNTER der Seitenleiste
   (`.mainnav` ist hier schon 232px breit, `#app` selbst weicht ihr mit
   `padding-left` aus) -- Kind-Inhalt links im Blatt war darum abgeschnitten.
   `left` bekommt hier dieselbe Breite wie die Seitenleiste. */
@media (min-width: 761px) and (max-width: 899.98px) {
  .k-blattfest {
    inset-inline-start: var(--k-seitenleiste-w);
    bottom: calc(env(safe-area-inset-bottom, 0px) + var(--k-blattfest-versatz, 0px)); }
  .k-blattfest[data-level="halb"] {
    height: max(200px,
      calc(min(45vh, 100vh - 57px) - var(--k-blattfest-versatz-hoehe, 0px))); }
  .k-blattfest[data-level="voll"] {
    height: calc(min(85vh, 100vh - 57px) - var(--k-blattfest-versatz-hoehe, 0px)); }
}
.k-blattfest-griff { position: relative; flex: 0 0 auto; display: flex; justify-content: center;
  align-items: center; min-height: 40px; padding: var(--sp-2) 0; background: none; border: 0;
  cursor: grab; touch-action: none; }
.k-blattfest-griff:active { cursor: grabbing; }
.k-blattfest-griffstrich { width: 36px; height: 4px; border-radius: 2px;
  background: var(--border-strong); pointer-events: none; }
/* R4: das Schliesszeichen (Chevron) sitzt rechts IM Griff, sichtbar nur
   auf halb/voll -- auf klein ist das Blatt schon eingeklappt. */
.k-blattfest-chevron { position: absolute; inset-inline-end: var(--sp-3); top: 50%;
  transform: translateY(-50%); display: none; align-items: center; justify-content: center;
  width: 28px; height: 28px; padding: 0; background: none; border: 0; border-radius: var(--r-2);
  color: var(--ink-2); font-size: var(--fs-4); line-height: 1; cursor: pointer; }
.k-blattfest[data-level="halb"] .k-blattfest-chevron,
.k-blattfest[data-level="voll"] .k-blattfest-chevron { display: inline-flex; }
.k-blattfest-body { padding: var(--sp-2) var(--sp-4) var(--sp-4); display: flex;
  flex-direction: column; gap: var(--sp-3); min-height: 0; }
.k-blattfest[data-level="halb"] .k-blattfest-body,
.k-blattfest[data-level="voll"] .k-blattfest-body {
  overflow-y: auto; flex: 1 1 auto;
  padding-bottom: calc(var(--sp-4) + env(safe-area-inset-bottom, 0px)); }
@media (max-width: 899.98px) {
  .k-blattfest[data-level="klein"] .k-blattfest-nur-halb,
  .k-blattfest[data-level="klein"] .k-blattfest-nur-voll {
    display: none; }
  .k-blattfest[data-level="halb"] .k-blattfest-nur-voll {
    display: none; }
}
@media (min-width: 900px) {
  .k-blattfest { position: static; height: auto; max-height: none;
    box-shadow: none; border: 1px solid var(--border); border-radius: var(--r-3);
    overflow: visible; }
  .k-blattfest-griff { display: none; }
  /* Die Stufenhoehen (45vh/85vh, overflow hidden) gelten nur unter 900px --
     als Seitenleiste folgt die Hoehe dem Inhalt, sonst wird die letzte
     Karte abgeschnitten (Screenshot a248 desktop1024, Fable 25.09.). */
  .k-blattfest[data-level] { height: auto; }
  .k-blattfest-body, .k-blattfest[data-level] .k-blattfest-body { overflow: visible; padding: var(--sp-4); }
  /* Was nur die Stufe klein braucht (z. B. die Zeile der naechsten Uebung,
     die der Tagesplan darunter ohnehin enthaelt), bleibt in der
     Seitenleiste weg -- dort steht immer der volle Inhalt. */
  .k-blattfest-nur-klein { display: none; }
}
.k-blattfest[data-level="halb"] .k-blattfest-nur-klein,
.k-blattfest[data-level="voll"] .k-blattfest-nur-klein { display: none; }

/* ---- 9b3. Versuche-Blatt (Auftrag 247, V07 Stufe 2) -----------------
   Zweites `kBlattFest` neben dem Urteil-Blatt (`#versuche-panel`,
   Schluessel "versuche"): Stufe klein zeigt nur die Punktereihe DIESER
   Sitzung + "n Versuche heute", Stufe halb/voll zusaetzlich die letzten
   eigenen Versuche DIESES Items vom Server. Der Abstand zum Urteil-Blatt
   darunter steht bei `.k-blattfest` selbst (`--k-blattfest-versatz`,
   Baustein 9b2, Auftrag 258) -- hier nur der INHALT dieses Blatts. */
.versuche-panel[hidden] { display: none; }
/* R6 (Auftrag 258, Befund 2): auf "klein" lag der Text direkt unter dem
   Griff ohne Luft und war oben abgeschnitten -- `.k-blattfest-body` traegt
   seither generell `padding-top: var(--sp-2)` (siehe Baustein 9b2), hier
   zusaetzlich die Mindesthoehe der antippbaren Zeile selbst. */
.versuche-kopf { display: flex; align-items: baseline; justify-content: space-between;
  gap: var(--sp-3); flex-wrap: wrap; min-height: 40px; }
.versuche-punkte { display: flex; align-items: center; gap: 4px; flex-wrap: wrap; }
.versuche-punkt { width: 8px; height: 8px; border-radius: 50%; flex: none;
  background: var(--border-strong); }
.versuche-punkt.is-ok { background: var(--good); }
.versuche-punkt.is-miss { background: var(--critical); }
.versuche-punkt.is-aktuell { background: var(--brand); }
.versuche-heute { display: inline-flex; align-items: center; gap: var(--sp-1);
  min-height: 40px; font-size: var(--fs-1); color: var(--muted); background: none; border: 0;
  padding: 0; cursor: pointer; font: inherit; }
.versuche-heute-pfeil { color: var(--ink-2); }
.versuche-liste { display: flex; flex-direction: column; gap: var(--sp-2); }
.versuche-leer { color: var(--muted); font-size: var(--fs-2); margin: 0; }
.versuche-row { display: grid; grid-template-columns: 72px 1fr; gap: var(--sp-2) var(--sp-3);
  align-items: center; padding: var(--sp-2); border: 1px solid var(--border);
  border-radius: var(--r-2); background: var(--surface); }
.versuche-row.is-ok { border-inline-start: 3px solid var(--good); }
.versuche-row.is-miss { border-inline-start: 3px solid var(--critical); }
.versuche-row-kontur { grid-row: 1 / 3; }
.versuche-row-info { display: flex; flex-direction: column; gap: 2px; }
.versuche-row-urteil { font-weight: 600; }
.versuche-row-zeit { font-size: var(--fs-1); color: var(--muted); }
.versuche-row-actions { display: flex; gap: var(--sp-2); grid-column: 2; }

/* ---- 9b4. Schreib-Blatt (Auftrag 249, V07 Stufe 4) -------------------
   Drittes `kBlattFest` neben Urteil- und Versuche-Blatt (`#schreib-panel`,
   Schluessel "schreiben") -- oeffnet sich erst auf den ✎-Chip hin, statt
   von Anfang an am Fuss zu stehen (kein Inhalt vor dem ersten Antippen).
   Der Versatz zu den Blaettern darunter UND der z-index (das zuletzt
   gehobene Blatt liegt oben) kommen seit Auftrag 258 aus dem Kit selbst
   (kBlattFest-Option `stapel: "uebung"`, Baustein 9b2, Regeln R1/R3/R5) --
   kein doppelter Versatz, kein fester z-index mehr hier. Die Schreibflaeche
   selbst (Zielzeile, Leinwand, Ergebnis) traegt die BESTEHENDEN Klassen der
   Handschrift-Schule (`.hw-target`/`.hw-cell`/`.hw-reveal`/`.k-tracepad`) --
   kein zweiter Satz Regeln fuer dieselbe Flaeche. */
.schreib-panel[hidden] { display: none; }
.schreib-kopf { display: flex; align-items: baseline; justify-content: space-between;
  gap: var(--sp-3); flex-wrap: wrap; }
/* Kein eigener margin-top mehr (Auftrag 258 Teil 2, I13): `.k-blattfest-body`
   ist ein Flex-Koerper mit `gap: var(--sp-3)` -- ein zusaetzlicher Abstand
   HIER verdoppelte ihn auf 20px, kein Rastermass mehr. */
.schreib-flaeche { display: flex; flex-direction: column; align-items: center;
  gap: var(--sp-3); margin-top: var(--sp-3); }
.schreib-flaeche .hw-target { width: 100%; }
.schreib-flaeche .k-tracepad { margin: 0 auto; }
.schreib-flaeche .k-row { justify-content: center; width: 100%; }
.schreib-flaeche .k-btn-block { width: 100%; }
.schreib-flaeche .hw-reveal { width: 100%; }

/* ---- 9c. Gruppe (kGruppe, Auftrag 117, Muster B) ------------------
   Eine einklappbare Kopfzeile ueber einer Liste — die Kompaktform fuer
   lange Seiten (Dialoge, Konto, Fortschritt, Lernheft, Vokabeln,
   Lesestufen): Titel, Zaehler, Chevron; Kopf und Pfeil bleiben immer
   sichtbar, der Listeninhalt klappt darunter auf. Dasselbe Muster wie
   die Hinweiszeile (Baustein 34), nur fuer LISTEN statt fuer einen
   Erklaertext.
   Dehnung: volle Breite des Behaelters; der Kopf ist eine Flexzeile, in
   der der Titel den Rest nimmt und mit Auslassungspunkten abschneidet. */
.k-gruppe { width: 100%; border: 1px solid var(--border);
  border-radius: var(--r-3); overflow: hidden; background: var(--surface); }
.k-gruppe + .k-gruppe { margin-top: var(--sp-3); }
.k-gruppe-kopf { display: flex; align-items: center; gap: var(--sp-2);
  width: 100%; padding: var(--sp-3); background: none; border: 0;
  font: inherit; font-weight: 650; text-align: start; cursor: pointer;
  color: var(--ink); }
.k-gruppe-title { flex: 1 1 auto; min-width: 0; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; }
.k-gruppe-count { flex: 0 0 auto; color: var(--muted); font-weight: 500;
  font-size: var(--fs-2); }
.k-gruppe-arrow { flex: 0 0 auto; color: var(--muted); }
.k-gruppe-body { display: flex; flex-direction: column; gap: var(--sp-2);
  padding: 0 var(--sp-3) var(--sp-3); }
.k-gruppe-body[hidden] { display: none; }

/* ---- 9d. Kapitel-Assistent (kKapitel, Auftrag 118, Muster A) ------
   „Seitwaerts statt runter": genau EIN Kapitel steht auf dem Schirm,
   Punktzeile oben, feste Leiste unten — dasselbe Bild wie die
   Daumenleiste (`.k-thumbbar`), nur mit zwei Faechern statt drei.
   Dehnung: volle Breite des Behaelters; am Desktop loest sich die
   Leiste aus dem festen Fuss und steht im Fluss (EIN Codepfad,
   Owner-Entscheid wie bei kSheet/kThumbBar). */
.k-kapitel { display: flex; flex-direction: column; gap: var(--sp-3); }
.k-kapitel-label { font-size: var(--fs-1); color: var(--muted);
  text-align: center; }
.k-kapitel-dots { display: flex; flex-wrap: wrap; justify-content: center;
  gap: var(--sp-1); }
.k-kapitel-dot { width: 10px; height: 10px; border-radius: 50%;
  border: 0; padding: 0; background: var(--border); cursor: pointer; }
.k-kapitel-dot.is-active { background: var(--brand); width: 22px;
  border-radius: var(--r-2); }
.k-kapitel-stage { display: flex; flex-direction: column; gap: var(--sp-3);
  min-width: 0; }
.k-kapitel-bar { display: flex; gap: var(--sp-2); }
.k-kapitel-bar .k-btn { flex: 1 1 0; }
@media (max-width: 760px) {
  .k-kapitel:not(.k-kapitel-inline) { padding-bottom: 84px; }
  .k-kapitel:not(.k-kapitel-inline) .k-kapitel-bar {
    position: fixed; inset-inline-start: 0; inset-inline-end: 0;
    bottom: calc(var(--k-fuss-h) + env(safe-area-inset-bottom, 0px));
    z-index: 40; padding: var(--sp-2) var(--sp-3);
    background: var(--brand-soft); border-top: 1px solid var(--brand);
  }
}
@media (min-width: 761px) { .k-kapitel-bar { margin-top: var(--sp-4); } }
/* `fixedBar: false` (Judge-Hilfe, innerhalb eines Dialogs): die Leiste
   bleibt immer im Fluss, unabhaengig von der Breite. */
.k-kapitel-inline .k-kapitel-bar { margin-top: var(--sp-2); }

/* ---- 10. Seitenkopf ---------------------------------------------
   Eine Form statt fuenf: Kruemelpfad · Zurueck · Titel; Variante
   „Assistent" mit „Abbrechen" statt „Zurueck".
   Dehnung: der Titel nimmt den Rest der Zeile und umbricht (kein
   Abschneiden mehr — das war der Kruemelpfad-Befund). */
.k-pagehead { display: flex; flex-wrap: wrap; align-items: center;
  gap: var(--sp-2) var(--sp-3); margin: var(--sp-4) 0 var(--sp-3); }
.k-pagehead-crumbs { flex: 1 1 100%; order: -1;
  font-size: var(--fs-1); color: var(--muted); display: flex;
  flex-wrap: wrap; align-items: center; gap: var(--sp-1); }
.k-pagehead-crumbs button { background: none; border: 0; font: inherit;
  color: var(--muted); cursor: pointer; padding: 0; }
.k-pagehead-crumbs button:hover { color: var(--brand); text-decoration: underline; }
.k-pagehead-title { font-size: var(--fs-5); font-weight: 700; margin: 0;
  min-width: 0; flex: 1 1 200px; overflow-wrap: anywhere; }
/* Kopf ohne Titel (Kalibrierung, Uebung, Lese-Uebung): der Platzhalter
   nimmt die Luecke, damit die Punktleiste rechts steht statt am Knopf zu
   kleben. Dehnung: nimmt den Rest der Zeile, bricht nie um. */
.k-pagehead-spacer { flex: 1 1 auto; min-width: 0; }
/* Zaehler und Punktleiste als Kopf-Aktion: sie stehen rechts — auch
   dann, wenn der Titel die Zeile fuellt und sie umbrechen.
   Dehnung: schiebt sich selbst an den rechten Rand, waechst nicht. */
.k-pagehead > .hint, .k-pagehead > .progress-dots { margin-inline-start: auto; }

/* ---- 11. Kennzahl-Kachel ----------------------------------------
   Pflichtangabe `data-kind`: „koennen" oder „fleiss" (Kit-Regel L1 —
   Fleiss steht nie an erster Stelle; der Waechter-Test prueft, dass
   die Angabe da ist). Dehnung: fuellt die Rasterzelle.

   Variante 5 C (Owner 01.09.): die Art ist jetzt SICHTBAR. Koennen
   spricht laut — 44 px, getoent, Rand in der Bandfarbe. Fleiss spricht
   leise — 22 px, grau, Rand wie jede andere Kachel. Vorher unterschied
   `data-kind` die beiden nur im Quelltext: „66 % Trefferquote" und
   „128 Versuche" sahen gleich wichtig aus. */
.k-stat {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-2); padding: var(--sp-3); text-align: center;
  min-width: 0;
}
.k-stat-value { font-size: var(--fs-6); font-weight: 700; letter-spacing: -0.02em;
  line-height: 1.1; }
.k-stat-label { font-size: var(--fs-1); color: var(--muted); margin-top: var(--sp-1); }
/* laut */
.k-stat[data-kind="koennen"] { border-color: var(--band-edge); }
/* Sichtbefund 01.09.2026: 44 px fest liess lange Koennen-Werte
   („12 von 20 Toenen") auf schmalen Schirmen aus der Kachel laufen. Die
   Zahl darf mitgehen — 11vw ist die Breite einer halben Kachel im
   Zweispalter, 28 px die untere Grenze, unter der die laute Zahl ihren
   Rang gegen die leise (22 px) verlieren wuerde. */
.k-stat[data-kind="koennen"] .k-stat-value { font-size: clamp(28px, 11vw, 44px);
  color: var(--brand-ink); }
/* leise */
.k-stat[data-kind="fleiss"] .k-stat-value { font-size: 22px; font-weight: 650;
  color: var(--muted); }
.k-stat[data-kind="fleiss"] .k-stat-label { color: var(--ink-2); }
.k-stat-trend { font-size: var(--fs-1); color: var(--good-text); }
.k-stat-trend.down { color: var(--critical); }
/* Gleichstand (Auftrag 15, 04.09.2026): die Zeile bleibt stehen, damit
   die Reihe ihre Hoehe behaelt, sagt aber nichts Gutes und nichts
   Schlechtes — also die leise Farbe, nicht Gruen und nicht Rot. */
.k-stat-trend.same { color: var(--muted); }
/* Einheit als eigene Zeile unter der Zahl: ‚114‘ laut, ‚Zeichen/Min‘
   leise. Sie steht zwischen Zahl und Beschriftung, weil sie zur Zahl
   gehoert und nicht zu deren Namen. */
.k-stat-unit { font-size: var(--fs-1); color: var(--muted); line-height: 1.2; }
/* Die kleine Bauform (Auftrag 9). Eine Stufe leiser in allen drei
   Zeilen, damit zwei bis drei Kacheln bei 360 px nebeneinander lesbar
   bleiben; auf dem Desktop deckelt der clamp bei 32 px statt 44 px —
   dieselbe Rangfolge, nur kleiner. */
.k-stat-compact { padding: var(--sp-2); }
.k-stat-compact[data-kind="koennen"] .k-stat-value {
  font-size: clamp(20px, 7vw, 32px); }
.k-stat-compact[data-kind="fleiss"] .k-stat-value { font-size: 18px; }
.k-stat-compact .k-stat-label { font-size: var(--fs-1); margin-top: var(--sp-1);
  line-height: 1.25; }
/* Die kleinste Beschriftung (Auftrag 58, Punkt 6, 05.09.2026). 0,68 rem
   ist KEINE neue Groesse — die kompakte Bauform benutzt sie fuer Einheit
   und Trend bereits; --fs-1 (0,72 bis 0,78 rem) waere die naechste Stufe
   darueber und in einer Reihe aus fuenf Kacheln bei 360 px zu breit.
   Kein Umbruch: die Reihe soll gleich hoch bleiben, und eine zweite
   Zeile in einer von fuenf Kacheln macht alle fuenf hoeher. Was dann
   nicht passt, wird abgeschnitten und mit einer Ellipse markiert —
   sichtbar abgeschnitten ist ehrlicher als still umgebrochen. Beide
   Selektoren, damit die Regel auch gegen `.k-stat-compact .k-stat-label`
   gewinnt (gleiche Spezifitaet, spaeter im Blatt). */
.k-stat-label-xs, .k-stat-compact .k-stat-label-xs {
  font-size: 0.68rem; line-height: 1.25;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.k-stat-compact .k-stat-unit { font-size: 0.68rem; }
.k-stat-compact .k-stat-trend { font-size: 0.68rem; line-height: 1.25; }
/* Die Trendzeile UNTER der Kachelreihe (Auftrag 21, 04.09.2026). Sie
   traegt weiter `.k-stat-trend`, damit gruen/rot/grau EINE Regel
   bleiben, steht aber in voller Reihenbreite statt in der ersten
   Kachel. Ausrichtung wie die Kacheln: zentriert wie deren Inhalt,
   kein eigener Einzug, dieselben Aussenkanten. Der Abstand nach oben
   ist um sp-1 verkuerzt — die Zeile gehoert zur Reihe darueber, nicht
   zum naechsten Block. */
.k-trend-line { text-align: center; line-height: 1.25;
  margin-top: calc(-1 * var(--sp-1)); }

/* ---- 12. Leerzustand / Fehlerzustand ----------------------------
   Der Baustein, den es nicht gab — und deshalb endeten elf
   Leerzustaende als Toast. Pflichtteil: GENAU EINE angebotene
   Handlung. Dehnung: volle Breite, Text auf Lesebreite zentriert. */
.k-empty {
  display: flex; flex-direction: column; align-items: center; gap: var(--sp-2);
  text-align: center; padding: var(--sp-6) var(--sp-4);
  background: var(--surface); border: 1px dashed var(--baseline);
  border-radius: var(--r-3); width: 100%;
}
.k-empty-icon { font-size: 2rem; line-height: 1; }
.k-empty-title { font-size: var(--fs-4); font-weight: 650; }
.k-empty-text { font-size: var(--fs-2); color: var(--ink-2); margin: 0;
  max-width: 42ch; }
.k-empty-error { border-style: solid; border-color: var(--critical); }

/* ---- App-weite Reparaturen, die am Kit haengen ------------------- */
/* Die Admin-Reiterleiste ist jetzt eine Kit-Reiterleiste: sie bricht um,
   statt die Seite auf 938 px zu ziehen (Inventur v2, Befund 1). */
#adm-tabs { margin-bottom: var(--sp-4); }

/* Sitzungsbilanz (E22): drei Zeilen, sonst nichts. */
.sr-line { font-size: var(--fs-3); color: var(--ink); }

/* ---- Start: Koennens-Kachel und der eine Weiter-Block ----
   Dehnung: die Kachel fuellt die Spalte. Sie bleibt eine Spalte, auch auf
   dem Desktop — die Zahl ist die Aussage, sie braucht keinen Nachbarn. */
.home-skill { margin: var(--sp-4) 0 var(--sp-2); }
/* Systemhinweis unter der Koennens-Kachel (Konzept §3.2): leiser Abstand,
   damit er sich nicht vor den Weiter-Block draengt. */
.home-sysnote { margin: 0 0 var(--sp-2); }
.home-sysnote[hidden] { display: none; }
.home-skill .k-card { gap: var(--sp-2); }
.home-skill .k-stat { border: 0; padding: 0; text-align: start; }
.home-skill .k-stat-value { font-size: var(--fs-5); line-height: 1.1; }
.home-skill .k-stat-label, .home-skill .k-stat-trend { margin-top: 0; }
.home-skill .k-card-actions { padding-top: 0; }
.home-next { margin: var(--sp-4) 0; }
/* "Naechste Lerneinheit" (Vorlage V7.7 Salbei Global, Auftrag 271):
   Unterzeile + "5 MIN"-Pille ueber der dunklen Aktionskarte. */
.home-next-hint { display: flex; align-items: center; justify-content: space-between;
  gap: var(--sp-3); margin-bottom: var(--sp-2); flex-wrap: wrap; }
.home-next-hint-text { margin: 0; color: var(--muted); font-size: var(--fs-2); }
/* Dunkle Aktionskarte: `#continue-btn` bleibt ein `.k-btn.k-btn-primary`
   (Fokusring/44px/Klick-Flaeche geerbt) -- diese Klasse ueberschreibt nur
   Flaeche, Radius (16, wie eine Karte) und Innenleben-Layout. */
.home-action-card {
  justify-content: flex-start; text-align: start; gap: var(--sp-3);
  min-height: 48px; padding: 16px; border-radius: var(--r-3);
  background: var(--brand-ink); border-color: var(--brand-ink); color: var(--on-brand);
}
.home-action-card:hover:not(:disabled) { filter: brightness(1.08); }
.home-action-body { display: flex; flex-direction: column; flex: 1 1 auto; min-width: 0; }
.home-action-title { font-weight: 700; font-size: var(--fs-4); white-space: normal; }
.home-action-sub { font-weight: 400; font-size: var(--fs-2);
  opacity: 0.85; margin-top: 2px; white-space: normal; }
.home-action-arrow { flex: none; width: 22px; height: 22px; }

/* ---- Start A2, nur <= 480 px (Auftrag 103 Q0c/1) --------------------
   Zwei kleine Kennzahl-Karten ("Dein Stand", "Diese Woche") und eine
   einzeilige Auffrisch-Zeile ersetzen dort die grosse Koennens-Kachel,
   die leise Zeile und die Auffrisch-Karte — nichts geloescht, nur auf
   breiteren Schirmen weiter sichtbar (Desktop >= 761 px unveraendert).
   Die Reihenfolge im Markup entspricht schon der Skizze; ein Umschalten
   passiert allein ueber `display`, kein `order` noetig. */
.home-mobile-stats, .home-mobile-refresh { display: none; }
@media (max-width: 480px) {
  /* Neun statt fuenf Bloecke auf Start A2: der Vorgabe-Abstand (22 px)
     summiert sich auf 844 px zuviel — dieselbe Bildschirmzeile enger, wie
     die Kachel-Raster es hier schon tun (kompakte Auspraegung). */
  .home-dash { gap: 8px; padding-top: 4px; }
  #skill-tile, #home-sysnote, #home-quiet { display: none; }
  .home-mobile-stats:not([hidden]) {
    display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px;
  }
  .home-mobile-stats .home-mini-card { padding: 10px; }
  .home-mobile-refresh:not([hidden]) { display: flex; }
  .home-next { padding: 12px; }
  /* Auftrag 248b (KUERZEN): die Tagesplan-Zeile, derentwegen der Weiter-
     Block auf Mobil bisher auf eine einzelne Textzeile auswich, ist weg --
     was jetzt noch drinsteht (nachgeholt, Pfad-Teaser, Wochenrueckblick)
     ist kurz genug, um auf allen Breiten zu stehen. */
}
.home-mini-card {
  display: flex; flex-direction: column; gap: 2px; padding: var(--sp-3);
}
.home-mini-title { font-size: 0.76rem; font-weight: 700; letter-spacing: 0.02em;
  color: var(--muted); text-transform: uppercase; }
.home-mini-value { font-size: 1.35rem; font-weight: 800; line-height: 1.15; }
.home-mini-sub { font-size: 0.78rem; color: var(--muted); }

/* Lernpfade: Abschnittsueberschrift (nur noch das Wochenband, Auftrag 248)
   und gesperrter „bald"-Schritt. Die Gewinnzeile je Abschnitt (Punkt 8a-A,
   Auftrag 234) zog mit der Abschnittsliste auf die Startseite (`.pfk-gain`,
   Auftrag 248) -- hier kein eigener Stil mehr noetig. */
.path-section { margin: var(--sp-4) 0 var(--sp-1); }
.path-step.done .k-listrow-icon { color: var(--good-text); }
.path-step.current { border-color: var(--brand); }
/* Band „Diese Woche" (Auftrag 233): dieselbe Zeile wie ein Pfadschritt,
   der Kreispfeil ersetzt Nummer/Haken, kein eigener Rahmen noetig. */
.path-weekly-band { margin-bottom: var(--sp-2); }
.path-weekly.done { opacity: 0.72; }

/* Die vier Lern-Einstellungen im Konto (aus dem Fortschritt gewandert). */
.pr-settings-card { display: flex; flex-direction: column; gap: var(--sp-3); }
.pr-settings-card .goal-row { margin: 0; }

/* Die Kachelreihen der Fortschrittsseite (#stat-row, .stat-row-fleiss)
   sind am 04.09.2026 im Wochenbericht und in der Aktivitaets-Zeile
   aufgegangen. */

/* ==================================================================
   UI-KIT WELLE 2 — DIE FACH-BAUSTEINE (01.09.2026)
   ------------------------------------------------------------------
   Fuenf Bausteine, die nur die Uebungs-Screens brauchen und die
   deshalb in Welle 1 fehlten. Dieselben Regeln: Abstaende aus --sp-*,
   Radien aus --r-*, Farben aus Tokens, und JEDER Baustein sagt hier
   sein Dehnverhalten.
   ================================================================== */

/* ---- 13. Konturanzeige -------------------------------------------
   Ein Rahmen fuer die Live-Spur und fuer die Ergebnis-Kontur.
   Dehnung: volle Breite des Containers, die HOEHE ist fest je Groesse
   — eine Tonhoehenkurve gewinnt nichts, wenn sie in die Hoehe waechst,
   aber sie verliert Zeitaufloesung, wenn sie schmaler wird. Deshalb
   waechst nur die Breite. */
.k-contour { width: 100%; background: var(--surface);
  border: 1px solid var(--border); border-radius: var(--r-3);
  padding: var(--sp-3); display: flex; flex-direction: column; gap: var(--sp-2); }
.k-contour-inline { background: none; border: 0; padding: 0; }
.k-contour-wrap { position: relative; width: 100%; }
.k-contour-canvas { display: block; width: 100%; height: 100%;
  touch-action: none; border-radius: var(--r-1); }
.k-contour-live .k-contour-wrap { height: 120px; }
.k-contour-preview .k-contour-wrap { height: 120px; }
/* 110 px statt 180 (Variante 1 B, Owner 01.09.): auf 390 px traegt die
   Ergebnisseite ausserdem Karaoke-Zeile, Ton-Chips und zwei Knoepfe —
   die volle Hoehe kostete den Weiter-Knopf unter der Falz. Die Kurve
   verliert dabei keine Zeitaufloesung, nur Amplitudenhoehe; die
   Zeitachse ist die Breite, und die bleibt. `live` und `tall` sind
   ausdruecklich unveraendert. */
.k-contour-result .k-contour-wrap { height: 110px; }
/* Mini-Kontur (Auftrag 247, Versuche-Blatt): eine Zeile unter vielen im
   Blatt, nicht die Hauptansicht -- deutlich flacher als "result", die
   Zeitachse bleibt trotzdem breit genug zum Ablesen der Kurvenform. */
.k-contour-klein { padding: 0; gap: 0; }
.k-contour-klein .k-contour-wrap { height: 40px; }
.k-contour-tall .k-contour-wrap { height: 220px; }
@media (max-width: 480px) {
  .k-contour-tall .k-contour-wrap { height: 180px; }
}
/* Leerzustand: liegt UEBER der Leinwand, damit die ihre Groesse behaelt
   (charts.js misst clientWidth beim ersten Zeichnen). */
.k-contour-empty { position: absolute; inset: 0; display: none;
  flex-direction: column; align-items: center; justify-content: center;
  gap: var(--sp-2); text-align: center; color: var(--muted);
  font-size: var(--fs-2); background: var(--page);
  border: 1px dashed var(--baseline); border-radius: var(--r-1); }
.k-contour.is-empty .k-contour-empty { display: flex; }
.k-contour.is-empty .k-contour-canvas { visibility: hidden; }
.k-contour-empty-icon { font-size: 1.5rem; line-height: 1; }
.k-contour-caption { margin: 0; text-align: center; color: var(--muted);
  font-size: var(--fs-1); }
.k-contour-tip { position: absolute; transform: translate(-50%, -160%);
  background: var(--ink); color: var(--page); padding: 2px var(--sp-2);
  border-radius: var(--r-1); font-size: var(--fs-1); pointer-events: none;
  white-space: nowrap; }
/* Legende: umbricht, statt die Karte breitzuziehen.
   Beim Ergebnis steht sie ZWEIZEILIG (Variante 1 B): zwei Spalten, also
   zwei Zeilen fuer die vier Eintraege (Deine Mitte · Sollkurve /
   Toleranzbereich · Deine Stimme). Sie behaelt damit alle vier Traeger
   und frisst trotzdem keine Breite mehr, als die geschrumpfte Leinwand
   uebrig laesst. */
.k-contour-legend { display: flex; flex-wrap: wrap; gap: var(--sp-1) var(--sp-3);
  font-size: var(--fs-1); color: var(--ink-2); }
.k-contour-result .k-contour-legend { display: grid; column-gap: var(--sp-3);
  row-gap: var(--sp-1); grid-template-columns: repeat(2, minmax(0, 1fr)); }
.k-contour-legend-item { display: inline-flex; align-items: center; gap: var(--sp-1); }
.k-contour-swatch { width: 16px; height: 10px; border-radius: 2px; flex: none; }
.k-contour-swatch-midline { background: var(--midline); height: 3px; }
.k-contour-swatch-target { background: repeating-linear-gradient(90deg,
  var(--band-edge) 0 4px, transparent 4px 8px); height: 3px; }
.k-contour-swatch-band { background: var(--band-fill); border: 1px solid var(--band-edge); }
.k-contour-swatch-voice { background: var(--brand); height: 3px; }
.k-contour-swatch-turn-target { background: var(--surface);
  border: 2px solid var(--band-edge); border-radius: 50%; width: 10px; height: 10px; }
.k-contour-swatch-turn-you { background: var(--brand); border-radius: 50%;
  width: 10px; height: 10px; }

/* ---- 14. Aufnahme-Steuerung --------------------------------------
   Fuenf Zustaende: bereit · zaehlt · nimmt auf · verarbeitet · Fehler.
   Dehnung: die Saeule fuellt die Breite und zentriert; der KNOPF
   bleibt rund und fest (56 px) — ein Mikrofonknopf, der mit dem
   Fenster waechst, ist auf dem Desktop absurd und auf dem Handy nicht
   besser zu treffen. Die Fehlerzeile darunter umbricht.

   Variante 2 A (Owner 01.09.): das KNOPFFELD `.k-rec-slot` ist in allen
   fuenf Zustaenden 88 px hoch. Vorher stand der Zaehler als eigene
   Zeile ueber dem Knopf, und weil es sie nur im Zustand „zaehlt" gab,
   wanderte der Knopf beim Wechsel bereit → zaehlt → nimmt auf unter dem
   Daumen weg. Der Zaehler steht jetzt IM Knopf, sein Fortschritt als
   umlaufender Ring darum. */
.k-recorder { display: flex; flex-direction: column; align-items: center;
  gap: var(--sp-2); width: 100%; margin-top: var(--sp-3); }
/* Das feste Feld: 88 px = 56 px Knopf + Platz fuer den umlaufenden Ring
   und den Druckschatten. Keine Zustandsregel darf es aendern. */
.k-rec-slot { height: 88px; width: 100%; flex: none;
  display: grid; place-items: center; }
.k-rec-btn { position: relative; width: 56px; height: 56px;
  border-radius: var(--r-pill);
  border: 0; flex: none; cursor: pointer; display: grid; place-items: center;
  background: var(--brand); color: var(--on-brand);
  box-shadow: 0 6px 20px var(--brand-glow);
  transition: transform .12s ease, background .12s ease; }
.k-rec-btn:active { transform: scale(0.94); }
.k-rec-btn:focus-visible { outline: 3px solid var(--focus); outline-offset: 3px; }
.k-rec-btn svg { width: 28px; height: 28px; }
/* Genau EIN Zeichen ist sichtbar, und die Zustandsklasse sagt welches. */
.k-rec-icon-stop, .k-rec-spinner { display: none; }
.k-recorder.is-recording .k-rec-icon-mic { display: none; }
.k-recorder.is-recording .k-rec-icon-stop { display: block; }
.k-recorder.is-busy .k-rec-icon-mic { display: none; }
.k-recorder.is-busy .k-rec-spinner { display: block; }
/* bereit: der Ruhezustand — die Marke, sonst nichts. */
.k-recorder.is-ready .k-rec-btn { background: var(--brand); }
/* zaehlt: der Knopf tritt zurueck, die Ziffer ist die Aussage. */
.k-recorder.is-counting .k-rec-btn { background: var(--baseline);
  color: var(--ink); box-shadow: none; cursor: default; }
.k-recorder.is-counting .k-rec-count { color: var(--ink); }
.k-recorder.is-recording .k-rec-btn { background: var(--critical);
  color: var(--on-critical); box-shadow: 0 6px 20px var(--brand-glow); animation: k-rec-pulse 1.4s ease-in-out infinite; }
.k-recorder.is-busy .k-rec-btn { background: var(--baseline); cursor: default;
  box-shadow: none; }
.k-recorder.is-error .k-rec-btn { background: var(--surface); color: var(--critical);
  box-shadow: none; border: 2px solid var(--critical); }
@keyframes k-rec-pulse { 50% { box-shadow: 0 6px 28px var(--brand-glow); } }
@media (prefers-reduced-motion: reduce) {
  .k-recorder.is-recording .k-rec-btn { animation: none; }
}
.k-rec-spinner { width: 20px; height: 20px; border-radius: 50%;
  border: 2px solid var(--border); border-top-color: var(--on-brand);
  animation: k-rec-spin .8s linear infinite; }
@keyframes k-rec-spin { to { transform: rotate(360deg); } }
.k-rec-label { font-size: var(--fs-2); color: var(--ink-2); text-align: center;
  min-height: 1.3em; }
/* Zaehler IM Knopf: liegt ueber dem Zeichen und ist nur sichtbar,
   solange gezaehlt wird — die Zustandsklasse entscheidet, nicht
   `hidden`. */
.k-rec-count { position: absolute; inset: 0; display: none;
  place-items: center; font-size: var(--fs-4); font-weight: 700;
  color: var(--on-brand); line-height: 1; }
.k-recorder.is-counting .k-rec-count { display: grid; }
.k-recorder.is-counting .k-rec-icon-mic { display: none; }
/* Der umlaufende Ring: ein Kreis um den Knopf herum, 8 px Luft, deshalb
   `inset: -8px`. Sichtbar nur waehrend des Zaehlens. */
.k-rec-ring { position: absolute; inset: -8px; display: none;
  overflow: visible; transform: rotate(-90deg); pointer-events: none; }
.k-recorder.is-counting .k-rec-ring { display: block; }
.k-rec-ring-arc { fill: none; stroke: var(--brand); stroke-width: 5;
  stroke-linecap: round; transition: stroke-dashoffset .3s linear; }
.k-rec-error { margin: 0; font-size: var(--fs-2); color: var(--critical);
  text-align: center; max-width: 42ch; }
/* Pegel: dehnt sich auf die Breite der Saeule, gedeckelt auf Lesebreite. */
.k-rec-level { width: 100%; max-width: 320px; height: 8px;
  border-radius: var(--r-pill); background: var(--grid); overflow: hidden; }
.k-rec-level-fill { height: 100%; width: 2%; border-radius: var(--r-pill);
  background: var(--good-text); transition: width .08s linear; }

/* ---- 15. Wiedergabe-Knopf ----------------------------------------
   Erbt den Kit-Knopf und ergaenzt nur den Zustand „spielt".
   Dehnung: wie .k-btn — inhaltsbreit, mit k-btn-block voll. */
.k-playback-icon { width: 16px; height: 16px; flex: none; }
.k-playback.is-playing { background: var(--brand-soft); border-color: var(--band-edge);
  color: var(--brand-ink); }
.k-playback.is-playing .k-playback-icon { animation: k-play-blink 1s ease-in-out infinite; }
@keyframes k-play-blink { 50% { opacity: .35; } }
@media (prefers-reduced-motion: reduce) {
  .k-playback.is-playing .k-playback-icon { animation: none; }
}
.k-slow-toggle { white-space: nowrap; }

/* ---- 16. Karaoke-Zeile -------------------------------------------
   Silben mit Markierung des aktuellen Fensters.
   Dehnung: die Zeile umbricht (flex-wrap) statt zu scrollen — ein Satz
   mit zwoelf Silben muss auf 390 px lesbar bleiben; die einzelne Zelle
   bleibt inhaltsbreit. */
.k-karaoke { display: flex; flex-wrap: wrap; gap: var(--sp-1) var(--sp-2);
  align-items: stretch; width: 100%; margin: var(--sp-2) 0; }
.k-karaoke-cell { display: inline-flex; flex-direction: column; align-items: center;
  gap: 1px; padding: var(--sp-1) var(--sp-2); min-height: 40px;
  border-radius: var(--r-1); border: 1px solid var(--border);
  border-top: 3px solid var(--baseline); background: var(--surface);
  color: var(--ink); font: inherit; font-size: var(--fs-2); }
button.k-karaoke-cell { cursor: pointer; }
button.k-karaoke-cell:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; }
.k-karaoke-num { font-size: var(--fs-1); color: var(--muted); line-height: 1; }
.k-karaoke-label { font-weight: 650; }
/* Veto-Silbe (§8ai): eine Richterstimme hat den ganzen Satz gekippt —
   das traegt einen doppelten Rand, nicht nur eine Farbe. */
.k-karaoke-cell.is-veto { border: 2px solid var(--critical); border-top-width: 4px; }
/* Die Miniaturkurve. Dehnung: feste 36x22 — sie ist ein Zeichen, kein
   Diagramm, und darf in keiner Zelle mitwachsen. */
.k-tonecurve { display: block; flex: none; }
.k-karaoke-cell .k-tonecurve polyline { fill: none; stroke-width: 2;
  stroke-linecap: round; stroke-linejoin: round; stroke: var(--muted); }
.k-karaoke-cell.is-good .k-tonecurve polyline { stroke: var(--good); }
.k-karaoke-cell.is-near .k-tonecurve polyline { stroke: var(--warning); }
.k-karaoke-cell.is-bad .k-tonecurve polyline { stroke: var(--critical); }
.k-karaoke-cell.is-nahdran .k-tonecurve polyline { stroke: var(--near); }
.k-karaoke-mark { font-size: var(--fs-1); line-height: 1; }
.k-karaoke-sub { font-size: var(--fs-1); color: var(--muted); }
/* Das aktuelle Fenster: Rahmen UND Flaeche, damit es auch ohne
   Farbwahrnehmung auffaellt. */
.k-karaoke-cell.is-active { background: var(--brand-soft); border-color: var(--brand);
  box-shadow: inset 0 0 0 2px var(--brand); }
.k-karaoke-cell.is-good { border-top-color: var(--good); }
.k-karaoke-cell.is-near { border-top-color: var(--warning); }
.k-karaoke-cell.is-bad { border-top-color: var(--critical); }
.k-karaoke-cell.is-nahdran { border-top-color: var(--near); }
.k-karaoke-cell.is-nahdran .k-karaoke-mark { color: var(--near-text); }
.k-karaoke-cell.is-unsure { border-top-color: var(--muted); color: var(--ink-2); }
.k-karaoke-cell.is-none { border-style: dashed; border-top-color: var(--muted);
  color: var(--muted); }
.k-karaoke-bandoff { align-self: center; font-size: var(--fs-1); color: var(--muted);
  border: 1px dashed var(--baseline); border-radius: var(--r-pill);
  padding: 0 var(--sp-2); }

/* ---- 17. Ton-Chip -------------------------------------------------
   Die fuenf Toene als Auswahl- oder Ergebnis-Chip. Die Tonfarbe faerbt
   NUR Kurve und Rand; Name und Kurve tragen die Information.
   Dehnung: inhaltsbreit, die Reihe umbricht (.k-row / .k-tonechips). */
.k-tonechips { display: flex; flex-wrap: wrap; gap: var(--sp-2);
  justify-content: center; }
.k-tonechip { display: inline-flex; align-items: center; gap: var(--sp-1);
  padding: var(--sp-1) var(--sp-3); min-height: 36px;
  border-radius: var(--r-pill); border: 1px solid var(--border);
  background: var(--surface); color: var(--ink); font: inherit;
  font-size: var(--fs-2); font-weight: 600; white-space: nowrap; }
.k-tonechip svg { display: block; flex: none; }
.k-tonechip polyline { fill: none; stroke-width: 2; stroke-linecap: round;
  stroke-linejoin: round; stroke: var(--k-tone, var(--brand)); }
.k-tonechip-mid { --k-tone: var(--tone-mid); }
.k-tonechip-low { --k-tone: var(--tone-low); }
.k-tonechip-falling { --k-tone: var(--tone-falling); }
.k-tonechip-high { --k-tone: var(--tone-high); }
.k-tonechip-rising { --k-tone: var(--tone-rising); }
button.k-tonechip { cursor: pointer; min-height: 44px; }
button.k-tonechip:hover { border-color: var(--k-tone, var(--band-edge)); }
button.k-tonechip:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; }
.k-tonechip.is-active { background: var(--brand-soft); border-color: var(--brand);
  color: var(--brand-ink); box-shadow: inset 0 0 0 1px var(--brand); }
.k-tonechip.is-good { border-color: var(--good); }
.k-tonechip.is-bad { border-color: var(--critical); }
.k-tonechip.is-near { border-color: var(--warning); }
.k-tonechip-mark { font-size: var(--fs-2); line-height: 1; }
.k-tonechip-name { line-height: 1; }

/* Erweiterung Auftrag 128 (Q38): Beispielsilbe der กา-Reihe als zweite
   Zeile im Chip (`beispiel`) und ein eigenstaendiger ▶-Knopf zum
   Vorhoeren (`vorhoeren`) — nie ein Knopf im Knopf, deshalb bei einer
   Waehl-Pille eine eigene Huelle `.k-tonechip-unit` NEBEN der Pille.
   Dehnung: die Huelle bleibt inline-flex und bricht mit der Reihe wie
   der Chip selbst. */
.k-tonechip-text { display: flex; flex-direction: column; align-items: flex-start;
  gap: 1px; line-height: 1.15; }
.k-tonechip-beispiel { font-size: var(--fs-1); font-weight: 500; color: var(--ink-2); }
.k-tonechip:has(.k-tonechip-text) { min-height: 48px; padding-block: var(--sp-2); }
.k-tonechip-unit { display: inline-flex; align-items: center; gap: var(--sp-1); }
.k-tonechip-play { display: inline-flex; align-items: center; justify-content: center;
  flex: none; width: 32px; height: 32px; padding: 0; border-radius: 50%;
  border: 1px solid var(--border); background: var(--surface); color: var(--ink);
  font: inherit; font-size: 0.75rem; cursor: pointer; }
.k-tonechip-play:hover { border-color: var(--brand); }
.k-tonechip-play:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; }
.k-tonechip-play.is-playing { background: var(--brand-soft); border-color: var(--brand);
  color: var(--brand-ink); }
.k-tonechip > .k-tonechip-play { margin-inline-start: 2px; }
/* Auftrag 250: TTS-Vorbild traegt dieselbe Warnfarbe wie das Stimmen-Blatt
   (voice-sheet-sub.is-warn, Auftrag 247) -- Regel 237, TTS nur Ergaenzung. */
.k-tonechip-play-tts { color: var(--cls-high); border-color: var(--warning); }

/* ---- 38. Ton-Reihe (kTonReihe, Auftrag 128) ---------------------------
   Die feste Vergleichszeile กา ก่า ก้า ก๊า ก๋า unter jedem Ton-Block der
   Reviewer-Aufgabe: fuenf `kToneChip`s mit Beispiel+Vorhoeren, damit die
   Reihe auch ohne einen einzelnen Chip anzutippen hoerbar bleibt.
   Dehnung: volle Blockbreite, die Chipreihe bricht wie `.k-tonechips`. */
.k-tonreihe { display: flex; flex-direction: column; gap: var(--sp-1);
  padding-top: var(--sp-2); border-top: 1px dashed var(--border); }
.k-tonreihe-label { font-size: var(--fs-1); color: var(--muted); font-weight: 600; }

/* ---------- 35. Hoerfrage (kHoerfrage, Auftrag 112 + Nachbesserung) ---
   Eine Bauform fuer die Wahrnehmungsfrage vor dem Sprechen (fuenf
   schlanke Pillen, `.k-hoerfrage-options`) und die reine Hoerunter-
   scheidung (vier Kacheln mit Wortzeile, `.k-hoerfrage-grid`) — vorher
   zwei Bauformen mit zwei Fragetexten und einem Hand-SVG, das immer
   markenfarben war statt tongefaerbt (`.discrim-option`, gestrichen).
   Farbe traegt die Kurve wie jeder Ton-Chip (`--tone-*`), nie allein.

   Die Kachel MIT Wortzeile ist bewusst KEINE Pille mehr (Nachbesserung
   08.09.2026: Pillenradius auf 108 px Mindesthoehe ergab „riesige Ovale",
   die 2+1+1 umbrachen und die vierte unter den Falz auf 390×844 schoben).
   Sie traegt `.k-card`-Radius/-Rand und steht in einem festen 2×2-Raster,
   damit alle vier Kacheln plus der Anhoeren-Knopf ohne Rollen aufs
   Handy passen.
   Dehnung: volle Kartenbreite; `.k-hoerfrage-options` ist eine
   umbrechende `k-tonechips`-Reihe, `.k-hoerfrage-grid` bleibt fest
   zweispaltig (kein Zusammenklappen auf einer Spalte — vier Kacheln
   untereinander waeren wieder zu hoch). */
.k-hoerfrage { display: flex; flex-direction: column; gap: var(--sp-3); }
.k-hoerfrage-q { margin: 0; text-align: center; color: var(--ink-2); font-weight: 550; }
.k-hoerfrage-options { justify-content: center; }
.k-hoerfrage-grid {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--sp-2);
}
.k-hoerfrage-tile {
  padding: var(--sp-2); gap: 2px; align-items: center; text-align: center;
  min-width: 0;
}
.k-hoerfrage-tile .k-tonechip { pointer-events: none; margin: 0 0 2px; }
.k-hoerfrage-tile.is-good { border-color: var(--good); }
.k-hoerfrage-tile.is-bad { border-color: var(--critical); }
.k-hoerfrage-word { font-size: 1.05rem; font-weight: 600; color: var(--ink);
  max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.k-hoerfrage-translit { font-size: 0.85rem; color: var(--ink-2);
  max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.k-hoerfrage-gloss { font-size: 0.72rem; color: var(--muted);
  max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Native-Studio: Halterung fuer den Kit-Wiedergabeknopf ueber der
   Aufnahmezone. Dehnung: zentriert, inhaltsbreit. */
.ns-play-zone { display: flex; justify-content: center; margin: var(--sp-3) 0 0; }
.ns-play-zone:empty { display: none; }
/* Kurzform der Karaoke-Zeile: nur die Nummern (mehrsilbiges Wort im
   Ergebnis). Dehnung wie die volle Zeile — sie umbricht. */
.k-karaoke-compact .k-karaoke-cell { min-height: 28px; padding: 0 var(--sp-2);
  border-top-width: 2px; }

/* ============ HVPT — Toene erkennen (Dossier N1, 01.09.2026) ============
   Die Ansicht baut ausschliesslich aus Kit-Bausteinen; hier stehen nur
   die drei Halterungen, die das Kit nicht kennt.
   Dehnung: die Ansicht traegt `k-flow` (Lesebreite), die Aufloesung
   fuellt die Karte und ist zentriert — auf 390 px wie auf dem Desktop
   dieselbe Saeule, nur breiter. */
/* Die Modulkachel: der Akzent kommt aus dem Marken-Token, weil HVPT
   das Modul mit der staerksten Evidenz ist und ganz vorn steht. */
.hvpt-view .k-tonechips { margin: var(--sp-3) 0; }
.hvpt-stage { text-align: center; }
.hvpt-stage .k-row { justify-content: center; }
/* Die Aufloesung: sie erscheint UNTER den Chips und schiebt den
   Weiter-Knopf nach, statt die Karte zu tauschen — der Ton, den man
   gerade gewaehlt hat, bleibt dabei sichtbar. */
.hvpt-reveal { display: flex; flex-direction: column; align-items: center;
  gap: var(--sp-2); margin-top: var(--sp-3); padding-top: var(--sp-3);
  border-top: 1px solid var(--border); }
.hvpt-reveal .k-btn-block { margin-top: var(--sp-2); }
.hvpt-word { font-size: var(--fs-4); }
.hvpt-voice { color: var(--muted); font-size: var(--fs-1); }

/* ============ TASTATURSCHULE — Thai tippen (Schreiben, Saeule 2) =======
   Baustein 18 (kThaiKeyboard, kFingerLegend) und die vier Halterungen
   der Ansicht. Farben aus Tokens, Abstaende aus --sp-*.
   ====================================================================== */

/* ---- 18. Bildschirmtastatur ---------------------------------------
   Dehnung: fuellt die Breite des Containers; jede Taste ist `flex: 1`
   und darf schrumpfen (`min-width: 0`), die Schrift skaliert mit der
   Containerbreite. Die 13 Spalten bleiben auf 390 px STEHEN und
   brechen nicht um — die Tastenlage ist das Einzige, was die Tastatur
   lehrt, und eine umgebrochene Lage lehrt eine falsche. Auf sehr
   schmalen Geraeten wird sie dadurch flach, nicht mehrzeilig. */
.k-keyboard { container-type: inline-size; width: 100%;
  display: flex; flex-direction: column; gap: var(--sp-1);
  padding: var(--sp-2); background: var(--surface);
  border: 1px solid var(--border); border-radius: var(--r-2); }
.k-keyboard-row { display: flex; gap: var(--sp-1); }
/* gestaffelt wie eine echte Tastatur — die Einrueckung ist der Grund,
   warum man Tastenlagen ueberhaupt wiedererkennt */
.k-keyboard-top { padding-inline-start: 2%; }
.k-keyboard-home { padding-inline-start: 4%; padding-inline-end: 2%; }
.k-keyboard-bottom { padding-inline-start: 7%; padding-inline-end: 6%; }
.k-keyboard-space { padding: 0 22%; }
.k-key { flex: 1 1 0; min-width: 0; display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 1px;
  aspect-ratio: 1 / 1; padding: 0; border: 1px solid var(--border);
  border-bottom: 3px solid var(--baseline); border-radius: var(--r-1);
  background: var(--page); color: var(--ink); font-family: inherit;
  line-height: 1.1; cursor: default; overflow: hidden;
  transition: background .12s ease, transform .08s ease; }
button.k-key { cursor: pointer; }
button.k-key:active { transform: translateY(1px); }
button.k-key:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; }
.k-key-base { font-size: 4.2cqw; }
.k-key-shift { font-size: 3.0cqw; color: var(--muted); }
/* Umschaltebene: die andere Beschriftung wird gross, die eine klein —
   dasselbe Kaestchen, kein zweites Tastenbild. */
.k-keyboard.is-shift .k-key-base { font-size: 3.0cqw; color: var(--muted); }
.k-keyboard.is-shift .k-key-shift { font-size: 4.2cqw; color: var(--ink); }
.k-key-space { aspect-ratio: auto; height: 5.5cqw; min-height: 22px; }
/* Der Fingerstreifen: die EINZIGE Stelle, an der die Fingerfarbe
   auftaucht — eine flaechig eingefaerbte Taste waere ein Ampelbrett. */
.k-key-l5 { border-bottom-color: var(--finger-l5); }
.k-key-l4 { border-bottom-color: var(--finger-l4); }
.k-key-l3 { border-bottom-color: var(--finger-l3); }
.k-key-l2 { border-bottom-color: var(--finger-l2); }
.k-key-r2 { border-bottom-color: var(--finger-r2); }
.k-key-r3 { border-bottom-color: var(--finger-r3); }
.k-key-r4 { border-bottom-color: var(--finger-r4); }
.k-key-r5 { border-bottom-color: var(--finger-r5); }
/* Zieltaste: Flaeche UND Rahmen, nie nur Farbe (Barrierefreiheit). */
.k-key.is-aim { background: var(--brand-soft); border-color: var(--brand);
  box-shadow: inset 0 0 0 1px var(--brand); font-weight: 600; }
.k-key.is-hit { background: var(--band-fill); border-color: var(--band-edge); }
.k-key.is-miss { background: var(--cat-vocab-bg); border-color: var(--critical); }
/* Fingerlegende. Dehnung: umbricht, statt die Karte breitzuziehen. */
.k-finger-legend { display: flex; flex-wrap: wrap; gap: var(--sp-1) var(--sp-3);
  font-size: var(--fs-1); color: var(--ink-2); margin-top: var(--sp-2); }
.k-finger-item { display: inline-flex; align-items: center; gap: var(--sp-1); }
.k-finger-dot { width: 10px; height: 10px; border-radius: var(--r-pill); flex: none; }
.k-finger-dot-l5 { background: var(--finger-l5); }
.k-finger-dot-l4 { background: var(--finger-l4); }
.k-finger-dot-l3 { background: var(--finger-l3); }
.k-finger-dot-l2 { background: var(--finger-l2); }
.k-finger-dot-r2 { background: var(--finger-r2); }
.k-finger-dot-r3 { background: var(--finger-r3); }
.k-finger-dot-r4 { background: var(--finger-r4); }
.k-finger-dot-r5 { background: var(--finger-r5); }

/* ---- Die Ansicht --------------------------------------------------
   Dehnung: die Ansicht traegt `k-flow` (Lesebreite); die Zielzeile ist
   zentriert und darf waagerecht scrollen, statt einen Satz umzubrechen
   — ein umgebrochener Satz verliert die Zuordnung Zeichen/Taste. */
.typ-target { display: flex; flex-wrap: wrap; justify-content: center;
  gap: var(--sp-1); margin: var(--sp-3) 0; }
/* Jedes Kaestchen hat eine MINDESTBREITE: ein alleinstehendes Tonzeichen
   ist ein schmales Haekchen auf einem Traegerkreis und verschwaende sonst
   in der Zeile — auf Stufe 1 ist es aber das ganze Ziel. */
.typ-cluster { font-size: var(--fs-6); line-height: 1.4; padding: 0 var(--sp-1);
  min-width: 1.6em; text-align: center; border-radius: var(--r-1) var(--r-1) 0 0;
  border-bottom: 3px solid var(--baseline); color: var(--ink); }
.typ-cluster.is-now { border-bottom-color: var(--brand); background: var(--brand-soft); }
.typ-cluster.is-done { color: var(--good-text); border-bottom-color: var(--good); }
.typ-cluster.is-bad { color: var(--critical); border-bottom-color: var(--critical); }
/* Das Eingabefeld ist unsichtbar: getippt wird auf die Zielzeile, nicht
   in ein Kaestchen. Es liegt trotzdem im Baum (und nicht auf
   `display:none`), weil eine System-Tastatur sonst gar nicht aufgeht. */
.typ-input { position: absolute; opacity: 0; width: 1px; height: 1px;
  border: 0; padding: 0; }
.typ-typed { min-height: 1.6em; text-align: center; font-size: var(--fs-4);
  color: var(--ink); letter-spacing: .02em; word-break: break-word; }
.typ-typed:empty::before { content: attr(data-empty); color: var(--muted);
  font-size: var(--fs-2); }
/* Die Eingabereihenfolge als nummerierte Kette (Stufe 2).
   Dehnung: umbricht auf schmalen Geraeten. */
.typ-order { display: flex; flex-wrap: wrap; justify-content: center;
  gap: var(--sp-2); margin: var(--sp-2) 0; }
.typ-step { display: inline-flex; align-items: center; gap: var(--sp-1);
  padding: var(--sp-1) var(--sp-2); border: 1px solid var(--border);
  border-radius: var(--r-pill); font-size: var(--fs-2); background: var(--page); }
.typ-step-n { display: inline-grid; place-items: center; width: 18px; height: 18px;
  border-radius: var(--r-pill); background: var(--brand); color: var(--on-brand);
  font-size: var(--fs-1); flex: none; }
.typ-step-char { font-size: var(--fs-4); }
/* Der Aufklapp-Hilfetext zur Systemumstellung. Dehnung: volle Breite. */
.typ-help { margin-top: var(--sp-3); border-top: 1px solid var(--border);
  padding-top: var(--sp-2); }
.typ-help > summary { cursor: pointer; font-size: var(--fs-2);
  color: var(--brand-ink); }
.typ-help p { margin: var(--sp-2) 0 0; font-size: var(--fs-2); color: var(--ink-2); }
.typ-keyboard-zone { margin-top: var(--sp-3); }
/* Die Karte, in der getippt wird, und die Aufloesung darunter — dasselbe
   Muster wie im HVPT: die Aufloesung erscheint UNTER dem Ziel und schiebt
   den Weiter-Knopf nach, statt die Karte zu tauschen. So bleibt sichtbar,
   was man gerade getippt hat. Dehnung: volle Kartenbreite, zentriert. */
.typ-stage { text-align: center; }
.typ-reveal { display: flex; flex-direction: column; align-items: stretch;
  gap: var(--sp-2); margin-top: var(--sp-3); padding-top: var(--sp-3);
  border-top: 1px solid var(--border); }
.typ-reveal .k-btn-block { margin-top: var(--sp-2); }

/* ============ HANDSCHRIFT-SCHULE — Thai schreiben (Saeule 3) ===========
   Baustein 19 (kTracePad, kInputToggle) und die Halterungen der Ansicht.
   Farben aus Tokens, Abstaende aus --sp-*.
   ====================================================================== */

/* ---- 19. Schreibflaeche -------------------------------------------
   Dehnung: die Flaeche fuellt die Breite ihres Containers und behaelt
   das Seitenverhaeltnis des Schreibkastens (die Leinwand rechnet ihre
   Hoehe selbst aus). Nach oben ist sie bei --hw-max gedeckelt: eine
   Schreibflaeche, die breiter ist als eine Handflaeche, laesst sich
   nicht mehr mit dem Finger bedienen — auf dem Desktop wird sie deshalb
   zentriert stehen gelassen statt aufgeblasen. `touch-action: none` ist
   Pflicht, sonst scrollt das Handy, statt zu schreiben. */
.k-tracepad { --hw-max: 420px; width: 100%; max-width: var(--hw-max);
  margin: var(--sp-3) auto; border: 1px solid var(--border);
  border-radius: var(--r-2); background: var(--page); overflow: hidden; }
.k-tracepad-canvas { display: block; width: 100%; touch-action: none;
  cursor: crosshair; }

/* Der Umschalter Finger/Stylus/Maus. Dehnung: die drei Knoepfe teilen
   sich die Breite und brechen auf sehr schmalen Geraeten um. */
.k-inputtoggle { display: flex; flex-wrap: wrap; gap: var(--sp-1);
  justify-content: center; margin: var(--sp-2) 0; }
.k-inputtoggle-btn { flex: 1 1 auto; min-width: 84px; padding: var(--sp-1) var(--sp-2);
  border: 1px solid var(--border); border-radius: var(--r-pill);
  background: var(--page); color: var(--ink-2); font: inherit;
  font-size: var(--fs-2); cursor: pointer; }
.k-inputtoggle-btn.is-on { background: var(--brand-soft); border-color: var(--brand);
  color: var(--brand-ink); font-weight: 600; }
.k-inputtoggle-btn:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; }

/* ---- Die Ansicht --------------------------------------------------
   Dehnung: die Ansicht traegt `k-flow` (Lesebreite); die Zielzeile ist
   zentriert und umbricht, weil ein Wort auf Stufe 4 laenger sein kann
   als der Kasten. */
.hw-target { display: flex; flex-wrap: wrap; justify-content: center;
  gap: var(--sp-1); margin: var(--sp-3) 0; }
/* Jedes Kaestchen hat eine Mindestbreite: ein alleinstehendes Tonzeichen
   ist ein schmales Haekchen auf einem Traegerkreis und verschwaende sonst
   in der Zeile — auf den ersten Stufen ist es aber das ganze Ziel. */
.hw-cell { font-size: var(--fs-6); line-height: 1.4; padding: 0 var(--sp-1);
  min-width: 1.6em; text-align: center; border-radius: var(--r-1) var(--r-1) 0 0;
  border-bottom: 3px solid var(--baseline); color: var(--ink); }
.hw-cell.is-now { border-bottom-color: var(--brand); background: var(--brand-soft); }
.hw-cell.is-done { color: var(--good-text); border-bottom-color: var(--good); }
.hw-input { margin-top: var(--sp-2); }
.hw-input .k-card-text { margin: 0 0 var(--sp-1); }
/* Der Aufklapp-Hilfetext zu Strichrichtung und Eingabeart.
   Dehnung: volle Breite. */
.hw-help { margin-top: var(--sp-3); border-top: 1px solid var(--border);
  padding-top: var(--sp-2); }
.hw-help > summary { cursor: pointer; font-size: var(--fs-2);
  color: var(--brand-ink); }
.hw-help .k-card-text { margin: var(--sp-2) 0 0; font-size: var(--fs-2);
  color: var(--ink-2); }
/* Die Karte, auf der geschrieben wird, und die Aufloesung darunter —
   dasselbe Muster wie in HVPT und Tastaturschule: die Aufloesung
   erscheint UNTER dem Ziel und schiebt den Weiter-Knopf nach, statt die
   Karte zu tauschen. So bleibt sichtbar, was man geschrieben hat.
   Dehnung: volle Kartenbreite, zentriert. */
.hw-stage { text-align: center; }
.hw-stage .k-row { justify-content: center; }
.hw-reveal { display: flex; flex-direction: column; align-items: stretch;
  gap: var(--sp-2); margin-top: var(--sp-3); padding-top: var(--sp-3);
  border-top: 1px solid var(--border); }
.hw-reveal .k-btn-block { margin-top: var(--sp-2); }
/* Auftrag 200 (Q40b): der Zwilling, den die Zeichenpruefung fand, wird
   gross gezeigt -- in derselben Schrift wie die Vorlage (Fable,
   Strichvorlagen Runde 4), damit die Verwechslung sofort ins Auge faellt
   statt im Fliesstext unterzugehen. Fallback-Kette wie ueberall im Kit:
   ohne die geladene Schrift bleibt der Text lesbar. */
.hw-twin-char { font-family: "Noto Sans Thai Looped", "Noto Sans Thai", "Thonburi",
  system-ui, sans-serif; font-size: var(--fs-7, 2.5rem); text-align: center;
  margin: 0; color: var(--ink); }
/* Eine leise Zeile — der ehrliche Hinweis auf den Teilbestand steht
   damit da, ohne die Karte zu beherrschen. */
.k-muted { color: var(--muted); }

/* ---- 20. Vergleichsmatrix (kMatrix) -------------------------------
   Dehnung: das Raster steht in einem EIGENEN Rollcontainer — passt es
   nicht in die Breite, rollt die Matrix und nie die Seite. Die Zellen
   haben eine Mindestbreite (--k-mx-cell), damit „84 %" und die Zeile
   darunter nebeneinander lesbar bleiben; ist mehr Platz da, verteilt
   `table-layout: fixed` ihn gleichmaessig. Die Kopfspalte bleibt beim
   Rollen stehen, sonst weiss man nach zwei Spalten nicht mehr, welche
   Zeile man liest. Farbe ausschliesslich aus Tokens. */
.k-matrix { --k-mx-cell: 66px; margin: var(--sp-2) 0 0;
  container-type: inline-size; }
.k-matrix-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.k-matrix-table { border-collapse: collapse; width: max-content;
  min-width: 100%; font-size: var(--fs-2); }
.k-matrix-table th, .k-matrix-table td { padding: var(--sp-1);
  border-bottom: 1px solid var(--border); text-align: center;
  min-width: var(--k-mx-cell); }
/* Die Spaltenkoepfe brechen NICHT um: zwei Tonnamen nebeneinander sind
   auf 390 px sonst nicht mehr auseinanderzuhalten. Passt die Zeile nicht,
   rollt die Matrix (dafuer hat sie ihren Container). */
.k-matrix-table thead th { color: var(--ink-2); font-weight: 600;
  white-space: nowrap; }
.k-matrix-corner, .k-matrix-rowhead { position: sticky; inset-inline-start: 0; z-index: 1;
  background: var(--surface); text-align: start; color: var(--ink-2);
  min-width: 92px; white-space: nowrap; }
.k-matrix-cell { line-height: 1.15; }
/* Die Diagnosezahl ist der Inhalt der Zelle und traegt deshalb IN BEIDEN
   Kopfvarianten dieselbe Groesse: --fs-3, mobil gemessen 15,3 px. Die
   Auflage ist >= 15 px; --fs-2 (mobil 14,0 px) hat sie im Langkopf
   verfehlt, waehrend der Kurzkopf schon auf --fs-3 stand — dieselbe Zahl
   war je nach Huellenbreite verschieden gross. Die Groesse haengt jetzt
   nicht mehr an der Variante. */
.k-matrix-value { display: block; font-weight: 600; font-size: var(--fs-3); }
.k-matrix-move { display: block; font-size: var(--fs-1); color: var(--ink-2); }
.k-mx-zero { color: var(--muted); }
.k-mx-ok .k-matrix-value { color: var(--good-text); }
.k-mx-bad .k-matrix-value { color: var(--ink); }
.k-mx-up { color: var(--good-text); }
.k-mx-down { color: var(--critical); }
.k-matrix-thin { opacity: 0.5; }
.k-matrix-legend { margin: var(--sp-1) 0 0; color: var(--ink-2);
  font-size: var(--fs-1); }
/* VARIANTE KURZKOPF (.k-matrix-kurzkopf). Ein Diagnosebild muss
   VOLLSTAENDIG zu sehen sein: auf 390 px lagen zwei der fuenf Spalten
   hinter der waagerechten Rolle, ohne dass etwas darauf hinwies. Ist es
   eng, traegt der Kopf deshalb den Kurznamen des Tons, die Zeilenkoepfe
   duerfen umbrechen und der Zellinnenabstand schrumpft. Die grosse Zahl
   bleibt dabei auf --fs-3 — sie schrumpft nicht mit der Spalte, sie ist
   der Inhalt (die Groesse steht am Baustein selbst, nicht hier). Ist
   Platz, ist alles wie zuvor.

   ENG heisst: der CONTAINER ist schmal, nicht das Sichtfeld. Die alte
   Sichtfeld-Abfrage (< 600 px) hat den Desktop-Fall verfehlt: dort steht
   die Karte in einer 443 px schmalen Spalte, die Langkopf-Tabelle braucht
   dort 476 px (de) bis 628 px (es) — die fuenfte Spalte lag hinter der
   Rolle, obwohl das Sichtfeld 1280 px breit war. Der Umschaltpunkt haengt
   deshalb an einer Container-Abfrage auf `.k-matrix` selbst.

   640 px ist gemessen, nicht gegriffen (`nightruns/messung_matrix.py`,
   CDP): so breit muss die Huelle sein, damit die langen Koepfe in JEDER
   Sprache stehen — es 628 px, de 476, en 475, ko 417, ja 396, th 397. Bis
   639 px also Kurzkopf. Die Regeln stehen ALLE unter der Abfrage und
   greifen an Nachfahren: ein Container kann sich selbst nicht abfragen,
   darum traegt die Tabelle die enge Zellbreite und nicht die Wurzel. */
.k-matrix-col-short { display: none; }
@container (max-width: 639px) {
  .k-matrix-kurzkopf .k-matrix-col-long { display: none; }
  .k-matrix-kurzkopf .k-matrix-col-short { display: inline; }
  .k-matrix-kurzkopf .k-matrix-table { --k-mx-cell: 44px;
    width: 100%; table-layout: fixed; }
  .k-matrix-kurzkopf .k-matrix-table th,
  .k-matrix-kurzkopf .k-matrix-table td { padding: var(--sp-1) 2px; }
  /* Der Kopf ist die Marginalie, die Zahl der Inhalt: er faellt auf
     --fs-1 und DARF umbrechen. Ohne das haengt ein langer Kurzname
     ("steigend", "descendente") ueber seine Spalte hinaus und die Matrix
     rollt wieder — genau das, was die Variante abschaffen soll. Der Kopf
     traegt zudem KEIN waagerechtes Polster: gemessen braucht "steigend"
     51 px und das thailaendische "เสียงสามัญ" 52 px, die Spalte hat 52 —
     mit 2 px Polster je Seite waeren beide zweizeilig ("steigen/d"). */
  .k-matrix-kurzkopf .k-matrix-table thead th { font-size: var(--fs-1);
    white-space: normal; overflow-wrap: anywhere; padding-inline-start: 0; padding-inline-end: 0; }
  /* Die Kopfspalte darf umbrechen und wird schmal: 4rem lassen den fuenf
     Tonspalten je 52 px, und 52 px sind der laengste Kurzname
     („steigend", gemessen 52 px). Bei 4,5 rem rollte die Matrix um genau
     diese 2 px weiter — sichtbar als Rollbalken unter einem Bild, das
     vollstaendig ist. */
  .k-matrix-kurzkopf .k-matrix-corner,
  .k-matrix-kurzkopf .k-matrix-rowhead { min-width: 0; width: 4rem;
    white-space: normal; hyphens: auto; }
}

/* ---- 21. Stimmen-Etikett (kVoiceTag) -------------------------------
   Dehnung: inline, bricht NICHT um (`white-space: nowrap`) — das
   Etikett ist eine Marginalie am Vorspiel-Knopf, kein Absatz. Es steht
   in `--ink-2` und einer Stufe unter dem Fliesstext, damit es den Knopf
   nicht ueberstimmt; nur der Halbton-Teil kann Farbe annehmen.

   Warnfarbe ab |6| Halbtoenen: `--warning` als Rand + `--cls-high` als
   Schrift, dieselbe Paarung wie `.k-chip-warn` — eine stark verschobene
   Stimme soll auffallen, ohne wie ein Fehler auszusehen. Beide Tokens
   sind hell und dunkel definiert; das Etikett setzt keine eigene
   Farbe. */
.k-voicetag {
  display: inline-flex; flex-wrap: wrap; align-items: center; gap: var(--sp-1);
  font-size: var(--fs-1); color: var(--ink-2);
  line-height: 1.4;
}
/* Der Platzhalter, in dem eine Ansicht das Etikett haelt: er bleibt
   stehen, waehrend die Halbton-Zahl nachgeladen wird, damit die Zeile
   nicht springt. */
.k-voicetag-slot { display: inline-flex; align-items: center; min-height: 1.4em; }
.k-voicetag-sep { color: var(--grid); }
.k-voicetag-kind { font-weight: 650; }
.k-voicetag-sex { font-size: 1.05em; }
.k-voicetag-label { font-variant-numeric: tabular-nums; }
.k-voicetag-shift {
  padding: 0 var(--sp-1); border-radius: var(--r-pill);
  border: 1px solid var(--border); font-variant-numeric: tabular-nums;
}
.k-voicetag-shift.is-strong { color: var(--cls-high); border-color: var(--warning);
  background: var(--cls-high-bg); font-weight: 650; }
/* Der Stimmen-Chip oeffnet seit Auftrag 247 die Stimmenauswahl -- derselbe
   Rahmen wie ein fokussierbarer Knopf, ohne den Rest des Etiketts
   anzufassen. */
.k-voicetag-slot-clickable { cursor: pointer; border-radius: var(--r-1); }
.k-voicetag-slot-clickable:hover, .k-voicetag-slot-clickable:focus-visible {
  outline: 3px solid var(--focus); outline-offset: 2px; }

/* ---- 21b. Stimmen-Blatt (Auftrag 247, V07 Stufe 2) -------------------
   Inhalt des "Vorbild waehlen"-`kSheet`: eine Zeile je verfuegbarer
   Stimme (eigenes Wiedergabezeichen, Titel+Untertitel, Haken an der
   aktiven Stimme), darunter der Tempo-Schalter (bestehender
   `kSlowToggle`, ein zweites Exemplar) und der Kontovorzug-Schalter. */
.voice-sheet-list { display: flex; flex-direction: column; gap: var(--sp-1); }
.voice-sheet-row {
  display: flex; align-items: center; gap: var(--sp-3); width: 100%;
  padding: var(--sp-2) var(--sp-3); border: 1px solid var(--border);
  border-radius: var(--r-2); background: var(--surface); text-align: start;
  cursor: pointer; font: inherit; color: inherit;
}
.voice-sheet-row.is-active { border-color: var(--brand); background: var(--brand-soft); }
.voice-sheet-play { flex: none; font-size: var(--fs-2); }
.voice-sheet-texts { display: flex; flex-direction: column; flex: 1 1 auto; min-width: 0; }
.voice-sheet-title { font-weight: 650; }
.voice-sheet-sub { font-size: var(--fs-1); color: var(--muted); }
.voice-sheet-sub.is-warn { color: var(--cls-high); }
.voice-sheet-check { flex: none; width: 1.4em; text-align: center;
  color: var(--brand); font-weight: 700; }
/* Auftrag 271c: Haken der aktiven Stimme als Linien-SVG statt Emoji. */
.voice-sheet-check svg { width: 1.1em; height: 1.1em; vertical-align: -0.15em; }
.voice-sheet-tempo { display: flex; }
.voice-sheet-vorzug { display: flex; align-items: center; gap: var(--sp-2);
  font-size: var(--fs-2); }

/* ---- 22. Faehigkeits-Kachel (kAreaTile) ----------------------------
   Sitzt auf `.k-card` und aendert nur, was eine Kachel ausmacht:
   Abzeichen gross, Name, Untertitel, darunter die Koennens-Zahl.
   Die Werte sind die der abgeloesten `.area-tile` — der Umzug ins Kit
   sollte die Kachel nicht umgestalten.
   Dehnung: fuellt ihre Rasterzelle; die Texte umbrechen. Die
   Koennens-Zeile faellt weg, solange keine Messung da ist
   (`:empty`) — kein „0 %", das wie ein Urteil aussieht.
   Varianten: mit/ohne Koennens-Zeile, mit/ohne Abzeichen. */
.k-areatile { gap: 2px; position: relative; }
.k-areatile:hover {
  transform: translateY(-1px); border-color: var(--band-edge);
  box-shadow: var(--lift); }
/* Auftrag 271: 32-px-Zelle fuer das Linien-Symbol (Vorlage), unabhaengig
   von der Leserichtung -- `emoji` (Rueckfall) sitzt in derselben Zelle. */
.k-areatile-icon {
  width: 32px; height: 32px; display: flex; align-items: center;
  justify-content: center; color: var(--brand); flex: none;
  font-size: 28px; line-height: 1.1;
}
.k-areatile-icon svg { width: 24px; height: 24px; }
.k-areatile-name { font-weight: 600; font-size: 1.02rem; margin-top: 4px;
  padding-inline-end: 22px; }
.k-areatile-sub { font-size: 0.83rem; color: var(--muted); }
.k-areatile-can { font-size: 0.82rem; font-weight: 650; color: var(--ink-2);
  margin-top: 6px; }
.k-areatile-can:empty { display: none; }
/* Kachel-Prozent (Owner-Entscheid 3, Auftrag 271): oben rechts auf der
   kompakten Kachel, wie das Abzeichen darunter positioniert (dieselbe Ecke,
   das Abzeichen sitzt nur auf der grossen Kachel, nie beide zugleich). */
.k-areatile-percent {
  position: absolute; top: 10px; inset-inline-end: 10px;
  font-size: 0.85rem; font-weight: 700; color: var(--brand-ink);
}
.k-areatile-percent[hidden] { display: none; }
/* Das Abzeichen (heute der Favoriten-Stern) liegt oben rechts auf. */
.k-areatile > .fav-btn { top: 6px; inset-inline-end: 8px; font-size: 0.95rem; }

/* Kompakte Zeilenkachel (Auftrag 103 Q0c/1+2, Nachbesserung Fable-Review
   07./08.09.2026, 2. Durchgang): Symbol OBEN (22 px), darunter Name (fett)
   und die Kennzahl (klein) — kein Untertitel, kein Favoriten-Stern.
   Befund 1: "90 % · fallender …" wurde mit `nowrap`/`ellipsis` abgeschnitten
   — das ist nicht abnehmbar; jetzt bricht beides um.
   Befund 2 (2. Durchgang): Symbol NEBEN dem Text liess der Kennzahl nur
   ~117 px Breite in der 2-Spalten-Kachel, das brach in 3-5 Zeilen und
   sprengte die 844-px-Vorgabe (912 px gemessen). Symbol ueber dem Text
   gibt Name/Kennzahl die volle Kachelbreite (~150 px) — meist 1-2 Zeilen. */
.k-areatile-compact {
  flex-direction: column; align-items: flex-start; gap: 3px;
  padding: 8px 10px; min-height: 0;
}
.k-areatile-compact .k-areatile-icon { width: 32px; height: 32px; font-size: 22px; }
.k-areatile-compact .k-areatile-icon svg { width: 22px; height: 22px; }
.k-areatile-stack {
  display: flex; flex-direction: column; gap: 2px; min-width: 0; width: 100%;
}
.k-areatile-compact .k-areatile-name {
  font-weight: 600; font-size: 0.9rem; margin: 0; padding-inline-end: 0;
  white-space: normal; overflow-wrap: break-word;
}
.k-areatile-compact .k-areatile-can {
  font-size: 0.8125rem; margin: 0;
  white-space: normal; overflow-wrap: break-word;
}

/* ---- 23. Leise Zeile (kQuietRow) -----------------------------------
   Fleiss unter Koennen (§3.2 Punkt 4, E19/E20): kein Rand, keine
   Flaeche, kein Titel — die Zeile soll leiser sein als alles darueber.
   Dehnung: eine Reihe, die umbricht; die Aktionen sitzen rechts
   (`margin-inline-start: auto`). Unter 761 px faellt die Zielzeile weg — ihre
   Zahl steht schon im Ring; die Serienzeile bleibt, sie steht sonst
   nirgends.
   Varianten: mit/ohne Serienzeile, mit/ohne Aktion. */
/* ---- 23b. Fortschrittsbalken (kBar, Auftrag 271) --------------------- */
.k-bar { height: 6px; border-radius: var(--r-pill); background: var(--brand-soft);
  overflow: hidden; margin-top: 6px; }
.k-bar-fill { height: 100%; background: var(--brand); border-radius: inherit; }

.k-quietrow { display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap; }
.k-quietrow-meta { display: flex; flex-direction: column;
  gap: calc(var(--sp-1) / 2); min-width: 0; }
.k-quietrow-goal { color: var(--ink-2); font-size: 0.86rem; }
.k-quietrow-streak { font-size: 0.9rem; font-weight: 700; color: var(--ink-2); }
.k-quietrow-actions { margin-inline-start: auto; display: flex; gap: var(--sp-2);
  align-items: center; }
@media (max-width: 760px) {
  .k-quietrow-goal { display: none; }
  .k-quietrow-meta { align-items: center; }
}

/* ---- 24. Aufgaben-Schritt (kTaskStep) ------------------------------
   Die Karte einer Reviewer-Aufgabe (docs/reviewer-workflows.md §9): ein
   Schritt je Bildschirm, Telefon zuerst. Vier Ausbaustufen im selben
   Rahmen — Hinweis, Hoeren+Waehlen, Aufnehmen, Aufnehmen mit Urteil.

   Variante D (Owner-Entscheid 03.09.2026, Figma-Knoten 165:234): der
   Fortschritt steht in der Karte, Hoeren und Antworten sind zwei
   nummerierte Bloecke, und der Clip liegt auf einer gefaerbten Buehne
   statt in einem umrandeten Kasten. Gemessen auf 360 px Kartenbreite.

   Dehnung: eine Saeule, die die Breite ihres Behaelters nimmt und nach
   unten waechst; Chips, Optionen und Tonreihen brechen um statt zu
   scrollen (auf 390 px passen fuenf Tonchips in zwei Zeilen). Die
   Aktionszeile klebt am Fuss der Karte, der Absendeknopf ist
   vollbreit — er ist die eine Handlung des Schritts.
   Varianten: mit/ohne Clip, mit/ohne Tonreihen, mit/ohne Optionen,
   mit/ohne Freitext, Urteil sichtbar/verborgen (nur `record_graded`,
   und dort erst nach der Aufnahme). */
.k-taskstep { display: flex; flex-direction: column; gap: var(--sp-3);
  min-width: 0; }
.k-taskstep-text { margin: 0; color: var(--ink-2); line-height: 1.55; }
.k-taskstep-question { margin: 0; font-size: 1.05rem; font-weight: 650;
  min-width: 0; }

/* Fortschritt in der Karte (Variante D). Die Fuellung liest ihren
   Anteil aus `--k-progress`, das die Render-Funktion am Kasten setzt —
   kein Pixelwert aus dem JavaScript.
   Dehnung: der Titel schrumpft und wird gekuerzt, die Zaehlung nie. */
.k-taskstep-progress { display: flex; flex-direction: column; gap: var(--sp-1); }
.k-taskstep-progress-row { display: flex; align-items: baseline;
  justify-content: space-between; gap: var(--sp-2); }
.k-taskstep-progress-title { min-width: 0; font-size: 0.82rem; font-weight: 650;
  color: var(--ink-2); overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap; }
.k-taskstep-progress-count { flex: none; font-size: 0.82rem; color: var(--muted);
  font-variant-numeric: tabular-nums; }
.k-taskstep-progress-bar { height: 4px; border-radius: var(--r-pill);
  background: var(--grid); overflow: hidden; }
.k-taskstep-progress-fill { display: block; height: 100%;
  width: var(--k-progress, 0%); background: var(--brand);
  border-radius: inherit; }

/* Der Kopf eines Blocks: Ziffer und Titel. Die Ziffer gibt es nur,
   wenn die Karte zwei Bloecke hat.
   Dehnung: der Titel bricht um, die Ziffer bleibt rund. */
.k-taskstep-stephead { display: flex; align-items: center; gap: var(--sp-2);
  min-width: 0; }
.k-taskstep-stepnum { flex: none; width: 26px; height: 26px;
  border-radius: var(--r-pill); background: var(--brand); color: var(--on-brand);
  font-size: 0.85rem; font-weight: 650; display: grid; place-items: center; }
.k-taskstep-steptitle { min-width: 0; font-size: 1.05rem; font-weight: 650; }
/* Die drei Zustaende eines Blockkopfs (Variante H, Auftrag 89): `done`
   gruene Ziffernflaeche, `locked` graue, gedimmter Titel — die Sperre der
   BEDIENELEMENTE traegt `.k-taskstep-blockbody.is-locked` weiter unten. */
.k-taskstep-stephead-done .k-taskstep-stepnum { background: var(--good); color: var(--on-good); }
.k-taskstep-stephead-locked .k-taskstep-stepnum { background: var(--grid); color: var(--muted); }
.k-taskstep-stephead-locked .k-taskstep-steptitle { color: var(--muted); opacity: 0.75; }
/* Checkliste und „Jetzt"-Zeile stehen leer da, wenn es nichts zu zeigen
   gibt (`note`-Seiten, ein Schritt ohne mehrere Bloecke) — beide Kaesten
   sind dann einfach kein Kind im DOM (leeres `<div>` faellt weg). */
.k-taskstep-checklist:empty, .k-taskstep-banner:empty { display: none; }
/* Ein gesperrter Block ist gedimmt UND stumm (Buttons `disabled`, die
   Regel steht am Aufruf) — die Flaeche selbst zeigt nur die Dimmung.
   Dehnung: eine Saeule wie der Rest der Karte. */
.k-taskstep-blockbody { display: flex; flex-direction: column; gap: var(--sp-2);
  min-width: 0; transition: opacity .15s ease; }
.k-taskstep-blockbody.is-locked { opacity: 0.4; pointer-events: none; }
.k-taskstep-heard { font-size: 0.8rem; color: var(--brand-ink); font-weight: 600; }
.k-taskstep-heard[hidden] { display: none; }
/* Die geteilte Frage vor Ton/Urteil (Punkt 3): kein eigener Block, nur
   ein ruhiger Einleitungssatz — die Ziffern stehen an Ton und Urteil. */
.k-taskstep-question-lead { margin: 0; color: var(--ink-2); font-size: 0.95rem;
  line-height: 1.5; }

/* Die Buehne: der Clip mit allem, was zu ihm gehoert. Flaeche statt
   Rand, weil sie der ruhende Teil der Karte ist und die Frage darunter
   der arbeitende. Schrift auf `--brand-soft`: `--brand-ink` hell
   7,7:1, dunkel 9,6:1 — beide Modi tragen dieselbe Token-Wahl.
   Dehnung: eine zentrierte Saeule; das Wort bricht um, der
   Anhoeren-Knopf nimmt die volle Breite, die Stimmzeile bricht
   zwischen Name und Referenz-Knopf. */
.k-taskstep-clip { display: flex; flex-direction: column; gap: var(--sp-3);
  align-items: center; text-align: center; padding: var(--sp-4);
  border-radius: var(--r-3); background: var(--brand-soft); min-width: 0; }
.k-taskstep-clip-label { font-size: 0.72rem; font-weight: 700;
  letter-spacing: .08em; text-transform: uppercase; color: var(--brand-ink);
  font-variant-numeric: tabular-nums; }
.k-taskstep-meta { display: flex; flex-wrap: wrap; gap: var(--sp-1);
  justify-content: center; }
/* Kopf der Buehne (Kanya 03.09.2026): das Wort ist die Hauptsache,
   die Stimme die zweite. Beide waren vorher kleine Status-Chips. */
.k-taskstep-word { font-size: 2.6rem; line-height: 1.4; font-weight: 650;
  word-break: break-word; }
.k-taskstep-voice { display: flex; flex-wrap: wrap; gap: var(--sp-2);
  align-items: center; justify-content: space-between; width: 100%;
  min-width: 0; }
.k-taskstep-voice-who { display: inline-flex; align-items: center;
  gap: var(--sp-1); min-width: 0; }
.k-taskstep-voice-name { font-size: 0.9rem; font-weight: 650;
  color: var(--ink-2); }
/* Die Referenzsaetze stehen nur geschrieben, ohne Kasten (Owner
   03.09.2026 abends): der Satz genuegt zum Eichen des Ohrs. */
.k-taskstep-voiceref-text { font-size: 0.95rem; line-height: 1.6;
  color: var(--ink-2); text-align: center; word-break: break-word;
  min-width: 0; }
.k-taskstep-voiceref-sep { color: var(--muted); }
.k-taskstep-prompt { display: flex; flex-direction: column; gap: var(--sp-1);
  align-items: center; text-align: center; }
.k-taskstep-thai { font-size: 1.8rem; line-height: 1.5; }
.k-taskstep-roman { color: var(--muted); font-size: 0.9rem; }
.k-taskstep-reps { margin: 0; text-align: center; color: var(--muted);
  font-size: 0.85rem; font-variant-numeric: tabular-nums; }
/* Punktzeile + Ansage vor der ersten Aufnahme (Auftrag 129, Punkt 1):
   wie viele Aufnahmen insgesamt moeglich sind, wie viele schon gespeichert
   sind — bevor der Rekorder ueberhaupt loslegt. */
.k-taskstep-repshead { display: flex; flex-direction: column; gap: var(--sp-1);
  align-items: center; text-align: center; }
.k-taskstep-reppunkte { display: flex; gap: var(--sp-1); font-size: 1rem;
  line-height: 1; letter-spacing: 0.1em; }
.k-taskstep-reppunkt { color: var(--border); }
.k-taskstep-reppunkt.is-done { color: var(--good); }
.k-taskstep-rephint { margin: 0; color: var(--muted); font-size: 0.85rem; }
/* Der Erfolgszustand nach „Behalten" (Punkt 1: es gab vorher keinen). */
.k-taskstep-kept-status { margin: 0 0 var(--sp-1); flex: 1 1 100%;
  color: var(--good-text); font-weight: 650; }
.k-taskstep-tones, .k-taskstep-options { display: flex; flex-wrap: wrap;
  gap: var(--sp-2); align-items: center; }
/* Eine Silbe: die fuenf Toene als Kacheln in zwei Spalten, die letzte
   ueber beide (Variante D). Mehrsilbige Woerter behalten die
   beschrifteten Chipreihen. */
.k-taskstep-tones.is-tiles { display: grid; grid-template-columns: 1fr 1fr;
  gap: var(--sp-2); align-items: stretch; }
.k-taskstep-tones.is-tiles .k-tonechip { width: 100%; justify-content: center;
  padding: var(--sp-3); border-radius: var(--r-2); }
/* Auftrag 128: mit Vorhoeren steckt die Pille in einer Huelle
   (`.k-tonechip-unit`) — SIE ist jetzt das Gitterelement, deshalb
   stapelt sie sich in der Kachel (Pille oben, ▶-Knopf darunter) statt
   danebenzustehen wie in einer Chipreihe. */
.k-taskstep-tones.is-tiles > .k-tonechip-unit { flex-direction: column;
  width: 100%; gap: var(--sp-1); }
.k-taskstep-tones.is-tiles .k-tonechip:last-child:nth-child(odd),
.k-taskstep-tones.is-tiles > .k-tonechip-unit:last-child:nth-child(odd) {
  grid-column: 1 / -1; }
.k-taskstep-syl { font-size: 0.82rem; color: var(--muted); min-width: 3.5em; }
.k-taskstep-comment-toggle { align-self: flex-start; }
.k-taskstep-verdict { display: flex; flex-direction: column; gap: var(--sp-2);
  padding: var(--sp-3); border: 1px solid var(--border);
  border-radius: var(--r-2); background: var(--surface); }
.k-taskstep-verdict-head { display: flex; flex-wrap: wrap; align-items: center;
  gap: var(--sp-2); font-weight: 700; }
/* Der Probenhinweis steht leise neben dem Urteil (§8cm): kleiner als
   das Urteilswort, in der Randfarbe des Kits, ohne eigene Farbe. */
.k-taskstep-verdict-head .k-chip { font-weight: 600; font-size: var(--fs-1);
  min-height: 24px; padding: 0 var(--sp-2); color: var(--muted); }
.k-taskstep-verdict-rows { display: flex; flex-direction: column;
  gap: var(--sp-2); }
.k-taskstep-verdict-row { display: flex; flex-wrap: wrap; gap: var(--sp-2);
  align-items: center; }
.k-taskstep-issue { margin: 0; color: var(--ink-2); font-size: 0.88rem; }
.k-taskstep-actions { display: flex; flex-wrap: wrap; gap: var(--sp-2);
  align-items: center; margin-top: var(--sp-2); }
.k-taskstep-actions .k-btn-block { flex: 1 1 100%; }

/* Die Quittungskarte nach der Aufnahme (Punkt 5, Auftrag 89): gruene
   Flaeche, drei Chips, die Kontur, „Nochmal"/„Behalten". Steht zwischen
   Aufnahme und „behalten" — kein Urteil, nur eine Lesehilfe.
   Dehnung: eine Saeule wie die Karte; die Chips brechen um. */
.k-taskstep-receipt { display: flex; flex-direction: column; gap: var(--sp-3);
  padding: var(--sp-4); border-radius: var(--r-3);
  background: var(--cls-low-bg); border: 1px solid var(--good); min-width: 0; }
.k-taskstep-receipt-title { font-weight: 650; color: var(--good-text); }
.k-taskstep-receipt-chips { display: flex; flex-wrap: wrap; gap: var(--sp-1); }
.k-taskstep-receipt-legend { margin: 0; font-size: 0.78rem; color: var(--muted); }

/* Stoergeraeusch-Kennzeichen (Auftrag 95, Punkt 1): zwei Kaestchen
   (`.k-field.consent-check`, Baustein 8) auf der Hoer-Karte und der
   Quittungskarte. Dehnung: eine Saeule, wie jedes Formularfeld. */
.k-taskstep-noise { display: flex; flex-direction: column; gap: var(--sp-1); }

/* Antwortkarten (Punkt 8, Auftrag 89): grosse Radio-Karten statt Segment
   oder Chips — fuer Freitext-Schritte ohne Clip, oder sobald eine Option
   eine Unterzeile traegt. Dehnung: eine Saeule, jede Karte volle Breite. */
.k-taskstep-optcard { display: flex; align-items: flex-start; gap: var(--sp-2);
  width: 100%; padding: var(--sp-3); border: 1px solid var(--border);
  border-radius: var(--r-2); background: var(--surface); text-align: start;
  font: inherit; cursor: pointer; }
.k-taskstep-optcard:hover { border-color: var(--brand); }
.k-taskstep-optcard:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; }
.k-taskstep-optcard.is-active { border-color: var(--brand); background: var(--brand-soft); }
.k-taskstep-optcard-radio { flex: none; width: 18px; height: 18px; margin-top: 2px;
  border-radius: var(--r-pill); border: 2px solid var(--border-strong); }
.k-taskstep-optcard.is-active .k-taskstep-optcard-radio {
  border-color: var(--brand); background:
    radial-gradient(circle, var(--brand) 42%, transparent 46%); }
.k-taskstep-optcard-body { min-width: 0; }
.k-taskstep-optcard-title { font-weight: 650; }
.k-taskstep-optcard-hint { margin-top: 2px; color: var(--muted); font-size: 0.85rem; }

/* „Eigene Worte" (Punkt 8): Freitext bleibt hinter einem Knopf, der das
   Feld MIT der Thai-Tastatur oeffnet — steht schon Text da, offen.
   Dehnung: eine Saeule, die Tastatur nimmt die volle Breite (Baustein 18). */
.k-taskstep-freewrap { display: flex; flex-direction: column; gap: var(--sp-2); }

/* ---- 24a. Segmentwahl (kSegment) -----------------------------------
   Bis drei einander ausschliessende Antworten als eine Pille (Variante
   D). Der Unterschied zur Reiterleiste (Baustein 7): die Reiter
   wechseln die ANSICHT, das Segment beantwortet eine Frage — es kann
   deshalb auch leer sein (ein Klick auf das gewaehlte Feld waehlt ab).
   Dehnung: die Felder teilen sich die Breite gleich; wird die Karte
   schmaler als die Beschriftungen, brechen sie untereinander. */
.k-seg { display: flex; flex-wrap: wrap; min-width: 0; gap: var(--sp-1);
  padding: var(--sp-1); border: 1px solid var(--border);
  border-radius: var(--r-pill); background: var(--page); }
.k-seg-item { flex: 1 1 0; min-width: 0; min-height: 40px;
  padding: var(--sp-2) var(--sp-3); border: 0; border-radius: var(--r-pill);
  background: none; color: var(--ink-2); font: inherit; font-size: 0.82rem;
  font-weight: 600; text-align: center; cursor: pointer; }
.k-seg-item:hover { color: var(--brand-ink); }
.k-seg-item:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; }
.k-seg-item.is-active { background: var(--brand-soft); color: var(--brand-ink);
  font-weight: 650; }

/* ---- 33. Checkliste (kStepChecklist, Variante H, Auftrag 89) -------
   Eine Reihe verbundener Stationen — Aufgabenliste (Schrittgruppen) oder
   Kopf der Aufgabenkarte (Teilbloecke eines Schritts). `done` gruen und
   antippbar, `now` die Markenfarbe mit weisser Ziffer, `locked` grau und
   gedimmt mit Schloss.
   Dehnung: volle Kartenbreite; ab fuenf Stationen scrollt die Zeile
   waagerecht (`is-scroll`), ein Farbverlauf am rechten Rand zeigt das an,
   statt die Stationen zu quetschen. */
.k-checklist { display: flex; align-items: center; gap: var(--sp-1);
  overflow-x: visible; padding: 2px 0; }
.k-checklist.is-scroll { overflow-x: auto; scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
  mask-image: linear-gradient(90deg, #000 92%, transparent);
  -webkit-mask-image: linear-gradient(90deg, #000 92%, transparent); }
.k-checklist.is-scroll::-webkit-scrollbar { display: none; }
.k-checklist-sep { flex: none; width: 14px; height: 2px; background: var(--grid);
  margin: 0 -2px; align-self: center; }
.k-checklist-item { flex: none; display: inline-flex; align-items: center;
  gap: 6px; min-height: 32px; padding: 4px 10px; border-radius: var(--r-pill);
  border: 1px solid var(--border); background: var(--surface); font: inherit;
  font-size: 0.8rem; font-weight: 600; color: var(--ink-2); white-space: nowrap; }
button.k-checklist-item { cursor: pointer; }
button.k-checklist-item:hover { border-color: var(--good); }
button.k-checklist-item:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; }
.k-checklist-mark { display: inline-flex; align-items: center; justify-content: center;
  width: 18px; height: 18px; border-radius: 999px; font-size: 0.72rem; flex: none; }
.k-checklist-done { border-color: var(--good); color: var(--good-text);
  background: var(--cls-low-bg); }
.k-checklist-done .k-checklist-mark { background: var(--good); color: var(--on-good); }
.k-checklist-now { border-color: var(--brand); color: var(--brand-ink);
  background: var(--brand-soft); font-weight: 700; }
.k-checklist-now .k-checklist-mark { background: var(--brand); color: var(--on-brand); }
.k-checklist-locked { color: var(--muted); opacity: 0.7; }
.k-checklist-locked .k-checklist-mark { background: var(--grid); font-size: 0.68rem; }

/* ---- Reviewer-Aufgaben (Ansicht zu Baustein 24) --------------------
   Die Huelle um `kTaskStep`: Titel, das vorgeladene Tonelement und die
   Zeile fuer seinen Ladezustand. Alles Weitere baut das Kit.
   Dehnung: eine Saeule wie die Review-Ansicht; auf 390 px ohne
   Seitenabstand ausser dem der Seite. */
.tk { display: flex; flex-direction: column; gap: var(--sp-3); }
.tk-title { margin: 0; font-size: 1.1rem; }
.tk-audio { display: none; }
.tk-audio-state { margin: 0; color: var(--muted); font-size: 0.85rem; }
.tk-audio-state:empty { display: none; }

/* ---- Aufgabenliste, Variante H (Auftrag 89, 07.09.2026) ------------
   Bilanz-Zeile ueber den Karten, je Karte ein Kopf (Titel + Statuschip)
   ueber der Kurzbeschreibung und der Checkliste (`kStepChecklist`).
   Dehnung: eine Saeule wie jede Kartenliste; der Kopf bricht Titel und
   Chip auf 390 px in zwei Zeilen. */
.tk-balance { margin: 0 0 var(--sp-1); color: var(--muted); font-size: 0.85rem; }
/* Auftrag 271c: zwei Symbole (Hoeren+Sprechen) nebeneinander vor dem
   Aufgabentitel, wo `needs.listen && needs.speak` beide gelten. */
.tk-needs-icons { display: inline-flex; align-items: center; gap: 2px; vertical-align: -0.15em; }
.tk-card-head { display: flex; flex-wrap: wrap; align-items: center;
  justify-content: space-between; gap: var(--sp-2); }
.tk-card-head .k-card-title { margin: 0; }

/* ---- Abschlusskarte (Punkt 9, Auftrag 89) --------------------------
   Bilanz statt „fertig": Haken, Titel, Zeilen, Dank, naechste Aufgabe.
   Dehnung: eine zentrierte Saeule; die Zeilen bleiben zweispaltig, bis
   die Karte schmaler als ihre Beschriftungen wird. */
.tk-finish { display: flex; flex-direction: column; gap: var(--sp-2);
  text-align: center; }
.tk-finish-icon { font-size: 2rem; color: var(--good-text); }
.tk-finish-sub { margin: 0; color: var(--muted); }
.tk-finish-rows { width: 100%; display: flex; flex-direction: column;
  gap: var(--sp-1); text-align: start; margin-top: var(--sp-2); }
.tk-finish-row { display: flex; flex-wrap: wrap; justify-content: space-between;
  gap: var(--sp-2); font-size: 0.9rem; }
.tk-finish-row-label { color: var(--muted); }
.tk-finish-thanks { margin: var(--sp-2) 0 0; color: var(--ink-2); }

/* ---- 25. Geraetezeile (kDeviceLine) --------------------------------
   Was der Browser ueber das Aufnahmegeraet EINES Takes gesagt hat
   (E3, 03.09.2026): Mikrofonbezeichnung, die drei Verarbeitungsschalter
   (AGC, Rauschunterdrueckung, Echoausloeschung), die Abtastrate und der
   Browserstring. Sie steht klein unter dem Take in der Admin-Ansicht.
   Dehnung: waagerecht flexibel, bricht UM (`flex-wrap`) statt zu
   scrollen — auf 390 px stehen die Schalter untereinander; der
   Browserstring ist auf eine Zeile gekuerzt und traegt den vollen Text
   im `title`. Fehlt eine Angabe, faellt sie weg; die Zeile erfindet
   nichts.
   Farbe: eingeschaltete Verarbeitung ist der Befund, den man sehen
   soll — `--warning` als Rand, `--cls-high` als Schrift, dieselbe
   Paarung wie beim Stimmen-Etikett. Sie steht nie ALLEIN in der Farbe:
   jeder Schalter nennt seinen Zustand in Worten. */
.k-deviceline {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-1);
  font-size: var(--fs-1); color: var(--ink-2); line-height: 1.4;
  min-width: 0;
}
.k-deviceline-label { font-weight: 650; }
.k-deviceline-sep { color: var(--grid); }
.k-deviceline-rate { font-variant-numeric: tabular-nums; }
.k-deviceline-flag {
  padding: 0 var(--sp-1); border-radius: var(--r-pill);
  border: 1px solid var(--border); white-space: nowrap;
}
.k-deviceline-flag.is-on {
  color: var(--cls-high); border-color: var(--warning);
  background: var(--cls-high-bg); font-weight: 650;
}
.k-deviceline-ua {
  flex: 1 1 12rem; min-width: 0; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; color: var(--muted);
}

/* ==================================================================
   UI-KIT WELLE 3 — DIE BAUSTEINE DER FORTSCHRITTSSEITE (04.09.2026)
   ------------------------------------------------------------------
   Vorlage E („Bericht mit naechstem Schritt") mit dem Tonprofil aus
   Variante D. Drei Bausteine, die die Seite vorher von Hand baute:
   der Abschnittskopf, das Tonprofil und das Abzeichenband.
   ================================================================== */

/* Baustein 26 — Abschnittskopf.
   Dehnung: der Titel nimmt den freien Platz, der Link bleibt rechts;
   beide Teile sind kurz, deshalb bricht auf 360 px nichts um. */
.k-secthead {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: var(--sp-2); margin: var(--sp-4) 0 var(--sp-2);
}
.k-secthead-title {
  margin: 0; font-size: var(--fs-1); font-weight: 700;
  letter-spacing: .08em; text-transform: uppercase; color: var(--brand-ink);
}
.k-secthead-link { font-size: var(--fs-1); color: var(--brand); white-space: nowrap; }

/* Baustein 27 — Tonprofil.
   Dehnung: Etikett (Punkt + Name) und Zahlenspalte stehen fest, die
   Spur in der Mitte dehnt sich ueber den Rest. Auf 360 px bleibt die
   Spur ueber 100 px breit — schmaler waere sie nicht mehr lesbar. */
.k-toneprofile { display: flex; flex-direction: column; gap: var(--sp-3); }
.k-toneprofile-legend {
  display: flex; flex-wrap: wrap; justify-content: flex-end;
  gap: var(--sp-1) var(--sp-4); font-size: var(--fs-1); color: var(--ink-2);
}
.k-toneprofile-legenditem { display: inline-flex; align-items: center; gap: var(--sp-1); }
.k-toneprofile-legendlabel { white-space: nowrap; }
.k-toneprofile-mark { width: 14px; height: 4px; border-radius: 2px; display: inline-block; }
.k-toneprofile-mark-speak { background: var(--brand); }
.k-toneprofile-mark-listen { background: var(--listen); }
.k-toneprofile-row {
  display: grid; grid-template-columns: minmax(5.5rem, max-content) 1fr max-content;
  gap: var(--sp-2); align-items: center;
}
.k-toneprofile-label {
  display: inline-flex; align-items: center; gap: var(--sp-1);
  font-size: var(--fs-2); color: var(--ink-2); min-width: 0;
}
.k-toneprofile-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.k-toneprofile-dot { width: 8px; height: 8px; border-radius: 50%; flex: none; }
.k-toneprofile-dot-mid { background: var(--tone-mid); }
.k-toneprofile-dot-low { background: var(--tone-low); }
.k-toneprofile-dot-falling { background: var(--tone-falling); }
.k-toneprofile-dot-high { background: var(--tone-high); }
.k-toneprofile-dot-rising { background: var(--tone-rising); }
.k-toneprofile-bars { display: flex; flex-direction: column; gap: 3px; min-width: 100px; }
.k-toneprofile-track {
  background: var(--grid); border-radius: var(--r-pill); overflow: hidden;
}
.k-toneprofile-bar { display: block; min-width: 2px; border-radius: var(--r-pill); }
.k-toneprofile-bar-speak { height: 8px; background: var(--brand); }
.k-toneprofile-bar-listen { height: 6px; background: var(--listen); }
.k-toneprofile-vals {
  display: flex; flex-direction: column; align-items: flex-end; gap: 1px;
  font-variant-numeric: tabular-nums; text-align: end; white-space: nowrap;
}
.k-toneprofile-val-speak { font-size: var(--fs-2); font-weight: 700; color: var(--ink); }
.k-toneprofile-val-listen { font-size: var(--fs-1); color: var(--muted); }

/* Baustein 28 — Abzeichenband.
   Dehnung: eine Reihe, die waagerecht scrollt statt umzubrechen —
   zwei Reihen laesen sich wie zwei Bedeutungen. */
.k-badgestrip {
  display: flex; gap: var(--sp-2); overflow-x: auto; padding-bottom: var(--sp-1);
  scrollbar-width: thin;
}
.k-badgestrip-item {
  flex: none; width: 44px; height: 44px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid var(--border); background: var(--surface);
  font-size: 1.25rem; line-height: 1; padding: 0;
}
.k-badgestrip-item.is-earned { background: var(--brand-soft); border-color: var(--brand); }
.k-badgestrip-item.is-locked { opacity: .45; filter: grayscale(1); }
.k-badgestrip-icon { pointer-events: none; }

/* Baustein 30 — Bewertungsmodus-Chip (Auftrag 74).
   Er sagt in jeder Sprechuebung, wie streng der naechste Versuch
   beurteilt wird. „Pruefen" traegt die Warnfarbe als KANTE und Schrift,
   nicht als Flaeche: es ist eine Ansage, keine Stoerung.
   Dehnung: inhaltsbreit, einzeilig — der Chip sitzt in der Kopfzeile
   neben Kruemelpfad und Fortschritt und darf sie nicht umbrechen. */
/* Der Chip traegt FARBE (Owner 06.09.2026, Nachtrag zu Auftrag 74): der
   Modus ist die Antwort auf „warum ist das schon wieder rot" und darf
   deshalb nicht als graue Marginalie neben dem Kruemelpfad verschwinden.
   Zwei Fuellungen statt einer Kante:
     Ueben   --brand-soft mit --brand-ink (hell 7,12:1 · dunkel 9,94:1) —
             ruhig, die Farbe der App selbst: hier darf man scheitern.
     Pruefen --cls-high-bg mit --strict-ink (hell 5,65:1 · dunkel 7,78:1),
             Kante --warning: dieselbe Bernsteinfamilie wie bisher, nur
             gefuellt.
   Bei „auto" gilt die Farbe des AUFGELOESTEN Modus — der Chip faerbt, was
   wirklich passiert, nicht die Einstellung.
   Groesse: --fs-2 statt --fs-1 und dasselbe Raster wie der kleine Kit-Knopf
   daneben (min-height 32px, Polster --sp-1/--sp-3), damit Chip und
   „Lektion beenden" eine Zeile bilden und nicht zwei Hoehen.
   Zwei Traeger im Chip (Auftrag 77b): das Praefix faellt unter 761 px
   weg, der Wert nie. */
.k-judgemode-prefix, .k-judgemode-value { white-space: nowrap; }
.lbl-short { display: none; }
.k-judgemode {
  display: inline-flex; align-items: center; gap: var(--sp-1);
  min-height: 32px; padding: var(--sp-1) var(--sp-3);
  border: 1px solid var(--border); border-radius: 999px;
  background: var(--surface); color: var(--ink-2);
  font-size: var(--fs-2); font-weight: 600; white-space: normal;
  overflow-wrap: break-word; text-align: center;
  cursor: pointer;
}
.k-judgemode:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; }
.k-judgemode.is-easy {
  background: var(--brand-soft); color: var(--brand-ink); border-color: var(--brand);
}
.k-judgemode.is-strict {
  background: var(--cls-high-bg); color: var(--strict-ink); border-color: var(--warning);
}
/* Das Auswahlblatt im kModal: drei Zeilen, jede mit einem Satz.
   Dehnung: die Zeilen fuellen die Dialogbreite, der Satz bricht um. */
.k-judgemode-opt {
  display: flex; flex-direction: column; gap: 2px; width: 100%;
  padding: var(--sp-2); text-align: start;
  border: 1px solid var(--border); border-radius: var(--r-2);
  background: var(--surface); color: var(--ink); cursor: pointer;
}
.k-judgemode-opt.is-active { background: var(--brand-soft); border-color: var(--brand); }
.k-judgemode-opt:disabled { cursor: default; opacity: .6; }
.k-judgemode-opt:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; }
.k-judgemode-opt-name { font-weight: 700; font-size: var(--fs-2); }
.k-judgemode-opt-body { font-size: var(--fs-1); color: var(--ink-2); }
.k-judgemode-hint { font-size: var(--fs-1); color: var(--ink-2); margin: 0; }
/* Die Modus-Zeile der Zusammenfassung („Bewertet im Modus: …").
   Dehnung: volle Kartenbreite, zentriert wie der Untertitel. */
.summary-jm { font-size: var(--fs-1); color: var(--ink-2); text-align: center; margin: 0; }

/* ---------- 31. Naehe-Balken ------------------------------------------
   Vier Segmente, eine Beschriftung und die letzten fuenf Versuche desselben
   Items. Der Balken sagt WIE WEIT die Aufnahme vom Ziel weg war — die
   Urteilszeile darueber sagt nur ob/ob nicht, und genau daran zerbrach die
   Motivation des Owners: neunzehnmal dasselbe Rot, obwohl der Abstand
   schrumpfte.

   Der Baustein RECHNET NICHT: die Stufe 1-4 kommt fertig vom Server
   (``result.naehe``), der Verlauf aus dem Sitzungsring in state.js.

   Dehnung: der Balken ist FEST 78x6 (er ist eine Skala, keine Fuellung —
   eine mitwachsende Skala waere in jeder Ansicht eine andere), die Zeile
   drumherum fuellt die Breite und legt den Verlauf rechts ab. */
.k-naehe { display: flex; align-items: center; gap: var(--sp-2);
  flex-wrap: wrap; margin: var(--sp-2) 0 0; }
.k-naehe-bar { display: flex; gap: 3px; width: 78px; flex: 0 0 78px; }
.k-naehe-seg { height: 6px; flex: 1 1 0; border-radius: 3px; opacity: .18; }
.k-naehe-seg.is-on { opacity: 1; }
.k-naehe-seg-1 { background: var(--critical); }
.k-naehe-seg-2 { background: var(--near); }
.k-naehe-seg-3 { background: var(--warning); }
.k-naehe-seg-4 { background: var(--good); }
.k-naehe-label { font-size: var(--fs-1); color: var(--ink-2); }
/* Der Verlauf: fuenf Zeichen, das juengste rechts. Er steht rechts in der
   Zeile und rueckt bei schmalem Fenster unter den Balken. */
.k-naehe-verlauf { margin-inline-start: auto; display: inline-flex; gap: 4px;
  font-size: var(--fs-1); line-height: 1; }
.k-naehe-verlauf i { font-style: normal; }
.k-naehe-verlauf i.st-1 { color: var(--critical); }
.k-naehe-verlauf i.st-2 { color: var(--near-text); }
.k-naehe-verlauf i.st-3 { color: var(--warning); }
.k-naehe-verlauf i.st-4 { color: var(--good-text); }


/* ======================================================================
   §Sprechuebung am Handy kompakt (Auftrag 77, 06.09.2026)
   Owner-Entscheid F: auf 390 px stand die Uebung 1.339 px hoch — jeder
   Griff zu ,Nochmal', ,Weiter', ,Du', ,Vorbild' oder zur Verlaufskurve
   kostete Scrollen. Drei Hebel: die eine Handlung wandert in eine
   Daumenleiste, die Aufgabenkarte wird zweispaltig, und alles, was man
   selten braucht, zieht in ein Optionen-Blatt.
   ====================================================================== */

/* ---- 32. Daumenleiste (kThumbBar) -----------------------------------
   Ein Knoten, zwei Auspraegungen: unter 761 px klebt er ueber der
   Navigationsleiste, ab 761 px steht er im Fluss am Fuss seiner Spalte.
   Nicht zwei DOM-Baeume und kein Umhaengen beim Drehen des Geraets.

   Dehnung: volle Breite des Behaelters; ein einzelnes Kind nimmt die
   ganze Zeile, zwei teilen sie sich. Die Hoehe folgt dem Inhalt
   (mindestens 64 px), unten kommt die Geraete-Sicherheitszone dazu. */
.k-thumbbar { display: flex; align-items: center; }
.k-thumbbar[hidden] { display: none; }
.k-thumbbar-slot { display: flex; align-items: center; gap: var(--sp-2);
  width: 100%; }
.k-thumbbar-slot > * { flex: 1 1 0; min-width: 0; }
/* Der Aufnahmeknopf zieht MIT SEINER LOGIK hinein (Pegel, Laufzeit,
   Fehlertext) — nur seine Saeule legt sich in der Leiste auf die Seite,
   damit die Leiste eine Leiste bleibt. Das Knopffeld ist auch hier in
   allen fuenf Zustaenden gleich hoch, nur eben 56 statt 88 px. */
.k-thumbbar .k-recorder { flex-direction: row; justify-content: center;
  gap: var(--sp-3); margin: 0; }
.k-thumbbar .k-rec-slot { height: 56px; width: auto; flex: none; }
.k-thumbbar .k-rec-label { text-align: start; }
.k-thumbbar .result-actions { margin: 0; width: 100%; }
@media (max-width: 760px) {
  .k-thumbbar {
    position: fixed; inset-inline-start: 0; inset-inline-end: 0;
    bottom: calc(var(--k-fuss-h) + env(safe-area-inset-bottom, 0px));
    z-index: 40; min-height: 64px;
    padding: var(--sp-2) var(--sp-3);
    background: var(--brand-soft); border-top: 1px solid var(--brand);
  }
  /* Der Inhalt darunter bekommt genau die Hoehe der Leiste als Polster,
     sonst verdeckt sie die letzte Zeile der Ansicht. */
  .has-thumbbar { padding-bottom: 84px; }
  /* Auftrag 264 (Fable-Review 25.09.2026, nach dem ersten Audit-Lauf): ein
     Boden-Zuschlag auf `.has-thumbbar` sprengte die Vorgabe „passt aufs
     Handy ohne Rollen" (test_die_uebung_passt_am_handy_auf_den_schirm,
     Owner 06.09.2026 -- 1107px statt 844px). Die Uebungsseite bleibt wie
     vor 264: NUR die Kit-Variablen (`--k-blattfest-boden`,
     `--k-blattfest-stapel-uebung`) werden berechnet, `.view` UND
     `.has-thumbbar` bleiben bei ihrem Grundwert -- kein Seitenboden hier,
     siehe die Journal-Abweichung. */
  /* Auftrag 246b: bleibt die Leiste im Zustand "aufnahme", waehrend das
     Urteil-Blatt offen ist (Stufe klein/halb), haengt sie sich exakt ueber
     den GANZEN Blatt-Stapel (`--k-blattfest-stapel-uebung`, kit.js Baustein
     9b2, Auftrag 258) -- nicht mehr nur ueber das Urteil-Blatt wie vor 258
     (der alte CSS-Wert kannte Versuche/Schreib darueber nicht). Ohne
     offene Blaetter bleibt die Variable bei ihrer Vorgabe 0px, die Leiste
     klebt wie vor 246b direkt am Fuss. */
  .practice:has(#result-panel:not([hidden])) .k-thumbbar {
    bottom: calc(var(--k-fuss-h) + env(safe-area-inset-bottom, 0px) + var(--k-blattfest-stapel-uebung, 0px));
  }
}
@media (min-width: 761px) { .k-thumbbar { margin-top: var(--sp-4); } }

/* ---- Aufgabenkarte kompakt (Entwurf K1; Wortzeile entwirrt, Auftrag 102)
   Links das Wort, rechts NUR der Vorbild-Knopf; das Stimmen-Etikett zieht
   in die Chip-Zeile darunter, neben den Tonchip — beide teilen sich EINE
   Zeile ueber `.ic-opts-start`. Vorher stand das Etikett neben dem Knopf
   (`.ic-side`) und ueberlappte die Umschrift, sobald sein Text laenger war
   als die 46 % der rechten Spalte (,Original (Lage zu weit entfernt)' bei
   „khaa", Ist-Lauf 07.09.2026 — `white-space: nowrap` liess den Text
   ueber den Kartenrand hinauswachsen, statt zu brechen).

   Dehnung: die linke Spalte nimmt den Rest, die rechte (nur der Knopf)
   ihre Inhaltsbreite; unter 360 px brechen beide untereinander. */
.ic-top { display: flex; align-items: flex-start; gap: var(--sp-3);
  text-align: start; }
.ic-main { flex: 1 1 auto; min-width: 0; }
.ic-side { flex: 0 0 auto; max-width: 46%; display: flex;
  flex-direction: column; align-items: flex-end; gap: var(--sp-1); }
.ic-words { display: flex; flex-wrap: wrap; align-items: baseline;
  gap: var(--sp-2); }
.ic-words .translit, .ic-words .gloss { margin: 0; }
.ic-voice { display: flex; flex-wrap: wrap; align-items: center;
  gap: var(--sp-1); font-size: var(--fs-1); color: var(--muted); }
.ic-voice .model-range-toggle { margin: 0; }
/* Die Tonchip-Reihe als EIGENE Zeile (Auftrag 112, E4): stand sie mit
   dem Stimmen-Etikett in derselben Zeile (`.ic-opts-start`), brach sie
   bei zwei und mehr Silben INNERHALB dieser Zeile um — und schob die
   Optionen-Chip-Zeile daneben auf eine dritte Zeile darunter (430 statt
   290 px, l3/l8). Jetzt bekommt sie ihre eigene Breite, egal wie viele
   Toene sie traegt; Native-Zeile und Optionen-Chip bleiben darunter in
   EINER Zeile, wie beim Einsilber. */
.ic-tones:empty { display: none; }
.ic-tones { margin: 0 0 var(--sp-2); }
.ic-opts { display: flex; flex-wrap: wrap; align-items: center;
  justify-content: space-between; gap: var(--sp-2); margin-top: var(--sp-2); }
/* Die START-Gruppe (Stimmen-Etikett) nimmt den Rest der Zeile; die
   Optionen-Chip-Gruppe daneben behaelt ihre Inhaltsbreite. */
.ic-opts-start { display: flex; flex-wrap: wrap; align-items: center;
  gap: var(--sp-2); flex: 1 1 auto; min-width: 0; }
.ic-opts-end { display: flex; align-items: center; gap: var(--sp-2); }
/* Der Sprech-Anker aendert, WAS aufgenommen wird — er traegt deshalb die
   Warnfarbe des strengen Modus und nicht die leise Chip-Flaeche. Ein
   Lerner, der ihn versehentlich anhat, spricht das nackte Wort in eine
   Phrasen-Bewertung (Owner-Meldung 06.09. 13:12).
   Dehnung: wie jeder Chip — Inhaltsbreite, bricht in die naechste Zeile. */
.k-chip.is-anchor-speak {
  background: var(--cls-high-bg); border-color: var(--warning);
  color: var(--strict-ink); font-weight: 700; }
/* Die Ablage: hier warten die Bedienelemente, die das Optionen-Blatt
   zeigt. Sie sind GEBAUT und verdrahtet, nur nicht sichtbar — deshalb
   eine Ablage und keine zweite Verdrahtung. */
.ic-stash { display: none; }
.opt-sheet { display: flex; flex-direction: column; gap: var(--sp-3);
  text-align: start; }
.opt-sheet-group { display: flex; flex-direction: column;
  align-items: stretch; text-align: start; }
/* Eine Zeile je Einstellung — dieselbe Bauform fuer alle, damit das
   Blatt nicht aus Pillen, unterstrichenen Links und Fettschrift besteht
   (Auftrag 77b, Punkt 5). Die Umschalter tragen ihren Namen selbst und
   fuellen die Zeile; nur Anker und Lernheft brauchen eine
   Beschriftung.
   Dehnung: volle Breite; die breite Variante stellt Beschriftung und
   Bedienelement untereinander. */
.opt-row { display: flex; align-items: center; justify-content: space-between;
  gap: var(--sp-3); padding: var(--sp-2) 0;
  border-bottom: 1px solid var(--border); }
.opt-row:last-child { border-bottom: 0; }
.opt-row-wide { flex-direction: column; align-items: stretch; }
.opt-row-label { font-size: var(--fs-2); font-weight: 600; color: var(--ink); }
/* Die Umschalter: aus einem unterstrichenen Link wird eine Zeile. */
.opt-sheet .linklike, .opt-sheet .k-btn-text {
  flex: 1 1 auto; display: flex; align-items: center; justify-content: space-between;
  margin: 0; padding: var(--sp-1) 0; text-align: start;
  text-decoration: none; font-size: var(--fs-2); font-weight: 600;
  color: var(--ink); }
.opt-sheet .bookmark-btn { flex: none; font-size: 1.2rem; }
/* Der Anker wird zum Segment: drei GLEICH BREITE Felder ueber die volle
   Blattbreite. Ein Flexrahmen mit `nowrap` hat die drei Beschriftungen
   uebereinandergeschoben, sobald eine laenger war als ihr Drittel
   (Bild 390_2 vom 06.09., Auftrag 77c) — ein Raster teilt die Breite
   dagegen, ohne den Inhalt zu befragen. Auf den Feldern stehen die
   kurzen Namen, der lange steht im Titel (audio.js).
   Dehnung: volle Breite des Blattes, drei Drittel; ein Feld bricht in
   sich um, statt seinen Nachbarn zu ueberschreiben. */
.opt-sheet .anchor-zone { margin: 0; display: grid; gap: 0;
  grid-template-columns: repeat(3, minmax(0, 1fr)); width: 100%;
  border: 1px solid var(--border-strong); border-radius: var(--r-pill);
  overflow: hidden; }
.opt-sheet .anchor-zone > .hint:first-child { display: none; }
.opt-sheet .anchor-zone .duel-chip.small {
  min-width: 0; border: 0; border-radius: 0; margin: 0;
  padding: 10px 4px; font-size: 0.82rem; line-height: 1.2;
  text-align: center; overflow-wrap: anywhere; background: var(--surface); }
.opt-sheet .anchor-zone .duel-chip.small + .duel-chip.small {
  border-inline-start: 1px solid var(--border); }
.opt-sheet .anchor-zone .duel-chip.small.active {
  background: var(--brand); color: var(--on-brand); font-weight: 700; }
/* Der Hinweis zum Sprech-Anker steht UNTER dem Segment, ueber die ganze
   Breite — im Raster waere er ein viertes Feld. */
.opt-sheet #anchor-hint, .opt-sheet #listen-anchor-hint { grid-column: 1 / -1;
  margin: 0; padding: var(--sp-1) var(--sp-2); font-size: var(--fs-1);
  border-top: 1px solid var(--border); }
.opt-sheet #anchor-hint:empty, .opt-sheet #listen-anchor-hint:empty {
  display: none; }
/* Hoer-Anker (Auftrag 78): dieselbe Bauform wie der Sprech-Anker — ein
   Raster aus drei gleichen Spalten. Die dritte Stellung (Ankerphrase)
   ist gesperrt, wo es zu dieser Stimme keinen Traegersatz-Take gibt;
   sie bleibt SICHTBAR, damit klar ist, dass es sie gibt, und sagt im
   Titel, warum sie nicht geht. */
.listen-anchor-zone .duel-chip.small:disabled { opacity: 0.45; cursor: not-allowed; }
/* Das Etikett neben dem Abspielknopf: was gerade GEKLUNGEN hat.
   Dehnung: es bricht mit dem Knopf in die naechste Zeile um. */
.listen-anchor-tag { display: inline-flex; align-items: center;
  padding: 2px var(--sp-2); border-radius: var(--r-pill);
  background: var(--brand-soft); color: var(--ink);
  font-size: var(--fs-1); line-height: 1.4; white-space: nowrap; }
.opt-sheet .anchor-zone, .opt-sheet .perception-toggle,
.opt-sheet .model-range-toggle, .opt-sheet .k-slow-toggle { margin: 0; }

/* ---- Hoerpruefung: Anhoeren-Knopf + Hoer-Anker-Etikett -------------
   Auftrag 112 (E2): der eigentliche Frage-/Antwortteil ist `kHoerfrage`
   (Kit-Abschnitt); hier steht nur noch der Anhoeren-Knopf mit dem
   Hoer-Anker-Etikett daneben, in einer Zeile wie im Sprechschritt. */
.discrim-head { display: flex; align-items: center; gap: var(--sp-3);
  flex-wrap: wrap; }

/* ---- Ergebniskopf, Vergleichsreihe, Kurvenkarte --------------------- */
/* Drei kurze Chips in EINER Zeile — der lange Name steht im Titel
   (Auftrag 77b, Punkt 4). Kein waagerechtes Rollen: ein Chip, den man
   erst herbeischieben muss, ist keiner.
   Dehnung: die Chips teilen sich die Zeile und brechen erst um, wenn
   ihre Beschriftung nicht mehr passt. */
.compare-row.compare-line { flex-wrap: wrap; overflow: visible;
  margin-bottom: 0; }
.compare-row.compare-line .btn { flex: 1 1 auto; white-space: nowrap;
  padding-inline-start: 10px; padding-inline-end: 10px; }
/* A-B-A ist der Vergleich, der am meisten traegt — er ist betont. */
.compare-row .btn.aba { background: var(--brand-soft);
  border-color: var(--brand); color: var(--brand-ink); font-weight: 650; }
.verdict .seg-line { flex: 1 1 100%; font-size: var(--fs-1);
  font-weight: 550; }
/* Die Legendenzeile der Kurvenkarte: zwei Spalten Schraffuren, rechts
   daneben die dritte Spalte mit dem Naehe-Balken.
   Dehnung: die Schraffuren nehmen den Rest, die dritte Spalte ihre
   Inhaltsbreite; unter 360 px rutscht sie unter die beiden anderen. */
.k-contour-legendrow { display: flex; flex-wrap: wrap;
  align-items: flex-start; gap: var(--sp-1) var(--sp-3); }
.k-contour-legendrow > .k-contour-legend { flex: 1 1 10rem; min-width: 0; }
.k-contour-legend-extra { flex: 0 1 auto; min-width: 8rem; }
.k-contour-legend-extra .k-naehe { margin: 0; }
.chart-block .k-contour-wrap { cursor: zoom-in; }
.chart-zoom { width: 100%; }

/* Legende der Ergebniskurve auf dem Handy (Auftrag 102): bis zu drei
   Zeilen UEBER der Kurve wurden zu EINER kompakten, umbrechenden Zeile
   DARUNTER — auf 390 px kostete die Legende sonst mehr Hoehe als die
   Kurve selbst (Ist-Bild ,ergebnis-abgelehnt' 07.09.2026). Reihenfolge
   per `order`, nicht per Verschieben im DOM: `.k-contour` ist schon eine
   Flexspalte (Baustein 13), der Desktop (kein Treffer dieser Abfrage)
   behaelt die Legende oben wie bisher. Die Kurvenhoehe selbst steht schon
   unbedingt bei 110 px (Variante 1 B, 01.09.); hier nur als eigene,
   ausdrueckliche Regel fuer 480 px und schmaler (Owner-Ziel: <= 130 px). */
@media (max-width: 480px) {
  .chart-block .k-contour-legendrow { order: 3; margin-top: var(--sp-1); }
  .chart-block .k-contour-wrap { order: 1; }
  .chart-block .k-contour-caption { order: 2; }
  .chart-block .k-contour-legend,
  .chart-block .k-contour-result .k-contour-legend {
    display: flex; grid-template-columns: none; font-size: 0.72rem;
    gap: 2px var(--sp-2);
  }
  .chart-block .k-contour-legend-item { gap: 3px; }
  .chart-block .k-contour-swatch { width: 11px; height: 7px; }
  .chart-block .k-contour-legend-extra { flex-basis: 100%; font-size: 0.72rem; min-width: 0; }
  .chart-block .k-contour-result .k-contour-wrap { height: 110px; }
}

@media (max-width: 760px) {
  /* Der Kruemelpfad kuerzt sich auf den letzten Schritt und teilt sich
     die Kopfzeile mit Knopf, Chip und Punkten — 40 px statt zwei
     Zeilen. */
  .practice .k-pagehead, .dialog-play .k-pagehead {
    margin: var(--sp-2) 0; flex-wrap: nowrap; align-items: center;
    gap: var(--sp-2); }
  /* Der Kruemelpfad faellt ganz weg: sein letzter Schritt steht seit
     Auftrag 77b im Zurueck-Knopf (,← Lektion'), und auf 390 px nahm er
     den drei Traegern daneben die Breite (77b, Punkt 1). */
  .practice .k-pagehead-crumbs, .dialog-play .k-pagehead-crumbs { display: none; }
  /* In der einzeiligen Kopfzeile darf keine Beschriftung umbrechen —
     ein umgebrochener Zurueck-Knopf machte den Kopf 68 statt 40 px hoch. */
  /* In der einzeiligen Kopfzeile gibt der Zurueck-Knopf zuerst nach: der
     Bewertungs-Chip (Auftrag 74) muss GANZ lesbar bleiben, sonst stuende
     dort ,Bewertung: Aut'. */
  .practice .k-pagehead .k-btn, .dialog-play .k-pagehead .k-btn,
  .practice .k-judgemode, .dialog-play .k-judgemode {
    white-space: nowrap; min-width: 0; overflow: hidden;
    text-overflow: ellipsis; }
  .practice .k-pagehead .k-btn, .dialog-play .k-pagehead .k-btn { flex: 0 3 auto; }
  .practice .k-judgemode, .dialog-play .k-judgemode { flex: 0 0 auto; }
  /* Die Kennzahl zeigt im Kopf nur ihre Zahl; der ganze Satz steht als
     Vorlesetext an der Kachel (practice.js). Sie bekommt ihre Breite
     zugesichert — abgeschnitten sagt ,0/1' gar nichts (77b, Punkt 1). */
  .k-pagehead .k-stat-label { display: none; }
  .practice .k-pagehead .k-stat, .dialog-play .k-pagehead .k-stat {
    flex: 0 0 auto; min-width: 2.6rem; overflow: visible; }
  /* Das Praefix des Bewertungs-Chips faellt weg: ,Auto → Üben' sagt
     dasselbe, und Titel wie Vorlesetext nennen es weiter. */
  .k-judgemode-prefix { display: none; }
  /* Der Zurueck-Knopf zeigt die kurze Beschriftung. */
  .lbl-long { display: none; }
  .lbl-short { display: inline; }
  .item-card { padding: 12px 14px; }
  /* Zielton und Optionen teilen sich eine Zeile — beide leiser als auf
     dem Desktop, damit die Karte unter 140 px bleibt. */
  .item-card .k-tonechip, .item-card .ic-opts .k-chip {
    padding: 5px 10px; font-size: 0.82rem; }
  .item-card .thai { font-size: 2.5rem; line-height: 1.15; }
  .item-card .translit { font-size: 1.05rem; }
  .item-card .gloss { margin-top: 2px; font-size: 0.86rem; }
  .practice .record-zone, .dialog-play .record-zone { margin: 12px 0; }
  /* Zwei Beschriftungen fallen weg, weil ihr Inhalt danebensteht: die
     Knoepfe heissen ,▶ Du' und ,▶ Vorbild', und die Bildunterschrift der
     Kurve steht in der Grossansicht. Zusammen 46 px — genau die, die dem
     Kurvenblatt zum Sichtfeld fehlten. */
  .compare-caption { display: none; }
  .chart-block .k-contour-caption { display: none; }
  .result-panel { gap: 8px; }
  .chart-block { padding: 10px; }
  .cues li { padding: 7px 10px; font-size: 0.85rem; }
  /* Die Kennzahl steht klein in der Kopfzeile, nicht mehr als Kachel
     ueber dem Urteil. */
  .k-pagehead .k-stat { padding: 0; border: 0; background: none;
    min-width: 0; display: flex; align-items: baseline; gap: var(--sp-1); }
  /* Die Kennzahl-Kachel setzt ihre Zahl ueber `[data-kind]` — der Kopf
     muss dieselbe Genauigkeit mitbringen, sonst gewinnt clamp(28px…). */
  .k-pagehead .k-stat[data-kind] .k-stat-value { font-size: 0.95rem; }
  .k-pagehead .k-stat { flex: 0 1 auto; overflow: hidden; }
}

/* ---- Desktop: zwei Spalten statt einer Handy-Saeule ----------------- */
@media (min-width: 900px) {
  .practice { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: 0 var(--sp-4); align-items: start; }
  .practice > .k-pagehead { grid-column: 1 / -1; }
  .practice > .item-card, .practice > .discrim-zone,
  .practice > .record-zone { grid-column: 1; }
  .practice > .result-panel { grid-column: 2; grid-row: 2 / span 3; }
  .practice > .k-thumbbar { grid-column: 1; }
  .practice > .k-thumbbar[data-state="ergebnis"] { grid-column: 2; }
}

/* ---- 34. Hinweiszeile (kHinweisZeile, Auftrag 102) -------------------
   Eine einzeilige Klappkarte statt eines immer offenen Absatzes — der
   Ausfall-Hinweis fraass auf 390 px eine ganze Bildschirmhoehe, obwohl er
   nur beim ersten Mal gelesen werden muss (Owner-Entscheid 07.09.2026).
   Kopf und Pfeil bleiben immer da; der Text klappt darunter auf.
   Dehnung: volle Breite des Behaelters; der Kopf ist eine Flexzeile, in
   der der Titel den Rest nimmt und mit Auslassungspunkten abschneidet,
   statt den Pfeil aus der Zeile zu draengen. */
.k-hinweiszeile { width: 100%; border: 1px solid var(--band-edge);
  background: var(--band-fill); border-radius: var(--r-3); overflow: hidden; }
.k-hinweiszeile-kopf { display: flex; align-items: center; gap: var(--sp-2);
  width: 100%; padding: var(--sp-2) var(--sp-3); background: none; border: 0;
  font: inherit; font-weight: 650; text-align: start; cursor: pointer;
  color: var(--ink); }
.k-hinweiszeile-icon { flex: 0 0 auto; }
.k-hinweiszeile-icon.k-icon-cell { width: 18px; height: 18px; }
.k-hinweiszeile-icon.k-icon-cell svg { width: 18px; height: 18px; }
.k-hinweiszeile-title { flex: 1 1 auto; min-width: 0; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; }
.k-hinweiszeile-arrow { flex: 0 0 auto; color: var(--muted); }
.k-hinweiszeile-body { margin: 0; padding: 0 var(--sp-3) var(--sp-3);
  color: var(--ink-2); font-size: var(--fs-2); }
.k-hinweiszeile-body[hidden] { display: none; }

/* =====================================================================
   UI-KIT — AUFTRAG 182 (Lernkarte-Bausteine, Q55b/Q55c, 13.09.2026)
   Fuenf neue Bausteine fuer den Lernen-Tab-Umbau (Auftrag 183 verdrahtet
   sie in reading.js). Alle Farben ueber Tokens, keine Hexwerte. */

/* ---- 39. Sprungleiste (kSprungleiste) --------------------------------
   Pillen mit Emoji+Text, surface-Flaeche, kraeftiger Rand. Eine inaktive
   Pille ist 60% deckend und traegt keinen Klick-Handler.
   Dehnung: `flex-wrap`, jede Pille behaelt ihre Eigenbreite; bei wenig
   Platz brechen die Pillen in mehrere Zeilen um. */
.lk-sprungleiste { display: flex; flex-wrap: wrap; gap: var(--sp-2); }
.lk-sprung { display: inline-flex; align-items: center; gap: var(--sp-1);
  background: var(--surface); border: 1px solid var(--border-strong);
  border-radius: var(--r-2); padding: var(--sp-1) var(--sp-3);
  font: inherit; font-size: var(--fs-2); color: var(--ink); cursor: pointer; }
.lk-sprung:hover:not(.is-inaktiv) { background: var(--brand-soft); border-color: var(--band-edge); }
.lk-sprung.is-inaktiv { opacity: 0.6; cursor: default; pointer-events: none; }
.lk-sprung-icon { font-size: 1rem; line-height: 1; }
.lk-sprung-label { white-space: nowrap; }

/* ---- 40. Lernkarte (kLernkarte) ---------------------------------------
   Zeichen gross links, Beispiel+Umschrift+Bedeutung in der Mitte,
   Wiedergabe rechts, Sprungleiste darunter.
   Dehnung: der Kopf ist eine Flexzeile (`flex-wrap`); die Mitte nimmt
   den Rest und bricht, unter 360px stapeln Zeichen/Mitte/Wiedergabe. */
.lk-lernkarte { display: flex; flex-direction: column; gap: var(--sp-3);
  padding: var(--sp-3); border: 1px solid var(--border); border-radius: var(--r-2);
  background: var(--surface); }
.lk-lk-kopf { display: flex; flex-wrap: nowrap; align-items: center; gap: var(--sp-3); }
.lk-lk-zeichen { display: flex; flex-direction: column; align-items: center;
  gap: 2px; flex: 0 0 auto; }
.lk-lk-thai { font-family: "Noto Sans Thai", "Thonburi", system-ui, sans-serif;
  font-size: 34px; line-height: 1; color: var(--ink); }
.lk-lk-laut { font-size: 11px; color: var(--muted); }
.lk-lk-mitte { display: flex; flex-direction: column; gap: 2px;
  min-width: 0; flex: 1 1 0; }
.lk-lk-beispiel { font-family: "Noto Sans Thai", "Thonburi", system-ui, sans-serif;
  font-size: 22px; color: var(--ink); }
.lk-lk-translit { font-size: 15px; font-weight: 600; color: var(--ink-2); }
.lk-lk-bedeutung { font-size: 13px; color: var(--ink-2); }
/* Auftrag 189b (Q55e-Nachzug, Brief 189 §5): "?"-Chip neben der Umschrift,
   springt ins Regelheft Abschnitt 0. */
.lk-lk-translit-row { display: flex; align-items: center; gap: 6px; }
.lk-translit-help { flex: none; width: 18px; height: 18px; line-height: 16px; padding: 0;
  border: 1px solid var(--border); border-radius: 999px; background: var(--surface);
  color: var(--ink-2); font-size: 11px; font-weight: 600; cursor: pointer; }
.lk-translit-help:hover, .lk-translit-help:focus-visible { border-color: var(--brand); color: var(--brand); }

/* ---- 41. Stellungskarte (kStellungskarte) -----------------------------
   Vokal-Stellungszeile als eigene Karte: Thai (Vokalzeichen brand-
   eingefaerbt) + Umschrift + Position links, Wiedergabe + Sprung
   Nachzeichnen rechts.
   Dehnung: zweispaltig (`justify-content: space-between`), die rechte
   Spalte stapelt unter 360px unter die linke. */
.lk-stellungskarte { display: flex; flex-wrap: wrap; justify-content: space-between;
  align-items: center; gap: var(--sp-3); padding: var(--sp-3);
  border: 1px solid var(--border); border-radius: var(--r-2); background: var(--surface); }
.lk-stk-links { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.lk-stk-thai { font-family: "Noto Sans Thai", "Thonburi", system-ui, sans-serif;
  font-size: 22px; color: var(--ink); }
.lk-stk-vz { color: var(--brand); font-weight: 700; }
.lk-stk-translit { font-size: 14px; font-weight: 600; color: var(--ink-2); }
.lk-stk-position { font-size: var(--fs-1); color: var(--muted); }
.lk-stk-rechts { display: flex; align-items: center; gap: var(--sp-2); flex: 0 0 auto; }

/* ---- 42. Hoerreihe (kHoerreihe) ---------------------------------------
   Eine Zeile je Ton: Tonbalken (Farbtoken `--tone-*`, wie kToneChip) +
   Thai + Umschrift + Tonname + Bedeutung + Wiedergabe. Der Tonname
   steht immer als Text daneben — der Balken traegt die Information nie
   allein.
   Dehnung: jede Zeile eine Flexreihe, die Mittelspalte nimmt den Rest
   und bricht bei knappem Platz um. */
.lk-hoerreihe { display: flex; flex-direction: column; gap: var(--sp-2); width: 100%; }
.lk-hr-row { display: flex; align-items: center; gap: var(--sp-2);
  padding: var(--sp-1) 0; border-bottom: 1px solid var(--border); }
.lk-hr-row:last-child { border-bottom: none; }
.lk-hr-balken { flex: 0 0 auto; width: 6px; height: 32px; border-radius: var(--r-1);
  background: var(--k-tone, var(--tone-mid)); }
.lk-hr-balken-mid { --k-tone: var(--tone-mid); }
.lk-hr-balken-low { --k-tone: var(--tone-low); }
.lk-hr-balken-falling { --k-tone: var(--tone-falling); }
.lk-hr-balken-high { --k-tone: var(--tone-high); }
.lk-hr-balken-rising { --k-tone: var(--tone-rising); }
.lk-hr-mitte { display: flex; flex-wrap: wrap; align-items: baseline;
  gap: var(--sp-1) var(--sp-3); min-width: 0; flex: 1 1 160px; }
.lk-hr-thai { font-family: "Noto Sans Thai", "Thonburi", system-ui, sans-serif;
  font-size: 22px; color: var(--ink); }
.lk-hr-translit { font-size: 14px; font-weight: 600; color: var(--ink-2); }
.lk-hr-info { display: flex; flex-wrap: wrap; gap: var(--sp-1) var(--sp-2);
  font-size: var(--fs-2); color: var(--ink-2); }
.lk-hr-ton { font-weight: 600; }

/* ---- 43. Kapitelende (kKapitelende) -----------------------------------
   Abrufknopf am Kapitelende (E11): brand-soft-Kasten, Titel, Text,
   Primaerknopf ueber die volle Breite.
   Dehnung: volle Breite des Behaelters, der Knopf ist `.k-btn-block`. */
.lk-kapitelende { display: flex; flex-direction: column; gap: var(--sp-2);
  padding: var(--sp-4); border-radius: var(--r-2); background: var(--brand-soft); }
.lk-ke-titel { font-size: var(--fs-4); font-weight: 650; color: var(--ink); }
.lk-ke-text { margin: 0; font-size: var(--fs-2); color: var(--ink-2); }

/* ---- kPlayback-Rundvariante (36/28px, brand-soft) --------------------
   Fuer Lernkarte/Stellungskarte/Hoerreihe: ein reiner Wiedergabe-Kreis
   ohne sichtbaren Text (der Text bleibt fuer Screenreader als
   aria-label/versteckte Beschriftung erhalten).
   Dehnung: feste Groesse ueber `--k-playback-size` (Vorgabe 36px), waechst
   nicht mit dem Text. */
.k-playback-round { position: relative; width: var(--k-playback-size, 36px);
  height: var(--k-playback-size, 36px); padding: 0; border-radius: var(--r-pill);
  background: var(--brand-soft); border-color: var(--brand-soft); display: inline-flex;
  align-items: center; justify-content: center; flex: 0 0 auto; }
.k-playback-round .k-playback-icon { width: 60%; height: 60%; }
.k-playback-round .k-playback-label { position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0);
  white-space: nowrap; border: 0; }

/* ---- Kapitel-Uebung (reading_kapitel.js, Auftrag 182 P3) -------------
   Eigene, schlanke Ansicht in einem kModal statt eines zweiten Uebungs-
   geruests neben #tab-practice. Nutzt kChip/kBtn fuer die Antworten. */
.lk-kap-body { display: flex; flex-direction: column; gap: var(--sp-3); }
.lk-kap-fortschritt { font-size: var(--fs-1); color: var(--muted); }
.lk-kap-prompt { display: flex; flex-direction: column; align-items: center;
  gap: var(--sp-1); padding: var(--sp-4) 0; }
.lk-kap-thai { font-family: "Noto Sans Thai", "Thonburi", system-ui, sans-serif;
  font-size: 2.2rem; color: var(--ink); }
.lk-kap-sound { font-size: 1.6rem; color: var(--ink); }
.lk-kap-sub { font-size: var(--fs-2); color: var(--muted); }
.lk-kap-optionen { display: flex; flex-wrap: wrap; gap: var(--sp-2); justify-content: center; }
.lk-kap-feedback { display: flex; flex-direction: column; gap: var(--sp-2); align-items: flex-start; }
.lk-kap-ok { color: var(--good-text); font-weight: 650; margin: 0; }
.lk-kap-bad { color: var(--critical); font-weight: 650; margin: 0; }
.lk-kap-explain { margin: 0; font-size: var(--fs-2); color: var(--ink-2); }

/* Auftrag 183 (Q55b/Q55c Teil 2): Lernkarten-Listen im Lernen-Tab (Buchstaben/
   Vokale/Buildup/Beispiele/Stellungszeile/Hoerreihe) — derselbe senkrechte
   Abstand, egal welcher Baustein darin steht. */
.lk-liste { display: flex; flex-direction: column; gap: var(--sp-3); margin: 4px 0 8px; }

/* Antippbares Thai im Fliesstext (Befund B2): kein eigener Knopf-Rahmen,
   nur eine gepunktete Unterstreichung in Markenfarbe — der Text bleibt
   Lesefluss, keine Schaltflaechenreihe. */
.lk-thai { border: 0; background: transparent; padding: 0; margin: 0; cursor: pointer;
  color: inherit; font: inherit; border-bottom: 1px dotted var(--brand);
  font-family: "Noto Sans Thai", "Sathu", "Thonburi", system-ui, sans-serif; }
.lk-thai:hover, .lk-thai:focus-visible { color: var(--brand); }

/* Popover-Inhalt (kSheet) fuer einen angetippten Thai-Lauf. */
.lk-pop-translit { font-size: 1.1rem; font-weight: 600; color: var(--ink); margin: 0 0 4px; }
.lk-pop-gloss { color: var(--ink-2); margin: 0; }

/* Auftrag 238: der zentrale Melden-Knopf, Schritt 2 (Figma B2) -- die
   Kontextkarte (Wo/Clip/Geraet) und der Diktierknopf neben dem
   Kommentarfeld. Beide bestehen nur aus vorhandenen Tokens (`.hint` traegt
   die Zeilen selbst, hier steht nur der Rahmen darum). */
.report-context { display: flex; flex-direction: column; gap: var(--sp-1);
  background: var(--grid); border-radius: var(--r-2); padding: var(--sp-3); }
.report-context p.hint { margin: 0; }
.report-context strong { color: var(--ink); }
.report-dictate { margin-top: var(--sp-1); align-self: flex-start; }
/* Auftrag 271c: Diktier-Symbol als Linien-SVG statt Emoji. */
.report-dictate svg { width: 1.1em; height: 1.1em; display: block; }

/* ---------- RTL-Grundlage (Auftrag 271, Owner-Entscheid 4) ---------------
   Aktivierung ueber document.documentElement.dir = "rtl" (app.js liest die
   Oberflaechensprache); zum Testen ohne RTL-Sprache im Produkt ein
   Dev-Hook `?dir=rtl` (webapp/js/devhooks.js). Die 118 physischen
   left/right-Angaben oben sind auf logische Eigenschaften umgestellt
   (margin-inline-*, padding-inline-*, inset-inline-*, border-inline-*,
   border-*-start/-end-radius, text-align: start/end); sie spiegeln damit
   automatisch mit `dir`. Zwei Hilfsklassen fuer den Rest:
     .k-spiegelt        Icon/Inhalt soll unter dir="rtl" gespiegelt werden
                         (Pfeile, Chevrons, Zurueck-Symbol).
     .k-spiegelt-nicht   Inhalt bleibt IMMER physisch, auch unter dir="rtl"
                         (Play-Dreieck, Fortschrittsbalken, Ton-/Kontur-
                         kurven, Waveform, Spotlight-Ueberlagerung) -- macht
                         die Ausnahme aus der Vorlage ("RTL spiegelt
                         Spalten, nicht Datenvisualisierungen") im Markup
                         explizit. */
[dir="rtl"] .k-spiegelt { transform: scaleX(-1); }
[dir="rtl"] .k-spiegelt-nicht { transform: none !important; }

/* ---------- Symbol-Zelle (Auftrag 271c) ----------------------------------
   `kIconOrText` haengt diese Klasse an, sobald `icon:` einen Namen aus
   K_ICON_NAMES traegt: eine eigene 32x32-px-Zelle fuer das Linien-Symbol
   (Handoff V7.7), unabhaengig von der umgebenden Schriftgroesse -- und
   unabhaengig von der Leserichtung, denn eine Symbol-Zelle mit fester
   Bedeutung (Schloss, Glocke, Papierkorb …) ist keine Navigationsrichtung,
   die unter RTL spiegeln sollte (.k-spiegelt-nicht gleich mit). */
.k-icon-cell { display: inline-flex; align-items: center; justify-content: center;
  width: 32px; height: 32px; flex: none; }
.k-icon-cell svg { width: 22px; height: 22px; display: block; }
[dir="rtl"] .k-icon-cell { transform: none !important; }
