/* DE: Reset, Basistypografie und das Seitengeruest (header/main/footer, Skip-Link, Fusszeile).
   EN: Reset, base typography and the page chrome (header/main/footer, skip link, footer). */

/* ============================ reset / base ============================ */
*,*::before,*::after{box-sizing:border-box}
body,h1,h2,h3,p,ul,ol,figure{margin:0}
ul{padding:0}
img,video{max-inline-size:100%;display:block}

body{
  margin:0;font-family:var(--font-ui);color:var(--text);background:var(--bg);
  font-size:var(--step-0);line-height:1.55;-webkit-font-smoothing:antialiased;
  min-block-size:100dvh;display:flex;flex-direction:column;
  background-image:radial-gradient(120% 80% at 100% 0,color-mix(in oklab,var(--brand) 9%,transparent),transparent 60%);
  background-attachment:fixed;
}
h1,h2,h3{font-family:var(--font-display);line-height:1.13;letter-spacing:-.02em;font-weight:800}
a{color:var(--link);text-underline-offset:.18em;text-decoration-thickness:.08em}
a:hover{text-decoration-thickness:.12em}
:focus-visible{outline:var(--ring);outline-offset:2px;border-radius:.2rem}
/* DE: Untergrenze für Auswahlfelder — 24×24 px, WCAG 2.2 AA 2.5.8, auch mit der Maus. Die Ausnahme für
   ungestylte Bedienelemente gäbe es, aber „Wörter in meiner Sprache“ und „Schulschrift“ bedienen Kinder.
   EN: Floor for selects — 24×24px, WCAG 2.2 AA 2.5.8, mouse included. The unstyled-control exemption would
   apply; these are children's controls. */
select{min-block-size:24px;min-inline-size:24px}
@media (prefers-reduced-motion:reduce){*{transition:none!important;animation:none!important;scroll-behavior:auto!important}}
/* Honour an explicit request for more contrast: darken borders + muted text, thicken underlines. */
@media (prefers-contrast:more){
  :root,:root[data-theme="light"]{--border:color-mix(in oklab,var(--c-ink-900) 60%,transparent);--text-muted:var(--c-ink-700)}
  :root[data-theme="dark"]{--border:oklch(.7 .02 110);--text-muted:oklch(.88 .012 110)}
  a{text-decoration-thickness:.12em}
  .badge,.language-card,.feature-card,.search-result,.concept-sibling,.didactics-panel{border-color:var(--text)}
}

/* Skip link: off-screen until focused (keyboard users reach <main> first). */
.skip-link{position:absolute;inset-inline-start:-9999px;inset-block-start:0;z-index:1000;
  padding:.6rem 1rem;background:var(--brand-strong);color:var(--text-on-brand);
  border-radius:0 0 var(--radius-md) 0;font-weight:700}
.skip-link:focus{inset-inline-start:0}

/* Visually-hidden ("vh"): kept in the accessibility tree for screen readers but off-screen visually.
   Shared utility — belongs in base so every surface that loads base has it. The SyllableArcsComponent
   emits a `.vh` copy of the plain word next to the aria-hidden arcs (so the arc glyphs are never read
   aloud); the LTI picker uses it for a hidden field label. Both render on the LTI surface, which loads
   base but not word-page.css/print-cards.css, where this used to live only. */
.vh{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}

/* ============================ layout chrome ============================ */
/* centre content of the real <header>/<main>/<footer> without markup changes */
/* DE: EINE Spaltenbreite für Kopf, Inhalt und Fuß — nur deshalb steht der Seiteninhalt bündig unter der
   Kopfzeile. Wer die Breite ändern will, muss sie hier ändern, nicht an `main` allein; sonst ragt der
   Inhalt über die Kante der Kopfzeile hinaus. Deshalb eine Variable statt einer Zahl. */
body{--page-max:72rem}
body > header > *, body > main, body > footer > *{
  max-inline-size:var(--page-max);margin-inline:auto;padding-inline:var(--sp-lg);inline-size:100%}

/* DE: Arbeitsflächen bekommen eine breitere Spalte (Owner-Entscheid 2026-08-25). 72rem ist eine LESEbreite:
   richtig für eine Wortseite, die man liest, zu eng für eine Fläche, auf der man arbeitet — dort stehen
   Tabellen, Prüfspuren und Aufnahme-Karten nebeneinander und hatten links und rechts Platz übrig.
   Der Hebel ist `body.editorial`, gesetzt vom Redaktions- UND vom Lehrkraft-Layout: beides angemeldete
   Arbeitsflächen. Kindseitige Klassenraum-Ansichten hängen am `ApplicationController` und damit am
   öffentlichen Layout — sie behalten die Lesebreite. Zugesichert in
   test/integration/working_column_scope_test.rb, weil dieselbe Body-Klasse in zwei Layouts steht und eine
   Verwechslung hier leise wäre.
   Fließtext bleibt schmal: `main p` kappt ihn unabhängig davon auf `--measure`. Die Spalte wächst also für
   das, was von Breite profitiert, nicht für Lesetext.
   EN: Working surfaces get a wider column (owner call 2026-08-25). 72rem is a READING width — right for a
   word page, too tight for a surface you work on. The lever is `body.editorial`, set by the editorial AND
   the teacher layout; child-facing classroom views hang off ApplicationController and keep the reading
   width. Prose stays narrow regardless: `main p` caps it at `--measure`.
   Auch die Kopfleiste ist hier höher (zehn Navigationspunkte statt sieben, sie bricht um: 89 statt 65 px),
   deshalb ein eigener `--header-offset`. The header is taller here too, hence its own offset. */
body.editorial{--page-max:90rem;--header-offset:6rem}

/* DE: Platz, den die klebende Kopfleiste oben einnimmt. Wer selbst etwas kleben lässt, rechnet ihn ein —
   sonst rutscht es darunter. Herleitung, Messwerte und Grenzen: test/assets/sticky_offset_css_test.rb.
   EN: Room the sticky header takes at the top; anything else that sticks must account for it or slide
   underneath. Derivation, measurements and limits: test/assets/sticky_offset_css_test.rb. */
:root{--header-offset:5rem}

body > header{position:sticky;inset-block-start:0;z-index:200;
  background:color-mix(in oklab,var(--bg) 84%,transparent);
  backdrop-filter:saturate(1.2) blur(8px);border-block-end:1px solid var(--border)}
body > header nav{display:flex;align-items:center;gap:var(--sp-md);min-block-size:4rem;flex-wrap:wrap}
body > header nav a{color:var(--text-muted);text-decoration:none;font-weight:600}
body > header nav a:hover{color:var(--brand)}
body > header nav a.brand{font-size:var(--step-1);font-weight:800;color:var(--text);
  letter-spacing:-.01em;margin-inline-end:auto}

body > main{flex:1;padding-block:var(--sp-2xl) var(--sp-3xl)}
body > footer{border-block-start:1px solid var(--border);color:var(--text-muted);
  font-size:var(--step--1);padding-block:var(--sp-lg)}
/* footer (adapted from wort.schule): tagline, links, CC0 licence, partners — child-safe, no tracking.
   Each row stays inside the same centred 72rem column as the header (body > footer > * handles that —
   so DON'T override margin-inline here). */
body > footer{display:flex;flex-direction:column;gap:var(--sp-sm);align-items:center;text-align:center}
body > footer p{margin-block:0}
body > footer a{color:var(--text-muted);text-decoration:underline;text-underline-offset:2px}
body > footer a:hover{color:var(--brand)}
.footer__tagline{font-weight:600;color:var(--text)}
.footer__links{display:flex;flex-wrap:wrap;gap:var(--sp-2xs) var(--sp-md);align-items:baseline;justify-content:center}
/* CC0 as a small pill at the end of the centred link row (wort.schule shows CC badges there) */
.footer__cc{text-decoration:none;font-size:var(--step--2);font-weight:600;letter-spacing:.02em;
  border:1px solid var(--border);border-radius:999px;padding:.1rem .55rem}
.footer__cc:hover{border-color:var(--brand);color:var(--brand)}
/* the partner row sits CENTRED on an always-light shelf so the logos read in BOTH themes (wort.schule's
   footer is light-only); the shelf never follows the dark theme */
.footer__partners{display:flex;flex-wrap:wrap;align-items:center;justify-content:center;gap:var(--sp-sm) var(--sp-md);
  background:#fbfaf7;border:1px solid rgba(0,0,0,.08);border-radius:12px;padding:var(--sp-sm) var(--sp-md)}
/* quiet build/deploy stamp at the very bottom — informational, not a link (child-safe, Hard Rule 9) */
.footer__build{color:var(--text-muted);font-size:var(--step--2);margin-block-start:var(--sp-sm);
  font-variant-numeric:tabular-nums}
/* Partner logos — the wort.schule treatment: greyscale + reduced opacity by default (a quiet credit),
   full brand colour + opacity on hover/focus. NOT brightness(0): that blackens the dense/filled logos
   (the bold red Aktion-Mensch mark, the Humboldt seal) into unrecognisable blobs; only clean line-marks
   survive it. The logos sit on the light shelf above, so they read in both themes. */
.footer__partner{display:inline-flex;align-items:center}
.footer__partner-logo{height:2.6rem;width:auto;filter:grayscale(1);opacity:.55;
  transition:filter var(--dur) var(--ease),opacity var(--dur) var(--ease)}
.footer__partner:hover .footer__partner-logo,
.footer__partner:focus-visible .footer__partner-logo{filter:none;opacity:1}

/* sectioning + prose rhythm */
main h1{font-size:var(--step-3);margin-block-end:var(--sp-sm)}
main section{margin-block-start:var(--sp-2xl)}
main section > h2{font-size:var(--step-1);margin-block-end:var(--sp-md);
  display:flex;align-items:baseline;gap:var(--sp-sm)}
main section > h2::after{content:"";flex:1;block-size:1px;background:var(--border)}
main p{max-inline-size:var(--measure)}
/* imprint (legal page): semantic <address> without the browser italic. Rhythm lives in imprint.css. */
.impressum address{font-style:normal;margin-block:.1rem 0}
.impressum h3{font-size:var(--step-0);margin-block:var(--sp-md) .2rem}
.impressum__funders,.imprint-contributors__list{margin-block:.4rem 0;padding-inline-start:var(--sp-lg)}

/* --- WS4 U-1: jede Tabelle scrollt in ihrem eigenen Container statt die Seite horizontal zu schieben
   (Konjugation/Deklination auf der Kinder-Wortseite, Redaktions-Listen). Mobile-first: die Seite selbst
   darf nie horizontal scrollen. --- */
.table-scroll {
  overflow-x: auto;
  max-inline-size: 100%;
  -webkit-overflow-scrolling: touch;
}
.table-scroll > table { min-inline-size: max-content; }

/* --- WS4 U-5: Touch-Ziele. Der 44px-Standard existiert bereits (Suggestion-Listbox) — hier wird er auf
   die übrigen kindgerichteten Controls angewandt, nur für grobe Zeiger (Touch), Desktop bleibt kompakt.

   DE: VERDOPPELTE Klassennamen — dasselbe Muster wie `[hidden][hidden]` unten, und hier war der Schaden schon
   eingetreten: `search.css` (`.chip-choice`) und `grammar.css` (`.word-variety__option`) setzen dieselbe
   Eigenschaft bei GLEICHER Spezifität, laden aber später und gewannen. Eine Medienabfrage wiegt nichts. Auf
   einem Tablet waren die Chips damit 24 statt 44 Pixel hoch — für ein Kind, das danebentippt, dasselbe wie
   ein kaputter Knopf. Die Verdopplung hebt jeden Namen auf (0,2,0) und schlägt jede Ein-Klassen-Regel
   unabhängig von der Ladereihenfolge, ohne `!important`. Wer ein GRÖSSERES Ziel will, schreibt spezifischer:
   der Wächter verlangt „mindestens so groß" und rechnet den Gewinner über alle Blätter aus. Ausführlich in
   `test/assets/touch_target_css_test.rb`.
   EN: DOUBLED class names — the `[hidden][hidden]` pattern below; here the damage had already happened.
   `search.css` and `grammar.css` set the same property at the SAME specificity but load later and won (a media
   query adds no weight), so on a tablet the chips were 24px instead of 44 — to a child tapping beside them,
   the same as a broken button. Doubling raises each name to (0,2,0), beating any single-class rule regardless
   of load order, without `!important`. Wanting a LARGER target means writing more specifically: the guard
   demands "at least this big" and computes the winner across all sheets. Detail in the guard. --- */
@media (pointer: coarse) {
  /* DE: Der Boden für ALLES, was ein Finger bedient — hergeleitet, nicht aufgezählt: Vergessen schützt jetzt,
     statt zu entblößen. Verweise im Fließtext nimmt WCAG 2.5.8 selbst aus.
     EN: The floor for EVERYTHING a finger operates — derived, not listed; forgetting now protects.
     `.vh` bleibt außen vor: `min-*` schlägt `width`, sonst wird aus 1×1 ein 44×44-Kasten.
     EN: `.vh` stays out — `min-*` beats `width`, which would turn 1×1 into a 44×44 box.
     Herleitung, Messung, die 48 Ziele: test/system/touch_target_journeys_test.rb */
  button:not(.vh), select:not(.vh), summary, [role="button"], label:has(> input),
  input:not([type="hidden"]):not(.vh) {
    min-block-size: var(--touch-target);
    min-inline-size: var(--touch-target);
  }
  a[href]:not(.vh):not(:where(p, li, dd, blockquote, figcaption) a) {
    min-block-size: var(--touch-target);
    min-inline-size: var(--touch-target);
    display: inline-flex;
    align-items: center;
  }
  /* DE/EN: Die Kopfleiste zahlt mit dem Zeilenabstand, nicht mit klebender Höhe (132 statt 165 px). */
  body > header nav { row-gap: 0 }
  body > header nav a { justify-content: center }
}


/* DE: `hidden` muss überall wirken — es ist das Scharnier der progressiven Verbesserung. Knöpfe, die nur mit
   JavaScript etwas tun (Drucken, Vorlesen, Merken), stehen als `hidden` im Markup; erst der Controller zeigt
   sie. Eine Klasse, die `display` setzt (`.btn { display: inline-flex }`), hat aber dieselbe Spezifität wie das
   `[hidden]` des Browsers — und weil dieses Stylesheet ZUERST geladen wird, gewinnt die Klasse. Ohne JavaScript
   stand damit ein sichtbarer Knopf da, der nichts tut.

   Bisher wurde das je Fläche nachgezogen (`.trainer [hidden]`, `.practice [hidden]`, …). Das ist eine Liste,
   die jede neue Fläche vergisst — auf Erzählwürfel, geteilter Liste, Wortkarten und Wortseite war die Regel
   nicht da, gemessen in BRW-HIDDEN-1..3. Der doppelte Selektor hebt die Spezifität auf (0,2,0) und schlägt
   damit jede Ein-Klassen-Regel, unabhängig von der Ladereihenfolge — ohne `!important`, das sich nicht mehr
   zurücknehmen ließe.
   EN: `hidden` must work everywhere — it is the hinge of progressive enhancement. Buttons that only act with
   JavaScript sit in the markup as `hidden` and their controller reveals them. But a class setting `display`
   has the SAME specificity as the browser's `[hidden]`, and since this stylesheet loads FIRST, the class wins:
   without JavaScript a visible, dead button was on show. This was previously patched per surface — a list every
   new surface forgets. NACHTRAG: die Verdopplung (0,2,0) verlor gegen `:has()`, das die Spezifität seines
   Arguments übernimmt — daher `!important`; `hidden` BEDEUTET „nicht dargestellt".
   Warum und gehalten von: test/assets/hidden_stays_hidden_test.rb. */
[hidden][hidden] { display: none !important; }

/* U-6 · Server-Löschbestätigung: ruhige Karte, roter Bestätigungs-Knopf (Status nie nur Farbe — der
   Klartext-Titel trägt die Bedeutung). */
.confirm-destroy { max-inline-size: var(--measure); background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius-lg); padding: var(--sp-xl); margin-block: var(--sp-xl); }
.confirm-destroy__body { color: var(--text-muted); }
.confirm-destroy__actions { display: flex; flex-wrap: wrap; gap: var(--sp-sm); margin-block-start: var(--sp-lg); }
.confirm-destroy__actions form { display: inline; }
.btn--danger { background: var(--danger); border-color: var(--danger); color: var(--text-on-brand); }
.btn--danger:hover { filter: brightness(0.92); }

/* AI-image provenance (HR11): the ✦ marker overlaid on an AI-generated picture, on EVERY public surface (word
   page, search, gallery, word-of-the-day, trainer, pupil list, kept card). `ai_wrap` (and the JS) add the
   `.ai-thumb` wrapper ONLY around a generated image — a human/absent picture keeps its bare <img>, so its DOM
   and layout are exactly as before. */
.ai-thumb { position: relative; display: inline-block; line-height: 0; }
/* Fixed-size square tiles size the image to 100% of the tile — so the wrapper must FILL the tile, or
   `img{inline-size:100%}` re-resolves against the shrink-wrapped wrapper and the picture renders at intrinsic size. */
.result-card__thumb > .ai-thumb,
.student-card__thumb .ai-thumb,
.practice__thumb .ai-thumb { display: block; inline-size: 100%; block-size: 100%; }
/* No pointer-events:none — the ✦ must be hit-tested so its `title` (the „KI-erzeugt" explanation) appears on
   hover; the marker sits inside the card's link, so a click still follows through to the word page. */
.ai-mark { position: absolute; inset-block-start: .3rem; inset-inline-start: .3rem; display: inline-flex;
  align-items: center; gap: .25ch; padding: .1rem .45rem; border-radius: 999px; line-height: 1.25;
  background: oklch(0.28 0.02 152 / .85); color: #fff; font-size: .72rem; font-weight: 800; }

/* DE: Was gedruckt wird, ist der Inhalt — nie das Haus drumherum. Auf Papier haben Navigation, Fußzeile,
   Förderlogos, Tab-Leiste, Klassenbanner und Meldungen nichts verloren: sie kosten eine halbe Seite, und beim
   Schneidebogen oder den Stationskärtchen zerschneidet man sie auch noch mit.
   Das steht GLOBAL und nicht je Bogen, weil sonst jede neue Druckfläche es neu erfindet und eine es vergisst
   — genau so waren Bastelbogen und Stationskarten gebaut, bevor diese Regel da war. Der zweite `<header>`
   einer Seite gehört dem Inhalt, deshalb die Kindschafts-Auswahl `body > header`.
   EN: What prints is the content, never the house around it — nav, footer, funders, tab bar, class banner and
   flashes are screen furniture. Global rather than per sheet, or every new printable surface reinvents it and
   one forgets; `body > header` because a page's own second <header> belongs to the content. */
@media print {
  body > header,
  body > footer,
  body > .tabbar,
  body > .skip-link,
  .classroom-banner,
  .flash { display: none; }
  /* Der Inhalt beginnt oben am Papier, nicht dort, wo die Navigation stand. */
  main { padding-block: 0; margin-block: 0; }
}
