/* Redezeit: base, layout, components. Needs tokens.css loaded first.
   Direction "Pult": black on white, thick rules, Jost numerals, one yellow marker for "now",
   the clone hollow and you solid. No framework, no JS. Class contract: docs/UI-SPEC.md. */

/* ---------- Base ---------- */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; text-size-adjust: 100%; }
body {
  margin: 0;
  min-height: 100dvh;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font-ui);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  font-variant-numeric: tabular-nums lining-nums;
  -webkit-font-smoothing: antialiased;
  -webkit-tap-highlight-color: transparent;
  text-rendering: optimizeLegibility;
}
h1, h2, h3, p, ul, ol, dl, dd, figure { margin: 0; }
ul, ol { padding: 0; list-style: none; }
h1, h2, h3 {
  font-family: var(--font-num); font-weight: var(--fw-semibold);
  line-height: var(--lh-tight); letter-spacing: var(--tracking-display); text-wrap: balance;
}
p { text-wrap: pretty; max-width: var(--measure-body); }
a { color: var(--ink); text-decoration-thickness: 1.5px; text-underline-offset: 3px; }
a:hover { text-decoration-thickness: 2.5px; }
img, svg { display: block; max-width: 100%; }
button, input, select, textarea { font: inherit; color: inherit; }
button { cursor: pointer; }
button:disabled, [aria-disabled="true"] { cursor: not-allowed; }
/* .btn, .back-link a další třídy nastavují display; [hidden] musí vyhrát vždy */
[hidden] { display: none !important; }
::selection { background: var(--selection); color: var(--ink); }
/* focus: a black (white on black) ring with the yellow halo outside it */
:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; box-shadow: 0 0 0 7px var(--focus-halo); }
:focus:not(:focus-visible) { outline: none; }
/* router posílá focus na h1 (a chybový stav) po každé změně route: není to ovládací prvek, prstenec by
   po přihlášení klávesnicí visel kolem pozdravu. Screen reader dostane focus i tak. */
:where(h1, .error-title, .error-state, main)[tabindex="-1"]:focus { outline: none; box-shadow: none; }
html { scrollbar-color: var(--line-strong) transparent; scrollbar-width: thin; }
audio { width: 100%; }

.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.skip-link {
  position: absolute; left: var(--sp-3); top: -60px; z-index: 100;
  background: var(--block); color: var(--on-block); padding: var(--sp-2) var(--sp-4); font-weight: var(--fw-bold);
  min-height: var(--tap); display: inline-flex; align-items: center;
}
.skip-link:focus { top: var(--sp-3); }
.num { font-variant-numeric: tabular-nums lining-nums; white-space: nowrap; }
.muted { color: var(--ink-2); }
.faint { color: var(--ink-3); }
.nowrap { white-space: nowrap; }
.icon { width: 22px; height: 22px; flex: none; fill: none; stroke: currentColor; stroke-width: 1.75; stroke-linecap: round; stroke-linejoin: round; }
.icon-sm { width: 18px; height: 18px; }

/* ---------- App mark (wordmark): a black disc, the clone as a hollow bar, you as the yellow bar ---------- */
.mark {
  position: relative; flex: none; width: 26px; height: 26px; border-radius: 50%;
  background: var(--block); box-shadow: 0 0 0 1px var(--mark-ring);
}
.mark i { position: absolute; left: 6px; height: 3px; }
.mark i:first-child { top: 8px; width: 9px; box-shadow: inset 0 0 0 1px var(--on-block); }
.mark i:last-child { top: 14px; width: 14px; background: var(--mark); }

/* ---------- App shell ---------- */
.app-shell {
  min-height: 100dvh;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: auto 1fr;
  grid-template-areas: "header" "main";
}
.app-header {
  grid-area: header;
  display: flex; align-items: center; gap: var(--sp-3);
  min-height: 56px;
  padding: calc(env(safe-area-inset-top) + var(--sp-2)) var(--gutter) var(--sp-2);
}
.app-header > .tally-label { margin-left: auto; }
.wordmark {
  display: inline-flex; align-items: center; gap: 10px;
  font-family: var(--font-num); font-weight: var(--fw-semibold); font-size: var(--fs-subtitle); letter-spacing: .01em;
  color: var(--ink); text-decoration: none;
}
/* the 404 page of a share link (server/share.py) still uses the icon file */
.wordmark img, .share-brand img { width: 26px; height: 26px; border-radius: 50%; box-shadow: 0 0 0 1px var(--mark-ring); }
a.wordmark { min-height: var(--tap); }
.app-header .header-actions { margin-left: auto; margin-right: calc(-1 * var(--sp-3)); display: flex; gap: var(--sp-2); }
/* logout says what it does: a "×" next to the wordmark read as "close this screen" */
.header-logout { font-size: var(--fs-meta); font-weight: var(--fw-medium); color: var(--ink-2); }
@media (hover: hover) { .header-logout:hover { color: var(--ink); } }
.app-main {
  grid-area: main;
  width: 100%; max-width: var(--content-max); margin-inline: auto;
  padding: var(--sp-2) var(--gutter) calc(var(--nav-h) + env(safe-area-inset-bottom) + var(--sp-7));
}

/* Bottom tab bar on phones and tablets, left rail from 1024. The current item is highlighted like a marked
   word in the score: a yellow block behind the label. */
.nav {
  position: fixed; inset: auto 0 0 0; z-index: var(--z-nav);
  display: flex; justify-content: space-around;
  height: calc(var(--nav-h) + env(safe-area-inset-bottom));
  padding-bottom: env(safe-area-inset-bottom);
  background: var(--bg);
  border-top: var(--rule-2) solid var(--ink);
}
.nav-item {
  flex: 1 1 0; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px;
  min-height: var(--tap);
  color: var(--ink-2); text-decoration: none; font-size: var(--fs-small); font-weight: var(--fw-medium);
}
.nav-item .icon { width: 24px; height: 24px; }
.nav-item span { padding: 0 4px; line-height: 1.35; }
@media (hover: hover) { .nav-item:hover { color: var(--ink); } }
.nav-item[aria-current="page"] { color: var(--ink); font-weight: var(--fw-bold); }
.nav-item[aria-current="page"] .icon { stroke-width: 2.25; }
.nav-item[aria-current="page"] span { background: var(--mark); color: var(--on-mark); }
.nav .wordmark { display: none; }

/* Focus mode: recording hides navigation and header chrome */
.app-shell[data-mode="focus"] .nav,
.app-shell[data-mode="focus"] .app-header .header-actions { display: none; }
.app-shell[data-mode="focus"] .app-main { padding-bottom: var(--sp-4); }

/* ---------- Screen structure ---------- */
.screen { display: flex; flex-direction: column; gap: var(--sp-8); }
.screen-head { display: flex; flex-direction: column; gap: var(--sp-2); padding-top: var(--sp-3); }
.screen-title { font-size: var(--fs-h1); line-height: 1.05; }
.screen-sub { color: var(--ink-2); font-size: var(--fs-lead); }
.back-link {
  display: inline-flex; align-items: center; gap: var(--sp-1); align-self: flex-start;
  min-height: var(--tap); margin-left: -6px; padding-inline: 6px;
  color: var(--ink-2); text-decoration: none; font-weight: var(--fw-medium);
}
.back-link:hover { color: var(--ink); }
.section { display: flex; flex-direction: column; gap: var(--sp-4); }
/* every section opens with a staff line: a thick ink rule above the title */
.section-title {
  font-size: var(--fs-h2); padding-top: 10px; border-top: var(--rule) solid var(--ink);
}
.section-head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--sp-3); flex-wrap: wrap; }
.stack { display: flex; flex-direction: column; gap: var(--sp-3); }
.stack-lg { display: flex; flex-direction: column; gap: var(--sp-5); }
.row { display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap; }
.row-end { justify-content: flex-end; }
.grid-2 { display: grid; gap: var(--sp-5); grid-template-columns: minmax(0, 1fr); }
.meta { font-size: var(--fs-meta); color: var(--ink-3); }

/* ---------- Panels ---------- */
.card {
  background: var(--surface);
  padding: var(--sp-5);
  display: flex; flex-direction: column; gap: var(--sp-3);
}
.card-title { font-size: var(--fs-title); line-height: var(--lh-tight); }
/* the suggestion for today: a framed panel, its title marked like the current word */
.card[data-emphasis="today"] { background: var(--bg); box-shadow: inset 0 0 0 var(--rule) var(--ink); padding: var(--sp-5) var(--sp-5) var(--sp-6); }
.card[data-emphasis="today"] .card-title { align-self: flex-start; background: var(--mark); color: var(--on-mark); padding: 2px 6px; margin-left: -6px; }
.card-actions { display: flex; gap: var(--sp-2) var(--sp-3); flex-wrap: wrap; margin-top: var(--sp-1); }

/* ---------- Buttons ---------- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--sp-2);
  min-height: var(--tap); padding: 0 var(--sp-5);
  border: var(--rule-2) solid var(--ink); border-radius: var(--r-btn);
  background: transparent; color: var(--ink);
  font-weight: var(--fw-bold); font-size: var(--fs-body); line-height: 1.1;
  text-decoration: none; white-space: nowrap;
  transition: background-color var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out), transform var(--dur-fast) var(--ease-out);
}
.btn:active { transform: scale(.98); }
.btn-primary { background: var(--ink); color: var(--on-ink); }
.btn-quiet { border-color: transparent; padding-inline: var(--sp-3); }
/* hover jen tam, kde existuje kurzor: na iPhonu by po klepnutí zůstal viset šedý podklad */
@media (hover: hover) {
  .btn:hover { background: var(--surface-2); }
  .btn-primary:hover { background: var(--ink-2); border-color: var(--ink-2); }
  .btn-quiet:hover { background: var(--surface-2); }
}
.btn-wide { width: 100%; }
.btn-icon { width: var(--tap); padding: 0; border-color: transparent; border-radius: var(--r-pill); }
.btn:disabled, .btn[aria-disabled="true"] { opacity: .45; transform: none; }
.btn:disabled:hover { background: transparent; }
.btn-primary:disabled:hover { background: var(--ink); }
.btn[aria-busy="true"] { cursor: progress; color: transparent; position: relative; }
.btn[aria-busy="true"]::after {
  content: ""; position: absolute; width: 18px; height: 18px; border-radius: 50%;
  border: 2px solid var(--line-strong); border-top-color: var(--ink);
  animation: rz-spin .9s linear infinite;
}
.btn-primary[aria-busy="true"]::after { border-color: color-mix(in oklab, var(--on-ink) 40%, transparent); border-top-color: var(--on-ink); }

/* ---------- Pills: square labels; the clone dashed, attention inverted, live yellow ---------- */
.pill {
  display: inline-flex; align-items: center; gap: 6px;
  min-height: 26px; padding: 0 9px;
  background: transparent; color: var(--ink); box-shadow: inset 0 0 0 1.5px var(--ink);
  font-size: var(--fs-small); font-weight: var(--fw-bold); line-height: 1.2; white-space: nowrap;
}
.pill[data-tone="klon"] { box-shadow: none; outline: 1.5px dashed var(--klon); outline-offset: -1.5px; color: var(--ink-2); }
.pill[data-tone="fill"] { background: var(--ink); color: var(--on-ink); box-shadow: none; border-radius: var(--r-pill); }
.pill[data-tone="mis"] { background: var(--ink); color: var(--on-ink); box-shadow: none; }
.pill[data-tone="live"] { background: var(--mark); color: var(--on-mark); box-shadow: none; }
button.pill { border: 0; min-height: var(--tap); padding: 0 14px; }
button.pill[aria-pressed="true"] { background: var(--ink); color: var(--on-ink); }

/* ---------- Forms ---------- */
.field { display: flex; flex-direction: column; gap: 6px; }
.label { font-weight: var(--fw-bold); font-size: var(--fs-meta); color: var(--ink); }
.hint { font-size: var(--fs-meta); color: var(--ink-2); }
.input, .textarea, .select {
  width: 100%; min-height: var(--tap); padding: 10px 12px;
  background: var(--bg); color: var(--ink);
  border: var(--rule-2) solid var(--line-strong); border-radius: var(--r-input);
  transition: border-color var(--dur-fast) var(--ease-out);
}
.textarea { min-height: 180px; resize: vertical; font-family: var(--font-text); font-size: var(--fs-lead); line-height: 1.5; }
.input::placeholder, .textarea::placeholder { color: var(--ink-3); }
.input:hover, .textarea:hover, .select:hover { border-color: var(--ink-2); }
.input:focus-visible, .textarea:focus-visible, .select:focus-visible { border-color: var(--ink); outline-offset: 1px; }
/* invalid: a heavier ink frame and the sentence below with a yellow alert chip (no hue for errors) */
.field[data-invalid] .input, .field[data-invalid] .textarea, [aria-invalid="true"] { border-color: var(--ink); border-width: var(--rule); }
.error-text { font-size: var(--fs-meta); color: var(--ink); font-weight: var(--fw-bold); }
.error-text:not([hidden]) { display: flex; align-items: flex-start; gap: var(--sp-2); }
.error-text .icon { width: 22px; height: 22px; padding: 2px; margin-top: -1px; background: var(--mark); color: var(--on-mark); }
input[type="checkbox"], input[type="radio"] { accent-color: var(--ink); width: 20px; height: 20px; }
.check-row { min-height: var(--tap); cursor: pointer; }
/* Selects (Aussprache target, Begriff, Publikum, Variante) */
.select { appearance: none; -webkit-appearance: none; padding-right: 36px;
  background-image: linear-gradient(45deg, transparent 50%, var(--ink) 50%), linear-gradient(135deg, var(--ink) 50%, transparent 50%);
  background-position: calc(100% - 18px) 55%, calc(100% - 13px) 55%; background-size: 5px 5px; background-repeat: no-repeat; }

/* Segmented control (style choice, A/B switch, filters): a 2 px ink frame, the chosen one filled */
.segmented {
  display: inline-flex; padding: 3px; gap: 2px;
  background: transparent; box-shadow: inset 0 0 0 var(--rule-2) var(--ink);
  /* filtry s 5 až 7 volbami se na 375 px nevejdou: posouvat uvnitř, stránka nesmí přetéct */
  max-width: 100%; min-width: 0; overflow-x: auto; overscroll-behavior-x: contain;
  width: fit-content;   /* ve flex sloupci (.stack, .section) by se jinak roztáhl na celou šířku */
}
.segmented > * { flex: none; }
/* scrollable filter: fade the right edge while more options sit beyond it (util.js sets data-overflow/data-end) */
.segmented[data-overflow]:not([data-end]) {
  -webkit-mask-image: linear-gradient(90deg, #000 calc(100% - 32px), transparent);
  mask-image: linear-gradient(90deg, #000 calc(100% - 32px), transparent);
}
.segmented :focus-visible { outline-offset: -5px; box-shadow: none; }   /* overflow ořízne vnější prstenec, proto dovnitř */
.segmented button, .segmented label {
  min-height: var(--tap); padding: 0 14px; border: 0; border-radius: 0;
  background: transparent; color: var(--ink); font-weight: var(--fw-bold); font-size: var(--fs-meta);
  display: inline-flex; align-items: center; gap: 6px; cursor: pointer;
  transition: background-color var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}
@media (hover: hover) { .segmented button:not([aria-pressed="true"], [aria-checked="true"]):hover { background: var(--surface-2); } }
.segmented [aria-pressed="true"], .segmented [aria-checked="true"], .segmented input:checked + span {
  background: var(--ink); color: var(--on-ink);
}
.segmented input { position: absolute; opacity: 0; pointer-events: none; }

/* ---------- Lists (library, history, rounds) ---------- */
.list { display: flex; flex-direction: column; border-top: 1px solid var(--line); }
.list-item {
  display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; gap: 2px var(--sp-3);
  min-height: 64px; padding: var(--sp-3) var(--sp-1);
  border-bottom: 1px solid var(--line);
  color: inherit; text-decoration: none;
  transition: background-color var(--dur-fast) var(--ease-out);
}
@media (hover: hover) {
  a.list-item:hover, button.list-item:hover { background: var(--surface); }
}
a.list-item[aria-current="page"] { box-shadow: inset 0 0 0 var(--rule-2) var(--ink); }
button.list-item { width: 100%; border-inline: 0; border-top: 0; background: none; text-align: left; }
.list-title { font-weight: var(--fw-bold); font-size: var(--fs-lead); line-height: 1.3; }
.list-meta { font-size: var(--fs-meta); color: var(--ink-3); grid-column: 1; }
.list-end { grid-column: 2; grid-row: 1 / span 2; display: flex; align-items: center; gap: var(--sp-2); color: var(--ink-2); }
.list-score { font-family: var(--font-num); font-size: var(--fs-h2); font-weight: var(--fw-semibold); line-height: 1; color: var(--ink); }
/* a value outside its target (Publikum, Unterbrechung): inverted chip, the meta line says it in words */
.list-item[data-out] .list-score { background: var(--ink); color: var(--on-ink); padding: 2px 6px 4px; }

/* Drills (Übungen): nine parallel choices set as ruled entries, not as a card grid.
   Each entry opens with a 2 px ink rule; the drill icon sits in a hollow square like a sound glyph. */
.card:has(> .drill) { background: transparent; padding: var(--sp-3) 0 var(--sp-2); border-top: var(--rule-2) solid var(--ink); }
.drill { display: grid; grid-template-columns: 44px minmax(0, 1fr); gap: var(--sp-1) var(--sp-4); align-items: start; }
.drill > .icon {
  grid-row: 1 / span 2; width: 44px; height: 44px; padding: 9px; margin-top: 2px;
  box-shadow: inset 0 0 0 var(--rule-2) var(--ink); color: var(--ink); stroke-width: 1.9;
}
.drill-name { font-size: var(--fs-subtitle); align-self: center; }
.drill-desc { color: var(--ink-2); grid-column: 2; }
.drill-meta { grid-column: 2; font-size: var(--fs-meta); color: var(--ink-3); }
.drill .card-actions { grid-column: 2; margin-top: var(--sp-2); }
.drill .drill-field { grid-column: 2; margin-top: var(--sp-2); max-width: 20rem; }
.drill .drill-fields { grid-column: 2; align-items: flex-end; }
.drill .drill-fields .drill-field { grid-column: auto; margin-top: var(--sp-2); flex: 1 1 9rem; }

/* ---------- Reading text (the score on the desk) ---------- */
.reading-plate {
  border-top: var(--rule) solid var(--ink);
  padding: var(--sp-3) 0 0;
}
.reading-plate-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--sp-2) var(--sp-3); margin-bottom: 14px; }
.reading-title { font-family: var(--font-num); font-size: var(--fs-title); font-weight: var(--fw-semibold); }
.reading-plate-head .rec-hint { font-weight: var(--fw-bold); color: var(--ink); }
.reading-text {
  font-family: var(--font-text);
  font-size: var(--fs-read);
  line-height: var(--lh-read);
  letter-spacing: .005em;
  max-width: var(--measure-read);
  color: var(--ink);
  hyphens: manual;
  overflow-wrap: break-word;
}
.reading-text p, .reading-text .para { max-width: none; margin: 0 0 0.8em; }
.reading-text p:last-child { margin-bottom: 0; }
.reading-text .w {
  transition: background-color var(--dur-fast) linear, color var(--dur-fast) linear, box-shadow var(--dur-fast) linear;
}
/* during your take: passed words recede, the current word is marked like a highlighter, block wide */
.reading-text .w[data-state="spoken"] { color: var(--ink-3); }
.reading-text .w[data-state="current"] { background: var(--mark); color: var(--on-mark); box-shadow: 0 0 0 3px var(--mark); }
/* reference playback on the Text screen: the same yellow block follows the clone */
.reading-text .w[data-state="karaoke"] { background: var(--mark); color: var(--on-mark); box-shadow: 0 0 0 3px var(--mark); }
/* Shadowing (clone and you at once): the clone's word is drawn hollow, yours stays yellow */
.session .reading-text .w[data-state="karaoke"] { background: transparent; color: var(--ink); box-shadow: inset 0 0 0 2px var(--ink), 0 0 0 2px var(--bg); }
.reading-text .pause-mark {
  font-family: var(--font-ui); font-weight: var(--fw-bold); color: var(--klon);
  margin-inline: 0.2em; letter-spacing: -0.05em;
}
.reading-text .pause-mark[data-hit="yes"] { color: var(--ink); }
.reading-text .pause-mark[data-hit="no"] { background: var(--ink); color: var(--on-ink); padding: 0 .15em; }

/* Session layout: the text is the stage, the dock holds HUD + record button.
   Below 1024 the bottom padding lets the last sentence still scroll up into the upper third. */
.session { display: flex; flex-direction: column; gap: var(--sp-5); padding-bottom: 45vh; }
/* brief + plate: bez mezery by pilulky lepily na hranu desky (pod 1024 px) */
.session > .session-main { display: flex; flex-direction: column; gap: var(--sp-5); min-width: 0; }
.session-brief { display: flex; flex-direction: column; gap: var(--sp-2); }
.session-prompt { font-family: var(--font-text); font-size: var(--fs-read); line-height: 1.4; max-width: var(--measure-read); }
.session .reading-plate { scroll-margin-top: 30vh; }

/* ---------- Tally (the live marker) ---------- */
.tally {
  position: fixed; top: 0; left: 0; right: 0; z-index: var(--z-tally);
  height: calc(env(safe-area-inset-top) + 6px);
  background: var(--mark); box-shadow: 0 1px 0 var(--ink);
  transform: scaleX(0); transform-origin: 50% 50%;
  transition: transform var(--dur-slow) var(--ease-out);
  pointer-events: none;
}
.tally[data-on="true"] { transform: scaleX(1); }
.tally-label {
  display: inline-flex; align-items: center; gap: 8px;
  font-weight: var(--fw-bold); font-size: var(--fs-meta); color: var(--ink);
}
.tally-label::before { content: ""; width: 12px; height: 12px; border-radius: 50%; background: var(--mark); box-shadow: 0 0 0 1.5px var(--ink); }

/* ---------- Record dock, HUD ----------
   An inverted black block in both schemes (tokens.css remaps the neutrals inside). Base = the desktop Pult
   (positioned in the 1024 block). Below 1024 the same element becomes the sticky HUD bar at the top of the
   session (block "HUD bar, mobile" at the end of this file). */
.rec-dock {
  z-index: var(--z-dock);
  display: flex; flex-direction: column; align-items: center; gap: var(--sp-3);
}
/* three fixed cells so the record button never moves between Start and Stopp (discard left, label right) */
.rec-row { display: grid; grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr); align-items: center; gap: var(--sp-5); width: 100%; }
.rec-row > [data-action="discard"] { grid-column: 1; grid-row: 1; justify-self: end; }
.rec-row > .rec-button { grid-column: 2; grid-row: 1; }
.rec-row > .rec-label { grid-column: 3; grid-row: 1; justify-self: start; }
/* live label in the Pult: the header carries it on phones, from 1024 the header is gone */
.rec-dock > .tally-label { display: none; }
.rec-hint { font-size: var(--fs-meta); color: var(--ink-2); text-align: center; min-height: 1.4em; }

.hud {
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--sp-2);
  width: 100%; max-width: 560px;
}
.hud-meter {
  display: flex; flex-direction: column; gap: 4px; min-width: 0;
  padding: var(--sp-2);
}
.hud-label { font-size: var(--fs-micro); font-weight: var(--fw-bold); line-height: 1.1; color: var(--ink-2); white-space: nowrap; }
.hud-value {
  font-family: var(--font-num); font-size: var(--fs-hud); font-weight: var(--fw-semibold); line-height: 1; letter-spacing: -0.01em;
  font-variant-numeric: tabular-nums; color: var(--ink); white-space: nowrap;
}
.hud-unit { font-family: var(--font-ui); font-size: var(--fs-micro); font-weight: var(--fw-medium); color: var(--ink-2); letter-spacing: 0; margin-left: 3px; }
.hud-meter[data-idle] .hud-value { color: var(--ink-3); }
/* Ähm without live transcript: the word at HUD size overflows a quarter of the dock, so it is set small */
.hud-later {
  display: inline-block; max-width: 100%; overflow-wrap: anywhere; white-space: normal;
  font-family: var(--font-ui); font-size: var(--fs-meta); font-weight: var(--fw-bold); line-height: 1.2; letter-spacing: 0; color: var(--ink-2);
}
/* out of band: on black the value may turn yellow (the needle leaves the band as the shape cue) */
.hud-meter[data-out="slow"] .hud-value, .hud-meter[data-out="fast"] .hud-value { color: var(--mis); }

/* Tempo band scale: set --x (0..1 over 60..210 WPM) on .hud-band; band 120..150 is fixed at .4 to .6.
   A centre line with the target band shaded, the needle is the yellow marker. */
.hud-band {
  position: relative; height: 8px; margin-top: 3px;
  background:
    linear-gradient(90deg, transparent 40%, color-mix(in oklab, var(--ink) 30%, transparent) 40% 60%, transparent 60%),
    linear-gradient(var(--ink), var(--ink)) 0 50% / 100% 1px no-repeat;
}
.hud-needle {
  position: absolute; top: -3px; bottom: -3px; width: 4px;
  left: calc(clamp(0, var(--x, .5), 1) * 100% - 2px);
  background: var(--mark);
  transition: left 400ms var(--ease-out);
}
.hud-meter[data-idle] .hud-needle { display: none; }

/* Level meter: set --level (0..1, linear in dBFS from -60 to 0) on .hud-level; a framed bar, solid fill */
.hud-level {
  position: relative; height: 8px; margin-top: 3px; overflow: hidden;
  box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--ink) 45%, transparent);
}
.hud-level::before {
  content: ""; position: absolute; inset: 0;
  transform: scaleX(clamp(0, var(--level, 0), 1)); transform-origin: left center;
  background: var(--ink);
}
.hud-level::after { /* -12 dBFS and -3 dBFS reference ticks */
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(90deg, transparent calc(80% - 1px), var(--line-strong) calc(80% - 1px) 80%, transparent 80%, transparent calc(95% - 1px), var(--mis-mark) calc(95% - 1px) 95%, transparent 95%);
}
.hud-meter[data-clip] .hud-level::before { background: var(--mis-mark); }

/* Time: --t (0..1 of limit_s) draws a bar under the clock when a limit exists */
.hud-limit { height: 3px; background: var(--surface-2); overflow: hidden; margin-top: 4px; }
.hud-limit::before {
  content: ""; display: block; height: 100%; background: var(--ink-2);
  transform: scaleX(clamp(0, var(--t, 0), 1)); transform-origin: left center;
}

/* Record button (on black): idle = white ring with a white dot, recording = yellow disc with a black stop
   square, processing = ring sweep. Yellow replaces the red record light (Pult rationale 12). */
.rec-button {
  position: relative; display: grid; place-items: center; flex: none;
  width: var(--rec-size); height: var(--rec-size);
  border-radius: var(--r-pill);
  border: 3px solid var(--ink); background: transparent; color: var(--ink);
  transition: background-color var(--dur) var(--ease-out), border-color var(--dur) var(--ease-out), transform var(--dur-fast) var(--ease-out);
}
.rec-button::before { /* the core: dot when idle, square when recording */
  content: ""; width: 30px; height: 30px; border-radius: var(--r-pill);
  background: currentColor;
  transition: border-radius var(--dur) var(--ease-out), transform var(--dur) var(--ease-out), background-color var(--dur) var(--ease-out);
}
@media (hover: hover) { .rec-button:hover { background: var(--surface-2); } }
.rec-button:active { transform: scale(.96); }
.rec-button[data-state="recording"] { background: var(--mark); border-color: var(--mark); color: var(--on-mark); }
.rec-button[data-state="recording"]::before { transform: scale(.8); border-radius: var(--r-core); }
.rec-button[data-state="processing"] { border-color: var(--surface-2); color: var(--ink-3); cursor: progress; }
.rec-button[data-state="processing"]::before { transform: scale(.47); }
.rec-button[data-state="processing"]::after {
  content: ""; position: absolute; inset: -3px; border-radius: 50%;
  border: 3px solid transparent; border-top-color: var(--mark);
  animation: rz-spin 1.1s linear infinite;
}
.rec-button[data-state="disabled"], .rec-button:disabled { border-color: var(--line-strong); color: var(--ink-3); opacity: .6; }
.rec-button:focus-visible { outline-color: var(--focus); }
.rec-label { font-size: var(--fs-meta); font-weight: var(--fw-bold); color: var(--ink); }

/* ---------- Audio player and A/B ---------- */
.player { display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap; }
.player-btn {
  order: -1; width: 48px; height: 48px; border-radius: var(--r-pill); border: 0;
  background: var(--ink); color: var(--on-ink); display: grid; place-items: center; flex: none;
}
.player-btn .icon { width: 22px; height: 22px; }
/* the clone's player is drawn hollow */
.player-btn[data-voice="klon"] { background: transparent; color: var(--ink); box-shadow: inset 0 0 0 2.5px var(--ink); }
.player-time { font-family: var(--font-num); font-weight: var(--fw-medium); font-size: var(--fs-lead); color: var(--ink); min-width: 7ch; }
.player-scrub { order: 3; flex: 1 1 100%; min-height: var(--tap); margin: 0; }
/* range inputs (scrub, dev-viz): a 6 px track and a playhead needle, like the HUD */
input[type="range"] { accent-color: var(--ink); -webkit-appearance: none; appearance: none; background: transparent; min-height: var(--tap); cursor: pointer; }
input[type="range"]::-webkit-slider-runnable-track { height: 6px; background: var(--surface-2); }
input[type="range"]::-moz-range-track { height: 6px; background: var(--surface-2); }
input[type="range"]::-moz-range-progress { height: 6px; background: var(--ink); }
input[type="range"]::-webkit-slider-thumb { -webkit-appearance: none; width: 6px; height: 24px; margin-top: -9px; border: 0; border-radius: 0; background: var(--ink); }
input[type="range"]::-moz-range-thumb { width: 6px; height: 24px; border: 0; border-radius: 0; background: var(--ink); }
input[type="range"]:focus-visible { box-shadow: none; outline-offset: 0; }
.player > .segmented { order: 0; }
.player > .player-time { order: 1; }
.ab-switch [data-voice="klon"][aria-pressed="true"], .ab-switch [data-voice="klon"][aria-checked="true"] {
  background: transparent; color: var(--ink); box-shadow: inset 0 0 0 2px var(--ink); outline: 1.5px dashed var(--ink); outline-offset: -6px;
}

/* ---------- Scores ---------- */
/* the overall: a large Jost numeral over a 0 to 100 ruler, the running average as a hollow tick */
.overall {
  display: grid; grid-template-columns: auto minmax(0, 1fr);
  grid-template-areas: "v label" "v delta" "scale scale";
  column-gap: 14px; align-items: end;
}
.overall-value {
  grid-area: v; font-family: var(--font-num); font-weight: var(--fw-semibold);
  font-size: var(--fs-score); line-height: .78; letter-spacing: -.035em;
}
.overall-label { grid-area: label; font-family: var(--font-num); font-size: var(--fs-subtitle); font-weight: var(--fw-medium); color: var(--ink); }
.overall-delta { grid-area: delta; font-size: var(--fs-body); font-weight: var(--fw-medium); color: var(--ink-2); padding-bottom: 6px; }
/* no running average yet (first take): "von 100" sits on the baseline of the numeral */
.overall:not(:has(> .overall-delta)) { grid-template-areas: "v ." "v label" "scale scale"; }
.overall:not(:has(> .overall-delta)) .overall-label { padding-bottom: 4px; }
.overall-scale {
  grid-area: scale; display: block; position: relative; height: 22px; margin-top: 18px;
  background:
    linear-gradient(var(--ink), var(--ink)) 0 6px / calc(clamp(0, var(--v, 0), 100) * 1%) 8px no-repeat,
    linear-gradient(var(--line), var(--line)) 0 9px / 100% 2px no-repeat,
    repeating-linear-gradient(90deg, var(--line-strong) 0 1px, transparent 1px calc(10% - .1px)) 0 100% / calc(100% + 1px) 6px no-repeat;
}
.overall-scale[data-avg]::after { /* the running average: a grey tick with a page-coloured edge */
  content: ""; position: absolute; top: 2px; left: calc(clamp(0, var(--avg, 0), 100) * 1% - 1px); width: 2px; height: 16px;
  background: var(--klon); box-shadow: 0 0 0 1.5px var(--bg);
}
/* inside a panel (Vergleich) the numeral steps down */
.card .overall-value { font-size: var(--fs-score-s); }

/* Messwerte: one shared 0 to 100 axis per row. Solid dot = this take, hollow dot = the last take of the
   same drill, the line between them is the change. */
.dims-wrap { display: flex; flex-direction: column; gap: 6px; }
.dims { display: flex; flex-direction: column; }
.dim {
  display: grid; align-items: center;
  grid-template-columns: minmax(0, 1fr) auto;
  grid-template-areas: "name score" "bar bar" "raw raw";
  gap: 6px var(--sp-3); padding: 10px 0 12px; border-bottom: 1px solid var(--line);
}
.dim-name { grid-area: name; font-weight: var(--fw-bold); font-size: var(--fs-body); display: inline-flex; align-items: center; gap: var(--sp-2); }
.dim-score { grid-area: score; text-align: right; font-family: var(--font-num); font-size: var(--fs-h2); font-weight: var(--fw-semibold); line-height: 1; }
.dim-raw { grid-area: raw; font-size: var(--fs-meta); color: var(--ink-2); }
.dim-bar {
  --p: var(--v);
  grid-area: bar; position: relative; height: 18px;
  background:
    linear-gradient(var(--line-strong), var(--line-strong)) 0 50% / 100% 1px no-repeat,
    linear-gradient(var(--line-strong), var(--line-strong)) 100% 0 / 1px 100% no-repeat,
    linear-gradient(90deg, var(--line-strong) 0 1px, transparent 1px) 0 0 / 50% 100% repeat-x;
}
.dim-bar[data-prev] {
  background:
    /* background-position % resolves against (width - 14px): scale back to the full width so the hollow
       dot centres on p % exactly like the solid dot (::after) centres on v % (0 and 100 included) */
    radial-gradient(circle, var(--bg) 0 3.5px, var(--klon) 4px 6px, transparent 6.5px) calc(clamp(0, var(--p), 100) / 100 * (100% + 14px) - 7px) 50% / 14px 14px no-repeat,
    linear-gradient(var(--line-strong), var(--line-strong)) 0 50% / 100% 1px no-repeat,
    linear-gradient(var(--line-strong), var(--line-strong)) 100% 0 / 1px 100% no-repeat,
    linear-gradient(90deg, var(--line-strong) 0 1px, transparent 1px) 0 0 / 50% 100% repeat-x;
}
.dim-bar::before { /* the change since the last take */
  content: ""; position: absolute; top: 7px; height: 4px; background: var(--ink);
  left: calc(min(var(--p), var(--v)) * 1%); width: calc(max(var(--v) - var(--p), var(--p) - var(--v)) * 1%);
}
.dim-bar::after { /* this take */
  content: ""; position: absolute; top: 2px; width: 14px; height: 14px; border-radius: 50%;
  left: calc(clamp(0, var(--v, 0), 100) * 1% - 7px); background: var(--ink);
}
.dim[data-na] .dim-score, .dim[data-na] .dim-name { color: var(--ink-3); }
.dim[data-na] .dim-score { font-family: var(--font-ui); font-size: var(--fs-meta); font-weight: var(--fw-medium); }
/* not rated: the axis stays, dashed in line-strong (in --line it vanished on white, 1.5:1) */
.dim[data-na] .dim-bar { background: linear-gradient(90deg, var(--line-strong) 0 4px, transparent 4px 8px) 0 50% / 8px 1px repeat-x; }
.dim[data-na] .dim-bar::before, .dim[data-na] .dim-bar::after { display: none; }
/* uncertain: the dot is drawn dashed and hollow, the score says "unsicher" */
.dim[data-uncertain] .dim-score { font-family: var(--font-ui); font-size: var(--fs-meta); font-weight: var(--fw-bold); color: var(--ink-2); }
.dim[data-uncertain] .dim-bar::after { background: var(--bg); border: 2px dashed var(--ink); }
.dim[data-uncertain] .dim-bar::before { display: none; }
/* Schwerpunkt: the name is marked in yellow; the word stays for screen readers */
.dim[data-weakest] .dim-name { justify-self: start; background: var(--mark); color: var(--on-mark); padding: 1px 6px; margin-left: -6px; }
.dim[data-weakest] .pill { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; padding: 0; }
.dims-note { font-size: var(--fs-meta); color: var(--ink-2); }

/* ---------- Radar (SVG drawn by charts.js): this take solid ink, the last take dashed grey ---------- */
.radar { width: 100%; max-width: 380px; aspect-ratio: 1; margin-inline: auto; }
.radar svg { width: 100%; height: 100%; overflow: visible; }
.radar .radar-grid { fill: none; stroke: var(--line-strong); stroke-width: 1; }
.radar .radar-axis { stroke: var(--line); stroke-width: 1; }
.radar .radar-area { fill: color-mix(in oklab, var(--ink) 10%, transparent); stroke: var(--ink); stroke-width: 2.5; stroke-linejoin: miter; }
.radar .radar-area[data-series="prev"] { fill: none; stroke: var(--klon); stroke-dasharray: 3 3; stroke-width: 1.5; }
.radar .radar-dot { fill: var(--ink); }
.radar .radar-label { fill: var(--ink-2); font-size: 12.5px; font-weight: 500; font-family: var(--font-ui); }
.radar .radar-label[data-na] { fill: var(--ink-3); }
.radar .radar-value { font-family: var(--font-num); font-size: 15px; font-weight: 600; fill: var(--ink); }
/* Shape-Too: uncertain = dashed axis, hollow dashed dot */
.radar .radar-axis[data-unc] { stroke: var(--ink-3); stroke-dasharray: 3 3; stroke-width: 1.5; }
.radar .radar-dot[data-unc] { fill: var(--bg); stroke: var(--ink); stroke-width: 2; stroke-dasharray: 2 2; }
.radar .radar-value[data-unc] { fill: var(--ink-2); }

/* ---------- Aussprache: a sorted bar chart, one row per sound, the Schwerpunkt boxed in yellow ---------- */
.aussprache-rel { display: flex; flex-direction: column; gap: var(--sp-3); }
.aussprache-head {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px var(--sp-3);
  padding-top: 10px; border-top: var(--rule) solid var(--ink);
}
.aussprache-head .dim-score { font-size: var(--fs-num-l); }
.aussprache-title { font-size: var(--fs-h2); }
.aussprache-rel .dim-raw { max-width: var(--measure-body); }
.pd-list { display: flex; flex-direction: column; border-top: 1px solid var(--line); }
.pd-item { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3); min-height: 48px; border-bottom: 1px solid var(--line); }
.pd-word { font-family: var(--font-text); font-size: var(--fs-lead); }
.pron-list { display: flex; flex-direction: column; margin: 0; padding: 0; list-style: none; }
.pron-target {
  display: grid; align-items: center;
  grid-template-columns: 48px minmax(0, 1fr) minmax(2.75rem, auto);
  grid-template-areas: "glyph name score" "glyph bar score" "glyph words words";
  column-gap: var(--sp-3); row-gap: 4px; padding: 10px 0; border-bottom: 1px solid var(--line);
}
.pron-glyph {
  grid-area: glyph; align-self: start; width: 48px; height: 48px; display: grid; place-items: center;
  box-shadow: inset 0 0 0 var(--rule-2) var(--ink);
  font-family: var(--font-num); font-size: var(--fs-h2); font-weight: var(--fw-semibold); line-height: 1; color: var(--ink);
}
.pron-target[data-focus] .pron-glyph { background: var(--mark); color: var(--on-mark); box-shadow: inset 0 0 0 var(--rule-2) var(--on-mark); }
.pron-name { grid-area: name; font-weight: var(--fw-bold); display: inline-flex; align-items: center; flex-wrap: wrap; gap: 4px var(--sp-2); }
.pron-bar { grid-area: bar; position: relative; height: 12px; background: var(--surface-2); }
.pron-bar::before {
  content: ""; position: absolute; inset: 0 auto 0 0;
  width: calc(clamp(0, var(--v, 0), 100) * 1%); background: var(--ink);
}
.pron-score { grid-area: score; align-self: center; text-align: right; font-family: var(--font-num); font-size: var(--fs-h2); font-weight: var(--fw-semibold); line-height: 1; }
.pron-words { grid-area: words; font-size: var(--fs-meta); color: var(--ink-2); }
/* few measurements: the box is dashed, the row recedes */
.pron-target[data-few] .pron-glyph { box-shadow: none; outline: var(--rule-2) dashed var(--line-strong); outline-offset: -2px; color: var(--ink-3); }
.pron-target[data-few] .pron-name, .pron-target[data-few] .pron-score { color: var(--ink-3); }
.pron-target[data-few] .pron-bar::before { background: var(--ink-3); }
.aussprache-rel[data-pron="pending"] .hint { color: var(--ink-2); }
.aussprache-rel .card-actions { margin-top: var(--sp-1); }
.drill-words { display: flex; flex-wrap: wrap; gap: var(--sp-2); }

/* ---------- Wort für Wort: two voices on one system, a timeline with bars as long as each word ---------- */
.track { display: flex; flex-direction: column; gap: var(--sp-3); }
.track-legend { display: flex; flex-wrap: wrap; gap: var(--sp-2) var(--sp-4); font-size: var(--fs-meta); color: var(--ink-2); }
.track-legend .key { display: inline-flex; align-items: center; gap: 6px; }
.track-legend .key i { width: 22px; height: 12px; display: inline-block; }
.track-legend .key[data-st="missing"] i { background: repeating-linear-gradient(135deg, var(--ink) 0 1.5px, transparent 1.5px 5px); }
.track-legend .key[data-st="extra"] i { box-shadow: inset 0 0 0 2px var(--ink); }
.track-legend .key[data-st="filler"] i { background: var(--ink); border-radius: var(--r-key); }
/* same wavy line as the misread word itself (mask keeps the token colour; img-src allows data:) */
.track-legend .key[data-st="misread"] i {
  height: 5px; background: var(--ink);
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='5'%3E%3Cpath d='M0 2.5Q2 .5 4 2.5T8 2.5' fill='none' stroke='%23000' stroke-width='1.5'/%3E%3C/svg%3E") repeat-x 0 50% / 8px 5px;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='5'%3E%3Cpath d='M0 2.5Q2 .5 4 2.5T8 2.5' fill='none' stroke='%23000' stroke-width='1.5'/%3E%3C/svg%3E") repeat-x 0 50% / 8px 5px;
}
/* a sound drifted: an inverted chip under the word, same shape in the legend */
.track-legend .key[data-st="pron"] i { width: 20px; height: 14px; background: var(--ink); }

.track-scroll {
  overflow-x: auto; overscroll-behavior-x: contain; scroll-snap-type: x proximity;
  padding: 6px 0 10px;
  border-top: 1px solid var(--line); border-bottom: 1px solid var(--line);
  -webkit-mask-image: linear-gradient(90deg, #000 calc(100% - 24px), transparent);
  mask-image: linear-gradient(90deg, #000 calc(100% - 24px), transparent);
}
/* Items in order: 2 heads, then per source index the pair [klon cell, du cell]. --scale = px per second */
.track-system {
  --scale: 90px;
  display: grid; grid-auto-flow: column; grid-template-rows: auto auto;
  grid-auto-columns: max-content;
  column-gap: 2px; row-gap: 0;
  position: relative; padding-left: 12px; padding-right: 16px;
}
.track-system::before { /* the system bar joining both voices */
  content: ""; position: absolute; left: 3px; top: 8px; bottom: 8px; width: 3px; background: var(--ink);
}
.track-head {
  position: sticky; left: 0; z-index: 1;
  display: flex; flex-direction: column; justify-content: center;
  min-width: 64px; padding: 0 var(--sp-3) 0 var(--sp-2);
  background: var(--track-bg, var(--bg)); /* set --track-bg: var(--surface) when the track sits on a panel */
  font-weight: var(--fw-bold); font-size: var(--fs-meta); color: var(--ink);
}
.track-head small { font-family: var(--font-num); font-weight: var(--fw-medium); color: var(--ink-3); font-size: var(--fs-micro); white-space: nowrap; letter-spacing: .02em; }
.track-word {
  --dur: .3; --pb: 0;
  position: relative; display: flex; flex-direction: column; justify-content: flex-start; gap: 4px;
  min-height: 52px; min-width: 18px; margin: 0;
  padding: 10px 6px 8px calc(6px + min(var(--pb) * var(--scale), 72px));
  border: 0; border-radius: 0; background: none;
  font-family: var(--font-text); font-size: var(--fs-body); line-height: 1.2; color: var(--ink); text-align: left;
  scroll-snap-align: start; white-space: nowrap;
}
.track-word::after { /* duration bar, length ∝ dur: you solid */
  content: ""; display: block; height: 10px;
  width: clamp(4px, calc(var(--dur) * var(--scale)), 240px);
  background: var(--ink);
}
/* the clone: grey word, hollow bar */
.track-word[data-voice="klon"] { color: var(--ink-2); }
.track-word[data-voice="klon"]::after { background: none; box-shadow: inset 0 0 0 1.5px var(--klon); }
.track-word::before { /* pause before, drawn as a rest line in the indent */
  content: ""; position: absolute; left: 6px; bottom: 13px; height: 1px;
  width: max(0px, min(var(--pb) * var(--scale) - 6px, 66px));
  background: var(--line-strong);
}
.track-word[data-long-pause]::before { height: 3px; bottom: 12px; background: var(--ink); }
@media (hover: hover) { button.track-word:hover { background: var(--surface-2); } }
/* the word being played: the yellow marker on both rows */
.track-word[aria-current="true"] { background: var(--mark); color: var(--on-mark); }
.track-word[aria-current="true"]::after { background: var(--on-mark); box-shadow: none; }
.track-word[data-voice="klon"][aria-current="true"]::after { background: none; box-shadow: inset 0 0 0 1.5px var(--on-mark); }
.track-word[data-st="missing"] {
  color: var(--ink-2); text-decoration: line-through; text-decoration-thickness: 1.5px;
  background: repeating-linear-gradient(135deg, color-mix(in oklab, var(--ink) 30%, transparent) 0 1.5px, transparent 1.5px 6px);
}
.track-word[data-st="missing"]::after { visibility: hidden; }
.track-word[data-st="extra"] { box-shadow: inset 0 0 0 2px var(--ink); }
.track-word[data-st="extra"]::after { background: none; box-shadow: inset 0 0 0 1.5px var(--ink); }
.track-word[data-st="filler"] { background: var(--ink); color: var(--on-ink); border-radius: var(--r-capsule); font-family: var(--font-ui); font-weight: var(--fw-bold); }
.track-word[data-st="filler"]::after { background: var(--on-ink); }
/* the wave sits inside the word's line box (offset 2 px), so it clears the duration bar and the bar keeps
   the same height as its neighbours */
.track-word[data-st="misread"] { text-decoration: underline wavy var(--ink); text-decoration-thickness: 1.5px; text-underline-offset: 2px; }
.track-word[data-st="gap"] { pointer-events: none; }
.track-word[data-st="gap"]::after, .track-word[data-st="gap"]::before { display: none; }
/* Laut weicht ab (Timeline ps < 70): the chip carries the word's pronunciation score */
.pron-tag {
  align-self: flex-start; display: inline-flex; align-items: baseline; gap: 3px; white-space: nowrap;
  font-family: var(--font-num); font-weight: var(--fw-semibold); font-size: var(--fs-micro); line-height: 1.2;
  background: var(--ink); color: var(--on-ink); padding: 1px 6px 2px;
}
.track-word[aria-current="true"] .pron-tag { background: var(--block); color: var(--on-block); }
.track-contour { height: 28px; width: 100%; overflow: visible; }
.track-contour .c-du { fill: none; stroke: var(--ink); stroke-width: 2; stroke-linejoin: round; }
.track-contour .c-klon { fill: none; stroke: var(--klon); stroke-width: 1.5; stroke-dasharray: 4 3; }

/* ---------- Coach card: the inverted block (tokens.css remaps the neutrals inside) ---------- */
.coach-card { padding: 22px 20px; display: flex; flex-direction: column; gap: var(--sp-4); }
.coach-card .rec-hint { text-align: left; }
.coach-head { display: flex; align-items: center; gap: var(--sp-3); }
.coach-head .card-title { margin-right: auto; font-size: var(--fs-h2); }
.coach-item { font-size: var(--fs-body); line-height: 1.5; max-width: 62ch; }
.coach-lead { font-weight: var(--fw-bold); margin-right: .4em; }
.coach-frage { font-family: var(--font-text); font-size: var(--fs-subtitle); line-height: 1.4; font-weight: var(--fw-medium); }
.coach-next { border-top: 1px solid var(--line); padding-top: var(--sp-4); display: flex; flex-direction: column; gap: var(--sp-3); }
/* what the clone will read (model answer, the audience's question): drawn hollow and dashed */
.coach-muster {
  font-family: var(--font-text); font-size: var(--fs-lead); line-height: var(--lh-read);
  padding: var(--sp-4); background: transparent; outline: 1.5px dashed var(--klon); outline-offset: -1.5px;
}
.coach-muster .coach-lead { display: block; margin-bottom: var(--sp-1); font-family: var(--font-ui); font-size: var(--fs-meta); }
.coach-meta { font-size: var(--fs-meta); color: var(--ink-2); }
.coach-card[data-state="pending"] .coach-item { color: var(--ink-3); }
.coach-card[data-state="pending"] .coach-wait { display: flex; align-items: center; gap: var(--sp-3); color: var(--ink-2); }
.coach-wait::before {
  content: ""; width: 16px; height: 16px; border-radius: 50%; flex: none;
  border: 2px solid var(--line-strong); border-top-color: var(--mark); animation: rz-spin 1s linear infinite;
}
.coach-card:not([data-state="pending"]) .coach-wait { display: none; }
.coach-card .list { border-top-color: var(--line); }

/* ---------- Progress ---------- */
.level { display: flex; flex-direction: column; gap: var(--sp-3); padding-top: 10px; border-top: var(--rule) solid var(--ink); }
.level-name { font-size: var(--fs-h2); }
.level-bar { height: 12px; background: var(--surface-2); overflow: hidden; }
.level-bar::before { content: ""; display: block; height: 100%; width: calc(clamp(0, var(--v, 0), 1) * 100%); background: var(--ink); }

/* Serie: one square per week. Done solid, joker half, this week yellow, missed a hairline frame */
.week-dots { display: flex; gap: 6px; flex-wrap: wrap; }
.week-dot { width: 22px; height: 22px; box-shadow: inset 0 0 0 1.5px var(--line-strong); background: transparent; }
.week-dot[data-state="done"] { background: var(--ink); box-shadow: none; }
.week-dot[data-state="joker"] { background: linear-gradient(90deg, var(--ink) 50%, transparent 50%); box-shadow: inset 0 0 0 1.5px var(--ink); }
.week-dot[data-state="current"] { background: var(--mark); box-shadow: inset 0 0 0 1.5px var(--on-mark); }
.week-dot[data-state="missed"] { box-shadow: inset 0 0 0 1px var(--line); }

/* Ankertext: small multiples on one 0..100 scale, each opens with a rule. Dots hollow, the last one solid */
.series { display: grid; gap: var(--sp-5) var(--sp-4); grid-template-columns: repeat(2, minmax(0, 1fr)); }
.series-cell {
  display: flex; flex-direction: column; gap: 4px; min-width: 0;
  padding-top: var(--sp-2); border-top: var(--rule-2) solid var(--ink);
}
.series-cell[data-main] { grid-column: 1 / -1; border-top-width: var(--rule); }
.series-title { display: flex; justify-content: space-between; align-items: baseline; gap: var(--sp-2); font-size: var(--fs-meta); font-weight: var(--fw-bold); }
.series-value { font-family: var(--font-num); font-size: var(--fs-subtitle); font-weight: var(--fw-semibold); line-height: 1; }
.series-cell[data-main] .series-value { font-size: var(--fs-num-l); }
.series-svg { width: 100%; height: 56px; overflow: visible; }
.series-cell[data-main] .series-svg { height: 120px; }
.series-line { fill: none; stroke: var(--ink); stroke-width: 2; stroke-linejoin: miter; stroke-linecap: square; }
.series-dot { fill: var(--bg); stroke: var(--ink); stroke-width: 2; }
.series-dot[data-last] { fill: var(--ink); }
.series-hit { fill: transparent; stroke: none; }
.series-gridline { stroke: var(--line); stroke-width: 1; }
.series-axis { fill: var(--ink-2); font-size: 12px; font-family: var(--font-num); font-weight: 500; }
/* empty Ankertext section: same voice as the empty Meilensteine sentence, button set off below it */
.series-empty { color: var(--ink-2); }
.series-empty + .btn { margin-top: var(--sp-3); }

.heatmap { display: flex; flex-direction: column; gap: var(--sp-2); }
.heatmap-grid {
  display: grid; grid-auto-flow: column; grid-template-rows: repeat(7, 1fr);
  grid-auto-columns: minmax(0, 1fr); gap: 3px;
  max-width: 520px;
}
.heatmap-day { font-size: var(--fs-micro); line-height: 1; color: var(--ink-2); display: flex; align-items: center; min-width: 0; }
.heatmap-cell { aspect-ratio: 1; background: var(--heat-0); }
.heatmap-cell[data-level="1"] { background: var(--heat-1); }
.heatmap-cell[data-level="2"] { background: var(--heat-2); }
.heatmap-cell[data-level="3"] { background: var(--heat-3); }
.heatmap-cell[data-today] { box-shadow: 0 0 0 1.5px var(--bg), 0 0 0 3.5px var(--ink); }
.heatmap-cell[data-future] { background: transparent; }
.heatmap-legend { display: flex; align-items: center; gap: 4px; font-size: var(--fs-micro); color: var(--ink-2); }
.heatmap-legend .heatmap-cell { width: 12px; }

/* Meilensteine: rows with a square mark; new ones filled yellow, locked ones dashed */
.achievements { display: flex; flex-direction: column; border-top: 1px solid var(--line); }
.achievement {
  display: grid; grid-template-columns: 36px minmax(0, 1fr) auto; align-items: center; gap: 2px var(--sp-3);
  padding: var(--sp-3) 0; border-bottom: 1px solid var(--line);
}
.achievement-mark { width: 32px; height: 32px; box-shadow: inset 0 0 0 var(--rule-2) var(--ink); display: grid; place-items: center; color: var(--ink); }
.achievement-mark .icon { width: 18px; height: 18px; stroke-width: 2.5; }
.achievement-name { font-weight: var(--fw-bold); }
.achievement-hint { grid-column: 2; font-size: var(--fs-meta); color: var(--ink-2); }
.achievement-date { grid-column: 3; grid-row: 1; font-size: var(--fs-meta); color: var(--ink-3); }
.achievement[data-locked] .achievement-mark { box-shadow: none; outline: 1.5px dashed var(--line-strong); outline-offset: -1.5px; color: transparent; }
.achievement[data-locked] .achievement-name { color: var(--ink-2); font-weight: var(--fw-medium); }
.achievement[data-new] .achievement-mark { background: var(--mark); color: var(--on-mark); box-shadow: inset 0 0 0 var(--rule-2) var(--on-mark); }

/* ---------- States ---------- */
.empty-state, .error-state {
  display: flex; flex-direction: column; align-items: flex-start; gap: var(--sp-3);
  padding: var(--sp-6) var(--sp-5);
}
.empty-state { outline: var(--rule-2) dashed var(--line-strong); outline-offset: calc(-1 * var(--rule-2)); }
.empty-state p, .error-state p { color: var(--ink-2); }
.error-state { box-shadow: inset 0 0 0 var(--rule) var(--ink); }
.error-title { font-family: var(--font-num); font-weight: var(--fw-semibold); font-size: var(--fs-h2); line-height: var(--lh-tight); }
.error-title::before { /* the yellow alert block: attention without a hue for "error" */
  content: "!"; display: inline-grid; place-items: center; width: 1.1em; height: 1.1em; margin-right: .35em;
  background: var(--mark); color: var(--on-mark); font-size: .8em; vertical-align: .1em;
}
.loading { display: flex; align-items: center; gap: var(--sp-3); color: var(--ink-2); padding: var(--sp-5) 0; }
.loading::before {
  content: ""; width: 18px; height: 18px; flex: none; border-radius: 50%;
  border: 2px solid var(--line-strong); border-top-color: var(--ink); animation: rz-spin 1s linear infinite;
}
.notice { /* inline information, e.g. headphones required */
  display: flex; gap: var(--sp-3); align-items: flex-start;
  padding: var(--sp-3) var(--sp-4); background: var(--surface); color: var(--ink);
}
.notice .icon { color: var(--ink); margin-top: 1px; }
.notice a { color: inherit; }
/* warnings (silent mic, failed analysis, no reference): a yellow block with black text */
.notice[data-tone="mis"] { background: var(--mis-soft); color: var(--on-mark); }
.notice[data-tone="mis"] .icon { color: var(--on-mark); }

.toast-region {
  position: fixed; left: 50%; transform: translateX(-50%); z-index: var(--z-toast);
  bottom: calc(var(--nav-h) + env(safe-area-inset-bottom) + var(--sp-4));
  width: min(92vw, 440px); display: flex; flex-direction: column; gap: var(--sp-2);
  pointer-events: none;
}
.toast {
  display: flex; align-items: center; gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-4);
  box-shadow: var(--shadow-float), 0 0 0 1px var(--block-ring);
  font-weight: var(--fw-medium); pointer-events: auto;
  animation: rz-toast-in var(--dur) var(--ease-out);
}
.toast[data-tone="error"] { background: var(--mark); color: var(--on-mark); box-shadow: var(--shadow-float), inset 0 0 0 2px var(--on-mark); font-weight: var(--fw-bold); }
.toast .btn { min-height: var(--tap); margin-left: auto; color: inherit; border-color: currentColor; }
/* session screen on phones: the HUD bar owns the top edge and the tab bar is gone, toasts sit at the bottom */
@media (max-width: 1023.98px) {
  body:has(.session) .toast-region { bottom: calc(env(safe-area-inset-bottom) + var(--sp-4)); }
}

/* ---------- Login: the desk before the take ---------- */
.login { min-height: 100dvh; display: grid; place-items: center; padding: var(--gutter); }
.login-plate { width: min(100%, 400px); display: flex; flex-direction: column; gap: var(--sp-5); }
.login-plate.card { background: transparent; padding: 0; }
.login-plate .wordmark { padding-bottom: var(--sp-4); border-bottom: var(--rule) solid var(--ink); }
.login-plate .btn-primary { min-height: 52px; font-size: var(--fs-lead); }

/* ---------- Motion ---------- */
@keyframes rz-spin { to { transform: rotate(360deg); } }
@keyframes rz-toast-in { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { transition-duration: 1ms !important; scroll-behavior: auto !important; }
  .toast { animation: none; }
  .hud-needle { transition: none; }
  /* spinners keep turning slowly; the text next to them carries the state */
  .rec-button[data-state="processing"]::after, .btn[aria-busy="true"]::after, .loading::before, .coach-wait::before { animation-duration: 2.4s; }
}

/* ---------- Responsive ---------- */
@media (max-width: 559.98px) {
  /* four meters in one row (dev-viz, the bare HUD): units move under the number */
  .hud-unit { display: block; margin: 3px 0 0; font-size: var(--fs-micro); line-height: 1; }
  .hud { gap: var(--sp-1); grid-template-columns: repeat(3, minmax(0, 1fr)) minmax(0, 1.25fr); } /* Zeit holds "10:00" */
  .hud-meter { padding-inline: var(--sp-1); }
  .hud-value { font-size: var(--fs-h2); }
}

@media (max-width: 359.98px) {
  :root { --gutter: 12px; --rec-size: 68px; }
  .hud { gap: 0; }
  .pron-target { grid-template-columns: 40px minmax(0, 1fr) minmax(2.5rem, auto); }
  .pron-glyph { width: 40px; height: 40px; font-size: var(--fs-subtitle); }
}

/* ---------- HUD bar, mobile (below 1024) ----------
   The dock turns into one sticky black row under the header: [Verwerfen] Tempo, Ähm, Pegel, Zeit [record].
   session.js writes the measured bar height to --rz-bar-h (scroll padding, read-along follow).
   No tab bar and no header actions on this screen: Abbrechen and the wordmark lead out. */
@media (max-width: 1023.98px) {
  .app-shell:has(.session) .nav,
  .app-shell:has(.session) .app-header .header-actions { display: none; }
  .app-main:has(> .session) { padding-bottom: calc(env(safe-area-inset-bottom) + var(--sp-4)); }
  html:has(.session) { scroll-padding-top: calc(var(--rz-bar-h, 0px) + env(safe-area-inset-top) + var(--sp-2)); }

  .session { gap: var(--sp-3); }
  .session > .session-main { gap: var(--sp-4); }
  /* brief in two rows: Abbrechen with the pills, then the title (the text stays the largest type) */
  .session-brief { display: grid; grid-template-columns: auto minmax(0, 1fr); align-items: center; gap: var(--sp-1) var(--sp-2); }
  .session-brief > * { grid-column: 1 / -1; }
  .session-brief > .back-link { grid-column: 1; grid-row: 1; }
  .session-brief > .row { grid-column: 2; grid-row: 1; justify-content: flex-end; gap: 6px; }
  .session-brief > .row > .pill { padding-inline: 7px; }
  .session .screen-title { font-size: var(--fs-title); line-height: var(--lh-tight); }
  .session .reading-plate { padding-top: var(--sp-3); }
  /* one line, always: a longer note (live count unavailable) must not push the text down mid-read */
  .session .reading-plate-head { margin-bottom: var(--sp-3); flex-wrap: nowrap; justify-content: space-between; }
  .session .reading-plate-head > .meta { flex: none; }
  .session .reading-plate-head .rec-hint { min-width: 0; text-align: right; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .session .reading-plate-head .rec-hint, .session-main > .rec-hint { min-height: 0; }
  .session-main > .rec-hint { text-align: left; color: var(--ink-2); }
  .session-main > .rec-hint:empty { display: none; }

  .session .rec-dock {
    order: -1;
    position: sticky; top: env(safe-area-inset-top, 0px);
    flex-direction: row; flex-wrap: wrap; align-items: center; gap: var(--sp-2);
    min-height: 60px;
    margin-inline: calc(-1 * var(--gutter));
    padding: 7px var(--gutter) 7px calc(var(--gutter) - 4px);   /* 48 px button + 2 × 7 = 62 px */
  }
  /* standalone PWA: the stuck bar also covers the status bar inset above it */
  .session .rec-dock::before {
    content: ""; position: absolute; left: 0; right: 0; bottom: 100%;
    height: env(safe-area-inset-top, 0px); background: inherit;
  }
  .session .rec-row { display: contents; }
  .session .rec-dock > .tally-label, .session .rec-label { display: none; }

  .session .rec-dock .hud {
    flex: 1 1 0; min-width: 0; max-width: 560px; gap: var(--sp-2);
    grid-template-columns: minmax(0, 1.45fr) minmax(0, .82fr) minmax(0, 1.08fr) minmax(0, 1.02fr);
  }
  .session .rec-dock .hud-meter { padding: 0; gap: 3px; }
  .session .rec-dock .hud-label { line-height: 1.2; }
  .session .rec-dock .hud-value { font-size: var(--fs-hud-bar); letter-spacing: -0.01em; }
  .session .rec-dock .hud-unit { display: inline; margin: 0 0 0 2px; font-size: var(--fs-micro); line-height: 1; }
  .session .rec-dock .hud-later { font-size: var(--fs-micro); line-height: 1; white-space: nowrap; }
  .session .rec-dock .hud-band, .session .rec-dock .hud-level { height: 5px; margin-top: 2px; }
  .session .rec-dock .hud-needle { top: -3px; bottom: -3px; width: 3px; left: calc(clamp(0, var(--x, .5), 1) * 100% - 1.5px); }
  .session .rec-dock .hud-limit { margin-top: 2px; }

  /* Verwerfen at the far left, away from the thumb on the record button */
  .session .rec-dock [data-action="discard"] { order: -1; min-width: var(--tap); padding: 0; }
  .session .rec-dock [data-action="discard"] .btn-text { display: none; }
  .session .rec-button { order: 1; --rec-size: 48px; border-width: 2px; margin-left: auto; }
  .session .rec-button::before { width: 18px; height: 18px; }
  .session .rec-button[data-state="processing"]::after { inset: -2px; border-width: 2px; }
  /* Nochmal senden after a failed upload, the Zwischenfrage: a second row of the bar */
  .session .rec-dock > .btn, .session .rec-dock > .notice { order: 2; flex-basis: 100%; }
}
@media (max-width: 379.98px) {
  .session .rec-dock .hud { gap: 6px; grid-template-columns: minmax(0, 1.42fr) minmax(0, .82fr) minmax(0, 1.04fr) minmax(0, 1fr); }
  .session .rec-dock .hud-value { font-size: var(--fs-hud-bar-s); letter-spacing: -0.02em; }
}

@media (min-width: 768px) {
  .grid-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: var(--sp-7); }
  .series { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .card { padding: var(--sp-6); }
  .card:has(> .drill) { padding: var(--sp-3) 0 var(--sp-2); }
  .coach-card { padding: var(--sp-6); }
  .coach-item { display: grid; grid-template-columns: 9rem minmax(0, 1fr); gap: var(--sp-4); }
  .coach-lead { margin: 0; }
  .coach-next { display: grid; grid-template-columns: 9rem minmax(0, 1fr); gap: var(--sp-3) var(--sp-4); }
  .coach-next > .card-actions { grid-column: 2; }
}

/* Ergebnis: overall, Aussprache, Messwerte, radar. Phone: one column in that order. From 1024 two columns:
   score beside the radar, Messwerte beside Aussprache. */
.result-top { display: flex; flex-direction: column; gap: var(--sp-8); }
@media (min-width: 1024px) {
  .result-top {
    display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    grid-template-areas: "overall radar" "dims pron";
    gap: var(--sp-8) var(--sp-9); align-items: start;
  }
  .result-top > .overall { grid-area: overall; align-self: end; }
  .result-top > .radar { grid-area: radar; }
  .result-top > .dims-wrap { grid-area: dims; }
  .result-top > .aussprache-rel { grid-area: pron; }
  /* no Aussprache block (pron off, no legacy value): Messwerte take the full width under the score */
  .result-top:not(:has(> .aussprache-rel)) { grid-template-areas: "overall radar" "dims dims"; }
  .result-top:not(:has(> .radar)) { grid-template-areas: "overall pron" "dims pron"; }
}

@media (min-width: 1024px) {
  .app-shell {
    grid-template-columns: var(--rail-w) minmax(0, 1fr);
    grid-template-rows: 1fr;
    grid-template-areas: "nav main";
  }
  .app-header { display: none; }
  .nav {
    grid-area: nav; position: sticky; top: 0; inset: auto; height: 100dvh;
    flex-direction: column; justify-content: flex-start; gap: 4px;
    padding: var(--sp-6) var(--sp-4); border-top: 0; border-right: var(--rule-2) solid var(--ink);
    background: var(--bg);
  }
  .nav .wordmark { display: inline-flex; margin: 0 var(--sp-2) var(--sp-7); }
  .nav-item {
    flex: none; flex-direction: row; justify-content: flex-start; gap: var(--sp-3);
    padding: 0 var(--sp-3);
    font-size: var(--fs-body); font-weight: var(--fw-medium); color: var(--ink-2);
  }
  .nav-item .icon { width: 22px; height: 22px; }
  @media (hover: hover) { .nav-item:hover { background: var(--surface); color: var(--ink); } }
  .nav-item[aria-current="page"] { font-weight: var(--fw-bold); }
  .app-shell[data-mode="focus"] { grid-template-columns: minmax(0, 1fr); grid-template-areas: "main"; }
  .app-main { padding: var(--sp-7) var(--sp-8) var(--sp-9); }
  .screen-head { padding-top: 0; }

  /* Session: text column + the Pult (HUD and record button) on the right, both visible at once */
  .session { display: grid; grid-template-columns: minmax(0, 1fr) var(--pult-w); gap: var(--sp-8); align-items: start; padding-bottom: 0; }
  .session .rec-dock {
    position: sticky; top: var(--sp-7); inset: auto; bottom: auto;
    padding: var(--sp-6); gap: var(--sp-5);
  }
  .app-shell:not([data-mode="focus"]) .session .rec-dock { bottom: auto; }
  .rec-dock > .tally-label { display: inline-flex; align-self: flex-start; }
  .rec-row > [data-action="discard"] .icon { display: none; }   /* the icon is for the phone bar */
  .session .hud { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 22px 18px; }
  .session .rec-dock .hud-meter { padding: 0; }
  .session .rec-dock .hud-label { font-size: var(--fs-small); }
  .session .rec-dock .hud-unit { font-size: var(--fs-meta); }
  .session .rec-dock .hud-band, .session .rec-dock .hud-level { height: 12px; margin-top: 6px; }
  .session .rec-dock .hud-later { font-size: var(--fs-body); }
  .session .rec-dock .rec-hint { color: var(--ink); }
  .session .reading-plate-head .rec-hint { display: none; }
  .session .reading-title { display: none; }
  /* the text to read stays the largest type on the desk (30 px): the title steps down to h2 (26 px)
     instead of the 44 px screen title, which also took three lines in the 1024 text column */
  .session .screen-title { font-size: var(--fs-h2); line-height: var(--lh-tight); }
  .toast-region { bottom: var(--sp-6); }
  .series { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .series-cell[data-main] { grid-column: span 2; grid-row: span 2; }
  .series-cell[data-main] .series-svg { height: 180px; }
  .coach-card { padding: var(--sp-7); }
  .coach-item, .coach-next { grid-template-columns: 10rem minmax(0, 1fr); column-gap: var(--sp-7); }
}

@media (min-width: 1440px) {
  .app-main { padding-inline: var(--sp-9); }
}

@media print {
  .nav, .rec-dock, .tally, .toast-region { display: none; }
}
