/* ==========================================================================
   SpeakingPractice — design tokens
   Source of truth: design-system/SpeakingPractice/MASTER.md
   Style: Minimalism & Swiss — grid, whitespace, high contrast, no decoration.
   ========================================================================== */

@font-face {
  font-family: "Vazirmatn";
  src: url("../fonts/Vazirmatn.woff2") format("woff2-variations");
  font-weight: 100 900;
  font-display: swap;
}

/* Interface fonts. The settings page lets each user pick one English family and
 * one Persian family; <html data-font-en data-font-fa> swaps the two token
 * stacks below. All files are served from this origin (scripts/fetch_fonts.py):
 * Google Fonts is unreliable from Iran, and a font request must never delay the
 * first paint. An @font-face nobody selects downloads nothing, so listing them
 * all is free. */
@font-face {
  font-family: "Roboto";
  src: url("../fonts/Roboto.woff2") format("woff2");
  font-weight: 100 900;
  font-display: swap;
}
@font-face {
  font-family: "Montserrat";
  src: url("../fonts/Montserrat.woff2") format("woff2");
  font-weight: 100 900;
  font-display: swap;
}
@font-face {
  font-family: "Open Sans";
  src: url("../fonts/OpenSans.woff2") format("woff2");
  font-weight: 300 800;
  font-display: swap;
}
@font-face {
  font-family: "Playfair Display";
  src: url("../fonts/PlayfairDisplay.woff2") format("woff2");
  font-weight: 400 900;
  font-display: swap;
}
@font-face {
  font-family: "Shabnam";
  src: url("../fonts/Shabnam-Regular.woff2") format("woff2");
  font-weight: 400;
  font-display: swap;
}
@font-face {
  font-family: "Shabnam";
  src: url("../fonts/Shabnam-Bold.woff2") format("woff2");
  font-weight: 700;
  font-display: swap;
}

/* Proxima Nova is a commercial family and is never committed. If a licensed
 * webfont is dropped in as ProximaNova-Regular.woff2 (+ -Bold), the settings
 * page offers it automatically and the stack below becomes its first choice;
 * until then nothing references the missing file. */
@font-face {
  font-family: "Proxima Nova";
  src: url("../fonts/ProximaNova-Regular.woff2") format("woff2");
  font-weight: 400;
  font-display: swap;
}
@font-face {
  font-family: "Proxima Nova";
  src: url("../fonts/ProximaNova-Bold.woff2") format("woff2");
  font-weight: 700;
  font-display: swap;
}

:root {
  color-scheme: light;

  /* colour — palette 264653 / 2a9d8f / e9c46a / f4a261 / e76f51.
     The five hues are the palette; the extra steps are lightness-only
     derivations where a hue could not carry text at 4.5:1. Every pair below
     was measured, not chosen by eye (see the contrast table in
     design-system/SpeakingPractice/MASTER.md).

     #2a9d8f with white text is 3.32:1 and #e76f51 with white is 3.09:1, so
     neither may be a text background. They stay as fills for the *accent* and
     for tints, and text-bearing fills use the darkened steps #1f7a70 and
     #b8442a (5.15:1 and 5.39:1). */
  --primary: #264653;          /* deep teal — filled buttons, 10.08:1 with white */
  --primary-bright: #2a9d8f;   /* persian green — hover, focus ring, accents */
  --primary-ink: #17323c;      /* headings, pressed states */
  --on-primary: #ffffff;
  --secondary: #2a9d8f;
  --accent: #e9c46a;           /* maize — 6.03:1 with #264653 */
  --on-accent: #264653;
  --bg: #f7faf9;
  --fg: #264653;               /* 10.08:1 on card, 9.60:1 on bg */
  --card: #ffffff;
  --muted: #e9efee;
  --muted-fg: #4d6b73;         /* 5.73:1 on card */
  --border: #ccdad7;
  --border-soft: #e2ebe9;
  --destructive: #b8442a;      /* burnt sienna, darkened for text: 5.39:1 */
  --on-destructive: #ffffff;
  --destructive-soft: #e76f51; /* the palette hue, fills only */
  --good: #1f7a70;
  --warn: #e9c46a;
  --ring: #2a9d8f;

  /* score bands: a tint of the palette hue plus a text step that passes 4.5:1
     on that tint. Measured, see MASTER.md. */
  --band-excellent-bg: #dcefec;
  --band-excellent-fg: #1a6b62;
  --band-good-bg: #e6f3f0;
  --band-good-fg: #1f7a70;
  --band-fair-bg: #fbf0d4;
  --band-fair-fg: #7a5a12;
  --band-weak-bg: #f8e3dc;
  --band-weak-fg: #a33d22;

  /* chat: two clearly different surfaces, not two tints of the same grey */
  --tutor-bg: #eef4f3;
  --tutor-fg: #264653;
  --learner-bg: #1f7a70;
  --learner-fg: #ffffff;

  /* spacing — 4/8 rhythm */
  --s1: 4px;
  --s2: 8px;
  --s3: 12px;
  --s4: 16px;
  --s5: 24px;
  --s6: 32px;
  --s7: 48px;
  --s8: 64px;

  /* shape */
  --r-card: 10px;
  --r-ctl: 8px;
  --r-md: 10px;
  --r-pill: 999px;
  --shadow: 0 1px 2px rgba(38, 70, 83, 0.08);

  /* type — named sizes for the conversation view; body is 16px, .small is
     0.8125rem (13px), so these line up with what the page already does. */
  --fs-xs: 12px;
  --fs-sm: 13px;
  --fs-base: 15px;
  --fs-lg: 20px;

  /* type */
  --font-fa: "Vazirmatn", "Segoe UI", Tahoma, sans-serif;
  --font-lat: "Roboto", "Vazirmatn", "Segoe UI", Tahoma, sans-serif;
  --font-ipa: "Segoe UI", "Noto Sans", "DejaVu Sans", sans-serif;
  --font-mono: ui-monospace, "Cascadia Mono", Consolas, monospace;

  /* motion */
  --ease: cubic-bezier(0.2, 0.8, 0.2, 1);
  --t-fast: 150ms;
  --t-base: 220ms;
}

/* The chosen interface fonts. The attributes are written on <html> by the page
 * template from the stored settings, so the first paint already uses them; the
 * settings page only changes the setting and reloads. Every stack keeps the
 * system fallbacks, so a missing file degrades instead of blanking the text. */
html[data-font-en="roboto"] { --font-lat: "Roboto", "Vazirmatn", "Segoe UI", Tahoma, sans-serif; }
html[data-font-en="montserrat"] { --font-lat: "Montserrat", "Vazirmatn", "Segoe UI", Tahoma, sans-serif; }
html[data-font-en="opensans"] { --font-lat: "Open Sans", "Vazirmatn", "Segoe UI", Tahoma, sans-serif; }
html[data-font-en="playfair"] { --font-lat: "Playfair Display", "Vazirmatn", "Segoe UI", Tahoma, sans-serif; }
html[data-font-en="proxima"] { --font-lat: "Proxima Nova", "Montserrat", "Vazirmatn", "Segoe UI", Tahoma, sans-serif; }
html[data-font-fa="shabnam"] { --font-fa: "Shabnam", "Vazirmatn", "Segoe UI", Tahoma, sans-serif; }

/* Dark theme. `data-theme` is set on <html> by an inline script before first
   paint, so there is no flash and no duplicated media query. The palette's
   hues are held; only lightness moves, and the green is lifted to #3fb8a8
   because #2a9d8f on a dark card is 3.2:1. */
[data-theme="dark"] {
  color-scheme: dark;

  --primary: #2a9d8f;
  --primary-bright: #3fb8a8;
  --primary-ink: #9fe3d8;
  --on-primary: #06231f;       /* 4.98:1 on #2a9d8f */
  --secondary: #3fb8a8;
  --accent: #e9c46a;
  --on-accent: #264653;
  --bg: #12232a;
  --fg: #e6efed;               /* 11.77:1 on card */
  --card: #1a3038;
  --muted: #223b44;
  --muted-fg: #a9c0c4;         /* 7.23:1 on card */
  --border: #3a5a64;
  --border-soft: #2a444d;
  --destructive: #e76f51;
  --on-destructive: #2a0f08;
  --destructive-soft: #e76f51;
  --good: #3fb8a8;
  --warn: #e9c46a;
  --ring: #3fb8a8;

  --band-excellent-bg: #1c3a42;
  --band-excellent-fg: #9fe3d8;
  --band-good-bg: #1c3a42;
  --band-good-fg: #9fe3d8;
  --band-fair-bg: #3a3520;
  --band-fair-fg: #e9c46a;
  --band-weak-bg: #3d2620;
  --band-weak-fg: #f0a58f;

  --tutor-bg: #223b44;
  --tutor-fg: #e6efed;
  --learner-bg: #2a9d8f;
  --learner-fg: #06231f;

  --shadow: 0 1px 2px rgba(0, 0, 0, 0.4);
}

[lang="en"] body {
  font-feature-settings: normal;
  /* An English page is set in the English interface font the user picked; any
     Persian glyph it still contains falls through to the Persian family. */
  font-family: var(--font-lat), var(--font-fa);
}

/* ------------------------------------------------------------------ reset */
*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  -webkit-text-size-adjust: 100%;
  /* The topbar is sticky, so an anchored target or a focused element would
     otherwise land underneath it. */
  scroll-padding-block-start: 72px;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--fg);
  font-family: var(--font-fa);
  font-size: 16px;
  line-height: 1.75;
  /* Vazirmatn's ss01 redraws Latin digits in Persian shapes. That is right for
     a Persian page and wrong for an English one, where "42" must look like 42
     — the DOM was already correct, only the glyphs disagreed. */
  font-feature-settings: "ss01";
  -webkit-font-smoothing: antialiased;
}

h1,
h2,
h3,
p,
ul,
ol,
figure {
  margin: 0;
}

button {
  font: inherit;
  color: inherit;
  cursor: pointer;
}

a {
  color: var(--primary);
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
}

:focus-visible {
  outline: 3px solid var(--ring);
  outline-offset: 2px;
  border-radius: 4px;
}

.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;
}

/* Latin / IPA inside RTL text. Without isolate, "/θ/" renders as "θ/". */
.ltr {
  direction: ltr;
  unicode-bidi: isolate;
  font-family: var(--font-lat);
}

.ipa {
  direction: ltr;
  unicode-bidi: isolate;
  font-family: var(--font-ipa);
  font-size: 1.05em;
  letter-spacing: 0.01em;
}

.num {
  font-variant-numeric: tabular-nums;
  direction: ltr;
  unicode-bidi: isolate;
}

/* ------------------------------------------------------------------ shell */
.topbar {
  position: sticky;
  top: 0;
  z-index: 20;
  display: flex;
  align-items: center;
  gap: var(--s5);
  padding: var(--s3) var(--s5);
  background: color-mix(in oklab, var(--bg) 92%, transparent);
  backdrop-filter: blur(8px);
  border-bottom: 1px solid var(--border-soft);
}

.brand {
  display: flex;
  align-items: center;
  gap: var(--s3);
  font-weight: 700;
  letter-spacing: -0.01em;
}

.brand svg {
  display: block;
}

.brand .sub {
  font-weight: 400;
  font-size: 0.8125rem;
  color: var(--muted-fg);
}

.nav {
  display: flex;
  gap: var(--s1);
  margin-inline-start: auto;
}

.nav a {
  display: inline-flex;
  align-items: center;
  min-height: 40px;
  padding: 0 var(--s4);
  border-radius: var(--r-pill);
  color: var(--muted-fg);
  text-decoration: none;
  font-size: 0.9375rem;
  transition: background var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
}

.nav a:hover {
  background: var(--muted);
  color: var(--fg);
}

.nav a[aria-current="page"] {
  background: var(--primary);
  color: var(--on-primary);
}

.page {
  max-width: 1120px;
  margin: 0 auto;
  padding: var(--s5) var(--s5) var(--s8);
}

/* ------------------------------------------------------------------- sets */
.sets {
  display: flex;
  gap: var(--s2);
  overflow-x: auto;
  padding: var(--s2) 0 var(--s3);
  scrollbar-width: thin;
}

.sets::-webkit-scrollbar {
  height: 4px;
}

.sets::-webkit-scrollbar-thumb {
  background: var(--border);
  border-radius: 2px;
}

.set-chip {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: var(--s2);
  min-height: 40px;
  padding: 0 var(--s4);
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--r-pill);
  font-size: 0.9375rem;
  color: var(--muted-fg);
  transition: border-color var(--t-fast) var(--ease), color var(--t-fast) var(--ease),
    background var(--t-fast) var(--ease);
}

.set-chip:hover {
  border-color: var(--primary-bright);
  color: var(--fg);
}

.set-chip[aria-pressed="true"] {
  background: var(--primary);
  border-color: var(--primary);
  color: var(--on-primary);
}

.set-chip .count {
  font-size: 0.75rem;
  opacity: 0.7;
}

/* ------------------------------------------------------------------ layout */
.grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 300px;
  gap: var(--s5);
  align-items: start;
}

.stack {
  display: grid;
  /* An implicit `auto` column sizes to the widest child's max-content, which
     let a four-column KPI grid push the whole page to 495px on a 375px phone.
     minmax(0, 1fr) pins the track to the container and lets the children
     shrink or scroll instead. */
  grid-template-columns: minmax(0, 1fr);
  gap: var(--s4);
}

.card {
  background: var(--card);
  border: 1px solid var(--border-soft);
  border-radius: var(--r-card);
  padding: var(--s5);
  box-shadow: var(--shadow);
}

.card--flat {
  box-shadow: none;
}

.eyebrow {
  display: flex;
  align-items: center;
  gap: var(--s2);
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted-fg);
}

.card h2 {
  font-size: 1rem;
  font-weight: 700;
}

/* -------------------------------------------------------------- sentence */
.target {
  margin: var(--s4) 0 var(--s3);
  font-size: 1.5rem;
  line-height: 1.5;
  font-weight: 500;
  color: var(--fg);
  direction: ltr;
  text-align: left;
  unicode-bidi: isolate;
  font-family: var(--font-lat);
}

@media (min-width: 768px) {
  .target {
    font-size: 1.75rem;
  }
}

.sentence-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s2);
}

.focus-chip {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  padding: 2px var(--s2);
  background: var(--muted);
  border-radius: var(--r-ctl);
  font-size: 0.875rem;
  color: var(--primary-ink);
}

.actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s2);
  margin-top: var(--s4);
}

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s2);
  min-height: 44px;
  padding: 0 var(--s4);
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--r-ctl);
  color: var(--fg);
  font-size: 0.9375rem;
  transition: background var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease),
    transform var(--t-fast) var(--ease);
}

.btn:hover {
  background: var(--muted);
  border-color: var(--primary-bright);
}

.btn:active {
  transform: translateY(1px);
}

.btn--primary {
  background: var(--primary);
  border-color: var(--primary);
  color: var(--on-primary);
}

.btn--primary:hover {
  background: var(--primary-ink);
  border-color: var(--primary-ink);
}

.btn[disabled],
.btn[aria-busy="true"] {
  opacity: 0.55;
  cursor: not-allowed;
  transform: none;
}

.btn svg {
  flex: 0 0 auto;
}

/* A link-sized variation for actions inside report cards: still a legal
 * touch target (36 px) but not the full 44 px primary button. */
.btn--sm {
  min-height: 36px;
  padding: 0 var(--s3);
  font-size: var(--fs-sm);
}

/* ---------------------------------------------------------------- record */
.record {
  display: grid;
  justify-items: center;
  gap: var(--s4);
  text-align: center;
}

.record-btn {
  position: relative;
  width: 96px;
  height: 96px;
  display: grid;
  place-items: center;
  background: var(--primary);
  color: var(--on-primary);
  border: 0;
  border-radius: var(--r-pill);
  transition: transform var(--t-base) var(--ease), background var(--t-base) var(--ease);
}

/* An inline SVG sits on the text baseline, which leaves descender space under
   it and pushes the glyph up out of the circle. Both wrappers must be block.
   The mic and the stop square occupy the SAME grid cell, so swapping them is a
   pure visibility change and nothing shifts by a pixel. */
.record-btn .rec-icon {
  grid-area: 1 / 1;
  display: grid;
  place-items: center;
  line-height: 0;
}

.record-btn .rec-icon svg {
  display: block;
}

.record-btn .rec-icon--stop {
  visibility: hidden;
}

.record-btn[aria-pressed="true"] .rec-icon--mic {
  visibility: hidden;
}

.record-btn[aria-pressed="true"] .rec-icon--stop {
  visibility: visible;
}

.record-btn.is-recording {
  background: var(--destructive-soft);
  color: var(--on-destructive);
}

.record-btn:hover {
  background: var(--primary-ink);
}

.record-btn[aria-pressed="true"] {
  background: var(--destructive);
  animation: breathe 1.6s ease-in-out infinite;
}

.record-btn[disabled] {
  background: var(--muted);
  color: var(--muted-fg);
  cursor: not-allowed;
  animation: none;
}

@keyframes breathe {
  0%,
  100% {
    box-shadow: 0 0 0 0 color-mix(in oklab, var(--destructive) 35%, transparent);
  }
  50% {
    box-shadow: 0 0 0 14px color-mix(in oklab, var(--destructive) 0%, transparent);
  }
}

.record-hint {
  font-size: 0.9375rem;
  color: var(--muted-fg);
  max-width: 34ch;
}

.record-timer {
  font-size: 0.875rem;
  color: var(--muted-fg);
  font-variant-numeric: tabular-nums;
  direction: ltr;
  unicode-bidi: isolate;
}

.meter {
  width: min(320px, 100%);
  height: 4px;
  background: var(--muted);
  border-radius: 2px;
  overflow: hidden;
  position: relative;
}

/* Where a healthy speaking level sits. Without it the bar is a number with no
 * reference, and a learner with a quiet mic reads "the bar barely moves" as
 * "the app is broken" instead of "my input level is low". */
.meter::before {
  content: "";
  position: absolute;
  inset-inline-start: 25%;
  inset-inline-end: 12%;
  top: 0;
  bottom: 0;
  background: color-mix(in oklab, var(--secondary) 22%, transparent);
  pointer-events: none;
}

.meter__fill {
  height: 100%;
  width: 0%;
  background: var(--secondary);
  border-radius: 2px;
}

.meter__fill.is-hot {
  background: var(--accent);
}

/* ---------------------------------------------------------------- result */
#result[hidden] {
  display: none;
}

.result-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s5);
}

.ring {
  position: relative;
  width: 132px;
  height: 132px;
  flex: 0 0 auto;
}

.ring svg {
  transform: rotate(-90deg);
}

.ring__track {
  stroke: var(--muted);
}

.ring__value {
  stroke: var(--primary);
  stroke-linecap: round;
  transition: stroke-dashoffset 700ms var(--ease), stroke var(--t-base) var(--ease);
}

.ring__label {
  position: absolute;
  inset: 0;
  display: grid;
  place-content: center;
  text-align: center;
  gap: 0;
}

.ring__score {
  font-size: 2rem;
  font-weight: 700;
  line-height: 1.1;
  font-variant-numeric: tabular-nums;
  direction: ltr;
  unicode-bidi: isolate;
}

.ring__of {
  font-size: 0.6875rem;
  color: var(--muted-fg);
}

.band {
  display: inline-flex;
  align-items: center;
  gap: var(--s2);
  padding: 2px var(--s3);
  border-radius: var(--r-pill);
  font-size: 0.8125rem;
  font-weight: 600;
  background: var(--muted);
  color: var(--primary-ink);
}

.band::before {
  content: "";
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: currentColor;
}

.band--excellent {
  background: var(--band-excellent-bg);
  color: var(--band-excellent-fg);
}
.band--good {
  background: var(--band-good-bg);
  color: var(--band-good-fg);
}
.band--fair {
  background: var(--band-fair-bg);
  color: var(--band-fair-fg);
}
.band--weak {
  background: var(--band-weak-bg);
  color: var(--band-weak-fg);
}

.headline {
  font-size: 1.0625rem;
  color: var(--fg);
  max-width: 46ch;
}

.result-meta {
  font-size: 0.8125rem;
  color: var(--muted-fg);
}

/* ------------------------------------------------------------ word chips */
.words {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s2);
  direction: ltr;
  justify-content: flex-start;
}

.word {
  display: inline-flex;
  align-items: baseline;
  gap: 4px;
  padding: 4px var(--s3);
  border: 1px solid var(--border-soft);
  border-radius: var(--r-ctl);
  background: var(--card);
  font-size: 1.0625rem;
  font-family: var(--font-lat);
  direction: ltr;
  transition: border-color var(--t-fast) var(--ease), background var(--t-fast) var(--ease);
}

.word:hover {
  border-color: var(--primary-bright);
}

.word[aria-pressed="true"] {
  border-color: var(--primary);
  box-shadow: inset 0 0 0 1px var(--primary);
}

.word--good {
  background: var(--band-good-bg);
}
.word--fair {
  background: var(--band-fair-bg);
}
.word--weak {
  background: var(--band-weak-bg);
  border-color: var(--band-weak-fg);
}

.word__score {
  font-size: 0.6875rem;
  color: var(--muted-fg);
  font-variant-numeric: tabular-nums;
}

.word-detail {
  margin-top: var(--s3);
  padding: var(--s3) var(--s4);
  background: var(--muted);
  border-radius: var(--r-ctl);
  font-size: 0.9375rem;
  color: var(--fg);
}

.word-detail .phones {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s2);
  margin-top: var(--s2);
  direction: ltr;
  justify-content: flex-start;
}

/* Self-listening controls under the phones. They wrap rather than overflow at
 * 375 px, where three buttons plus their labels are wider than the column. */
.word-detail__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s2);
  margin-top: var(--s3);
}

.ph {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 1px var(--s2);
  border-radius: 6px;
  background: var(--card);
  border: 1px solid var(--border-soft);
  font-size: 0.9375rem;
}

.ph--bad {
  border-color: #fca5a5;
  background: #fef2f2;
}

.ph--accent {
  border-color: #fcd34d;
  background: #fffbeb;
}

.ph__heard {
  color: var(--destructive);
  font-size: 0.8125rem;
}

/* --------------------------------------------------------------- coaching */
.coach {
  list-style: none;
  padding: 0;
  margin: 0;
  display: grid;
  gap: var(--s3);
}

.coach li {
  padding: var(--s3) var(--s4);
  background: var(--card);
  border: 1px solid var(--border-soft);
  border-inline-start: 3px solid var(--border);
  border-radius: var(--r-ctl);
}

.coach li.sev-high {
  border-inline-start-color: var(--destructive);
}
.coach li.sev-medium,
.coach li.sev-normal {
  border-inline-start-color: var(--accent);
}
.coach li.sev-low,
.coach li.accent {
  border-inline-start-color: var(--primary-bright);
}

.coach__head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s2);
  font-weight: 600;
}

.coach__tag {
  font-size: 0.6875rem;
  font-weight: 600;
  padding: 1px var(--s2);
  border-radius: var(--r-pill);
  background: var(--muted);
  color: var(--muted-fg);
}

.coach__tag--accent {
  background: var(--band-good-bg);
  color: var(--band-good-fg);
}

.pair {
  display: inline-flex;
  align-items: baseline;
  gap: 5px;
  direction: ltr;
  unicode-bidi: isolate;
}

.pair__arrow {
  color: var(--muted-fg);
  font-size: 0.875em;
}

.coach__word {
  font-family: var(--font-lat);
  font-weight: 700;
  font-size: 1.0625rem;
}

.word-detail__head {
  font-family: var(--font-lat);
  font-weight: 700;
  font-size: 1.0625rem;
}

.coach__body {
  margin-top: var(--s1);
  color: #1f2937;
  font-size: 0.9375rem;
}

.coach__drill {
  margin-top: var(--s2);
  font-size: 0.875rem;
  color: var(--primary-ink);
  display: flex;
  gap: var(--s2);
  align-items: flex-start;
}

.coach__drill svg {
  flex: 0 0 auto;
  margin-top: 4px;
}

.pair {
  display: inline-flex;
  gap: var(--s2);
  align-items: center;
  font-size: 0.8125rem;
  color: var(--muted-fg);
}

/* -------------------------------------------------------------------- rail */
.rail {
  display: grid;
  gap: var(--s4);
  position: sticky;
  top: 72px;
}

.stat-row {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  padding: var(--s2) 0;
  border-bottom: 1px dashed var(--border-soft);
  font-size: 0.9375rem;
}

.stat-row:last-child {
  border-bottom: 0;
}

.stat-row b {
  font-size: 1.125rem;
  font-variant-numeric: tabular-nums;
}

.muted {
  color: var(--muted-fg);
}

.small {
  font-size: 0.8125rem;
}

.empty {
  color: var(--muted-fg);
  font-size: 0.9375rem;
}

/* ------------------------------------------------------------------- toast */
.toast {
  position: fixed;
  inset-inline: 0;
  bottom: var(--s5);
  margin-inline: auto;
  width: fit-content;
  max-width: min(560px, calc(100vw - 32px));
  padding: var(--s3) var(--s5);
  background: #1f2937;
  color: #f9fafb;
  border-radius: var(--r-ctl);
  font-size: 0.9375rem;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.18);
  z-index: 60;
  transform: translateY(16px);
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--t-base) var(--ease), transform var(--t-base) var(--ease);
}

.toast.is-on {
  opacity: 1;
  transform: translateY(0);
}

.toast--error {
  background: #7f1d1d;
}

/* --------------------------------------------------------------- progress */
.kpi-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: var(--s3);
}

.kpi {
  padding: var(--s4);
  background: var(--card);
  border: 1px solid var(--border-soft);
  border-radius: var(--r-card);
}

.kpi__label {
  font-size: 0.8125rem;
  color: var(--muted-fg);
}

.kpi__value {
  font-size: 1.75rem;
  font-weight: 700;
  line-height: 1.2;
  font-variant-numeric: tabular-nums;
  direction: ltr;
  unicode-bidi: isolate;
}

.spark {
  display: block;
  width: 100%;
  height: 96px;
}

table.hist {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.9375rem;
}

table.hist th {
  text-align: start;
  font-weight: 600;
  font-size: 0.75rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted-fg);
  padding: var(--s2) var(--s2);
  border-bottom: 1px solid var(--border);
}

table.hist td {
  padding: var(--s3) var(--s2);
  border-bottom: 1px solid var(--border-soft);
  vertical-align: middle;
}

table.hist tr:last-child td {
  border-bottom: 0;
}

.cell-sentence {
  max-width: 34ch;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  direction: ltr;
  text-align: left;
  font-family: var(--font-lat);
}

.score-pill {
  display: inline-block;
  min-width: 52px;
  text-align: center;
  padding: 1px var(--s2);
  border-radius: var(--r-pill);
  font-variant-numeric: tabular-nums;
  font-weight: 600;
  font-size: 0.875rem;
}

.score-pill.band--excellent,
.score-pill.band--good {
  background: var(--band-good-bg);
  color: var(--band-good-fg);
}
.score-pill.band--fair {
  background: #fef3c7;
  color: #92400e;
}
.score-pill.band--weak {
  background: #fee2e2;
  color: #991b1b;
}

/* Two-column grid: the phonetic pair and its example words share the start
   column (right in RTL), the count sits in the end column. Using grid rather
   than flex + text-align means alignment follows the writing mode instead of
   depending on which side `start` happens to resolve to. */
.err-item {
  display: grid;
  grid-template-columns: 1fr auto;
  column-gap: var(--s2);
  row-gap: 2px;
  align-items: baseline;
  padding: var(--s3) 0;
  border-bottom: 1px dashed var(--border-soft);
}

.err-item:last-child {
  border-bottom: 0;
}

.err-line {
  display: contents;
}

.err-item .pair {
  grid-column: 1;
  justify-self: start;
}

/* The rail rows are buttons now (they expand a targeted drill). Reset the
 * native button chrome while keeping the existing two-column grid. */
button.err-item {
  width: 100%;
  background: none;
  border: 0;
  border-bottom: 1px dashed var(--border-soft);
  border-radius: 0;
  color: inherit;
  font: inherit;
  text-align: start;
  cursor: pointer;
}

button.err-item:hover,
button.err-item[aria-expanded="true"] {
  background: var(--muted);
}

.drill-panel {
  margin-top: var(--s3);
  padding: var(--s3);
  background: var(--muted);
  border-radius: var(--r-ctl);
}

.drill-words {
  display: grid;
  gap: var(--s2);
  margin-block: var(--s2);
}

.drill-word {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s2);
}

.drill-word__text {
  flex: 1 1 120px;
  font-family: var(--font-lat);
  font-size: 0.9375rem;
}

.err-count {
  grid-column: 2;
  font-size: 0.75rem;
  color: var(--muted-fg);
  white-space: nowrap;
}

.err-words {
  grid-column: 1;
  font-family: var(--font-lat);
  font-size: 0.75rem;
  color: var(--muted-fg);
}

.err-item .coach__tag {
  grid-column: 2;
}

/* -------------------------------------------------------------- responsive */
@media (max-width: 900px) {
  .grid {
    grid-template-columns: minmax(0, 1fr);
  }
  .rail {
    position: static;
  }
}

@media (max-width: 560px) {
  .page {
    padding: var(--s4) var(--s3) var(--s7);
  }
  .topbar {
    padding: var(--s3) var(--s3);
    gap: var(--s3);
  }
  .brand .sub {
    display: none;
  }
  .card {
    padding: var(--s4);
  }
  .target {
    font-size: 1.25rem;
  }
  .ring {
    width: 108px;
    height: 108px;
  }
  .ring__score {
    font-size: 1.625rem;
  }
}

/* --------------------------------------------------------- reduced motion */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
  .record-btn[aria-pressed="true"] {
    animation: none;
    box-shadow: 0 0 0 6px color-mix(in oklab, var(--destructive) 20%, transparent);
  }
}

/* --------------------------------------------------------------- print/off */
[hidden] {
  display: none !important;
}

/* ================================================================ conversation
   Chat, report and the small record control. Same tokens as the rest of the
   page: no new colours, no new radii, no decoration. */

.notice {
  background: color-mix(in oklab, var(--accent) 10%, var(--card));
  border: 1px solid color-mix(in oklab, var(--accent) 35%, var(--border));
  border-radius: var(--r-md);
  padding: var(--s4) var(--s5);
  margin-block-end: var(--s5);
}

.notice p {
  margin: 0 0 var(--s2);
  font-size: var(--fs-sm);
  line-height: 1.7;
}

.notice p:last-child {
  margin-block-end: 0;
}

.notice code {
  font-family: var(--font-mono);
  background: var(--muted);
  padding: 2px 6px;
  border-radius: 4px;
  font-size: 0.92em;
}

/* ------------------------------------------------------------------- chat */
/* Space carries the grouping, not lines. Within one speaker's turn the gap is
   8px; between speakers it is 24px, three times as much, so the eye reads two
   blocks instead of one alternating list. A separator rule between turns would
   add a line to a page that already has borders everywhere. */
.chat {
  display: flex;
  flex-direction: column;
  gap: 0;
  /* The conversation is in English, so it lays out left-to-right whatever the
     UI language is: the tutor sits on the left, the learner on the right. */
  direction: ltr;
  min-height: 180px;
  max-height: 52vh;
  overflow-y: auto;
  overscroll-behavior: contain;   /* scrolling past the end must not move the page */
  padding-block: var(--s2);
  margin-block-end: var(--s4);
}

.chat__empty {
  color: var(--muted-fg);
  font-size: var(--fs-sm);
  line-height: 1.9;
  /* Centre the placeholder in the reserved chat area: pinned to the top it
     leaves a hole between itself and the button underneath. */
  margin: auto 0;
  text-align: start;
}

.bubble {
  max-width: 78%;
  padding: var(--s3) var(--s4);
  border-radius: var(--r-card);
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
  margin-block-start: var(--s2);
}

.bubble--group-start {
  margin-block-start: var(--s5);
}

.chat > .bubble:first-child {
  margin-block-start: 0;
}

/* Two surfaces, not two tints of the same grey: the learner's own turn is a
   filled green block, the tutor's is a light panel. Contrast between them is
   what tells the reader at a glance who said what. */
.bubble--tutor {
  align-self: flex-start;
  background: var(--tutor-bg);
  color: var(--tutor-fg);
  border-start-start-radius: 4px;
}

.bubble--learner {
  align-self: flex-end;
  background: var(--learner-bg);
  color: var(--learner-fg);
  border-start-end-radius: 4px;
}

.bubble--learner .bubble__who,
.bubble--learner .bubble__text {
  color: var(--learner-fg);
}

/* Edit / re-record sit inside the latest learner bubble. They wrap rather than
 * widen the bubble on a phone, and the input replaces only the text line. */
.turn-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s2);
  margin-block-start: var(--s2);
}

.bubble__edit {
  width: 100%;
  min-width: 180px;
  margin-block-start: 2px;
}

.bubble__who {
  font-size: 11px;
  letter-spacing: 0.02em;
  color: var(--muted-fg);
  opacity: 0.85;
}

.bubble__text {
  margin: 0;
  font-size: var(--fs-base);
  line-height: 1.75;
}

/* English text is left-aligned inside its own box. */
.bubble__text[dir="ltr"] {
  text-align: left;
  unicode-bidi: isolate;
}

.bubble--thinking .bubble__dots {
  color: var(--muted-fg);
  letter-spacing: 2px;
}

/* --------------------------------------------------------------- controls */
.conv-controls {
  display: flex;
  align-items: center;
  gap: var(--s5);
  padding-block: var(--s3);
  border-block-start: 1px solid var(--border-soft);
  margin-block-end: var(--s3);
}

.record-btn--sm {
  width: 64px;
  height: 64px;
  flex: 0 0 auto;
}

.conv-meter {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: var(--s2);
}

.conv-meter .meter {
  width: 100%;
}

/* ----------------------------------------------------------------- report */
#report {
  scroll-margin-block-start: var(--s5);
}

.fb-summary {
  font-size: var(--fs-base);
  line-height: 1.9;
  margin: 0 0 var(--s5);
  padding: var(--s3) var(--s4);
  background: var(--muted);
  border-radius: var(--r-md);
}

.fb-block {
  margin-block-end: var(--s6);
}

.fb-block__h {
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--primary-ink);
  margin: 0 0 var(--s3);
  padding-block-end: var(--s2);
  border-block-end: 1px solid var(--border-soft);
}

.fix {
  padding: var(--s3) 0;
  border-block-end: 1px dashed var(--border-soft);
}

.fix:last-child {
  border-block-end: 0;
}

/* The error category, as a quiet label. It is what makes a stack of cards
   scannable: "حرف تعریف" five times in a row is a lesson, five unexplained
   sentences are noise. */
.fix__cat {
  display: inline-flex;
  align-items: center;
  gap: var(--s2);
  font-size: var(--fs-xs);
  color: var(--primary-ink);
  background: color-mix(in oklab, var(--primary) 10%, var(--card));
  border-radius: var(--r-pill);
  padding: 1px 9px;
}

/* One explanation per mistake in the sentence above. */
.fix__notes {
  list-style: none;
  margin: var(--s3) 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--s2);
}

.fix__note {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 4px;
}

/* The learner's sentence with the mistake marked, then the same sentence with
   the fix marked. Both lines are English and start at the left margin. */
.fix__line {
  margin: 0;
  font-size: var(--fs-base);
  line-height: 1.85;
  text-align: left;
  unicode-bidi: isolate;
}

.fix__line--bad {
  color: var(--muted-fg);
}

.fix__line--bad mark {
  color: var(--destructive);
  background: color-mix(in oklab, var(--destructive) 9%, transparent);
  text-decoration: underline;
  text-decoration-style: wavy;
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
  border-radius: 3px;
  padding: 0 2px;
}

.fix__line--good {
  color: var(--fg);
  margin-block-start: 2px;
}

.fix__line--good mark {
  color: var(--primary-ink);
  font-weight: 600;
  background: color-mix(in oklab, var(--primary) 13%, transparent);
  border-radius: 3px;
  padding: 0 2px;
}

.fix__arrow {
  color: var(--muted-fg);
}

.fix__fa {
  margin: 0;
  font-size: var(--fs-sm);
  line-height: 1.85;
  color: var(--muted-fg);
}

.fix__new {
  color: var(--fg);
  margin: 0;
  font-size: var(--fs-base);
  line-height: 1.8;
  text-align: left;
  unicode-bidi: isolate;
}

.stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(88px, 1fr));
  gap: var(--s3);
  margin-block-end: var(--s3);
}

.stat {
  background: var(--muted);
  border-radius: var(--r-md);
  padding: var(--s3);
  text-align: center;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.stat__v {
  font-size: var(--fs-lg);
  font-weight: 700;
  color: var(--primary-ink);
}

.stat__k {
  font-size: 11px;
  color: var(--muted-fg);
}

.vocab {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: var(--s2);
  font-size: var(--fs-base);
  padding-block: var(--s2);
}

.vocab__from {
  color: var(--muted-fg);
}

.vocab__to {
  color: var(--primary-ink);
  font-weight: 600;
}

.vocab__fa {
  font-size: var(--fs-xs);
  color: var(--muted-fg);
  margin-inline-start: auto;
}

.tip {
  padding: var(--s4);
  background: var(--card);
  border: 1px solid var(--border-soft);
  border-radius: var(--r-md);
  margin-block-end: var(--s3);
}

.tip__h {
  font-size: var(--fs-sm);
  margin: 0 0 var(--s2);
  color: var(--primary-ink);
}

.tip__body,
.tip__why {
  font-size: var(--fs-sm);
  line-height: 1.85;
  margin: 0 0 var(--s2);
}

.tip__ex {
  font-size: var(--fs-sm);
  line-height: 1.8;
  margin: 0 0 var(--s2);
  padding: var(--s2) var(--s3);
  background: var(--muted);
  border-radius: 6px;
}

.tip__why {
  color: var(--muted-fg);
  margin-block-end: 0;
}

/* ---------------------------------------------------------------- history */
.conv-history {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--s1);
}

.conv-history a {
  display: flex;
  justify-content: space-between;
  gap: var(--s3);
  padding: var(--s2) var(--s3);
  border-radius: 6px;
  color: var(--fg);
  text-decoration: none;
  font-size: var(--fs-sm);
}

/* Hover only where a pointer can hover: on touch, :hover latches after a tap
   and the row stays highlighted as if it were selected. */
@media (hover: hover) {
  .conv-history a:hover {
    background: var(--muted);
  }
}

@media (max-width: 720px) {
  .bubble {
    max-width: 92%;
  }

  .conv-controls {
    gap: var(--s3);
  }
}

.fix__count {
  font-size: var(--fs-xs);
  color: var(--muted-fg);
  background: var(--muted);
  border-radius: var(--r-pill);
  padding: 0 8px;
}

/* The header has to survive a phone. Three nav items plus the wordmark do not
   fit at 375px, and the failure mode is silent: the nav slides off the left
   edge and the page scrolls sideways. Measured at 375px before this rule:
   scrollWidth 459 against a 375px viewport. */
@media (max-width: 640px) {
  .topbar {
    gap: var(--s3);
    padding: var(--s3) var(--s4);
  }

  .brand .sub {
    display: none;
  }

  .nav a {
    padding: 0 var(--s3);
    font-size: 0.875rem;
  }
}

@media (max-width: 430px) {
  .brand > span {
    display: none;          /* keep the mark, drop the word */
  }

  .nav {
    margin-inline-start: auto;
  }

  .nav a {
    padding: 0 10px;
  }
}

/* ================================================================ chrome
   Theme toggle, prose pages and the settings form. Same tokens as the rest. */

.theme-toggle {
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  flex: 0 0 auto;
  margin-inline-start: var(--s2);
  background: transparent;
  border: 1px solid var(--border);
  border-radius: var(--r-pill);
  color: var(--muted-fg);
  transition: background var(--t-fast) var(--ease), color var(--t-fast) var(--ease),
    border-color var(--t-fast) var(--ease);
}

.theme-toggle svg {
  display: block;
}

/* One icon at a time, chosen by the state the button will switch *to*. */
.theme-toggle[data-current="light"] .theme-toggle__sun {
  display: none;
}

.theme-toggle[data-current="dark"] .theme-toggle__moon {
  display: none;
}

@media (hover: hover) {
  .theme-toggle:hover {
    background: var(--muted);
    color: var(--fg);
    border-color: var(--primary-bright);
  }
}

/* --------------------------------------------------------------- prose */
.prose {
  max-width: 68ch;
  margin: 0 auto;
}

.prose h1 {
  font-size: 1.5rem;
  margin: 0 0 var(--s3);
}

.prose .lead {
  font-size: 1.0625rem;
  color: var(--muted-fg);
  line-height: 1.9;
  margin: 0 0 var(--s6);
}

.help-block {
  margin-block-end: var(--s6);
}

.help-block h2 {
  display: flex;
  align-items: center;
  gap: var(--s2);
  font-size: 1.0625rem;
  margin: 0 0 var(--s2);
  color: var(--primary-ink);
}

.help-block h2 svg {
  color: var(--primary-bright);
  flex: 0 0 auto;
}

.help-block p {
  margin: 0;
  line-height: 1.95;
  color: var(--fg);
}

.callout {
  margin: 0 0 var(--s6);
  padding: var(--s4) var(--s5);
  background: var(--muted);
  border-inline-start: 3px solid var(--primary-bright);
  border-radius: var(--r-ctl);
  line-height: 1.9;
  color: var(--fg);
}

/* ------------------------------------------------------------ settings */
.settings {
  display: flex;
  flex-direction: column;
  gap: var(--s6);
}

.field {
  border: 0;
  padding: 0;
  margin: 0;
}

.field legend {
  font-weight: 600;
  font-size: var(--fs-base);
  padding: 0;
  margin-block-end: var(--s1);
}

.field__hint {
  margin: 0 0 var(--s3);
  font-size: var(--fs-sm);
  color: var(--muted-fg);
  line-height: 1.8;
}

/* Segmented control: radio inputs visually hidden, the label carries the
   state. Native inputs keep the keyboard and the screen reader working. */
.segmented {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s2);
}

.segment {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;
  padding: 0 var(--s5);
  border: 1px solid var(--border);
  border-radius: var(--r-ctl);
  background: var(--card);
  color: var(--fg);
  font-size: 0.9375rem;
  cursor: pointer;
  transition: background var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease);
}

.segment input {
  position: absolute;
  opacity: 0;
  width: 100%;
  height: 100%;
  inset: 0;
  margin: 0;
  cursor: pointer;
}

.segment:has(input:checked) {
  background: var(--primary);
  border-color: var(--primary);
  color: var(--on-primary);
}

.segment:has(input:focus-visible) {
  outline: 3px solid var(--ring);
  outline-offset: 2px;
}

.segment--missing {
  opacity: 0.45;
  cursor: not-allowed;
}

@media (hover: hover) {
  .segment:not(.segment--missing):has(input:not(:checked)):hover {
    background: var(--muted);
  }
}

/* ---------------------------------------------------------------- timer */
.record-timer {
  font-variant-numeric: tabular-nums;
  color: var(--muted-fg);
}

.record-timer.is-near {
  color: var(--destructive);
  font-weight: 600;
}

/* --------------------------------------------------------- small screens */
@media (max-width: 640px) {
  /* Five nav items do not fit as labels. Icons plus the tooltip and the
     aria-label keep them usable and named. */
  .nav a span {
    display: none;
  }

  .nav a {
    padding: 0 10px;
  }

  .bubble {
    max-width: 92%;
  }

  .chat {
    max-height: 58vh;
  }

  .prose {
    max-width: none;
  }

  .segment {
    padding: 0 var(--s4);
  }
}

@media (max-width: 430px) {
  .theme-toggle {
    width: 36px;
    height: 36px;
  }

  .conv-controls {
    gap: var(--s3);
  }
}

/* A four-column history table cannot fit 375px. Scrolling it inside its card
   keeps the page itself from scrolling sideways, which is the failure that
   breaks every other layout on the phone. */
.table-scroll {
  overflow-x: auto;
  overscroll-behavior-x: contain;
  margin-inline: calc(-1 * var(--s1));
  padding-inline: var(--s1);
}

/* ============================================================ app phase 4
   Settings form, exam mode, processing state, recording stop icon. */

/* --------------------------------------------------------------- buttons */
.btn--quiet {
  min-height: 38px;
  padding: 0 var(--s3);
  background: transparent;
  border-color: var(--border-soft);
  color: var(--muted-fg);
  font-size: var(--fs-sm);
}

.btn--quiet:hover {
  color: var(--fg);
  background: var(--muted);
}

/* The one button on /settings that changes anything. Every other control on
   that page is quiet. Accent maize with deep teal ink measures 6.03:1 in both
   themes, and a solid bottom edge gives it weight without a new colour. */
.btn--save {
  background: var(--accent);
  border: 1px solid color-mix(in oklab, var(--accent) 70%, var(--on-accent));
  color: var(--on-accent);
  font-weight: 700;
  padding: 0 var(--s6);
  min-height: 48px;
  box-shadow: 0 3px 0 color-mix(in oklab, var(--on-accent) 35%, transparent),
              0 6px 18px color-mix(in oklab, var(--accent) 35%, transparent);
}

.btn--save:hover {
  background: color-mix(in oklab, var(--accent) 88%, white);
  border-color: var(--on-accent);
  color: var(--on-accent);
  transform: translateY(-1px);
}

.btn--save:active {
  transform: translateY(2px);
  box-shadow: 0 1px 0 color-mix(in oklab, var(--on-accent) 35%, transparent);
}

/* ---------------------------------------------------------- form controls */
.field--group {
  border: 1px solid var(--border-soft);
  border-radius: var(--r-card);
  padding: var(--s5);
  background: color-mix(in oklab, var(--muted) 45%, var(--card));
}

.field__sub {
  margin-block-start: var(--s5);
  padding-block-start: var(--s4);
  border-block-start: 1px solid var(--border-soft);
}

.field__sub:first-of-type {
  margin-block-start: var(--s3);
  padding-block-start: 0;
  border-block-start: 0;
}

.field__label {
  display: block;
  font-size: var(--fs-base);
  font-weight: 600;
  margin-block-end: var(--s1);
}

.field__hint--warn {
  color: var(--destructive);
}

.text-input {
  width: 100%;
  min-height: 44px;
  padding: 0 var(--s3);
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--r-ctl);
  color: var(--fg);
  font-family: var(--font-mono);
  font-size: 0.875rem;
}

.text-input:focus-visible {
  outline: 3px solid var(--ring);
  outline-offset: 1px;
}

/* Custom-sentence row: textarea grows, button stays its own size, and the pair
 * wraps under 480 px instead of forcing a horizontal scrollbar. */
.custom-row {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: var(--s2);
  margin-block-end: var(--s2);
}

.custom-row .text-input {
  flex: 1 1 260px;
  width: auto;
  min-height: 64px;
  padding: var(--s2) var(--s3);
  line-height: 1.7;
  resize: vertical;
}

/* The sentence is English but the interface is Vazirmatn everywhere; the mono
 * input font made the custom box look like a code editor. */
.custom-textarea {
  font-family: var(--font-lat);
  font-size: 1rem;
}

.custom-samples {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s2);
  margin-block: var(--s2);
}

.custom-sample {
  max-width: 100%;
  padding: var(--s2) var(--s3);
  border: 1px solid var(--border);
  border-radius: var(--r-ctl);
  background: var(--card);
  color: var(--fg);
  font-family: var(--font-lat);
  font-size: var(--fs-sm);
  text-align: start;
  cursor: pointer;
}

.custom-sample:hover,
.custom-sample:focus-visible {
  border-color: var(--primary-bright);
  background: var(--muted);
}

.key-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s2);
  margin-block-end: var(--s3);
}

.key-row .text-input {
  flex: 1 1 260px;
  width: auto;
}

.segment--num {
  min-width: 62px;
}

/* -------------------------------------------------------- hardware presets */
.presets {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: var(--s2);
}

.preset {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: var(--s3);
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--r-ctl);
  cursor: pointer;
}

.preset input {
  position: absolute;
  opacity: 0;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  cursor: pointer;
}

.preset:has(input:checked) {
  background: var(--primary);
  border-color: var(--primary);
  color: var(--on-primary);
}

.preset:has(input:focus-visible) {
  outline: 3px solid var(--ring);
  outline-offset: 2px;
}

.preset__name {
  font-size: var(--fs-sm);
  font-weight: 600;
}

.preset__ram {
  font-size: var(--fs-xs);
  line-height: 1.6;
  color: var(--muted-fg);
}

.preset:has(input:checked) .preset__ram {
  color: var(--on-primary);
  opacity: 0.85;
}

#llm-test-result.is-ok {
  color: var(--good);
}

/* --------------------------------------------------------------- mode row */
.mode-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s2);
  margin-block-end: var(--s3);
}

.mode-chip {
  min-height: 40px;
  padding: 0 var(--s4);
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--r-pill);
  color: var(--fg);
  font-size: 0.9375rem;
}

.mode-chip[aria-pressed="true"] {
  background: var(--primary);
  border-color: var(--primary);
  color: var(--on-primary);
  font-weight: 600;
}

.mode-row__hint {
  flex: 1 1 240px;
  margin: 0;
}

.llm-badge {
  margin-inline-start: auto;
  /* .eyebrow uppercases its children. A model id is a proper name, not a
     label: GEMMA3:1B is harder to recognise than gemma3:1b. */
  text-transform: none;
  letter-spacing: 0;
  max-width: 40%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ------------------------------------------------------------ processing */
.processing {
  display: flex;
  align-items: center;
  gap: var(--s3);
  margin-block: var(--s3);
  padding: var(--s3) var(--s4);
  background: color-mix(in oklab, var(--accent) 16%, var(--card));
  border: 1px solid color-mix(in oklab, var(--accent) 45%, var(--border));
  border-radius: var(--r-ctl);
}

.processing[hidden] {
  display: none;
}

.processing__dots {
  display: inline-flex;
  gap: 5px;
  flex: 0 0 auto;
}

.processing__dots i {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--primary-bright);
  animation: pulse-dot 1.1s infinite ease-in-out;
}

.processing__dots i:nth-child(2) { animation-delay: 0.16s; }
.processing__dots i:nth-child(3) { animation-delay: 0.32s; }

@keyframes pulse-dot {
  0%, 60%, 100% { opacity: 0.3; transform: translateY(0); }
  30%           { opacity: 1;   transform: translateY(-3px); }
}

.processing__text {
  display: flex;
  flex-direction: column;
  gap: 1px;
  font-size: var(--fs-sm);
}

.processing__text small {
  color: var(--muted-fg);
  font-size: var(--fs-xs);
}

@media (prefers-reduced-motion: reduce) {
  .processing__dots i { animation: none; opacity: 1; }
}

/* ----------------------------------------------------------- voice speed */
.speed-ctl {
  display: flex;
  flex-direction: column;
  gap: var(--s1);
  flex: 0 0 auto;
}

.speed-select {
  min-height: 38px;
  padding: 0 var(--s2);
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--r-ctl);
  color: var(--fg);
  font-size: var(--fs-sm);
}

/* -------------------------------------------------------------- exam card */
.exam-card {
  margin-block: var(--s4);
  padding: var(--s4) var(--s5);
  background: color-mix(in oklab, var(--accent) 12%, var(--card));
  border: 1px solid color-mix(in oklab, var(--accent) 45%, var(--border));
  border-inline-start: 4px solid var(--accent);
  border-radius: var(--r-card);
}

.exam-card[hidden] { display: none; }

.exam-card__head {
  display: flex;
  align-items: center;
  gap: var(--s3);
  margin-block-end: var(--s3);
}

.exam-card__part {
  /* "Part 2" is English inside an RTL card. Without this the numeral comes
     first and the badge reads "2 Part". */
  direction: ltr;
  unicode-bidi: isolate;
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: 0.06em;
  color: var(--on-accent);
  background: var(--accent);
  border-radius: var(--r-pill);
  padding: 2px 10px;
}

.exam-card h3 {
  margin: 0 0 var(--s3);
  font-size: 1.0625rem;
  line-height: 1.6;
  font-family: var(--font-lat);
  direction: ltr;
  text-align: left;
}

.exam-card ul {
  margin: 0 0 var(--s3);
  padding-inline-start: var(--s5);
  direction: ltr;
  text-align: left;
  font-family: var(--font-lat);
  font-size: var(--fs-sm);
  line-height: 1.9;
}

.exam-card__note {
  margin-block-end: var(--s4);
  line-height: 1.8;
}

.exam-card__timer {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s3);
  padding-block-start: var(--s3);
  border-block-start: 1px solid var(--border-soft);
}

.exam-card__clock {
  font-size: 1.5rem;
  font-weight: 700;
  color: var(--primary-ink);
  font-variant-numeric: tabular-nums;
}

/* -------------------------------------------------------------- criteria */
.criteria {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--s2);
}

.criteria__row {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: var(--s3);
  background: var(--muted);
  border-radius: var(--r-ctl);
  border-inline-start: 3px solid var(--primary-bright);
}

.criteria--pronunciation {
  border-inline-start-color: var(--destructive-soft);
}

.criteria__name { font-size: var(--fs-base); }
.criteria__note { font-size: var(--fs-sm); color: var(--muted-fg); line-height: 1.7; }

/* --------------------------------------------------------- small screens */
@media (max-width: 640px) {
  .conv-controls {
    flex-wrap: wrap;
  }

  .conv-meter {
    flex: 1 1 180px;
  }

  .speed-ctl {
    flex: 1 1 100%;
    flex-direction: row;
    align-items: center;
    justify-content: space-between;
  }

  .field--group {
    padding: var(--s4);
  }

  .btn--save {
    flex: 1 1 100%;
  }
}

/* ============================================================== profile
   Habit strip, measured radar, learning paths. All three are arithmetic over
   stored rows; none of them is a CEFR level or a band score. */

/* ----------------------------------------------------------------- habit */
.habit {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s5);
  margin-block-end: var(--s4);
}

.habit__stat {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 92px;
}

.habit__v {
  font-size: 1.75rem;
  font-weight: 700;
  line-height: 1.2;
  color: var(--primary-ink);
  font-variant-numeric: tabular-nums;
}

.habit__k {
  font-size: var(--fs-sm);
  color: var(--muted-fg);
}

/* One square per day. Intensity is a colour, but every square also carries the
   date and the exact minutes in its title, so colour is never the only signal. */
.habit__days {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

.habit__day {
  width: 18px;
  height: 18px;
  border-radius: 4px;
  background: var(--muted);
  border: 1px solid var(--border-soft);
}

.habit__day[data-level="1"] { background: color-mix(in oklab, var(--primary-bright) 25%, var(--card)); }
.habit__day[data-level="2"] { background: color-mix(in oklab, var(--primary-bright) 45%, var(--card)); }
.habit__day[data-level="3"] { background: color-mix(in oklab, var(--primary-bright) 70%, var(--card)); }
.habit__day[data-level="4"] { background: var(--primary-bright); }

/* ----------------------------------------------------------------- radar */
.radar-wrap {
  display: grid;
  grid-template-columns: minmax(0, 320px) minmax(0, 1fr);
  gap: var(--s5);
  align-items: center;
}

.radar {
  width: 100%;
  max-width: 320px;
  height: auto;
  /* SVG text inherits the page direction; force LTR so the numbers and the
     Latin evidence strings do not flip inside an RTL page. */
  direction: ltr;
  overflow: visible;
}

.radar__ring {
  fill: none;
  stroke: var(--border-soft);
  stroke-width: 1;
}

.radar__spoke {
  stroke: var(--border-soft);
  stroke-width: 1;
}

.radar__shape {
  fill: color-mix(in oklab, var(--primary-bright) 22%, transparent);
  stroke: var(--primary-bright);
  stroke-width: 2;
  stroke-linejoin: round;
}

.radar__dot {
  fill: var(--primary-bright);
  stroke: var(--card);
  stroke-width: 1.5;
}

.radar__label {
  font-family: var(--font-fa);
  font-size: 10.5px;
  fill: var(--muted-fg);
}

.radar__value {
  font-size: 13px;
  font-weight: 700;
  fill: var(--primary-ink);
}

.radar-legend {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--s2);
}

.radar-legend__row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto auto;
  align-items: baseline;
  gap: var(--s3);
  padding-block: 6px;
  border-block-end: 1px dashed var(--border-soft);
}

.radar-legend__row:last-child {
  border-block-end: 0;
}

.radar-legend__name {
  font-size: var(--fs-base);
  color: var(--fg);
}

.radar-legend__v {
  font-size: 1.125rem;
  font-weight: 700;
  color: var(--primary-ink);
  font-variant-numeric: tabular-nums;
}

.radar-legend__ev {
  font-size: var(--fs-xs);
  color: var(--muted-fg);
  font-variant-numeric: tabular-nums;
}

/* ----------------------------------------------------------------- paths */
.paths {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
  gap: var(--s4);
}

.path {
  display: flex;
  flex-direction: column;
  gap: var(--s2);
  padding: var(--s4);
  background: var(--card);
  border: 1px solid var(--border-soft);
  border-radius: var(--r-card);
  color: var(--fg);
  text-decoration: none;
  transition: border-color var(--t-fast) var(--ease), background var(--t-fast) var(--ease);
}

.path:hover {
  border-color: var(--primary-bright);
  background: color-mix(in oklab, var(--primary-bright) 6%, var(--card));
}

.path:focus-visible {
  outline: 3px solid var(--ring);
  outline-offset: 2px;
}

.path__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s2);
}

.path__title {
  font-weight: 700;
  font-size: var(--fs-base);
  color: var(--primary-ink);
}

.path__count {
  font-size: var(--fs-xs);
  color: var(--muted-fg);
}

.path__bar {
  height: 6px;
  background: var(--muted);
  border-radius: var(--r-pill);
  overflow: hidden;
}

.path__bar > span {
  display: block;
  height: 100%;
  background: var(--primary-bright);
  border-radius: inherit;
}

.path__desc {
  margin: 0;
  font-size: var(--fs-sm);
  line-height: 1.8;
  color: var(--muted-fg);
}

.path__steps {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 4px;
}

.path__step {
  display: flex;
  align-items: center;
  gap: var(--s2);
  font-size: var(--fs-sm);
  color: var(--muted-fg);
}

.path__tick {
  width: 16px;
  height: 16px;
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  border-radius: 50%;
  border: 1px solid var(--border);
  font-size: 10px;
  color: var(--on-primary);
}

.path__step.is-done {
  color: var(--fg);
}

.path__step.is-done .path__tick {
  background: var(--primary-bright);
  border-color: var(--primary-bright);
  color: var(--on-primary);
}

@media (max-width: 720px) {
  .radar-wrap {
    grid-template-columns: minmax(0, 1fr);
    justify-items: center;
  }

  .radar-legend {
    width: 100%;
  }

  .habit {
    gap: var(--s4);
  }

  .habit__v {
    font-size: 1.5rem;
  }
}

/* --------------------------------------------------------------- landing */
.hero {
  display: grid;
  grid-template-columns: minmax(0, 1.05fr) minmax(0, 0.95fr);
  align-items: center;
  gap: var(--s7);
  padding: var(--s6);
  margin-bottom: var(--s6);
  border-radius: var(--r-card);
  background: linear-gradient(135deg,
      color-mix(in oklab, var(--primary) 9%, var(--card)) 0%,
      color-mix(in oklab, var(--accent-warm, #f4a261) 10%, var(--card)) 100%);
  border: 1px solid var(--border-soft);
  overflow: hidden;
}

.hero__copy {
  display: flex;
  flex-direction: column;
  gap: var(--s4);
  min-width: 0;
}

.hero__title {
  margin: 0;
  font-size: clamp(1.75rem, 3.4vw, 2.9rem);
  line-height: 1.35;
  letter-spacing: -0.01em;
  color: var(--fg);
}

.hero__lead {
  margin: 0;
  max-width: 46ch;
  font-size: 1.05rem;
  line-height: 1.9;
  color: var(--muted-fg);
}

.hero__steps {
  list-style: none;
  margin: var(--s2) 0 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: var(--s3) var(--s4);
  font-size: var(--fs-sm);
  color: var(--muted-fg);
}

.hero__steps li {
  display: inline-flex;
  align-items: center;
  gap: var(--s2);
}

.hero__steps .num {
  display: inline-grid;
  place-items: center;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  background: var(--card);
  border: 1px solid var(--border);
  color: var(--primary-ink);
  font-size: 0.75rem;
}

.hero__art {
  width: 100%;
  max-width: 560px;
  justify-self: end;
  border-radius: var(--r-card);
  display: block;
}

@media (max-width: 960px) {
  .hero {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--s4);
    padding: var(--s4);
  }

  .hero__art {
    justify-self: stretch;
    max-width: none;
    order: -1;
    max-height: 260px;
    object-fit: cover;
  }
}

/* ----------------------------------------------------------------- login */
.login-card {
  display: grid;
  grid-template-columns: minmax(0, 1.1fr) minmax(0, 0.9fr);
  gap: var(--s6);
  padding: var(--s6);
  border-radius: var(--r-card);
  background: var(--card);
  border: 1px solid var(--border-soft);
}

.login-card__copy {
  display: flex;
  flex-direction: column;
  gap: var(--s3);
  min-width: 0;
}

.login-card h1 {
  margin: 0;
  font-size: clamp(1.4rem, 2.6vw, 2rem);
}

.login-card .lead {
  margin: 0;
  color: var(--muted-fg);
  line-height: 1.9;
}

.login-card__dev {
  padding: var(--s4);
  border-radius: var(--r-card);
  background: var(--muted);
}

.login-card__dev h2 {
  margin: 0 0 var(--s3);
  font-size: 1rem;
}

/* Phone sign-in: a divider between the Telegram button and the two-step form,
   then the form itself, so the two entry points read as equals rather than one
   being an afterthought under the other. */
.login-card__or {
  display: flex;
  align-items: center;
  gap: var(--s3);
  margin: var(--s2) 0;
  color: var(--muted-fg);
  font-size: var(--fs-sm);
}

.login-card__or::before,
.login-card__or::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--border);
}

.login-card__or span {
  flex: none;
}

.login-card__sms {
  display: grid;
  gap: var(--s2);
}

.login-card__sub {
  margin: 0;
  font-size: 1rem;
}

.login-card__sms .text-input {
  /* A phone number and a one-time code are Latin digits: the field must not
     inherit the RTL flow, or the caret lands on the wrong side. */
  direction: ltr;
  text-align: left;
  font-family: var(--font-lat);
  letter-spacing: 0.04em;
}

#sms-code {
  font-size: 1.15rem;
  letter-spacing: 0.35em;
}

#sms-status.is-error {
  color: var(--destructive);
}

@media (max-width: 800px) {
  .login-card {
    grid-template-columns: minmax(0, 1fr);
    padding: var(--s4);
  }
}

