:root {
  /* ---- color tokens (light) — source of truth: docs/design/redesign-proposal.md §3.1 + mockup.html ---- */
  --bg: #f6f4ef;
  --surface: #ffffff;
  --surface-2: #efece4;
  --ink: #22271f;
  --ink-muted: #5b6157;
  --brand: #1f4d3a;
  --brand-ink: #ffffff;
  --brand-soft: #dbe8df;
  --accent: #b3402a;
  --border: #e2ddd2;
  /* Form-control boundary: >=3:1 vs the --surface fill (WCAG 1.4.11 non-text
     contrast). Kept distinct from the subtle --border used on content cards. */
  --control-border: #8a8578;
  --focus: #1f4d3a;

  /* banner/state colors — warn + info are proposal tokens; the app also needs
     borders + an ok/success + an error set the proposal table doesn't enumerate
     (see report). Kept as tokens so dark mode stays readable. */
  --warn-bg: #fbe9c8;
  --warn-ink: #6b5518;
  --warn-border: #e4c98a;
  --info-bg: #eef3ff;
  --info-ink: #33477d;
  --info-border: #c3d2f5;
  --ok-bg: #eef7f0;
  --ok-border: #cfe4d6;
  --error-bg: #fce8e4;
  --error-ink: #8a2f1c;
  --error-border: #eebfb4;

  /* ---- radius / elevation (proposal §3.3, mockup.html) ---- */
  --r-sm: 0.5rem;
  --r-md: 0.75rem;
  --r-lg: 1.25rem;
  --r-pill: 999px;
  --e-1: 0 1px 4px rgb(0 0 0 / 8%);
  --e-2: 0 -2px 12px rgb(0 0 0 / 6%);
  --e-float: 0 6px 20px rgb(31 77 58 / 35%);

  /* ---- type scale (mockup.html §3.2) ---- */
  --fs-display: 1.6rem;
  --fs-title: 1.15rem;
  --fs-body: 1rem;
  --fs-sub: 0.88rem;
  --fs-label: 0.72rem;

  /* ---- 4-based spacing scale (proposal §3.3) ---- */
  --sp-1: 0.25rem;
  --sp-2: 0.5rem;
  --sp-3: 0.75rem;
  --sp-4: 1rem;
  --sp-5: 1.5rem;
  --sp-6: 2rem;

  /* Mic glyph (mockup) drawn as a mask on the PTT button's ::before, so the
     JS `textContent` swaps ("Hold to talk" ↔ "Tap to send") never wipe it. */
  --mic-icon: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2024%2024'%3E%3Cpath%20d='M12%2015a3%203%200%200%200%203-3V6a3%203%200%201%200-6%200v6a3%203%200%200%200%203%203Z'/%3E%3Cpath%20d='M19%2011a1%201%200%201%200-2%200%205%205%200%200%201-10%200%201%201%200%201%200-2%200%207%207%200%200%200%206%206.92V21a1%201%200%201%200%202%200v-3.08A7%207%200%200%200%2019%2011Z'/%3E%3C/svg%3E");

  color-scheme: light dark;
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #14171a;
    --surface: #1e2227;
    --surface-2: #262b31;
    --ink: #e8ebe6;
    --ink-muted: #a2aaa0;
    --brand: #5fae86;
    --brand-ink: #0d1512;
    --brand-soft: #233a30;
    --accent: #e0795f;
    --border: #333a40;
    --control-border: #71787f;
    --focus: #8fd0ac;

    --warn-bg: #3a3320;
    --warn-ink: #e5cf9c;
    --warn-border: #5c4f2e;
    --info-bg: #1f2a44;
    --info-ink: #b9c7ee;
    --info-border: #33507e;
    --ok-bg: #1c2a22;
    --ok-border: #2f4a3a;
    --error-bg: #3a2420;
    --error-ink: #f0a996;
    --error-border: #5c342c;

    --e-float: 0 6px 20px rgb(0 0 0 / 55%);
  }
}

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  font-family: system-ui, sans-serif;
  background: var(--bg);
  color: var(--ink);
}

/* Immersive/flow screens: a centered readable column (~640px — forms and
   reading documents, §20), vertically centered only while it fits. */
#app {
  max-width: 40rem;
  margin: 0 auto;
  padding: 1rem;
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  justify-content: center;
}

.card {
  background: var(--surface);
  border-radius: var(--r-md);
  padding: 1.5rem;
  box-shadow: var(--e-1);
}

/* Adjacent sibling buttons directly on a card (consent, placement) breathe. */
.card > button + button {
  margin-inline-start: 0.5rem;
}

@media (min-width: 768px) {
  .card {
    padding: 2rem;
  }
}

/* ---------- persistent nav shell (redesign §4, BL-1) ---------- */
/* Only browse screens render a `.shell`; :has() scopes the full-bleed shell
   layout to them so immersive `.card`/`.conversation` screens keep the
   centered-card layout above with no JS class bookkeeping. */
#app:has(.shell) {
  display: block;
  max-width: none;
  padding: 0;
}

.shell {
  /* Definite height so the nav stays pinned and only .shell-content scrolls —
     min-height would let a tall browse screen (e.g. Progress) grow the page and
     scroll the tab bar/rail out of view. */
  height: 100dvh;
  display: flex;
  flex-direction: column;
}

.shell-content {
  flex: 1;
  /* min-height:0 lets this flex/grid child shrink below its content so its own
     overflow-y engages instead of the page scrolling. */
  min-height: 0;
  overflow-y: auto;
  padding: 1rem;
}

/* Keep browse content readable when the shell removes #app's width cap. */
.shell-content > * {
  max-width: 32rem;
  margin-inline: auto;
}

/* Bottom tab bar (mobile). Icons + raised center mic per the mockup. */
.nav {
  flex: none;
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  align-items: center;
  background: var(--surface);
  border-top: 1px solid var(--border);
  padding-bottom: env(safe-area-inset-bottom);
  position: relative;
}

.nav .tab {
  background: none;
  border: none;
  cursor: pointer;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  padding: 0.5rem 0 0.55rem;
  color: var(--ink-muted);
  font-size: 0.62rem;
  font-weight: 600;
}

.nav .tab svg {
  width: 22px;
  height: 22px;
}

.nav .tab.active {
  color: var(--brand);
}

/* Grid slot under the raised mic — reserved, never interactive. */
.nav .tab.center {
  visibility: hidden;
}

.mic-fab {
  position: absolute;
  left: 50%;
  top: -18px;
  transform: translateX(-50%);
  width: 58px;
  height: 58px;
  border-radius: 50%;
  border: 4px solid var(--surface);
  background: var(--brand);
  color: var(--brand-ink);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: var(--e-float);
}

.mic-fab svg {
  width: 26px;
  height: 26px;
}

/* Rail-only affordances, hidden on mobile. */
.wordmark,
.rail-practice {
  display: none;
}

.nav .tab:focus-visible,
.rail-practice:focus-visible,
.mic-fab:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}

/* Desktop: bottom bar → full-height left rail; center-mic notch → rail button. */
@media (min-width: 768px) {
  .shell {
    display: grid;
    grid-template-columns: 15rem 1fr;
  }

  .shell-content {
    grid-column: 2;
    grid-row: 1;
  }

  /* Readable-first widths: hubs are narrow reading/decision columns; only the
     Learn topic grid earns the wide workspace (§20). */
  .shell-content > * {
    max-width: 46rem;
  }

  .shell-content > .dashboard {
    max-width: 64rem;
  }

  .shell-content {
    padding: 2rem 2.5rem;
  }

  .nav {
    grid-column: 1;
    grid-row: 1;
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 0.15rem;
    border-top: none;
    border-right: 1px solid var(--border);
    padding: 1.15rem 0.7rem;
  }

  .wordmark {
    display: block;
    font-weight: 700;
    color: var(--brand);
    font-size: 1.2rem;
    padding: 0.1rem 0.5rem 0.9rem;
  }

  .rail-practice {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    width: 100%;
    min-height: 46px;
    border: none;
    border-radius: var(--r-sm);
    background: var(--brand);
    color: var(--brand-ink);
    font: 600 var(--fs-body) system-ui;
    cursor: pointer;
    margin-bottom: 0.9rem;
  }

  .rail-practice svg {
    width: 20px;
    height: 20px;
  }

  .nav .tab {
    flex-direction: row;
    justify-content: flex-start;
    gap: 0.7rem;
    padding: 0.6rem 0.7rem;
    border-radius: var(--r-sm);
    font-size: 0.9rem;
  }

  .nav .tab.active {
    background: var(--brand-soft);
  }

  .nav .tab.center,
  .mic-fab {
    display: none;
  }
}

h1 {
  margin-top: 0;
  color: var(--brand);
}

label {
  display: block;
  margin-bottom: 0.25rem;
  font-weight: 600;
}

input,
select {
  width: 100%;
  padding: 0.6rem;
  margin-bottom: 0.75rem;
  border: 1px solid var(--control-border);
  border-radius: var(--r-sm);
  font-size: 1rem;
  font-family: inherit;
  background: var(--surface);
  color: var(--ink);
}

button {
  padding: 0.6rem 1.1rem;
  border: none;
  border-radius: var(--r-sm);
  background: var(--brand);
  color: var(--brand-ink);
  font-size: 1rem;
  font-weight: 600;
  min-height: 44px; /* touch target (design §3.4) */
  cursor: pointer;
}

button:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}

button.secondary {
  background: transparent;
  color: var(--brand);
  border: 1px solid var(--brand);
}

/* ---------- Home: one "Next up" card + quiet alternative rows ---------- */

/* Home is a narrow decision screen, not a dashboard. */
.home-start {
  max-width: 34rem;
}

.home-context {
  min-height: 1em; /* reserve the line so the async fill doesn't shift layout */
  margin-bottom: 0.4rem;
}

/* The single most important thing on Home: what to practise now. */
.next-card {
  background: var(--surface);
  border-radius: var(--r-lg);
  padding: 1.35rem 1.5rem 1.5rem;
  box-shadow: var(--e-1);
  margin-bottom: 1rem;
}

.next-card .eyebrow {
  color: var(--accent);
}

.next-card h2 {
  margin: 0 0 0.3rem;
  font-size: 1.3rem;
  line-height: 1.3;
  color: var(--ink);
}

.next-card .next-hint {
  margin: 0 0 1.1rem;
}

button.primary-cta {
  display: block;
  width: 100%;
  padding: 0.85rem 1.1rem;
  font-size: 1.1rem;
  font-weight: 600;
  min-height: 52px;
}

/* Alternative practice rows: list rows, deliberately quieter than the card. */
.home-next-stack {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  margin: 0 0 0.75rem;
}

button.stack-row {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.1rem;
  width: 100%;
  text-align: left;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  padding: 0.7rem 0.95rem;
  color: var(--ink);
  cursor: pointer;
}

button.stack-row:hover {
  border-color: var(--brand);
}

.stack-row-label {
  font-weight: 600;
  font-size: var(--fs-body);
  color: var(--brand);
}

.stack-row-hint {
  font-size: var(--fs-sub);
  color: var(--ink-muted);
}

button:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

/* Primary/secondary pair on a card: one row, primary left, secondary right.
   The secondary button used to sit outside the <form>, so the two rendered as
   separate blocks flush left with no spacing. */
.card-actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: center;
  gap: 0.75rem;
  margin-top: 1.25rem;
}

.note {
  color: var(--ink-muted);
  font-size: 0.9rem;
  min-height: 1.2em;
}

.conversation {
  display: flex;
  flex-direction: column;
  height: 100dvh;
}

/* Immersive conversation goes edge-to-edge (like the shell), so the transcript
   can be a centered reading column and the mic dock spans the full width. */
#app:has(.conversation) {
  display: block;
  max-width: none;
  padding: 0;
}

/* Focus top bar: session context left, the single End escape right. */
.focus-top {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.6rem;
  padding: 0.55rem 1rem;
  border-bottom: 1px solid var(--border);
  background: var(--surface);
}

.convo-title {
  font-weight: 600;
  font-size: var(--fs-sub);
  color: var(--ink);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
}

.back-end {
  flex: none;
  background: transparent;
  border: 1px solid var(--brand);
  border-radius: var(--r-pill);
  color: var(--brand);
  font: 600 var(--fs-sub) system-ui;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  min-height: 40px;
  padding: 0 0.9rem;
}

/* In-session mission checklist: header chrome under .focus-top, so it is
   full-bleed like the bar above it and never separates a degradation banner
   from the transcript it describes. */
.goals {
  flex: none;
  background: var(--surface);
  border-bottom: 1px solid var(--border);
}

.goals[hidden] {
  display: none;
}

summary.goals-head {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  min-height: 44px; /* >=44px touch target (design §3.4) */
  padding: 0 var(--sp-4);
  cursor: pointer;
  list-style: none;
}

summary.goals-head::-webkit-details-marker {
  display: none;
}

.goals-head .eyebrow {
  margin: 0;
}

.goals-head .chip {
  margin-left: auto;
}

/* Own chevron, since the UA marker is suppressed. Same border trick as the
   .goal.done tick, so neither needs an icon asset. */
summary.goals-head::after {
  content: "";
  flex: none;
  width: 0.45rem;
  height: 0.45rem;
  border-right: 2px solid var(--ink-muted);
  border-bottom: 2px solid var(--ink-muted);
  transform: rotate(45deg);
  transition: transform 120ms ease;
}

.goals[open] > summary.goals-head::after {
  transform: rotate(-135deg);
}

/* .conversation is a fixed 100dvh column whose only flexible child is
   #transcript, so an open panel takes its height straight out of the transcript.
   Capped and scrollable: on a short viewport (a small phone in landscape) an
   uncapped panel would squeeze #transcript to zero and push .mic-dock below the
   fold. */
.goals-panel {
  padding: 0 var(--sp-4) var(--sp-3);
  /* Floor as well as a cap: at 568x320 a bare 30dvh is 96px against ~101px of
     content, clipping the third row behind a scrollbar a touch device never
     draws. */
  max-height: max(30dvh, 8rem);
  overflow-y: auto;
}

.goals-aim {
  font-size: var(--fs-label);
  color: var(--ink-muted);
  line-height: 1.35;
  margin: 0 0 var(--sp-2);
}

.goals-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--sp-1);
}

.goal {
  display: flex;
  gap: var(--sp-2);
  align-items: start;
  font-size: var(--fs-sub);
  line-height: 1.35;
  color: var(--ink);
}

.goal-mark {
  flex: none;
  width: 18px;
  height: 18px;
  border-radius: var(--r-pill);
  border: 1.5px solid var(--control-border);
  margin-top: 1px;
  transition:
    background 120ms ease,
    border-color 120ms ease;
}

.goal.done .goal-mark {
  background: var(--brand);
  border-color: var(--brand);
}

.goal.done .goal-mark::after {
  content: "";
  display: block;
  width: 9px;
  height: 5px;
  margin: 3px auto 0;
  border-left: 2px solid var(--brand-ink);
  border-bottom: 2px solid var(--brand-ink);
  transform: rotate(-45deg);
}

/* Muted, and nothing else: struck-through low-contrast text is the wrong
   treatment for someone reading a second language. */
.goal.done .goal-text {
  color: var(--ink-muted);
}

.goals-clear {
  background: var(--ok-bg);
  border: 1px solid var(--ok-border);
  border-radius: var(--r-sm);
  color: var(--ink-muted);
  font-size: var(--fs-label);
  padding: var(--sp-2);
  margin-top: var(--sp-2);
}

.goals-clear[hidden] {
  display: none;
}

/* Honest degradation surfaces sit inside the edge-to-edge column, so re-inset. */
.conversation .voice-banner,
.conversation .banners,
.conversation #stats {
  margin-inline: 1rem;
}

#transcript {
  flex: 1;
  overflow-y: auto;
  list-style: none;
  margin: 0;
  padding: 1rem 1rem 0;
  display: flex;
  flex-direction: column;
  gap: 0.55rem;
}

/* Chat bubbles (mockup .bubble): tutor left, learner right. Body-size text —
   the learner reads this screen for 20+ minutes (§21). */
#transcript li {
  max-width: 82%;
  padding: 0.6rem 0.9rem;
  border-radius: 1rem;
  font-size: var(--fs-body);
  line-height: 1.5;
}

/* "Tutor is replying…" indicator: three gently pulsing dots. */
#transcript li.thinking {
  display: inline-flex;
  gap: 0.3rem;
  align-items: center;
  min-height: 1.5em;
}

.thinking .dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--ink-muted);
  opacity: 0.4;
  animation: thinking-pulse 1.2s ease-in-out infinite;
}

.thinking .dot:nth-child(2) {
  animation-delay: 0.2s;
}

.thinking .dot:nth-child(3) {
  animation-delay: 0.4s;
}

@keyframes thinking-pulse {
  0%,
  100% {
    opacity: 0.3;
  }
  50% {
    opacity: 0.9;
  }
}

@media (prefers-reduced-motion: reduce) {
  .thinking .dot {
    animation: none;
    opacity: 0.6;
  }
}

#transcript li.tutor {
  background: var(--surface-2);
  border-bottom-left-radius: 0.35rem;
  align-self: flex-start;
}

#transcript li.learner {
  background: var(--brand-soft);
  color: var(--brand);
  border-bottom-right-radius: 0.35rem;
  align-self: flex-end;
}

/* Speaker eyebrow added in CSS so addLine() stays a plain textContent set. */
#transcript li.tutor::before,
#transcript li.learner::before {
  display: block;
  font-size: var(--fs-label);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  font-weight: 700;
  opacity: 0.6;
  margin-bottom: 0.15rem;
}

#transcript li.tutor::before {
  content: "Tutor";
}

#transcript li.learner::before {
  content: "You";
}

/* Meta lines (banners, safety redirects) — a centered pill, not a bubble. */
#transcript li.meta {
  align-self: center;
  max-width: 100%;
  text-align: center;
  background: var(--surface-2);
  color: var(--ink-muted);
  border-radius: var(--r-pill);
  font-size: var(--fs-label);
  padding: 0.25rem 0.75rem;
}

/* The manual-reconnect meta line carries a button, so it can't be a tight pill. */
#transcript li.meta.reconnect-failed {
  border-radius: var(--r-md);
}

/* Safety/crisis redirects (safety.redirect) must NOT read as a muted meta pill —
   restore warning-level emphasis so the only safety response can't be overlooked
   (AGENTS.md safety; warn tokens are defined in both light and dark). */
#transcript li.meta.safety {
  background: var(--warn-bg);
  color: var(--warn-ink);
  border: 1px solid var(--warn-border);
  border-radius: var(--r-md);
  font-size: var(--fs-body);
  font-weight: 600;
  padding: 0.6rem 0.9rem;
}

/* Docked mic + text row (mockup .mic-dock / .dock-inner). */
.mic-dock {
  flex: none;
  padding: 0.8rem 1rem calc(0.8rem + env(safe-area-inset-bottom));
  border-top: 1px solid var(--border);
  background: var(--surface);
  box-shadow: var(--e-2);
}

.dock-inner {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
}

/* .ptt promoted to the first-class mic control — idle / latched / active.
   JS keeps toggling `.active` (recording) and `.latched` (tap-to-talk on) and
   swapping the label text; only the affordance styling changes here. */
.ptt {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.6rem;
  width: 100%;
  min-height: 60px;
  border: none;
  border-radius: var(--r-lg);
  background: var(--brand);
  color: var(--brand-ink);
  font: 600 1.05rem system-ui;
  cursor: pointer;
  touch-action: none;
  user-select: none;
  -webkit-user-select: none;
}

/* enterTextOnly()/enterCapHit() hide the mic via `hidden`; display:flex above
   would otherwise beat the UA [hidden] rule, so re-assert none. */
.ptt[hidden] {
  display: none;
}

.ptt::before {
  content: "";
  flex: none;
  width: 22px;
  height: 22px;
  background: currentColor;
  -webkit-mask: var(--mic-icon) center / contain no-repeat;
  mask: var(--mic-icon) center / contain no-repeat;
}

/* Latched (tap-to-talk armed): a quiet ring, still brand-colored. */
.ptt.latched {
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--brand) 40%, transparent);
}

/* Recording: accent fill + a gentle pulse (gated on reduced-motion). */
.ptt.active {
  background: var(--accent);
  animation: mic-pulse 1.4s ease-in-out infinite;
}

@keyframes mic-pulse {
  0%,
  100% {
    box-shadow: 0 0 0 0 color-mix(in srgb, var(--accent) 55%, transparent);
  }
  50% {
    box-shadow: 0 0 0 10px color-mix(in srgb, var(--accent) 0%, transparent);
  }
}

@media (prefers-reduced-motion: reduce) {
  .ptt.active {
    animation: none;
  }

  .goal-mark,
  summary.goals-head::after {
    transition: none;
  }
}

.text-row {
  display: flex;
  gap: 0.5rem;
}

.text-row input {
  flex: 1;
  margin-bottom: 0;
}

.text-row button[type="submit"] {
  flex: none;
  background: transparent;
  color: var(--brand);
  font-weight: 600;
}

/* Desktop: one centered reading column, never a 320px sliver in whitespace. */
@media (min-width: 768px) {
  #transcript {
    max-width: 46rem;
    margin-inline: auto;
    width: 100%;
  }

  .mic-dock {
    display: flex;
    justify-content: center;
  }

  .dock-inner {
    max-width: 46rem;
    width: 100%;
  }

  .conversation .voice-banner,
  .conversation .banners,
  .conversation #stats {
    max-width: 46rem;
    margin-inline: auto;
  }

  /* The container stays full-bleed so its bottom border spans the window like
     .focus-top's; only the contents join the reading column. */
  .goals > summary,
  .goals > .goals-panel {
    max-width: 46rem;
    margin-inline: auto;
    width: 100%;
  }
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
}

/* ---------- degradation UX (M4d) ---------- */

/* Persistent honest banner: voice off / cap hit. */
.voice-banner {
  margin: 0 0 0.5rem;
  padding: 0.6rem 0.8rem;
  border-radius: 0.6rem;
  background: var(--warn-bg);
  border: 1px solid var(--warn-border);
  color: var(--warn-ink);
  font-size: 0.9rem;
}

.voice-banner[hidden] {
  display: none;
}

/* Info tone: a calm status (e.g. text-only tutor replies), not a warning. */
.voice-banner.info {
  background: var(--info-bg);
  border-color: var(--info-border);
  color: var(--info-ink);
}

/* Transient, stacked notices — visually distinct from errors. */
.banners {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
}

.banner {
  padding: 0.45rem 0.7rem;
  border-radius: 0.5rem;
  font-size: 0.85rem;
}

.banner-degraded {
  background: var(--info-bg);
  border: 1px solid var(--info-border);
  color: var(--info-ink);
}

.banner-info {
  background: var(--ok-bg);
  border: 1px solid var(--ok-border);
  color: var(--brand);
}

.banner-error {
  background: var(--error-bg);
  border: 1px solid var(--error-border);
  color: var(--error-ink);
}

/* "Not what I said?" on a learner line + manual reconnect. */
button.retry {
  display: block;
  margin-top: 0.4rem;
  padding: 0.2rem 0.55rem;
  font-size: 0.75rem;
  background: transparent;
  color: var(--brand);
  border: 1px solid var(--brand);
}

.reconnect-failed button {
  margin-left: 0.4rem;
  padding: 0.2rem 0.6rem;
  font-size: 0.85rem;
}

/* ---------- coaching view ---------- */

.coaching h2 {
  font-size: 1.05rem;
  margin: 0 0 0.6rem;
  color: var(--brand);
}

/* Section rhythm through spacing, not extra borders (§22). */
.coaching section {
  margin-bottom: 1.5rem;
}

.coaching ul,
.coaching ol {
  margin: 0;
  padding-left: 1.1rem;
}

.targets ol,
.coaching-transcript ol {
  list-style: none;
  padding-left: 0;
}

.target {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  padding: 0.85rem;
  margin-bottom: 0.6rem;
  box-shadow: var(--e-1);
}

/* Humanized category label ("Repeated pattern"), not an enum shout — the
   humanizeCode output is already sentence case, keep it that way. */
.category {
  display: inline-block;
  font-size: var(--fs-label);
  letter-spacing: 0.02em;
  font-weight: 600;
  background: var(--brand-soft);
  color: var(--brand);
  border-radius: var(--r-pill);
  padding: 0.2rem 0.55rem;
  margin-bottom: 0.35rem;
}

.explanation {
  margin: 0.25rem 0;
}

.alt {
  margin: 0.25rem 0;
  font-style: italic;
}

.alt-label {
  font-style: normal;
  font-weight: 600;
}

/* The natural alternative, blanked while the repair loop asks for it from memory. */
.repair-hidden {
  color: var(--ink-muted);
  font-weight: 400;
  font-style: italic;
}

.rate {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-top: 0.5rem;
}

.rate button {
  padding: 0.3rem 0.8rem;
  font-size: 0.85rem;
  min-height: 36px;
  background: transparent;
  color: var(--ink-muted);
  border: 1px solid var(--border);
  border-radius: var(--r-pill);
}

.rate button:hover {
  color: var(--brand);
  border-color: var(--brand);
}

.rate-status {
  font-size: 0.85rem;
  color: var(--ink-muted);
}

/* Transcript uncertainty — deliberately NOT the feedback/highlight styling. */
.uncertain {
  border-bottom: 1px dotted var(--accent);
  font-style: italic;
  color: var(--ink-muted);
}

.coaching-transcript li {
  margin: 0.4rem 0;
  padding: 0.5rem 0.75rem;
  border-radius: 1rem;
  max-width: 85%;
  font-size: var(--fs-sub);
  line-height: 1.4;
}

.coaching-transcript li.learner {
  background: var(--brand-soft);
  border-bottom-right-radius: 0.35rem;
  margin-left: auto;
}

.coaching-transcript li.tutor {
  background: var(--surface-2);
  border-bottom-left-radius: 0.35rem;
}

.speaker {
  font-weight: 600;
  margin-right: 0.25rem;
}

/* Praise: the named ability leads, its single supporting quote follows. */
.highlights .ability {
  display: block;
  font-weight: 600;
}

.highlights .quote {
  color: var(--ink-muted);
  font-style: italic;
}

/* Session-complete header: the completion moment before the details. */
.coaching-head {
  margin-bottom: 1.25rem;
}

.coaching-head .eyebrow {
  color: var(--accent);
  margin-bottom: 0.25rem;
}

.coaching-head h1 {
  margin: 0;
}

/* Three-score debrief: per-item status rows, never an overall pass/fail
   banner. A dot carries the state alongside the text (never color alone). */
.debrief-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.45rem;
}

.debrief-row {
  display: flex;
  align-items: baseline;
  gap: 0.55rem;
}

.debrief-dot {
  flex: none;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  border: 1.5px solid var(--control-border);
  align-self: center;
}

.debrief-row.done .debrief-dot {
  background: var(--brand);
  border-color: var(--brand);
}

.debrief-row.focus .debrief-dot {
  background: var(--accent);
  border-color: var(--accent);
}

.debrief-label {
  font-weight: 600;
  min-width: 0;
}

.debrief-status {
  margin-left: auto;
  flex: none;
  font-size: var(--fs-sub);
  color: var(--ink-muted);
}

.debrief-empty {
  color: var(--ink-muted);
  margin: 0;
}

/* End-of-report actions: one Continue, quieter alternatives beside it. */
.coaching-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
  margin-top: 0.75rem;
}

/* Transcript is secondary: collapsed, heading inline inside the disclosure. */
.coaching-transcript {
  margin-top: 1.5rem;
  padding-top: 1rem;
  border-top: 1px solid var(--border);
}

.coaching-transcript summary {
  cursor: pointer;
}

/* Narrow screens: the status drops under its label instead of squeezing it. */
@media (max-width: 480px) {
  .debrief-row {
    flex-wrap: wrap;
  }

  .debrief-status {
    margin-left: 1.05rem;
    flex-basis: 100%;
  }
}

.coaching-transcript summary h2 {
  display: inline;
}

/* Repair is practice, not mastery: muted panel, no celebration chrome. */
.repair {
  background: var(--surface-2);
  border: 1px dashed var(--control-border);
  border-radius: var(--r-md);
  padding: 0.85rem;
  box-shadow: none;
}

.repair h2 {
  color: var(--ink);
  font-size: 1rem;
  margin-top: 0;
}

.repair-prompt {
  font-weight: 600;
}

.repair-say {
  display: block;
  margin-bottom: 0.5rem;
}

.repair textarea {
  width: 100%;
  padding: 0.6rem;
  border: 1px solid var(--control-border);
  border-radius: 0.5rem;
  font: inherit;
  background: var(--surface);
  color: var(--ink);
}

/* M1 repair loop: notice → retrieve → variation. The struck-through span is
   the learner's own words; the alternative is only ever shown in the notice. */
.repair-step {
  margin: 0.5rem 0;
}

.repair-prompt s {
  color: var(--ink-muted);
  margin-right: 0.35rem;
}

.repair-expected,
.repair-step-done {
  font-weight: 600;
  margin: 0.5rem 0;
}

.item {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
  padding: 0.5rem 0;
  border-bottom: 1px solid var(--border);
  list-style: none;
}

.items ul {
  padding-left: 0;
}

.item-text {
  font-weight: 600;
}

.item-kind {
  font-size: 0.75rem;
  color: var(--ink-muted);
}

.item-status {
  font-size: 0.85rem;
  color: var(--brand);
}

.item-actions {
  margin-left: auto;
  display: flex;
  gap: 0.4rem;
}

.item-actions button {
  padding: 0.3rem 0.7rem;
  font-size: 0.85rem;
}

.coaching-next {
  margin-top: 1rem;
}

.rec {
  font-weight: 600;
}

/* ---------- page head (shared by every browse hub) ---------- */
/* One consistent pattern for screen titles: eyebrow context → title → sub.
   Spacing (not a card border) separates it from the content below. */
.page-head {
  margin: 0.35rem 0 1.35rem;
}

.page-head .title,
.dashboard .title {
  font-size: var(--fs-display);
  margin: 0.2rem 0 0.1rem;
  color: var(--brand);
}

.page-head .sub,
.dashboard .sub {
  color: var(--ink-muted);
  font-size: var(--fs-sub);
  margin: 0.1rem 0 0;
}

/* Learn's view switch (Situations / Grammar). Scrolls rather than wraps so a
   third view later cannot silently push one off the row. */
.topic-card .topic-skills .eyebrow {
  display: block;
  margin-bottom: 0.15rem;
}

.subtabs {
  display: flex;
  gap: 0.35rem;
  margin: 0.9rem 0 1.1rem;
  overflow-x: auto;
  scrollbar-width: none;
}

.subtabs::-webkit-scrollbar {
  display: none;
}

button.subtab {
  flex: 0 0 auto;
  background: none;
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 0.4rem 0.9rem;
  font-size: var(--fs-sub);
  color: var(--ink-muted);
  cursor: pointer;
}

button.subtab.active {
  background: var(--brand);
  border-color: var(--brand);
  color: var(--brand-ink);
}

/* Tertiary text-styled action: navigation/links that must not compete with
   real buttons. */
button.link-btn {
  background: none;
  border: none;
  padding: 0.35rem 0.25rem;
  color: var(--brand);
  font-size: var(--fs-sub);
  font-weight: 600;
  cursor: pointer;
  border-radius: var(--r-sm);
}

button.link-btn:hover {
  text-decoration: underline;
}

/* Quiet link rows (Progress footer): inline, separated by spacing only. */
.quiet-links {
  display: flex;
  gap: 1.25rem;
  margin-top: 0.75rem;
}

/* ---------- Topics dashboard + detail (Learn tab, BL-2) ---------- */

.progress-empty {
  color: var(--ink-muted);
}

.eyebrow {
  font-size: var(--fs-label);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-muted);
  font-weight: 700;
  margin: 0 0 0.55rem;
}

.chip {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  font-size: var(--fs-label);
  font-weight: 600;
  letter-spacing: 0.02em;
  background: var(--brand-soft);
  color: var(--brand);
  border-radius: var(--r-pill);
  padding: 0.2rem 0.55rem;
}

.chip.ghost {
  background: transparent;
  border: 1px solid var(--border);
  color: var(--ink-muted);
}

.cat {
  margin-bottom: 1.35rem;
}

.cat-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 0.6rem;
}

.cat-head h3 {
  margin: 0;
  font-size: var(--fs-title);
  color: var(--ink);
}

/* Mobile: single-column list (topic titles are full objectives, not labels). */
.topic-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 0.7rem;
}

.topic-card {
  text-align: left;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  padding: 0.75rem 0.8rem;
  cursor: pointer;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.45rem;
  box-shadow: var(--e-1);
  color: var(--ink);
}

.topic-card:hover {
  border-color: var(--brand);
}

.topic-card h4 {
  margin: 0;
  font-size: 0.98rem;
  line-height: 1.25;
  color: var(--ink);
}

.topic-card p {
  margin: 0;
  font-size: var(--fs-sub);
  color: var(--ink-muted);
  line-height: 1.35;
}

/* Detail (immersive card, shell-free): hero + skills + sticky-ish CTA. */
.detail .back {
  margin-bottom: 1rem;
  padding: 0.3rem 0.7rem;
  font-size: var(--fs-sub);
}

.detail-hero .chip {
  margin-bottom: 0.7rem;
}

.detail-hero h1 {
  margin: 0.1rem 0 0.35rem;
  font-size: var(--fs-display);
  color: var(--ink);
}

.detail-hero p {
  margin: 0;
  color: var(--ink-muted);
  font-size: var(--fs-sub);
  line-height: 1.5;
}

.detail-brief,
.detail-skills {
  margin-top: 1.5rem;
}

.detail-brief p {
  margin: 0;
  line-height: 1.5;
}

.detail-brief .eyebrow {
  margin-bottom: 0.4rem;
}

.detail-skills ul {
  margin: 0;
  padding-left: 1.1rem;
  color: var(--ink-muted);
  font-size: var(--fs-sub);
}

.detail-cta {
  margin-top: 1.5rem;
}

.detail-cta .hint {
  text-align: center;
  margin: 0.6rem 0 0;
}

/* Grammar mini-lessons (Rules detail) */
.mini-lesson h2 {
  margin: 0.25rem 0 0.15rem;
  font-size: var(--fs-title);
  color: var(--ink);
}

.tip-block {
  margin-top: 1rem;
}

.tip-formula {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: var(--fs-sub);
  color: var(--ink);
}

.tip-examples {
  margin: 0;
  padding-left: 1.1rem;
}

.tip-examples .tip-en {
  margin: 0.35rem 0 0;
  color: var(--ink);
}

.tip-examples .tip-ru {
  margin: 0.1rem 0 0.45rem;
}

.tip-forms {
  width: 100%;
  margin-top: 0.4rem;
  border-collapse: collapse;
  font-size: var(--fs-sub);
}

.tip-forms th,
.tip-forms td {
  padding: 0.3rem 0.5rem 0.3rem 0;
  text-align: left;
  vertical-align: top;
  border-bottom: 1px solid var(--border);
}

.tip-forms th {
  width: 30%;
  font-weight: 600;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  color: var(--ink);
}

.tip-mistakes {
  margin: 0.35rem 0 0;
  padding-left: 0;
  list-style: none;
}

.tip-mistakes li {
  margin-bottom: 0.7rem;
}

.tip-mistakes p {
  margin: 0.1rem 0;
}

/* The glyph carries the verdict; keep it off the text so long lines wrap flush. */
.tip-mistakes .mistake-bad,
.tip-mistakes .mistake-good {
  display: flex;
  gap: 0.4rem;
}

.tip-mistakes .mistake-bad {
  color: var(--danger, #8a3b3b);
}

.tip-mistakes .mistake-good {
  color: var(--ok, #2f6b4f);
}

.tip-exercises {
  margin: 0.4rem 0 0;
  padding-left: 1.1rem;
}

.tip-exercise {
  margin-bottom: 0.9rem;
}

.tip-options {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  margin-top: 0.45rem;
}

.tip-input {
  width: 100%;
  margin-top: 0.4rem;
  margin-bottom: 0.35rem;
}

.tip-feedback.tip-ok {
  color: var(--ok, #2f6b4f);
}

.tip-feedback.tip-bad {
  color: var(--danger, #8a3b3b);
}

.tip-missions ul {
  margin: 0.35rem 0 0;
  padding-left: 0;
  list-style: none;
}

.tip-missions li {
  margin-bottom: 0.4rem;
}

.mini-lesson {
  margin-top: 1.5rem;
  padding-top: 1.25rem;
  border-top: 1px solid var(--border);
}

/* Desktop: fluid multi-column grid, capped so cards don't stretch (mockup). */
@media (min-width: 768px) {
  .topic-grid {
    grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
  }
}

/* ---------- Profile (You tab) — grouped rows (mockup .you) ---------- */

.profile .eyebrow {
  margin-top: 1.25rem;
}

/* Learner facts as label→value rows. */
.profile-fields {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 0.55rem 1rem;
  margin: 0;
}

.profile-fields dt {
  color: var(--ink-muted);
  font-size: var(--fs-sub);
  font-weight: 400;
}

.profile-fields dd {
  margin: 0;
  text-align: right;
  font-weight: 600;
}

/* Placeholder account rows (e.g. Google link) as bordered rows. */
.profile-stubs {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.profile-stubs li {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.7rem;
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  padding: 0.7rem 0.85rem;
}

.profile-stubs b {
  display: block;
  font-size: var(--fs-body);
  font-weight: 600;
}

.profile-stubs li div span {
  font-size: var(--fs-sub);
  color: var(--ink-muted);
}

/* Natural-width secondary actions in a row — never stretched bars (§20). */
.profile-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
  margin-top: 1.25rem;
}

.profile > .card {
  margin-bottom: 1rem;
}

/* ---------- Your data (BL-6 privacy / account) ---------- */
.your-data .eyebrow {
  margin-top: 1.25rem;
}

.consent-toggles {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.consent-toggle {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 0.75rem;
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  padding: 0.7rem 0.85rem;
  cursor: pointer;
}

.consent-toggle-text {
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
  min-width: 0;
}

.consent-toggle-label {
  font-weight: 600;
  font-size: var(--fs-body);
}

.consent-toggle-hint {
  font-size: var(--fs-sub);
  color: var(--ink-muted);
}

.consent-toggle input[type="checkbox"] {
  width: 1.15rem;
  height: 1.15rem;
  margin-top: 0.15rem;
  flex-shrink: 0;
  accent-color: var(--brand);
}

.your-data #your-data-export,
.your-data #your-data-delete,
.your-data .your-data-delete,
.your-data #your-data-back {
  margin-top: 0.5rem;
}

.your-data-delete {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  margin-top: 0.5rem;
}

.note.danger {
  color: var(--error-ink);
  background: var(--error-bg);
  border: 1px solid var(--error-border);
  border-radius: var(--r-sm);
  padding: 0.55rem 0.7rem;
}

button.danger {
  background: var(--accent);
  color: #fff;
  border-color: var(--accent);
}

button.danger-outline {
  color: var(--accent);
  border-color: var(--accent);
  background: transparent;
}

/* ---------- Quick review: the prompt IS the screen ---------- */

.review-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  margin-bottom: 1.25rem;
}

.review-head h1 {
  margin: 0;
  font-size: var(--fs-title);
}

.review-progress {
  margin: 0;
  font-size: var(--fs-sub);
  color: var(--ink-muted);
}

.review-hint {
  margin: 0 0 0.5rem;
}

/* The learning content gets the visual weight, not the chrome (§21/§37). */
.review-prompt {
  font-size: 1.45rem;
  line-height: 1.35;
  font-weight: 600;
  margin: 0 0 0.75rem;
}

.review-result {
  min-height: 1.3em;
  margin: 0 0 0.75rem;
}

/* M1: the card shows a cue, the learner produces, then the answer is revealed. */
.review-cue {
  margin: 0 0 0.75rem;
}

.review-answer {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
  margin: 0 0 0.75rem;
}

.review-answer input {
  flex: 1 1 12rem;
  padding: 0.6rem;
  border: 1px solid var(--control-border);
  border-radius: 0.5rem;
  font: inherit;
  background: var(--surface);
  color: var(--ink);
}

.review-expected {
  font-size: 1.45rem;
  line-height: 1.35;
  font-weight: 600;
  margin: 0 0 0.75rem;
}

.review-adjust {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.6rem;
  margin: 0.75rem 0;
}

.review .link-btn {
  display: block;
  margin: 0.9rem auto 0;
}

/* ---------- Mission preparation: study the phrase, then produce it ---------- */

/* The phrase itself carries the visual weight, wherever it is legitimately
   shown (STUDY after "Show me", and the verdict). It is simply absent from the
   DOM in between. */
.prepare-target {
  font-size: 1.45rem;
  line-height: 1.35;
  font-weight: 600;
  margin: 0 0 0.75rem;
}

/* The meaning in the learner's own language — present in every state. */
.prepare-cue {
  margin: 0 0 0.75rem;
  color: var(--ink-muted);
}

.prepare .link-btn {
  display: block;
  margin: 0.9rem auto 0;
}

/* ---------- Progress: stat tiles + sectioned cards ---------- */

.stat-row {
  display: flex;
  gap: 0.75rem;
}

.stat {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.1rem;
  padding: 0.7rem 0.5rem;
  background: var(--surface-2);
  border-radius: var(--r-md);
}

.stat-value {
  font-size: 1.35rem;
  font-weight: 700;
  color: var(--brand);
}

.stat-label {
  font-size: var(--fs-label);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--ink-muted);
  font-weight: 600;
}

.progress > .card {
  margin-bottom: 1rem;
}

.progress-mastery {
  margin-top: 1rem;
}

.progress-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
  margin-top: 1.25rem;
}

.progress-sessions {
  margin: 0.75rem 0 0;
}

.progress-review-hint {
  margin-top: 0.5rem;
}

.vocab-split {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  margin-bottom: 0.6rem;
}

.vocab-split p {
  margin: 0;
}

.vocab-split strong {
  font-size: 1.15rem;
  color: var(--brand);
  margin-right: 0.25rem;
}

.vocab-split span {
  color: var(--ink-muted);
  font-size: var(--fs-sub);
}

.vocab-bar {
  height: 8px;
  border-radius: var(--r-pill);
  background: var(--surface-2);
  overflow: hidden;
}

.vocab-bar-fill {
  display: block;
  height: 100%;
  border-radius: var(--r-pill);
  background: var(--brand);
}

/* ---------- Session history (BL-7) ---------- */
.session-history-list {
  list-style: none;
  margin: 1rem 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.65rem;
}

.session-history-row {
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  padding: 0.75rem 0.85rem;
  display: flex;
  flex-direction: column;
  gap: 0.55rem;
}

.session-history-meta {
  margin: 0;
  font-size: var(--fs-body);
}

.session-history-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}

.session-history-actions button {
  min-width: 6rem;
}

.session-history-obs {
  display: flex;
  align-items: flex-start;
  gap: 0.55rem;
  font-size: var(--fs-sub);
  color: var(--ink-muted);
  cursor: pointer;
}

.session-history-obs input {
  width: 1.1rem;
  height: 1.1rem;
  margin-top: 0.1rem;
  flex-shrink: 0;
  accent-color: var(--brand);
}

.session-history #session-history-back,
.session-history #session-history-retry,
.session-history #session-history-delete-confirm,
.session-history #session-history-delete-cancel {
  margin-top: 0.5rem;
}
