/* Block components — cinematic frosted-glass aesthetic.
   Matches err/aoa-academy-design-html/{Lectures-example-template, flashcards-template #1}.html
   Glass-panel pattern reusable across callouts, flashcards, definitions, etc. */

/* ----------------------------------------------------------------------
   Glass panel (reusable surface)
---------------------------------------------------------------------- */
.glass-panel {
  background: var(--glass-bg);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  border: var(--glass-border);
  border-radius: var(--r-xl);
  box-shadow: var(--glass-shadow);
  padding: 1.75em 2em;
  break-inside: avoid;
}

/* ----------------------------------------------------------------------
   Callouts — 4 variants from grand-language-template.html lines 96-173.
   "1:1 copy of AOA-COURSES-TEMPLATE.pdf p.13". All use --accent (level-tied).

   Used by :::definition, :::important, :::note directives. Type sizes
   below are scaled down ~60% from the 1920x1080 slide template so they
   fit inline in prose without dominating the page. */

/* (1) ОПРЕДЕЛЕНИЕ — definition card */
.callout-definition {
  background: var(--callout-solid-bg);
  border: 1.5px solid var(--accent);
  border-radius: 28px;
  padding: 1.3em 1.6em;
  margin: 1.75em 0;
  break-inside: avoid;
}
.callout-definition .row-top {
  display: flex;
  align-items: center;
  gap: 0.6em;
  margin-bottom: 0.5em;
}
.callout-definition .logo {
  font-size: 1.9em;
  line-height: 1;
  flex-shrink: 0;
  font-family: 'Apple Color Emoji', 'Segoe UI Emoji', 'Noto Color Emoji', sans-serif;
}
.callout-definition .label {
  font-family: var(--font-display);
  font-size: 1.3em;
  font-weight: 800;
  letter-spacing: 0.02em;
  color: var(--accent);
  text-transform: uppercase;
  line-height: 1;
}
.callout-definition .body {
  font-size: 1.05em;
  font-weight: 400;
  color: var(--callout-text);
  line-height: 1.45;
  margin: 0;
}
.callout-definition .body > :first-child { margin-top: 0; }
.callout-definition .body > :last-child  { margin-bottom: 0; }

/* (2) ВАЖНО — vertical-label important banner */
.callout-vertical {
  display: flex;
  align-items: stretch;
  gap: 0.7em;
  margin: 1.75em 0;
  min-height: 5em;
  break-inside: avoid;
}
.callout-vertical .v-label {
  writing-mode: vertical-rl;
  transform: rotate(180deg);
  font-family: var(--font-display);
  font-size: 0.75em;
  font-weight: 800;
  letter-spacing: 0.4em;
  text-transform: uppercase;
  color: var(--accent);
  opacity: 0.45;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0.4em 0.2em;
}
.callout-vertical .v-rule {
  width: 2px;
  align-self: stretch;
  background: var(--accent);
  border-radius: 2px;
  margin: 0.5em 0;
}
.callout-vertical .v-body {
  flex: 1;
  padding: 1em 1.4em;
  display: flex;
  align-items: center;
  font-size: 1em;
  color: var(--callout-text);
  font-weight: 500;
  line-height: 1.45;
  background: var(--callout-grad-bg);
  border-radius: 18px;
}
.callout-vertical .v-body > :first-child { margin-top: 0; }
.callout-vertical .v-body > :last-child  { margin-bottom: 0; }

/* (3) ЗАМЕТКА — note with vertical-badge label */
.callout-note {
  display: flex;
  align-items: stretch;
  gap: 0.55em;
  margin: 1.5em 0;
  min-height: 4em;
  break-inside: avoid;
}
.callout-note .n-badge {
  writing-mode: vertical-rl;
  transform: rotate(180deg);
  border: 1.5px solid var(--accent);
  border-radius: 12px;
  padding: 0.7em 0.3em;
  font-family: var(--font-display);
  font-size: 0.65em;
  font-weight: 800;
  letter-spacing: 0.35em;
  color: var(--accent);
  text-transform: uppercase;
  display: flex;
  align-items: center;
  justify-content: center;
}
.callout-note .n-rule {
  width: 2px;
  align-self: stretch;
  background: var(--accent);
  border-radius: 2px;
  margin: 0.45em 0;
}
.callout-note .n-body {
  flex: 1;
  padding: 0.85em 1.25em;
  display: flex;
  align-items: center;
  font-size: 0.95em;
  color: var(--callout-text);
  font-weight: 400;
  line-height: 1.45;
  background: var(--callout-grad-bg);
  border-radius: 16px;
}
.callout-note .n-body > :first-child { margin-top: 0; }
.callout-note .n-body > :last-child  { margin-bottom: 0; }

/* (4) ВАЖНО — horizontal compact variant */
.callout-horizontal {
  display: flex;
  align-items: center;
  gap: 0.85em;
  background: var(--callout-grad-bg);
  border-radius: 18px;
  padding: 0.8em 1.4em;
  margin: 1.5em 0;
  break-inside: avoid;
}
.callout-horizontal .h-label {
  font-family: var(--font-display);
  font-size: 0.78em;
  font-weight: 800;
  letter-spacing: 0.3em;
  color: var(--accent);
  text-transform: uppercase;
  flex-shrink: 0;
}
.callout-horizontal .h-rule {
  width: 2px;
  height: 1.6em;
  background: var(--accent);
  border-radius: 2px;
  flex-shrink: 0;
}
.callout-horizontal .h-body {
  font-size: 0.95em;
  color: var(--callout-text);
  font-weight: 500;
  line-height: 1.45;
}
.callout-horizontal .h-body > :first-child { margin-top: 0; }
.callout-horizontal .h-body > :last-child  { margin-bottom: 0; }

/* GitHub-alerts plugin output — route into the closest variant via class.
   Plugin emits .markdown-alert.markdown-alert-{note,tip,important,warning,caution}.
   Map to our variants for visual consistency. */
.markdown-alert {
  display: flex;
  align-items: center;
  gap: 0.85em;
  background: var(--callout-grad-bg);
  color: var(--callout-text);
  border-radius: 18px;
  padding: 0.95em 1.4em;
  margin: 1.5em 0;
  break-inside: avoid;
}
.markdown-alert > :first-child { margin-top: 0; }
.markdown-alert > :last-child  { margin-bottom: 0; }
.markdown-alert-title {
  font-family: var(--font-display);
  font-size: 0.78em;
  font-weight: 800;
  letter-spacing: 0.25em;
  color: var(--accent);
  text-transform: uppercase;
  margin-right: 0.6em;
}
.markdown-alert-important { border-left: 2px solid var(--accent); padding-left: 1em; }
.markdown-alert-caution   { border-left: 2px solid var(--accent); padding-left: 1em; }

/* ----------------------------------------------------------------------
   Dialogue — two-speaker conversation, framed as a chat window.
   Speakers alternate (1,2,1,2), left/right alignment + named avatar
   circles. Driven by :::dialogue.
---------------------------------------------------------------------- */
.dialogue {
  display: grid;
  gap: 0.85em;
  margin: 2em 0;
  padding: 1.4em 1.5em 1.6em;
  background: var(--glass-bg);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  border: 1px solid var(--rule);
  border-radius: var(--r-lg);
  box-shadow: var(--glass-shadow);
  break-inside: avoid;
  position: relative;
}

/* Frame header — chat-window title bar with both speakers + small icon */
.dialogue-header {
  display: flex;
  justify-content: center;
  gap: 0.8em;
  align-items: center;
  margin: -0.2em -0.2em 0.85em;
  padding: 0 0 0.95em;
  border-bottom: 1px solid var(--rule);
  font-family: var(--font-display);
  font-size: 0.75rem;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--fg-muted);
}
.dialogue-header::before {
  content: "💬";
  font-size: 1.1em;
  margin-right: 0.2em;
  opacity: 0.85;
}
.dialogue-header strong {
  color: var(--accent);
  font-weight: 700;
}
.dialogue-header span {
  color: var(--fg-faint);
  font-weight: 400;
}

/* Lines + bubbles */
.dialogue-line {
  display: flex;
  align-items: flex-end;
  gap: 0.7em;
  max-width: 90%;
}
.dialogue-line.speaker-a { justify-self: start; }
.dialogue-line.speaker-b { justify-self: end; flex-direction: row-reverse; text-align: right; }

.dialogue-avatar {
  width: 38px;
  height: 38px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 0.85rem;
  color: var(--accent);
  background: var(--accent-soft);
  border: 1.5px solid var(--accent-dim);
  flex-shrink: 0;
  text-transform: uppercase;
}
.dialogue-line.speaker-b .dialogue-avatar {
  background: var(--surface-subtle);
  border: 1.5px solid var(--rule);
  color: var(--fg-muted);
}

.dialogue-bubble {
  position: relative;
  padding: 0.7em 1.05em;
  border-radius: 18px;
  background: var(--callout-grad-bg);
  color: var(--callout-text);
  font-size: 1rem;
  line-height: 1.5;
  border: 1px solid var(--rule);
}
.dialogue-line.speaker-a .dialogue-bubble {
  border-bottom-left-radius: 4px;
}
.dialogue-line.speaker-b .dialogue-bubble {
  background: var(--accent-soft);
  border: 1px solid var(--accent-dim);
  color: var(--fg);
  border-bottom-right-radius: 4px;
}
.dialogue-bubble > :first-child { margin-top: 0; }
.dialogue-bubble > :last-child  { margin-bottom: 0; }

.dialogue-name {
  display: block;
  font-family: var(--font-display);
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: var(--accent);
  margin-bottom: 0.35em;
}
.dialogue-line.speaker-b .dialogue-name { color: var(--fg-muted); }

/* Arabic line (top of bubble) — Amiri Naskh, RTL, right-aligned, fluid size */
.dialogue-ar {
  font-family: var(--font-arabic);
  font-size: clamp(1.2rem, 1rem + 1.3vw, 1.7rem);   /* fluid across phone → desktop */
  line-height: 1.7;
  color: var(--fg);
  direction: rtl;
  text-align: right;
  margin: 0 0 0.15em;
  font-weight: 400;
  overflow-wrap: anywhere;
  font-feature-settings: 'liga' 1, 'calt' 1, 'kern' 1;
}
.dialogue-line.speaker-b .dialogue-ar { color: var(--fg); }

/* Transliteration (under Arabic) — mono, italic, muted */
.dialogue-tr {
  font-family: var(--font-mono);
  font-size: 0.85rem;
  line-height: 1.4;
  color: var(--fg-muted);
  margin: 0;
  font-style: italic;
  letter-spacing: 0.01em;
  direction: ltr;
  text-align: left;
}
.dialogue-line.speaker-b .dialogue-tr { text-align: right; }

/* Optional Russian translation (third pipe field) */
.dialogue-ru {
  font-size: 0.92rem;
  line-height: 1.45;
  color: var(--fg-subtle);
  margin: 0.35em 0 0;
  padding-top: 0.35em;
  border-top: 1px dashed var(--rule);
}

/* ----------------------------------------------------------------------
   Audio pill — from lecture-assetLib-template / grand-language-template.
   Play button + animated waveform + duration. Driven by audio.js.
---------------------------------------------------------------------- */
.audio-pill {
  display: flex;
  align-items: center;
  gap: 1em;
  background: var(--glass-bg);
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);
  border: 1px solid var(--rule);
  padding: 0.75em 1.4em 0.75em 0.75em;
  border-radius: var(--r-pill);
  margin: 1.5em 0;
  width: 100%;
  box-sizing: border-box;
  break-inside: avoid;
}
.audio-pill .play-btn {
  width: 44px;
  height: 44px;
  border-radius: 50%;
  background: var(--accent);
  display: grid;
  place-items: center;
  color: var(--on-accent);
  border: none;
  cursor: pointer;
  flex-shrink: 0;
  transition: transform 0.15s, box-shadow 0.15s;
}
.audio-pill .play-btn:hover {
  transform: scale(1.06);
  box-shadow: 0 0 24px var(--accent-glow);
}
.audio-pill .play-btn svg { width: 18px; height: 18px; fill: currentColor; }
.audio-pill .waveform {
  display: flex;
  gap: 3px;
  align-items: center;
  height: 28px;
  flex: 1;
  min-width: 0;        /* allow shrinking inside narrow containers */
}
.audio-pill .wave-bar {
  width: 3px;
  background: var(--fg-subtle);
  border-radius: 2px;
  transition: background 0.15s, height 0.15s;
}
.audio-pill .wave-bar.active { background: var(--accent); }
.audio-pill .audio-time {
  font-family: var(--font-mono);
  font-size: 0.85em;
  color: var(--fg-muted);
  letter-spacing: 0.02em;
}
.audio-pill .audio-title {
  font-size: 0.88em;
  color: var(--fg-muted);
  margin-left: 0.5em;
  border-left: 1px solid var(--rule);
  padding-left: 0.85em;
}

/* ----------------------------------------------------------------------
   Quiz — multiple choice (A/B/C). Click to reveal correctness.
   Driven by quiz.js. Each .quiz-option has data-correct="true|false".
---------------------------------------------------------------------- */
.quiz-block {
  display: grid;
  gap: 0.8em;
  margin: 2em 0;
  break-inside: avoid;
}
.quiz-question {
  font-family: var(--font-display);
  font-size: 1.15rem;
  font-weight: 700;
  color: var(--fg);
  margin: 0 0 0.6em;
  letter-spacing: -0.01em;
}
.quiz-kicker {
  display: block;
  font-size: 0.72em;
  font-weight: 700;
  letter-spacing: 0.25em;
  text-transform: uppercase;
  color: var(--accent);
  margin-bottom: 0.4em;
}
.quiz-option {
  display: flex;
  align-items: center;
  gap: 0.9em;
  background: var(--ed-card);
  border: 1.5px solid var(--ed-line);
  padding: 0.9em 1.1em;
  border-radius: 12px;
  font-size: 1rem;
  font-weight: 500;
  color: var(--fg);
  cursor: pointer;
  text-align: left;
  font-family: var(--font-body);
  width: 100%;
  box-shadow: var(--ed-inset);
  transition: border-color 0.18s, background 0.18s, transform 0.15s, box-shadow 0.18s;
}
.quiz-option:hover:not(.revealed) {
  border-color: var(--accent);
  background: var(--ed-gold-bg);
  transform: translateY(-1px);
  box-shadow: var(--ed-inset), var(--ed-lift-sm);
}
.quiz-option .quiz-letter {
  width: 32px;
  height: 32px;
  border-radius: 9px;
  background: var(--ed-gold-bg-2);
  display: grid;
  place-items: center;
  color: var(--ed-gold-ink);
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 0.92rem;
  flex-shrink: 0;
  transition: background 0.18s, color 0.18s;
}
.quiz-option:hover:not(.revealed) .quiz-letter {
  background: var(--ed-grad);
  color: var(--on-accent);
}
.quiz-option.revealed[data-correct="true"] {
  border-color: var(--accent);
  background: var(--accent-dim);
  box-shadow: 0 0 30px var(--accent-dim);
}
.quiz-option.revealed[data-correct="true"] .quiz-letter {
  background: var(--accent);
  color: var(--on-accent);
}
.quiz-option.revealed[data-correct="false"] {
  border-color: var(--ed-bad);
  background: var(--ed-bad-bg);
  opacity: 0.6;
}
.quiz-option.revealed[data-correct="false"] .quiz-letter {
  background: var(--ed-bad-bg);
  color: var(--fg);
}
.quiz-feedback {
  font-size: 0.92em;
  color: var(--accent);
  font-style: italic;
  margin: 0.5em 0 0;
  opacity: 0;
  transform: translateY(-4px);
  transition: opacity 0.25s, transform 0.25s;
}
.quiz-block.revealed .quiz-feedback {
  opacity: 1;
  transform: translateY(0);
}

/* ----------------------------------------------------------------------
   Timeline — horizontal nodes with date + Arabic + body.
   Adapted from grand-history-template + lecture-assetLib.
---------------------------------------------------------------------- */
.timeline {
  display: flex;
  justify-content: space-between;
  gap: 1.5em;
  position: relative;
  margin: 2.5em 0;
  padding: 2em 1em 1em;
  break-inside: avoid;
  flex-wrap: wrap;
}
.timeline-line {
  position: absolute;
  top: 2.55em;
  left: 1em;
  right: 1em;
  height: 2px;
  background: linear-gradient(90deg, transparent, var(--accent), var(--accent), transparent);
  z-index: 1;
  pointer-events: none;
}
.timeline-node {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  flex: 1;
  min-width: 130px;
  max-width: 200px;
  position: relative;
  z-index: 2;
}
.timeline-node .node-dot {
  width: 24px;
  height: 24px;
  background: var(--bg);
  border: 3px solid var(--accent);
  border-radius: 50%;
  margin-bottom: 0.85em;
}
.timeline-node.active .node-dot {
  background: var(--accent);
  box-shadow: 0 0 20px var(--accent-glow);
  animation: aoa-pulse 2.5s infinite;
}
.timeline-node .node-ar {
  font-family: var(--font-arabic);
  font-size: clamp(1.5rem, 1.1rem + 1.7vw, 2.1rem);
  color: var(--accent);
  line-height: 1.4;
  margin-bottom: 0.3em;
  direction: rtl;
  text-align: right;
  font-weight: 400;
  overflow-wrap: anywhere;
  font-feature-settings: 'liga' 1, 'calt' 1, 'kern' 1;
}
.timeline-node .node-date {
  font-family: var(--font-display);
  font-size: 1.05rem;
  font-weight: 700;
  color: var(--accent);
  margin: 0 0 0.4em;
}
.timeline-node .node-body {
  font-size: 0.85rem;
  color: var(--fg-muted);
  line-height: 1.45;
  margin: 0;
}
@keyframes aoa-pulse {
  0%   { box-shadow: 0 0 0 0 var(--accent-glow); }
  70%  { box-shadow: 0 0 0 14px rgba(0, 0, 0, 0); }
  100% { box-shadow: 0 0 0 0 rgba(0, 0, 0, 0); }
}

/* ----------------------------------------------------------------------
   Root tree — central Arabic root + radiating word panels.
   The Method-of-the-Root visualization. From lecture-assetLib.
---------------------------------------------------------------------- */
.root-tree {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  gap: 1.5em;
  align-items: center;
  margin: 2.5em 0;
  padding: 1em;
  break-inside: avoid;
}
.root-tree-hub {
  width: clamp(120px, 34vw, 180px);
  height: clamp(120px, 34vw, 180px);
  border-radius: 50%;
  background: var(--accent-dim);
  border: 2px solid var(--accent);
  box-shadow: 0 0 60px var(--accent-glow);
  display: grid;
  place-items: center;
  font-family: var(--font-arabic);
  font-size: clamp(2.6rem, 1.4rem + 5vw, 4.2rem);
  color: var(--accent);
  direction: rtl;
  line-height: 1;
  text-shadow: 0 0 30px var(--accent-glow);
  justify-self: center;
  font-weight: 400;
  font-feature-settings: 'liga' 1, 'calt' 1, 'kern' 1;
}
.root-tree-branches {
  display: grid;
  gap: 0.7em;
}
.root-tree-branches.left  { justify-items: end;   text-align: right; }
.root-tree-branches.right { justify-items: start; text-align: left; }
.root-branch {
  display: inline-grid;
  gap: 0.15em;
  background: var(--glass-bg);
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);
  border: 1px solid var(--rule);
  border-radius: var(--r-md);
  padding: 0.7em 1.1em;
  transition: border-color 0.18s, transform 0.18s, box-shadow 0.18s;
}
.root-branch:hover {
  border-color: var(--accent-dim);
  transform: translateY(-2px);
  box-shadow: 0 8px 24px var(--accent-dim);
}
.root-branch .ar {
  font-family: var(--font-arabic);
  font-size: clamp(1.4rem, 1.1rem + 1.3vw, 1.9rem);
  color: var(--fg);
  direction: rtl;
  font-weight: 400;
  margin: 0;
  line-height: 1.5;
  overflow-wrap: anywhere;
  font-feature-settings: 'liga' 1, 'calt' 1, 'kern' 1;
}
.root-branch .gloss {
  font-size: 0.82rem;
  color: var(--fg-muted);
  margin: 0;
}

/* ----------------------------------------------------------------------
   Stat card — big gold number + uppercase label
---------------------------------------------------------------------- */
.stat-card {
  text-align: center;
  padding: 1.5em 1em;
  margin: 1.5em 0;
  border-radius: var(--r-lg);
  background: var(--glass-bg);
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);
  border: 1px solid var(--rule);
  break-inside: avoid;
}
.stat-card .stat-num {
  font-family: var(--font-display);
  font-size: 4.5rem;
  font-weight: 900;
  color: var(--accent);
  line-height: 1;
  letter-spacing: -0.04em;
  text-shadow: 0 0 40px var(--accent-dim);
  margin: 0;
}
.stat-card .stat-label {
  font-size: 0.82rem;
  letter-spacing: 0.25em;
  text-transform: uppercase;
  color: var(--fg-muted);
  margin: 0.4em 0 0;
  font-weight: 600;
}

/* ----------------------------------------------------------------------
   Flashcard — adapted from flashcards-template #1.html.
   Two-column glass card: text left, big Arabic right.
---------------------------------------------------------------------- */
/* Flashcard — exact layout from flashcards-template #1.html:
   2-column glass card. Left column = kicker + Russian title + subtitle +
   meta rows (plural, root, synonyms). Right column = huge Arabic hero.
   Click to flip → back side shows full word-info page (examples, notes,
   antonyms, conjugations, etc.) matching the AOA app's WordInfoSheet. */
.flashcard {
  position: relative;
  perspective: 2000px;
  /* Vertical breathing room above/below so the card reads as a hero element
     rather than another block of text. Phone-app feel: cap card width around
     480px and auto-center, so it doesn't dominate a wide prose column but
     stays substantial enough to read comfortably on desktop (matches the
     PDF's typography sizes set in print.css). */
  margin: 2.2em auto;
  min-height: 240px;
  max-width: 480px;
  width: 100%;
  break-inside: avoid;
  cursor: pointer;
}
.flashcard-inner {
  position: relative;
  width: 100%;
  /* CSS Grid stack: both faces share a single cell, so the inner sizes to
     whichever face is taller — content drives the height, never the other
     way round. Replaces the old `position:absolute; inset:0` pattern which
     locked faces to a fixed `min-height` and clipped any overflow. The 3D
     flip still works: `transform-style: preserve-3d` is unaffected by the
     change in 2D layout. */
  display: grid;
  grid-template-areas: 'stack';
  min-height: 240px;        /* floor for empty cards; content can exceed it */
  transition: transform 0.7s cubic-bezier(0.4, 0, 0.2, 1);
  transform-style: preserve-3d;
}
.flashcard.is-flipped .flashcard-inner {
  transform: rotateY(180deg);
}
.flashcard-face {
  grid-area: stack;          /* both faces stack into the same cell */
  position: relative;        /* anchor for absolute children (tabs, hint chip) */
  width: 100%;
  box-sizing: border-box;
  /* Query container: the card's contents size against the CARD's own width,
     not the viewport. Essential inside a :::columns cell, which can hand the
     card far less than its ~240px design width — the narrow re-flow lives in
     the @container block after the back side. inline-size only, so the face
     height still grows with its content. */
  container-type: inline-size;
  -webkit-backface-visibility: hidden;
  backface-visibility: hidden;
  border-radius: var(--r-xl);
  background: var(--glass-bg);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  border: 1px solid var(--rule);
  box-shadow: var(--glass-shadow);
  overflow: hidden;
  /* `backdrop-filter` defeats `backface-visibility: hidden` in Chromium/WebKit
     (a filtered element still paints its backface), so the rotated-away face
     bled through the translucent glass back. Reliable fix, independent of that
     bug: *step* each face's opacity (0s change) at the 90° mid-point of the
     0.7s flip — the card is edge-on then, so the swap is invisible. The
     .is-flipped rules below drive it; pointer-events follow so the hidden face
     never intercepts clicks. */
  transition: opacity 0s linear 0.35s;
}
.flashcard-back {
  transform: rotateY(180deg);
  opacity: 0;                 /* hidden until the flip passes edge-on */
  pointer-events: none;
}
.flashcard.is-flipped .flashcard-front { opacity: 0; pointer-events: none; }
.flashcard.is-flipped .flashcard-back  { opacity: 1; pointer-events: auto; }

/* Dialect tabs — chip row pinned to the TOP of each face. Sits above the
   2-column content via padding-top on .flashcard-front (added when the card
   carries .flashcard--has-dialects). Clicks stopPropagation in flashcard.js
   so they swap the dialect without triggering a flip. */
.flashcard-dialects {
  position: absolute;
  top: 0.95em;
  left: 1em;
  right: 1em;
  display: flex;
  flex-wrap: wrap;
  gap: 0.4em;
  z-index: 3;          /* above the flip-hint chip, below modal overlays */
}
.flashcard-dialect {
  font: 700 0.66rem/1 var(--font-body);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  padding: 0.55em 0.95em;
  border-radius: var(--r-pill);
  border: 1px solid var(--rule);
  background: transparent;
  color: var(--fg-muted);
  cursor: pointer;
  transition: background 0.15s, color 0.15s, border-color 0.15s;
}
.flashcard-dialect:hover {
  color: var(--fg);
  border-color: var(--accent-dim);
}
.flashcard-dialect.is-active {
  background: var(--accent);
  color: var(--on-accent);
  border-color: var(--accent);
}
.flashcard-dialect:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* Flip-hint chip — bottom right of each face */
.flashcard-flip-hint {
  position: absolute;
  bottom: 0.9em;
  right: 1em;
  display: inline-flex;
  align-items: center;
  gap: 0.4em;
  font-family: var(--font-body);
  font-size: 0.65rem;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--accent);
  opacity: 0.55;
  pointer-events: none;
  z-index: 2;
}
/* No back content → nothing to flip to → don't show the "Подробнее" hint. */
.flashcard--noflip .flashcard-flip-hint { display: none !important; }

/* All Arabic inside a flashcard uses var(--font-ar-card) (Amiri Naskh) — the
   same family as the prose default, but kept as its own token so the card word
   can be tuned independently later. `.flashcard [lang="ar"]` (0,2,0) ties
   .prose :lang(ar) but wins by source order (components.css loads after
   prose.css), and also covers the .landing context. */
.flashcard [lang="ar"] { font-family: var(--font-ar-card); }
.flashcard-flip-hint::before {
  content: '↻';
  font-size: 1.2em;
  letter-spacing: 0;
}

/* ── FRONT side ─────────────────────────────────────────────────────
   Vertical, left-aligned stack: tag → Arabic (full width) → Russian →
   transliteration → meta. Stacking (vs. the old text|glyph 2-column grid)
   gives the Arabic word the full card width to breathe. */
.flashcard-front {
  display: flex;
  flex-direction: column;
  align-items: stretch;          /* children fill width; text-align does the left-align */
  justify-content: flex-start;   /* tag sits at the TOP (small space above via padding) */
  gap: 0.5em;
  text-align: left;
  padding: 1.6em 1.9em 1.9em;
}
/* When dialect tabs are present, leave room above for the chip row. */
.flashcard--has-dialects .flashcard-front { padding-top: 4em; }
.flashcard-text {
  display: flex;
  flex-direction: column;
  gap: 0;
  min-width: 0;
  width: 100%;
}
.flashcard-tag {
  font-family: var(--font-body);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--accent);
  margin: 0 0 0.15em;            /* small gap before the Arabic word */
  display: block;
}
.flashcard-russian {
  font-family: var(--font-body);   /* Inter — the "Claude" UI font (same family used site-wide) */
  /* Matches the printable PDF (print.css forces 1.75rem on paper). Web min
     ~1.7rem so it's readable on phones; max 2.6rem on desktop to stay close
     to the app's 44px (~2.75rem) base. */
  font-size: clamp(1.7rem, 4vw, 2.6rem);
  font-weight: 800;
  line-height: 1.05;
  letter-spacing: -0.025em;
  color: var(--fg);
  margin: 0;
  text-shadow: var(--text-shadow-mid);
  word-break: break-word;
  max-width: 100%;
}
.flashcard-subtitle {
  font-family: var(--font-body);
  font-size: 1rem;
  font-weight: 400;
  color: var(--fg-muted);
  margin: 0.4em 0 0;
  letter-spacing: 0.02em;
}
.flashcard-subtitle .translit {
  font-family: var(--font-mono);
  color: var(--accent);
  font-style: normal;
}
.flashcard-subtitle .dot {
  color: var(--fg-faint);
  margin: 0 0.4em;
}

/* Meta rows: "Множ. число | باصات" */
.flashcard-meta {
  margin-top: 1.05em;
  padding-top: 0.85em;
  border-top: 1px solid var(--rule);
  display: grid;
  gap: 0.5em;
}
.flashcard-meta-row {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 1em;
  font-size: 0.9rem;
}
.flashcard-meta-label {
  color: var(--fg-muted);
  font-family: var(--font-body);
}
.flashcard-meta-value {
  font-family: var(--font-ar-card);   /* Amiri — flashcards stay on the card font */
  font-size: clamp(1.25rem, 1.1rem + 0.8vw, 1.6rem);
  font-weight: 700;
  color: var(--fg);
  direction: rtl;
  line-height: 1.25;
  font-feature-settings: 'liga' 1, 'calt' 1, 'kern' 1;
  text-align: right;
}
.flashcard-meta-value.latn {
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 1rem;
  direction: ltr;
  color: var(--fg);
}

/* Big Arabic hero — full-width row in the vertical stack, right-aligned. */
.flashcard-hero {
  display: block;
  width: 100%;
  min-width: 0;
  overflow: hidden;              /* safety net: never let the glyph push the card wider */
  container-type: inline-size;   /* size the glyph against the card width, not the viewport */
}
/* `.flashcard .flashcard-arabic-hero` (0,2,0) MUST out-specify
   `.prose :lang(ar)` (0,1,1) — the hero glyph carries lang="ar", so the prose
   rule's 1.45em was shrinking it to ~24px on screen while the PDF's
   `6rem !important` kept it huge. That mismatch is the "tiny in HTML, huge in
   PDF" bug. The doubled-class selector wins everywhere (prose + landing). */
.flashcard .flashcard-arabic-hero {
  font-family: var(--font-ar-card);   /* Amiri — Naskh hero word for the card */
  /* Sized smaller than the PDF (print.css still forces 6rem on paper). The
     container query scales the glyph against the card's own column so it always
     fits; the cap keeps it from blowing up on wide cards. */
  font-size: 3.4rem;                              /* fallback when cqi isn't supported */
  font-size: clamp(2.4rem, 15cqi, 4.2rem);        /* tighter on screen than v1 — Ali asked smaller */
  line-height: 1.1;
  color: var(--accent);
  text-align: right;                              /* push the Arabic word to the right edge of the frame */
  direction: rtl;
  text-shadow:
    0 0 40px var(--accent-dim),
    var(--text-shadow-mid);
  margin: 0;
  font-weight: 400;                                /* Amiri Naskh regular reads clean at size */
  font-feature-settings: 'liga' 1, 'calt' 1, 'kern' 1;
  word-break: break-word;
  max-width: 100%;
}

/* ── BACK side (info page) ──────────────────────────────────────── */
.flashcard-back-inner {
  display: grid;
  grid-template-rows: auto 1fr;
  gap: 1em;
  padding: 1.8em 1.95em 2.2em;
  height: 100%;
  overflow-y: auto;
}
.flashcard-back-header {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1em;
  padding-bottom: 0.85em;
  border-bottom: 1px solid var(--rule);
}
.flashcard .flashcard-back-arabic {
  font-family: var(--font-ar-card);   /* Amiri */
  font-size: clamp(2.2rem, 1.6rem + 2.4vw, 3.2rem);
  font-weight: 400;                   /* Amiri Naskh regular (no 300 master) */
  color: var(--accent);
  direction: rtl;
  line-height: 1.1;
  margin: 0;
  text-shadow: 0 0 22px var(--accent-dim);   /* slightly tamer glow to match the lighter stroke */
  font-feature-settings: 'liga' 1, 'calt' 1, 'kern' 1;
}
.flashcard-back-meta {
  text-align: right;
  font-family: var(--font-body);
}
.flashcard-back-translation {
  font-size: 1.25rem;
  font-weight: 700;
  color: var(--fg);
  margin: 0;
}
.flashcard-back-translit {
  font-family: var(--font-mono);
  font-size: 0.85rem;
  color: var(--fg-muted);
  margin: 0.2em 0 0;
}
.flashcard-back-body {
  display: grid;
  gap: 1em;
  align-content: start;
}
.flashcard-back-section {
  display: grid;
  gap: 0.5em;
}
.flashcard-back-section-label {
  font-family: var(--font-body);
  font-size: 0.65rem;
  font-weight: 700;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--accent);
}
.flashcard-back-field {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 1em;
  font-size: 0.9rem;
}
.flashcard-back-field .label { color: var(--fg-muted); }
.flashcard-back-field .value {
  font-family: var(--font-ar-card);   /* Tajawal */
  font-size: 1.25rem;
  font-weight: 700;
  color: var(--fg);
  direction: rtl;
  font-feature-settings: 'liga' 1, 'calt' 1;
}
.flashcard-back-field .value.latn {
  font-family: var(--font-body);
  font-size: 0.95rem;
  font-weight: 600;
  direction: ltr;
}

/* Example sentences on back */
.flashcard-back-example {
  display: grid;
  gap: 0.15em;
  padding: 0.7em 0.9em;
  background: var(--surface-subtle);
  border-radius: var(--r-md);
  border-left: 2px solid var(--accent);
}
.flashcard-back-example .ar {
  font-family: var(--font-ar-card);   /* Tajawal */
  font-size: 1.25rem;
  font-weight: 600;
  color: var(--fg);
  direction: rtl;
  text-align: right;
  line-height: 1.55;
}
.flashcard-back-example .ru {
  font-size: 0.88rem;
  color: var(--fg-muted);
  font-style: italic;
}

.flashcard-back-note {
  font-size: 0.88rem;
  color: var(--fg-muted);
  font-style: italic;
  line-height: 1.55;
  padding: 0.6em 0.85em;
  background: var(--accent-soft);
  border-radius: var(--r-md);
}

/* ── Narrow card (inside a :::columns cell, etc.) ───────────────────────
   A single flashcard fills its column, so a 2- or 3-column layout can hand it
   well under its ~240px design width. The existing @media (max-width:600px)
   block only fires when the whole VIEWPORT is small — useless for a card that
   is narrow because of its column on an otherwise wide screen. Keying off the
   card's OWN width (container query on .flashcard-face) re-flows the front +
   back so it stays clean instead of overflowing. Normal / full-width cards are
   untouched — this only fires at ≤300px of card width. */
@container (max-width: 300px) {
  .flashcard-front { padding: 1.15em 1.2em 1.4em; }
  .flashcard--has-dialects .flashcard-front { padding-top: 3.3em; }

  /* The base size is clamp(1.7rem, 4vw, 2.6rem); the 4vw is VIEWPORT-relative,
     so on a wide screen it stayed huge inside a narrow card and overflowed.
     Re-anchor it to the card (cqi) so it shrinks with the column. */
  .flashcard-russian { font-size: clamp(1.3rem, 13cqi, 1.85rem); }
  .flashcard-subtitle { font-size: 0.9rem; }
  .flashcard .flashcard-arabic-hero { font-size: clamp(1.9rem, 15cqi, 4.2rem); }

  /* Stack each label/value row so the RTL value can't collide with its label
     across a hairline-narrow row (they share it via space-between by default). */
  .flashcard-meta-row,
  .flashcard-back-field {
    flex-direction: column;
    align-items: flex-start;
    gap: 0.15em;
  }
  .flashcard-meta-value,
  .flashcard-back-field .value { text-align: left; }
  .flashcard-meta-value { font-size: 1.3rem; }

  /* Dialect chips shrink so the row doesn't crowd the content below it. */
  .flashcard-dialect { padding: 0.45em 0.7em; letter-spacing: 0.08em; }

  /* Back side: tighten padding and stack the header (glyph over translation). */
  .flashcard-back-inner { padding: 1.2em 1.25em 1.5em; }
  .flashcard-back-header { flex-direction: column; align-items: flex-start; gap: 0.45em; }
  .flashcard-back-meta { text-align: left; }
  .flashcard .flashcard-back-arabic { font-size: 2.3rem; }
}

/* ----------------------------------------------------------------------
   Flashcard LIST — single-card swipe carousel, mirroring the AOA PWA's
   cards page UX. One card visible at a time, swipeable horizontally; prev
   and next arrow buttons live in the carousel's outer grid columns, so on
   wide desktops they fill the gutters that otherwise look like empty
   space next to a centered card (no more "hole"). The track itself caps
   the card at ~520px for readability; the outer container is wider so the
   buttons can sit outside the card.
---------------------------------------------------------------------- */
.flashcardlist-carousel {
  position: relative;
  width: 100%;
  max-width: 680px;                  /* 652 + the 28px of inline padding below */
  margin: 2.5em auto;
  padding-inline: 0.9em;             /* horizontal breathing room around the list */
  box-sizing: border-box;
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  grid-template-areas:
    "prev track next"
    ".    dots  .";
  column-gap: 0.8em;
  row-gap: 0.6em;
  align-items: stretch;
}
.flashcardlist-track {
  grid-area: track;
  position: relative;
  display: flex;
  align-items: stretch;              /* every card stretches to the tallest one */
  gap: 1.6em;                        /* breathing room between cards on swipe */
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  -webkit-overflow-scrolling: touch;
  /* overflow-x:auto forces overflow-y to clip too, so give the cards vertical
     AND a little horizontal room — otherwise their borders/soft shadows get
     cut off at the track edges. */
  padding: 0.85em 0.5em 1.05em;
  scroll-padding-inline: 0.5em;
  scrollbar-width: none;
  min-width: 0;
}
.flashcardlist-track::-webkit-scrollbar { display: none; }
.flashcardlist-track:focus-visible {
  outline: 2px solid var(--accent-dim);
  outline-offset: 3px;
  border-radius: var(--r-lg);
}
.flashcardlist-track .flashcard {
  flex: 0 0 100%;                    /* one card per swipe view */
  scroll-snap-align: center;
  scroll-snap-stop: always;
  margin: 0;
  max-width: none;                   /* override the standalone 560px cap */
  min-height: 360px;                 /* baseline so short cards don't look squat */
}
/* Force the single-column STACKED layout (Arabic glyph on top, full width)
   for cards inside the list, on every viewport. The default 2-col front
   squeezes the glyph into a narrow right column, which collapses the
   container-query (`cqi`) font size to ~24px — far smaller than the PDF's
   fixed 6rem. Stacking gives the glyph the card's full width so it renders
   large, matching the AOA app card + the PDF. */
/* List cards inherit the base vertical left-aligned front; just tune padding. */
.flashcardlist-track .flashcard-front { padding: 1.6em 1.7em 1.9em; }
.flashcardlist-track .flashcard--has-dialects .flashcard-front { padding-top: 4em; }
.flashcardlist-track .flashcard-meta { width: 100%; }

/* Prev / next nav buttons in the outer gutters */
.flashcardlist-nav {
  grid-row: 1;
  align-self: center;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  border: 1px solid var(--rule);
  background: var(--bg-elev);
  color: var(--fg);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  transition: background 0.15s, color 0.15s, border-color 0.15s, transform 0.15s;
}
.flashcardlist-nav--prev { grid-area: prev; }
.flashcardlist-nav--next { grid-area: next; }
.flashcardlist-nav:hover:not([disabled]) {
  background: var(--accent);
  color: var(--on-accent);
  border-color: var(--accent);
}
.flashcardlist-nav:active:not([disabled]) { transform: scale(0.94); }
.flashcardlist-nav:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}
.flashcardlist-nav[disabled] {
  opacity: 0.35;
  cursor: not-allowed;
}

/* ======================================================================
   Private-lesson gate — blurs the locked content + overlays a purchase card.
   `.lesson-gated` lights up via build.mjs when a `<!-- course: slug -->` is
   declared in the source MD. `.is-locked` is on until lesson-gate.js
   confirms the user has purchased.
====================================================================== */
.lesson-gated.is-locked main,
.lesson-gated.is-locked .prose,
.lesson-gated.is-locked .landing {
  filter: blur(12px);
  pointer-events: none;
  user-select: none;
}
.lesson-gate-overlay {
  position: fixed;
  inset: 0;
  z-index: 90;
  display: grid;
  place-items: center;
  padding: 1.2rem;
  background:
    radial-gradient(60% 50% at 50% 30%, var(--accent-soft), transparent 70%),
    rgba(7, 5, 4, 0.78);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  animation: lesson-gate-fade 0.28s ease-out;
}
@keyframes lesson-gate-fade {
  from { opacity: 0; }
  to   { opacity: 1; }
}
.lesson-gate-card {
  width: 100%;
  max-width: 480px;
  padding: 2.4em 2em 1.8em;
  border-radius: var(--r-lg);
  background:
    radial-gradient(140% 60% at 50% 0%, var(--accent-soft), transparent 60%),
    var(--bg);
  border: 1.5px solid var(--accent);
  box-shadow:
    0 30px 80px -22px var(--accent-glow),
    inset 0 0 0 1px rgba(255, 255, 255, 0.04);
  text-align: center;
  color: var(--fg);
}
.lesson-gate-icon {
  font-size: 2.6rem;
  line-height: 1;
  margin-bottom: 0.4em;
  color: var(--accent);
}
.lesson-gate-title {
  font-family: var(--font-display);
  font-size: 1.5rem;
  font-weight: 800;
  letter-spacing: -0.01em;
  color: var(--fg);
  margin: 0 0 0.5em;
}
.lesson-gate-sub {
  color: var(--fg-muted);
  font-size: 0.96rem;
  line-height: 1.55;
  margin: 0 0 1.3em;
  max-width: 38ch;
  margin-inline: auto;
}
.lesson-gate-actions {
  display: flex;
  flex-direction: column;
  gap: 0.7em;
  margin-bottom: 1em;
}
.lesson-gate-actions .btn {
  width: 100%;
  justify-content: center;
}
.lesson-gate-fineprint {
  font-size: 0.78rem;
  color: var(--fg-muted);
  letter-spacing: 0.02em;
  margin: 0;
}
:root[data-theme="light"] .lesson-gate-overlay {
  background:
    radial-gradient(60% 50% at 50% 30%, var(--accent-soft), transparent 70%),
    rgba(255, 255, 255, 0.84);
}

/* ======================================================================
   Floating "back to top" button — shared by every layout (page/landing/site).
   Lives in components.css (not landing.css) so it works on lesson pages too.
====================================================================== */
.scroll-top {
  position: fixed;
  right: clamp(0.9rem, 2.5vw, 1.8rem);
  bottom: clamp(0.9rem, 2.5vw, 1.8rem);
  z-index: 60;
  width: 2.85rem;
  height: 2.85rem;
  display: grid;
  place-items: center;
  border: 1px solid var(--accent-dim);
  border-radius: 50%;
  padding: 0;
  background: var(--accent);
  color: var(--on-accent);
  box-shadow:
    0 12px 28px -10px var(--accent-glow),
    inset 0 0 0 1px rgba(255, 255, 255, 0.08);
  cursor: pointer;
  opacity: 0;
  transform: translateY(12px);
  pointer-events: none;
  transition: opacity 0.22s, transform 0.22s, background 0.18s, box-shadow 0.22s;
}
.scroll-top svg {
  width: 1.2rem;
  height: 1.2rem;
  display: block;
}
.scroll-top.is-visible {
  opacity: 1;
  transform: translateY(0);
  pointer-events: auto;
}
.scroll-top:hover {
  background: var(--accent);
  transform: translateY(-3px);
  box-shadow:
    0 18px 36px -10px var(--accent-glow),
    inset 0 0 0 1px rgba(255, 255, 255, 0.18);
}
.scroll-top:active { transform: translateY(0); }
.scroll-top:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 4px;
}
/* Light theme: stronger border, lighter shadow so the gold button still reads
   on a white background. */
:root[data-theme="light"] .scroll-top {
  box-shadow: 0 10px 24px -10px rgba(0, 0, 0, 0.25);
}
/* On phones where a sticky `.mobile-cta` pill bar may sit at the bottom on
   landing/site pages, push the scroll-top button above it so they don't
   stack. The pill is ≤760px-only; outside that range the corner is free. */
@media (max-width: 760px) {
  .scroll-top {
    bottom: 5.4rem;   /* clears the ~4.6rem mobile-cta bar + a small gap */
  }
}
/* Never print the button (PDF export, /tracks pages printed by a buyer). */
@media print {
  .scroll-top { display: none !important; }
}

/* On narrow viewports the card spans full width and the two nav buttons
   drop below the dots, sitting side by side and centered (they meet in the
   middle of a 2-col row with a small gap). 44px gutters are too generous
   when every pixel matters on a phone. */
@media (max-width: 560px) {
  .flashcardlist-carousel {
    grid-template-columns: 1fr 1fr;
    grid-template-areas:
      "track track"
      "dots  dots"
      "prev  next";
    column-gap: 0;
  }
  .flashcardlist-nav {
    grid-row: auto;
    width: 40px;
    height: 40px;
  }
  .flashcardlist-nav--prev { grid-area: prev; justify-self: end;   margin-right: 0.35em; }
  .flashcardlist-nav--next { grid-area: next; justify-self: start; margin-left: 0.35em; }
}

/* dots */
.flashcardlist-dots {
  grid-area: dots;
  display: flex;
  justify-content: center;
  flex-wrap: wrap;
  gap: 0.5em;
  margin-top: 0;
}
.flashcardlist-dots button {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  border: 1px solid var(--rule);
  background: transparent;
  padding: 0;
  cursor: pointer;
  transition: background 0.15s, transform 0.15s;
}
.flashcardlist-dots button:hover { background: var(--accent-dim); }
.flashcardlist-dots button.is-active {
  background: var(--accent);
  border-color: var(--accent);
  transform: scale(1.2);
}

/* ======================================================================
   :::todo — branded checklist
   ====================================================================== */
.todo-card {
  margin: 2em 0;
  padding: 1.4em 1.6em;
  border: 1px solid var(--rule);
  border-radius: var(--r-lg);
  background: var(--glass-bg);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
}
.todo-title {
  font-family: var(--font-body);
  font-weight: 700;
  font-size: 0.72rem;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--accent);
  margin: 0 0 1em;
}
.todo-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.7em; }
.todo-item {
  display: flex;
  align-items: flex-start;
  gap: 0.75em;
  font-size: 1rem;
  line-height: 1.5;
  color: var(--fg);
}
.todo-box {
  flex-shrink: 0;
  width: 1.35em;
  height: 1.35em;
  margin-top: 0.05em;
  border: 1.5px solid var(--rule);
  border-radius: 0.4em;
  display: grid;
  place-items: center;
  font-size: 0.85em;
  font-weight: 800;
  color: var(--on-accent);
  transition: background 0.15s, border-color 0.15s;
}
.todo-item.is-done .todo-box { background: var(--accent); border-color: var(--accent); }
.todo-item.is-done .todo-text { color: var(--fg-muted); text-decoration: line-through; text-decoration-color: var(--accent-dim); }
/* interactive (JS-enabled) state: clickable + hover affordance */
.todo-item.is-interactive { cursor: pointer; border-radius: var(--r-sm); transition: background 0.12s; }
.todo-item.is-interactive:hover { background: var(--accent-soft); }
.todo-item.is-interactive:hover .todo-box { border-color: var(--accent); }
.todo-item.is-interactive:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* ======================================================================
   :::quote — featured pull-quote
   ====================================================================== */
/* duck.school editorial statement — NO card. A centred composition: a small
   gold quote mark on top, then a GIANT high-contrast Playfair Didone serif
   statement (the duck.school signature), then a tiny uppercase attribution.
   All colours come from theme tokens, so it reads correctly on cream AND dark
   without a separate light override. */
.pullquote {
  margin: 3.4em auto;
  padding: 0 clamp(0px, 2cqi, 1rem);  /* a hair of side gutter on narrow frames so text never touches the edge */
  /* Fluid measure: never wider than the column, capped at a comfortable line
     length, and never below a usable min on tiny phones. min()/clamp keep it
     readable from a 320px phone up to a wide monitor. */
  max-width: clamp(17rem, 92%, 34rem);
  /* Make the quote its OWN container so the statement scales with the quote's
     ACTUAL width (cqi below), not the raw viewport. This is what makes it adapt
     correctly inside a narrow reading column / in-app reader card on a wide
     screen — where vw would oversize the text. */
  container-type: inline-size;
  position: relative;
  background: none;
  border: 0;
  text-align: center;
}
/* Centred opening mark — the one gold note; sits as a flourish above the text.
   Scales with the quote width too, so it stays proportional on every frame. */
.pullquote::before {
  content: '\201C';                 /* “ */
  display: block;
  font-family: 'Playfair Display', var(--font-display);
  font-size: clamp(2.1rem, 1rem + 5.5cqi, 3rem);
  line-height: 0.6;
  margin-bottom: 0.35em;
  color: var(--accent);
  pointer-events: none;
}
/* Out-specify the generic `.prose blockquote` rule (0,1,1) which would else
   force italic + a left bar + an inset background onto the body. */
.pullquote .pullquote-body {
  margin: 0;
  border: 0;
  padding: 0;
  background: none;
  font-family: 'Playfair Display', var(--font-display);
  font-weight: 600;
  font-style: normal;
  /* cqi = 1% of the quote's own width → the statement grows with the BOX, not the
     viewport. Bounded so it stays elegant from a 320px phone (≈1.3rem) up to the
     34rem cap (≈2.05rem). overflow-wrap guards against any single long word. */
  font-size: clamp(1.3rem, 0.7rem + 4.2cqi, 2.05rem);
  line-height: 1.2;
  letter-spacing: -0.012em;
  color: var(--fg);
  overflow-wrap: break-word;
  hyphens: auto;
}
/* Drop the generic blockquote mark — `.pullquote::before` is our flourish. */
.pullquote .pullquote-body::before { content: none; }
/* Cite — tiny uppercase attribution, centred under the statement. Scoped under
   `.pullquote` to beat `.prose figcaption { font-style: italic }` (0,1,1). */
.pullquote .pullquote-cite {
  margin-top: 1.5em;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: baseline;
  gap: 0.2em 0.7em;
  font-style: normal;
  text-align: center;
}
.pullquote-author {
  font-family: var(--font-body);
  font-weight: 700;
  font-size: 0.78rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--fg);
}
/* a gold middot joins author → role */
.pullquote-author + .pullquote-role::before {
  content: '·';
  margin-right: 0.7em;
  color: var(--accent);
  font-weight: 700;
}
.pullquote-role {
  font-family: var(--font-body);
  font-size: 0.78rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--fg-muted);
}
/* *emphasis* inside the statement: gold italic so the key word pops. */
.pullquote-em { font-style: italic; color: var(--accent-strong); font-weight: 600; }

/* ======================================================================
   :::image — framed figure + caption  (also styles bare markdown images)
   ====================================================================== */
.media-figure {
  margin: 2.2em 0;
  padding: 0.6em;
  border: 1px solid var(--rule);
  border-radius: var(--r-lg);
  background: var(--glass-bg);
  box-shadow: var(--glass-shadow);
}
.media-figure--bare { padding: 0; border: 0; background: none; box-shadow: none; }
/* The .prose img rule (prose.css) adds its own border/margin/shadow to every
   image; override here (higher specificity) since the figure is the frame. */
.media-figure .media-img {
  display: block;
  width: 100%;
  height: auto;
  margin: 0;
  border: 0;
  box-shadow: none;
  border-radius: var(--r-md);
}
.figure-caption {
  margin-top: 0.7em;
  text-align: center;
  font-size: 0.85rem;
  color: var(--fg-muted);
  font-style: italic;
}

/* ======================================================================
   :::map — static location card (no tile service; offline + PDF safe)
   ====================================================================== */
.map-card {
  position: relative;
  margin: 2.2em 0;
  border: 1px solid var(--rule);
  border-radius: var(--r-lg);
  overflow: hidden;
  background: var(--glass-bg);
  color: inherit;
  transition: border-color 0.15s, transform 0.15s;
}
.map-card--link:hover { border-color: var(--accent-dim); transform: translateY(-2px); }
/* stretched link covers the whole card; meta/pin sit above for legibility */
.map-link { position: absolute; inset: 0; z-index: 3; text-indent: -9999px; overflow: hidden; }
.map-stage, .map-meta { position: relative; z-index: 1; }
.map-stage {
  position: relative;
  aspect-ratio: 16 / 7;
  display: grid;
  place-items: center;
  overflow: hidden;
}
.map-image { width: 100%; height: 100%; object-fit: cover; }
.map-stage--empty {
  background:
    radial-gradient(circle at 50% 40%, var(--accent-soft), transparent 70%),
    repeating-linear-gradient(0deg, var(--rule) 0 1px, transparent 1px 26px),
    repeating-linear-gradient(90deg, var(--rule) 0 1px, transparent 1px 26px),
    var(--bg-elev);
}
.map-pin {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -100%);
  color: var(--accent);
  filter: drop-shadow(0 3px 5px rgba(0,0,0,0.45));
}
.map-stage:not(.map-stage--empty) .map-pin { color: #fff; filter: drop-shadow(0 2px 4px rgba(0,0,0,0.6)); }
.map-meta {
  display: flex;
  flex-direction: column;
  gap: 0.25em;
  padding: 1em 1.2em;
}
.map-name { font-family: var(--font-display); font-weight: 700; font-size: 1.1rem; color: var(--fg); }
.map-coords { font-family: var(--font-mono); font-size: 0.82rem; color: var(--fg-muted); }
.map-note { font-size: 0.9rem; color: var(--fg-muted); }
.map-open { margin-top: 0.3em; font-size: 0.8rem; font-weight: 700; letter-spacing: 0.04em; color: var(--accent); }

/* ======================================================================
   :::chart — build-time inline SVG (pie / donut / bar / line / radar)
   ====================================================================== */
.chart-figure {
  margin: 2.4em 0;
  padding: 1.5em 1.4em 1.3em;
  border: 1px solid var(--rule);
  border-radius: var(--r-lg);
  background: var(--glass-bg);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
}
.chart-title {
  font-family: var(--font-body);
  font-weight: 700;
  font-size: 0.72rem;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--accent);
  text-align: center;
  margin-bottom: 1em;
}
.chart-canvas {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 1.4em;
}
.chart-svg { width: 100%; max-width: 360px; height: auto; overflow: visible; }
.chart-svg--pie { max-width: 260px; }
.chart-svg--radar { max-width: 520px; }
/* shared text + axis styling */
.chart-svg text { font-family: var(--font-body); fill: var(--fg); }
.chart-axis { stroke: var(--rule); stroke-width: 1; }
.chart-axis-label { font-size: 13px; fill: var(--fg-muted); }
.chart-bar-val { font-size: 14px; font-weight: 700; fill: var(--fg); }
.chart-bar { transition: opacity 0.15s; }
/* line */
.chart-line { stroke: var(--accent); stroke-width: 2.5; stroke-linejoin: round; stroke-linecap: round; }
.chart-area { fill: var(--accent-soft); }
.chart-dot { fill: var(--accent); stroke: var(--bg); stroke-width: 1.5; }
/* pie / donut */
.chart-donut-total { font-family: var(--font-display); font-weight: 800; font-size: 26px; fill: var(--fg); }
.chart-legend { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.5em; min-width: 130px; }
.chart-legend-item { display: flex; align-items: center; gap: 0.55em; font-size: 0.98rem; color: var(--fg); }
.chart-swatch { width: 0.85em; height: 0.85em; border-radius: 3px; flex-shrink: 0; }
.chart-legend-label { flex: 1; }
.chart-legend-val { font-weight: 700; color: var(--fg-muted); }
/* radar — big, readable plot; only the points capture the pointer (everything
   else is pointer-events:none) so the vertices are easy to hover/tap. */
.chart-radar-grid { fill: none; stroke: var(--rule); stroke-width: 1; pointer-events: none; }
.chart-radar-grid--outer { stroke: var(--rule-hi); stroke-width: 1.4; }
.chart-radar-axis { stroke: var(--rule); stroke-width: 1; pointer-events: none; }
.chart-radar-area { stroke: var(--accent); stroke-width: 2.5; stroke-linejoin: round; pointer-events: none; }
.chart-radar-hit { fill: transparent; pointer-events: all; cursor: pointer; }
.chart-radar-dot { fill: var(--accent-strong); stroke: var(--bg); stroke-width: 2; pointer-events: none; }
.chart-radar-pt.is-hot .chart-radar-dot { r: 7; filter: brightness(1.12); }
.chart-radar-label { font-size: 15px; fill: var(--fg); pointer-events: none; }
.chart-radar-val { font-weight: 800; font-size: 15.5px; fill: var(--accent-strong); }
.chart-radar-tick { font-size: 11px; fill: var(--fg-muted); paint-order: stroke; stroke: var(--bg); stroke-width: 3px; stroke-linejoin: round; pointer-events: none; }

/* ----------------------------------------------------------------------
   Note — subtle aside (less prominent than callout)
---------------------------------------------------------------------- */
.note-block {
  position: relative;
  padding: 1.1em 1.3em 1.1em 2.8em;
  margin: 1.5em 0;
  border-radius: var(--r-md);
  background: var(--surface-subtle);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  border: 1px solid var(--rule);
  color: var(--fg-muted);
  font-size: 0.97em;
  font-style: italic;
  break-inside: avoid;
}
.note-block::before {
  content: '✎';
  position: absolute;
  left: 1em;
  top: 1em;
  font-size: 1.15em;
  color: var(--accent);
  font-style: normal;
  opacity: 0.7;
}
.note-block > :first-child { margin-top: 0; }
.note-block > :last-child  { margin-bottom: 0; }

/* ----------------------------------------------------------------------
   Important — prominent banner
---------------------------------------------------------------------- */
.important-block {
  position: relative;
  padding: 1.5em 1.8em 1.5em 4.2em;
  margin: 2.25em 0;
  border-radius: var(--r-lg);
  background:
    linear-gradient(135deg, var(--accent-soft) 0%, transparent 100%),
    var(--glass-bg);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  border: 1px solid var(--accent-dim);
  box-shadow:
    0 30px 60px rgba(0, 0, 0, 0.4),
    inset 0 0 0 1px var(--accent-dim);
  color: var(--fg);
  break-inside: avoid;
}
.important-block::before {
  content: '★';
  position: absolute;
  left: 1.25em;
  top: 1.1em;
  font-size: 1.7em;
  color: var(--accent);
  filter: drop-shadow(0 0 12px var(--accent-glow));
}
.important-block::after {
  content: 'IMPORTANT';
  position: absolute;
  top: -0.65em;
  left: 3em;
  font-family: var(--font-body);
  font-size: 0.7rem;
  font-weight: 800;
  letter-spacing: 0.25em;
  color: var(--on-accent);
  background: var(--accent);
  padding: 0.4em 0.85em;
  border-radius: var(--r-pill);
}
.important-block > :first-child { margin-top: 0; }
.important-block > :last-child  { margin-bottom: 0; }

/* ----------------------------------------------------------------------
   Definition — glossary term + body
---------------------------------------------------------------------- */
.definition {
  display: grid;
  gap: 0.5em;
  padding: 1.4em 1.6em;
  margin: 1.75em 0;
  border-radius: var(--r-md);
  background: var(--glass-bg);
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);
  border: 1px solid var(--rule);
  border-top: 3px solid var(--accent);
  break-inside: avoid;
}
.definition-term {
  font-family: var(--font-display);
  font-size: 1.25rem;
  font-weight: 700;
  color: var(--accent);
  margin: 0;
  letter-spacing: -0.01em;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  text-shadow: 0 0 20px var(--accent-dim);
}
.definition-term::after {
  content: ' —';
  color: var(--fg-faint);
  font-weight: 400;
}
.definition-body {
  color: var(--fg-muted);
  font-size: 1em;
  margin: 0;
  line-height: 1.6;
}
.definition-body > :first-child { margin-top: 0; }
.definition-body > :last-child  { margin-bottom: 0; }

/* ----------------------------------------------------------------------
   Roadmap — numbered steps with arrow connectors
---------------------------------------------------------------------- */
.roadmap {
  margin: 2.5em 0;
  display: grid;
  gap: 1em;
  break-inside: avoid;
}
.roadmap-step {
  display: grid;
  grid-template-columns: 64px 1fr;
  gap: 1.25em;
  align-items: center;
  padding: 1.2em 1.5em;
  background: var(--glass-bg);
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);
  border: 1px solid var(--rule);
  border-radius: var(--r-lg);
  transition: transform 0.25s, box-shadow 0.25s, border-color 0.25s;
}
.roadmap-step:hover {
  transform: translateY(-2px);
  box-shadow: 0 18px 50px -10px var(--accent-dim);
  border-color: var(--accent-dim);
}
.roadmap-step-num {
  width: 64px;
  height: 64px;
  border-radius: var(--r-pill);
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-display);
  font-size: 1.55rem;
  font-weight: 800;
  background: var(--accent-soft);
  color: var(--accent);
  border: 1px solid var(--accent-dim);
  text-shadow: 0 0 20px var(--accent-glow);
}
.roadmap-step-body { display: grid; gap: 0.2em; }
.roadmap-step-title {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.15rem;
  color: var(--fg);
  margin: 0;
  letter-spacing: -0.01em;
}
.roadmap-step-meta {
  font-size: 0.88rem;
  color: var(--fg-muted);
  margin: 0;
}
.roadmap-arrow {
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.5rem;
  color: var(--accent);
  opacity: 0.5;
  line-height: 0.5;
  margin-block: -0.6em;
}

/* ----------------------------------------------------------------------
   Blockquote
---------------------------------------------------------------------- */
.prose blockquote {
  position: relative;
  margin: 2em 0;
  padding: 1.1em 1.6em;
  border-left: 3px solid var(--accent);
  background: var(--surface-subtle);
  border-radius: 0 var(--r-md) var(--r-md) 0;
  font-style: italic;
  color: var(--fg-muted);
  break-inside: avoid;
}
.prose blockquote::before {
  content: "\201C";
  position: absolute;
  left: 0.3em;
  top: -0.05em;
  font-size: 3.5em;
  line-height: 1;
  color: var(--accent);
  opacity: 0.2;
  font-family: Georgia, serif;
  pointer-events: none;
}
.prose blockquote > :first-child { margin-top: 0; }
.prose blockquote > :last-child  { margin-bottom: 0; }

/* ----------------------------------------------------------------------
   Code
---------------------------------------------------------------------- */
.prose pre,
.prose pre.shiki {
  background: var(--bg-code) !important;
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  border: 1px solid var(--rule);
  border-radius: var(--r-md);
  padding: 1.2em 1.4em;
  overflow-x: auto;
  font-size: 0.9em;
  line-height: 1.6;
  font-family: var(--font-mono);
  break-inside: avoid;
  position: relative;
}
.prose pre code {
  background: none;
  padding: 0;
  font-family: var(--font-mono);
  font-feature-settings: 'liga' 0;
}
.prose p code,
.prose li code,
.prose td code,
.prose th code {
  background: var(--accent-soft);
  color: var(--accent);
  padding: 0.15em 0.5em;
  border-radius: var(--r-sm);
  font-size: 0.88em;
  font-family: var(--font-mono);
  font-weight: 500;
  border: 1px solid var(--accent-dim);
}

.copy-btn {
  position: absolute;
  top: 0.6em;
  right: 0.6em;
  padding: 0.35em 0.85em;
  font-size: 0.72em;
  font-family: var(--font-body);
  font-weight: 700;
  background: var(--bg-elev);
  color: var(--fg-muted);
  border: 1px solid var(--rule);
  border-radius: var(--r-pill);
  cursor: pointer;
  opacity: 0;
  transition: opacity 0.15s;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
.prose pre:hover .copy-btn { opacity: 1; }
.copy-btn:hover { color: var(--accent); border-color: var(--accent); }
.copy-btn.copied { color: var(--accent); border-color: var(--accent); }

/* ----------------------------------------------------------------------
   Details / Toggle
---------------------------------------------------------------------- */
.prose details {
  margin: 1.5em 0;
  border: 1px solid var(--rule);
  border-radius: var(--r-md);
  background: var(--glass-bg);
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);
  overflow: hidden;
}
.prose details > summary {
  cursor: pointer;
  font-weight: 700;
  list-style: none;
  outline: none;
  padding: 1em 1.25em;
  display: flex;
  align-items: center;
  gap: 0.6em;
  transition: background 0.15s;
  font-family: var(--font-display);
}
.prose details > summary:hover {
  background: var(--bg-elev-hi);
}
.prose details > summary::-webkit-details-marker { display: none; }
.prose details > summary::before {
  content: "›";
  display: inline-block;
  font-size: 1.4em;
  line-height: 1;
  transition: transform 0.2s;
  color: var(--accent);
  font-weight: 700;
}
.prose details[open] > summary::before { transform: rotate(90deg); }
.prose details[open] > summary { border-bottom: 1px solid var(--rule); }
.prose details > :not(summary) {
  padding: 0.6em 1.25em 1em 1.9em;   /* extra left padding so the toggle body sits indented */
}

/* ----------------------------------------------------------------------
   Tables
---------------------------------------------------------------------- */
.prose table {
  width: 100%;
  border-collapse: collapse;
  margin: 2em 0;
  font-size: 0.95em;
  font-feature-settings: 'tnum' 1;
  background: var(--glass-bg);
  border: 1px solid var(--rule);
  border-radius: var(--r-md);
  overflow: hidden;
}
.prose th,
.prose td {
  padding: 0.85em 1.1em;
  border-bottom: 1px solid var(--rule);
  text-align: left;
  vertical-align: top;
}
.prose tr:last-child td { border-bottom: none; }
.prose th {
  background: var(--bg-elev);
  font-weight: 700;
  font-family: var(--font-display);
  font-size: 0.78em;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: var(--accent);
}
.prose tr { break-inside: avoid; }
.prose tbody tr:hover { background: var(--bg-elev-hi); }

/* ----------------------------------------------------------------------
   Footnotes
---------------------------------------------------------------------- */
.footnotes {
  margin-top: 4em;
  padding-top: 1.75em;
  border-top: 1px solid var(--rule);
  font-size: 0.88em;
  color: var(--fg-muted);
}
.footnotes ol { padding-left: 1.5em; }
.footnotes li { margin-bottom: 0.5em; }
.footnote-ref a {
  text-decoration: none;
  border-bottom: none;
  font-size: 0.85em;
  vertical-align: super;
  padding: 0 0.2em;
  color: var(--accent);
}

/* ----------------------------------------------------------------------
   Table of Contents
---------------------------------------------------------------------- */
.toc {
  background: var(--glass-bg);
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);
  border: 1px solid var(--rule);
  border-radius: var(--r-lg);
  padding: 1.4em 1.8em;
  margin: 2.5em 0 3em;
  font-family: var(--font-body);
  font-size: 0.95em;
}
.toc::before {
  content: "Содержание";
  display: block;
  font-weight: 700;
  font-size: 0.72em;
  text-transform: uppercase;
  letter-spacing: 0.25em;
  color: var(--accent);
  margin-bottom: 0.9em;
}
/* When toc.js attaches a real <button class="toc-toggle">, the script also
   adds `.toc--enhanced` so the static pseudo label hides — the button now
   plays that role and is interactive. Class-based (not :has) for broader
   browser support. */
.toc--enhanced::before { display: none; }
.toc-toggle {
  display: flex;
  align-items: center;
  gap: 0.7em;
  width: 100%;
  background: transparent;
  border: none;
  border-radius: var(--r-sm);
  padding: 0.35em 0.5em;
  margin: -0.35em -0.5em 0.9em;
  font: inherit;
  font-weight: 700;
  font-size: 0.72em;
  text-transform: uppercase;
  letter-spacing: 0.25em;
  color: var(--accent);
  cursor: pointer;
  transition: background 0.15s;
}
.toc-toggle:hover { background: var(--accent-soft); }
.toc-toggle:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
.toc-toggle-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--accent);
  flex-shrink: 0;
}
.toc-toggle-icon svg { display: block; }
/* Collapsed: hide the body. On mobile/inline TOC this is a simple vertical
   fold (toggle stays horizontal). Desktop adds a horizontal collapse — see
   the @media block lower in this file. */
.toc--collapsed > ul,
.toc--collapsed > ol { display: none; }
.toc--collapsed { padding-block: 0.6em; }
.toc--collapsed .toc-toggle { margin-bottom: 0; }
.toc ul { list-style: none; padding-left: 1.1em; margin: 0.25em 0; }
.toc > ul { padding-left: 0; }
.toc li { margin: 0.35em 0; position: relative; }
.toc a {
  display: block;
  color: var(--fg-muted);
  text-decoration: none;
  border-bottom: none;
  padding: 0.3em 0.55em 0.3em 0.7em;
  border-left: 2px solid transparent;
  border-radius: 0 var(--r-sm) var(--r-sm) 0;
  margin-left: -0.7em;        /* pull the rail flush with the TOC edge */
  transition: color 0.15s, background 0.15s, border-color 0.15s, transform 0.15s;
}
.toc a:hover {
  color: var(--accent);
  background: var(--accent-soft);
  border-left-color: var(--accent-dim);
}
/* Active section (set by toc.js as the reader scrolls) — visually clear so the
   reader always sees "where am I" at a glance: gold left rail + soft gold bg +
   bold text + a slight push-right to read as "you are here". */
.toc a.is-current {
  color: var(--accent);
  font-weight: 700;
  background: var(--accent-soft);
  border-left-color: var(--accent);
  box-shadow: inset 0 0 0 1px var(--accent-dim);
  transform: translateX(2px);
}
/* Parent section of the currently-active subsection. Subtler than is-current
   so the reader can still tell which one is the live target. */
.toc li.is-parent > a:not(.is-current) {
  color: var(--fg);
  border-left-color: var(--accent-dim);
}
/* "You are here" caret marker rendered to the left of the active link. */
.toc a.is-current::before {
  content: "";
  position: absolute;
  left: -1.4em;
  top: 50%;
  transform: translateY(-50%);
  width: 0;
  height: 0;
  border-top: 0.4em solid transparent;
  border-bottom: 0.4em solid transparent;
  border-left: 0.5em solid var(--accent);
  filter: drop-shadow(0 0 6px var(--accent-glow));
}

/* TOC as a sticky LEFT SIDEBAR on lessons that include [[toc]].
   `:has()` scopes this to pages WITH a TOC only — everything else keeps the
   centered single column. Below 901px (and in print, where the page box is
   ~794px) the sidebar layout doesn't apply, so the TOC flows inline at its
   original position. */
@media (min-width: 901px) {
  .prose:has(.toc) {
    /* RECORDING-FRIENDLY layout. Pages that include [[toc]] are lessons that
       get screen-recorded with a camera overlay on the RIGHT-MIDDLE side, so
       the block sits LEFT of centre: small left margin (breathing room from
       the viewport edge) and a LARGE right gap reserved for the camera.
       Content column is sized for comfortable reading (~80–90ch), not the
       full 160ch measure used on non-TOC pages — wide reading lines look
       cramped once the camera covers part of the page. */
    max-width: min(100% - 2rem, calc(220px + 2.5rem + 90ch));
    margin-left: clamp(2rem, 6vw, 6rem);
    margin-right: auto;
    padding-left: 0;
    display: grid;
    grid-template-columns: 220px minmax(0, 1fr);
    column-gap: 2.5rem;
    align-items: start;
  }
  .prose:has(.toc) > * { grid-column: 2; min-width: 0; }
  .prose:has(.toc) > .toc {
    grid-column: 1;
    grid-row: 1 / 9999;          /* span all rows → sits alongside everything */
    position: sticky;
    /* Clear the sticky top nav (~68px) so the TOC's top isn't hidden behind it. */
    top: 5.5rem;
    align-self: start;
    margin: 0;
    max-height: calc(100vh - 6.5rem);
    overflow-y: auto;
    font-size: 0.85em;
    padding: 1.2em 1.4em;
  }
  /* When user collapses the TOC: shrink the sidebar column to a narrow bar
     (~36px) so the reading column gets ~180px of freed width. The bar shows
     the hamburger icon + a vertical "СОДЕРЖАНИЕ" label. Click to reopen. */
  .prose:has(.toc--collapsed) {
    grid-template-columns: 36px minmax(0, 1fr);
    column-gap: 2rem;
  }
  .prose:has(.toc--collapsed) > .toc {
    padding: 0.9em 0.3em;
    width: 36px;
    min-width: 36px;
    overflow: hidden;
    transition: width 0.2s ease, padding 0.2s ease;
  }
  /* Vertical layout in collapsed mode — hamburger on top, vertical label below. */
  .toc--collapsed .toc-toggle {
    flex-direction: column;
    gap: 0.8em;
    padding: 0.35em 0;
    margin: 0;
    width: 100%;
    text-align: center;
    justify-content: flex-start;
  }
  .toc--collapsed .toc-toggle-label {
    writing-mode: vertical-rl;
    transform: rotate(180deg);   /* read bottom-to-top → natural for left rail */
    letter-spacing: 0.3em;
    line-height: 1;
  }
}
/* When jumping to a heading (TOC click / #anchor), keep it below the sticky
   nav instead of scrolling it underneath. */
.prose h1[id], .prose h2[id], .prose h3[id], .prose h4[id],
.prose h1, .prose h2, .prose h3, .prose h4 {
  scroll-margin-top: 5.5rem;
}

/* ----------------------------------------------------------------------
   Heading anchor permalinks
---------------------------------------------------------------------- */
.prose h1 .header-anchor,
.prose h2 .header-anchor,
.prose h3 .header-anchor,
.prose h4 .header-anchor {
  text-decoration: none;
  border-bottom: none;
  color: inherit;
}
.prose h1 .header-anchor:hover,
.prose h2 .header-anchor:hover,
.prose h3 .header-anchor:hover,
.prose h4 .header-anchor:hover {
  color: var(--accent);
}

/* ----------------------------------------------------------------------
   Site navigation chrome (top-bar with brand + buttons)
---------------------------------------------------------------------- */
.site-nav {
  position: sticky;
  top: 0;
  z-index: 10;
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 1em;
  padding: 1em 2em;
  background: var(--nav-bg);
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);
  border-bottom: 1px solid var(--rule);
}
.site-nav .brand {
  display: flex;
  align-items: center;
  gap: 0.85em;
}
.site-nav .brand-logo {
  width: 36px;
  height: 36px;
  border: 2px solid var(--accent);
  border-radius: var(--r-sm);
  display: grid;
  place-items: center;
  font-weight: 800;
  font-size: 0.78rem;
  color: var(--accent);
  font-family: var(--font-display);
  letter-spacing: 0.05em;
}
.site-nav .brand-text {
  font-size: 0.72rem;
  letter-spacing: 0.3em;
  color: var(--fg-muted);
  font-weight: 500;
  text-transform: uppercase;
}
.site-nav-right { display: flex; gap: 0.5em; }
.site-nav button {
  background: transparent;
  color: var(--fg);
  border: 1px solid var(--rule);
  border-radius: var(--r-pill);
  padding: 0.5em 1.1em;
  font-size: 0.82rem;
  font-weight: 600;
  font-family: var(--font-body);
  cursor: pointer;
  letter-spacing: 0.02em;
  transition: all 0.18s;
}
.site-nav button:hover {
  border-color: var(--accent);
  color: var(--accent);
  transform: translateY(-1px);
}
.site-nav .pdf-export {
  background: var(--accent);
  color: var(--on-accent);
  border-color: transparent;
}
.site-nav .pdf-export:hover {
  background: var(--accent-strong);
  color: var(--on-accent);
}

/* Admin indicator — shown only when copyright.js detected an admin unlock */
[data-aoa-admin="true"] .site-nav .brand::after {
  content: 'ADMIN';
  display: inline-block;
  margin-left: 0.6em;
  padding: 0.18em 0.55em;
  border-radius: var(--r-pill);
  font-size: 0.6rem;
  font-weight: 800;
  letter-spacing: 0.2em;
  background: var(--accent);
  color: var(--on-accent);
}

/* ======================================================================
   Responsive — components.css had NO media queries, so the flip-card and
   timeline (built for desktop) overflowed phones. Keep these last.
   Shared by the prose reader AND the landing/site layouts.
   ====================================================================== */

/* Flashcard — the 2-column front (text | big Arabic glyph) can't fit a
   phone. Stack it: glyph on top, text below, centered. The faces are
   absolutely positioned, so the container min-height must grow to fit. */
@media (max-width: 600px) {
  .flashcard, .flashcard-inner { min-height: 360px; }
  /* The front is already a vertical left-aligned stack (base layout); on mobile
     just tighten padding. The chip row stays left-aligned to match the stack. */
  .flashcard-front {
    gap: 0.45em;
    padding: 1.7em 1.5em 2.2em;
  }
  .flashcard--has-dialects .flashcard-front { padding-top: 3.85em; }
  .flashcard-dialects {
    top: 0.85em;
    left: 0.7em;
    right: 0.7em;
  }
  .flashcard-dialect { padding: 0.5em 0.8em; font-size: 0.6rem; }
  .flashcard .flashcard-arabic-hero { font-size: clamp(2.2rem, 18cqi, 3.8rem); }
  .flashcard-meta { width: 100%; }
  .flashcard-flip-hint { bottom: 0.7em; right: 0.8em; }
}

/* Timeline — horizontal on desktop, VERTICAL on small screens (logical:
   each milestone reads top-to-bottom with the rail running down the left). */
@media (max-width: 640px) {
  .timeline {
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    padding: 1.4em 0;
  }
  /* rail runs down the CENTER */
  .timeline-line {
    top: 1.4em;
    bottom: 1.4em;
    left: 50%;
    right: auto;
    width: 2px;
    height: auto;
    transform: translateX(-50%);
    background: linear-gradient(180deg, transparent, var(--accent) 8%, var(--accent) 92%, transparent);
  }
  /* each node: [content] [dot on the center rail] [content]; content alternates
     sides so it never crosses the line. (child 1 of .timeline is .timeline-line,
     so the nodes are children 2..5 — nth-child math accounts for that.) */
  .timeline-node {
    display: grid;
    grid-template-columns: 1fr 24px 1fr;
    column-gap: 0.9em;
    row-gap: 0.1em;
    align-items: start;
    min-width: 0;
    width: 100%;
    max-width: none;      /* desktop base caps nodes at 200px — lift it so they span the block */
    padding: 0.7em 0;
  }
  .timeline-node .node-dot { grid-column: 2; grid-row: 1 / -1; align-self: start; justify-self: center; margin: 0; }
  /* default — content to the RIGHT of the rail */
  .timeline-node .node-ar { grid-column: 3; grid-row: 1; text-align: left; margin-bottom: 0.1em; }
  .timeline-node .node-date { grid-column: 3; grid-row: 2; text-align: left; }
  .timeline-node .node-body { grid-column: 3; grid-row: 3; text-align: left; }
  /* alternate nodes — content to the LEFT of the rail */
  .timeline-node:nth-child(odd) .node-ar,
  .timeline-node:nth-child(odd) .node-date,
  .timeline-node:nth-child(odd) .node-body { grid-column: 1; text-align: right; }
}

/* Horizontal callout (e.g. ВАЖНО / ГАРАНТИЯ) — on a phone the wide letter-spaced
   label eats horizontal room, cramping the body. Flip the label to a vertical
   rail so the body gets the full remaining width and reads comfortably. */
@media (max-width: 560px) {
  .callout-horizontal {
    align-items: stretch;
    gap: 0.85em;
    padding: 0.9em 1.15em;
  }
  .callout-horizontal .h-label {
    writing-mode: vertical-rl;
    text-orientation: mixed;
    letter-spacing: 0.18em;
    line-height: 1;
    align-self: center;
  }
  .callout-horizontal .h-rule {
    height: auto;          /* short bar → full-height vertical divider */
    align-self: stretch;
  }
  .callout-horizontal .h-body { min-width: 0; }
}

/* Root tree — the 3-column hub layout (1fr | hub | 1fr) needs ~480px, so it
   overflowed phones. Stack it: the root glyph on top, derivative cards below. */
@media (max-width: 600px) {
  .root-tree {
    grid-template-columns: 1fr;
    justify-items: center;
    gap: 1.1em;
  }
  .root-tree-hub { order: -1; width: 130px; height: 130px; }
  .root-tree-branches { width: 100%; gap: 0.6em; }
  .root-tree-branches.left,
  .root-tree-branches.right { justify-items: stretch; text-align: center; }
  .root-branch { width: 100%; max-width: 320px; margin-inline: auto; }

  /* Wide multi-column tables: scroll horizontally instead of overflowing the
     page (so every column stays reachable on a phone — never clipped). */
  .prose table { display: block; overflow-x: auto; width: 100%; -webkit-overflow-scrolling: touch; }
}

/* ======================================================================
   :::whiteboard — iPad-style writable canvas asset
   ====================================================================== */
.whiteboard {
  margin: 2em 0;
  padding: 0.85em;
  background: var(--bg-elev);
  border: 1px solid var(--rule);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-md);
  /* `--wb-ink` is the default stroke colour for the "ink" swatch — it
     adapts to theme via .whiteboard--light/.whiteboard--dark. */
  --wb-ink: #f4ece0;
  --wb-paper: #1b160f;
  --wb-grid: rgba(255, 255, 255, 0.04);
  break-inside: avoid;
}
.whiteboard--light {
  --wb-ink: #1b160f;
  --wb-paper: #f7f1e5;
  --wb-grid: rgba(0, 0, 0, 0.04);
}

.whiteboard-title {
  margin: 0 0 0.4em;
  padding-inline: 0.4em;
  font-size: 0.95em;
  font-weight: 700;
  color: var(--fg);
}
.whiteboard-prompt {
  margin: 0 0 0.6em;
  padding: 0.5em 0.8em;
  background: var(--accent-soft);
  border-left: 3px solid var(--accent);
  border-radius: 0 var(--r-sm) var(--r-sm) 0;
  color: var(--fg-muted);
  font-size: 0.88em;
  line-height: 1.45;
}

/* ---- Toolbar ---- */
.whiteboard-toolbar {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.25em;
  padding: 0.35em;
  margin-bottom: 0.55em;
  background: var(--bg-code);
  border: 1px solid var(--rule);
  border-radius: var(--r-sm);
}
.wb-tool {
  display: inline-grid;
  place-items: center;
  width: 2em;
  height: 2em;
  background: transparent;
  border: 1px solid transparent;
  border-radius: 6px;
  color: var(--fg-muted);
  cursor: pointer;
  transition: background 140ms ease, color 140ms ease, border-color 140ms ease;
}
.wb-tool:hover {
  background: var(--bg-elev-hi);
  color: var(--fg);
  border-color: var(--rule);
}
.wb-tool.is-active {
  background: var(--accent-soft);
  color: var(--accent);
  border-color: var(--accent-dim);
}
.wb-tool svg { width: 1em; height: 1em; display: block; }
.wb-tool-grow { flex: 1; }
.wb-sep {
  width: 1px;
  height: 1.4em;
  background: var(--rule);
  margin-inline: 0.25em;
}
.wb-colors {
  display: inline-flex;
  align-items: center;
  gap: 0.2em;
}
.wb-color {
  width: 1.45em;
  height: 1.45em;
  padding: 0;
  border-radius: 50%;
  border: 1.5px solid transparent;
  background: var(--c, currentColor);
  cursor: pointer;
  transition: transform 140ms ease, border-color 140ms ease, box-shadow 140ms ease;
}
.wb-color[data-wb-color="ink"] {
  background: var(--wb-ink);
}
.wb-color:hover { transform: scale(1.1); }
.wb-color.is-active {
  border-color: var(--accent);
  box-shadow: 0 0 0 1px var(--bg-elev), 0 0 10px var(--accent-glow);
}
.wb-width {
  display: inline-flex;
  align-items: center;
  gap: 0.35em;
  color: var(--fg-muted);
  padding-inline: 0.25em;
}
.wb-width svg { width: 0.9em; height: 0.9em; }
.wb-width input[type="range"] {
  width: 80px;
  accent-color: var(--accent);
}

/* ---- Surface ---- */
.whiteboard-surface {
  position: relative;
  width: 100%;
  border-radius: var(--r-md);
  overflow: hidden;
  background: var(--wb-paper)
    /* subtle dot grid in the page tint so the surface feels like paper */
    radial-gradient(var(--wb-grid) 1px, transparent 1px) 0 0 / 18px 18px;
  border: 1px solid var(--rule);
  touch-action: none;     /* essential: stops the browser from scrolling instead of drawing */
  cursor: crosshair;
}
.whiteboard-surface canvas {
  display: block;
  width: 100%;
  height: 100%;
}
.whiteboard-placeholder {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  text-align: center;
  padding: 1em;
  color: var(--fg-subtle);
  font-size: 0.85em;
  pointer-events: none;
  transition: opacity 200ms ease;
}
.whiteboard[data-wb-has-strokes="true"] .whiteboard-placeholder { opacity: 0; }

.whiteboard-footnote {
  margin: 0.55em 0.5em 0;
  font-size: 0.7em;
  color: var(--fg-subtle);
  text-align: right;
}
.whiteboard-footnote.is-saving { color: var(--accent); }

/* Eraser cursor — a small circle outline so the user sees what they're erasing. */
.whiteboard-surface[data-wb-mode="eraser"] {
  cursor: cell;
}

/* Print: the canvas renders fine in puppeteer's PDF as long as we've
   replayed the saved strokes before capture (whiteboard.js handles
   that on __AOA_PDF_MODE). Hide the toolbar + placeholder in print. */
@media print {
  .whiteboard-toolbar,
  .whiteboard-placeholder,
  .whiteboard-footnote {
    display: none !important;
  }
  .whiteboard {
    break-inside: avoid;
    page-break-inside: avoid;
  }
}

/* Each width slider shows the numeric value to its right (so the
   learner sees "12" or "2.5" without having to fiddle). Pen and
   eraser get independent inputs that swap based on active tool. */
.wb-width-val {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 1.6em;
  padding: 0 0.4em;
  font-size: 0.78em;
  font-variant-numeric: tabular-nums;
  color: var(--fg-muted);
  background: var(--bg-elev-hi);
  border-radius: 4px;
  margin-left: 0.25em;
}
.wb-width--eraser input[type="range"] { accent-color: var(--fg-muted); }
.wb-width--chalk  input[type="range"] { accent-color: var(--wb-ink); }

/* ----- Whiteboard: lock button + locked-state overlay -----
   When the user clicks the padlock the board enters "view only" mode:
   the surface stops capturing pointer events (so no accidental smudges
   on top of carefully drawn work), the cursor goes back to default, a
   small "Заблокировано" badge appears in the bottom-right corner, and
   the lock icon flips closed. Click again to unlock. */
.wb-tool--lock[aria-pressed="true"] {
  background: var(--accent-soft);
  color: var(--accent);
  border-color: var(--accent-dim);
}
.whiteboard[data-wb-locked="true"] .whiteboard-surface {
  pointer-events: none;
  cursor: default;
}
.whiteboard[data-wb-locked="true"] .whiteboard-surface::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgba(0,0,0,0.04) 0%, rgba(0,0,0,0.10) 100%);
  pointer-events: none;
}
.whiteboard-lock-badge {
  position: absolute;
  bottom: 0.6em;
  right: 0.6em;
  display: none;
  align-items: center;
  gap: 0.35em;
  padding: 0.35em 0.7em;
  background: rgba(0,0,0,0.6);
  color: #fff;
  border-radius: var(--r-pill);
  font-size: 0.72em;
  letter-spacing: 0.06em;
  pointer-events: none;
  backdrop-filter: blur(6px);
}
.whiteboard-lock-badge svg { width: 0.9em; height: 0.9em; }
.whiteboard[data-wb-locked="true"] .whiteboard-lock-badge { display: inline-flex; }

/* ----- Whiteboard: drag-to-resize handle below the surface -----
   A thin strip with a centered grip pill. Drag down to grow the
   board, drag up to shrink. Keyboard ↑ / ↓ also adjusts (±20px) for
   accessibility. Persisted per-board in localStorage so the chosen
   height survives reload. */
.whiteboard-resize {
  height: 0.85rem;
  cursor: row-resize;
  user-select: none;
  touch-action: none;
  display: grid;
  place-items: center;
  margin-top: -0.15rem;
}
.whiteboard-resize:focus-visible { outline: none; }
.wb-resize-grip {
  display: inline-block;
  width: 3.5em;
  height: 3px;
  border-radius: 2px;
  background: var(--rule-hi);
  transition: background 160ms ease, width 160ms ease, height 160ms ease, box-shadow 160ms ease;
}
.whiteboard-resize:hover .wb-resize-grip,
.whiteboard-resize:focus-visible .wb-resize-grip {
  background: var(--accent);
  width: 5em;
  height: 4px;
  box-shadow: 0 0 14px var(--accent-glow);
}
.whiteboard-resize.is-dragging .wb-resize-grip {
  background: var(--accent);
  width: 6em;
  height: 4px;
  box-shadow: 0 0 16px var(--accent-glow);
}
/* While dragging force row-resize cursor everywhere so it doesn't
   flicker as the pointer crosses out of the 0.85rem strip. */
body.is-resizing-whiteboard, body.is-resizing-whiteboard * {
  cursor: row-resize !important;
  user-select: none !important;
}

/* =========================================================================
 * :::board — Miro/FigJam-style Excalidraw embed skeleton.
 *
 * The actual board UI is React + Excalidraw, mounted by notes-runtime/board.js
 * once the page is interactive. Until that happens this skeleton is what the
 * reader sees, so it has to look intentional (not "broken loading state").
 * The skeleton block is also what shows when the lesson HTML is opened
 * standalone (without notes-runtime) — the noscript fallback link drives
 * readers to aoa-academy.ru/board?id=<uuid> for the live view.
 * ========================================================================= */
.aoa-board-figure {
  margin: 1.8em 0;
  display: block;
  width: 100%;
  isolation: isolate;
}
.aoa-board-caption {
  font-size: .92em;
  color: var(--fg-muted);
  margin: 0 0 .55em;
  font-style: italic;
}
.aoa-board-skeleton {
  position: relative;
  display: grid;
  place-items: center;
  width: 100%;
  border: 1px solid var(--ed-line);
  border-radius: 14px;
  background:
    radial-gradient(circle at 24% 18%, rgba(212, 164, 55, 0.10), transparent 55%),
    radial-gradient(circle at 78% 82%, rgba(212, 164, 55, 0.06), transparent 55%),
    var(--ed-card);
  overflow: hidden;
}
.aoa-board-skeleton::before {
  content: "";
  position: absolute; inset: 0;
  background:
    linear-gradient(90deg, transparent 0, transparent 49.6%, var(--ed-line-2) 50%, transparent 50.4%) 0 0 / 64px 100%,
    linear-gradient(0deg,  transparent 0, transparent 49.6%, var(--ed-line-2) 50%, transparent 50.4%) 0 0 / 100% 64px;
  pointer-events: none;
  mask-image: radial-gradient(ellipse at center, black 35%, transparent 75%);
}
.aoa-board-skeleton-inner {
  text-align: center;
  padding: 28px 24px;
  max-width: 420px;
}
.aoa-board-skeleton-glyph {
  display: block;
  font-family: 'Amiri', serif;
  font-size: 56px;
  line-height: 1;
  color: var(--ed-gold);
  opacity: .8;
  margin-bottom: 12px;
}
.aoa-board-skeleton-title {
  margin: 0 0 6px;
  font-weight: 600;
  color: var(--ed-ink);
  font-size: 16px;
}
.aoa-board-skeleton-sub {
  margin: 0 0 14px;
  font-size: 12.5px;
  color: var(--ed-ink-soft);
}
.aoa-board-skeleton-link {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 8px 14px;
  border-radius: 999px;
  background: rgba(212, 164, 55, 0.12);
  color: var(--ed-gold-ink);
  text-decoration: none;
  font-size: 13px;
  font-weight: 500;
  border: 1px solid rgba(212, 164, 55, 0.32);
  transition: background-color .15s ease, border-color .15s ease;
}
.aoa-board-skeleton-link:hover {
  background: rgba(212, 164, 55, 0.22);
  border-color: rgba(212, 164, 55, 0.55);
}
/* When the React island has mounted, the skeleton is hidden by board.js
   adding [data-aoa-board-mounted="true"] to the figure. */
.aoa-board-figure[data-aoa-board-mounted="true"] .aoa-board-skeleton { display: none; }

.aoa-board-noscript {
  margin-top: 10px;
  padding: 10px 14px;
  border-radius: 10px;
  background: var(--surface-subtle);
  font-size: 12.5px;
  color: var(--ed-ink-soft);
}
.aoa-board-noscript a {
  color: var(--ed-gold-ink);
  text-decoration: underline;
}

/* The mounted React island carries a slot div that board.js inserts into
   the figure. Excalidraw paints into it directly — we just give it the
   requested pixel height. */
.aoa-board-mount {
  position: relative;
  width: 100%;
  border-radius: 14px;
  overflow: hidden;
  border: 1px solid var(--ed-line);
}

/* In PDF export mode the Excalidraw bundle is not loaded; the skeleton
   block is what gets rasterized. Keep it tight and readable on paper. */
@media print {
  .aoa-board-skeleton {
    background: #fff;
    border-color: #d4d4d4;
    color: #1b1207;
  }
  .aoa-board-skeleton-glyph { color: #d4a437; }
  .aoa-board-skeleton-title { color: #1b1207; }
  .aoa-board-skeleton-sub   { color: rgba(0,0,0,.55); }
  .aoa-board-skeleton-link  { color: #8a6a18; border-color: #d4a437; background: rgba(212,164,55,.08); }
}

/* ----------------------------------------------------------------------
   :::columns — multi-column layout primitive.

   Authoring lives in src/preprocess.mjs `renderColumns`. The container
   is a CSS Grid so cells stay equal-width by default; pass `min=...` in
   the directive to switch to a responsive auto-fit track instead.

   Cells inherit prose typography from their parent; we only handle
   layout, gap, and the narrow-screen collapse here.
---------------------------------------------------------------------- */
.columns {
  --cols-gap: 1.75rem;
  display: grid;
  gap: var(--cols-gap);
  margin: 1.5em 0;
  align-items: start;                /* default = align cells to top */
}

/* Fixed equal-width tracks for N columns — used when `min=` is NOT set. */
.columns.columns-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.columns.columns-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.columns.columns-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.columns.columns-5 { grid-template-columns: repeat(5, minmax(0, 1fr)); }
.columns.columns-6 { grid-template-columns: repeat(6, minmax(0, 1fr)); }

/* When the author provides `min=...`, switch to a fluid grid that
   collapses cells under the threshold instead of squishing them. */
.columns.columns--responsive {
  --cols-min: 220px;
  grid-template-columns: repeat(auto-fit, minmax(var(--cols-min), 1fr));
}

.columns.columns--align-middle { align-items: center; }
.columns.columns--align-bottom { align-items: end; }

/* Cell — kill the leading/trailing margin so column boundaries align with
   the surrounding prose rhythm. The blank-line padding inside the markup
   means the first child is normally a <p>; trim its top margin to avoid a
   visual gap above the cell. */
.columns .col > :first-child { margin-top: 0; }
.columns .col > :last-child  { margin-bottom: 0; }

/* Narrow screens — collapse to one column. The fixed-track variants need
   an override; the responsive variant already does this on its own via
   auto-fit, so we only target the fixed-N variants here. */
@media (max-width: 640px) {
  .columns.columns-2,
  .columns.columns-3,
  .columns.columns-4,
  .columns.columns-5,
  .columns.columns-6 {
    grid-template-columns: 1fr;
  }
}

/* Print — keep columns side-by-side on A4 (they were authored that way for
   a reason) but tighten the gap and prevent a cell from splitting across
   pages mid-paragraph. */
@media print {
  .columns { gap: 1rem; }
  .columns .col { break-inside: avoid; }
}

/* ====================================================================
   Practice blocks + visual-text additions
   Rendered by directives in preprocess.mjs: fillblank, match, order,
   truefalse, table, verse, compare, vocab, highlight.
   ==================================================================== */

.practice-block {
  margin: 1.75rem 0;
  padding: 1.25rem 1.4rem;
  background: var(--bg-elev);
  border: 1px solid rgba(212, 164, 55, 0.22);
  border-radius: 14px;
  position: relative;
}
.practice-kicker {
  display: inline-block;
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--accent);
  margin-bottom: 0.6rem;
  font-weight: 600;
}
.practice-actions {
  display: flex;
  gap: 0.5rem;
  flex-wrap: wrap;
  margin-top: 0.9rem;
}
.practice-btn {
  appearance: none;
  border: 1px solid rgba(212, 164, 55, 0.4);
  background: rgba(212, 164, 55, 0.08);
  color: var(--fg);
  padding: 0.45rem 0.95rem;
  border-radius: 8px;
  font: inherit;
  font-size: 13px;
  cursor: pointer;
  transition: background 120ms ease, border-color 120ms ease;
}
.practice-btn:hover { background: rgba(212, 164, 55, 0.16); }
.practice-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.practice-feedback { margin: 0.7rem 0 0; font-size: 13.5px; min-height: 1.2em; }
.practice-feedback.is-ok  { color: var(--ed-ok); }
.practice-feedback.is-err { color: var(--ed-bad); }
.practice-hint { margin: 0.4rem 0 0; padding: 0.5rem 0.8rem; background: var(--surface-subtle); border-left: 2px solid var(--accent); color: var(--fg-muted); font-size: 13.5px; border-radius: 0 6px 6px 0; }

/* ====================================================================
   Graded test — :::test → <form data-test>, scored by test.js.
   Deferred grading: answer everything, submit once, see score + verdict.
   ==================================================================== */
.test-card {
  margin: 2rem 0;
  padding: 1.5rem 1.6rem 1.6rem;
  background: var(--ed-card);
  border: 1px solid rgba(212, 164, 55, 0.28);
  border-radius: 16px;
  box-shadow: var(--ed-inset), var(--ed-lift-sm);
  position: relative;
  break-inside: avoid;
}
.test-head { margin-bottom: 1.1rem; }
.test-kicker {
  display: inline-block;
  font-size: 11px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--accent);
  font-weight: 700;
}
.test-title {
  font-family: var(--font-display);
  font-size: 1.4rem;
  line-height: 1.2;
  margin: 0.35rem 0 0.3rem;
  color: var(--fg);
}
.test-meta { margin: 0.25rem 0 0; font-size: 13px; color: var(--fg-subtle); }

.test-questions {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 1rem;
}
.test-q {
  padding: 1.05rem 1.1rem;
  background: var(--ed-card-2);
  border: 1px solid var(--ed-line-2);
  border-left: 3px solid var(--ed-line);
  border-radius: 12px;
  transition: border-color 160ms ease;
}
.test-q-text {
  margin: 0 0 0.8rem;
  font-size: 16px;
  line-height: 1.6;
  color: var(--fg);
  font-weight: 500;
}
.test-q-num { color: var(--accent); font-weight: 700; margin-right: 0.15rem; }

/* options (choice + true/false) */
.test-opts { display: grid; gap: 0.5rem; }
.test-opts--tf { grid-auto-flow: column; grid-auto-columns: 1fr; }
.test-opt {
  position: relative;
  display: flex;
  align-items: center;
  gap: 0.7rem;
  padding: 0.65rem 0.9rem;
  background: var(--ed-card);
  border: 1.5px solid var(--ed-line);
  border-radius: 10px;
  cursor: pointer;
  box-shadow: var(--ed-inset);
  transition: border-color 140ms ease, background 140ms ease, transform 120ms ease, box-shadow 140ms ease;
}
.test-opt:hover {
  border-color: var(--accent);
  background: var(--ed-gold-bg);
  transform: translateY(-1px);
  box-shadow: var(--ed-inset), var(--ed-lift-sm);
}
.test-opt input { position: absolute; opacity: 0; width: 0; height: 0; }
.test-opt-letter {
  width: 28px; height: 28px;
  flex-shrink: 0;
  display: grid; place-items: center;
  border-radius: 8px;
  background: rgba(212, 164, 55, 0.12);
  color: var(--accent);
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 0.85rem;
}
.test-opt-text { font-size: 15px; color: var(--fg); }
.test-opt-text[lang="ar"] { font-size: 1.3rem; }
/* keyboard focus + pre-grade selection */
.test-opt:has(input:focus-visible) { outline: 2px solid var(--accent); outline-offset: 2px; }
.test-opt:has(input:checked) {
  border-color: var(--accent);
  background: rgba(212, 164, 55, 0.10);
}
/* post-grade reveal */
.test-card.is-graded .test-opt { cursor: default; }
.test-opt.is-correct {
  border-color: var(--ed-ok);
  background: var(--ed-ok-bg);
}
.test-opt.is-correct .test-opt-letter { background: var(--ed-ok); color: #fff; }
.test-opt.is-wrong {
  border-color: var(--ed-bad);
  background: var(--ed-bad-bg);
}
.test-opt.is-wrong .test-opt-letter { background: var(--ed-bad-bg); color: var(--fg); }

/* fill-in inputs */
.test-fill {
  display: inline-block;
  min-width: 4.5em;
  padding: 0.15em 0.5em;
  margin: 0 0.15em;
  border: none;
  border-bottom: 2px solid var(--accent);
  background: rgba(212, 164, 55, 0.06);
  border-radius: 6px 6px 0 0;
  color: var(--fg);
  font: inherit;
  text-align: center;
}
.test-fill:focus { outline: none; background: rgba(212, 164, 55, 0.12); }
.test-fill--ar { font-size: 1.3rem; }
.test-fill.is-correct { border-bottom-color: var(--ed-ok); background: var(--ed-ok-bg); }
.test-fill.is-wrong { border-bottom-color: var(--ed-bad); background: var(--ed-bad-bg); }
.test-fill-expected { color: var(--ed-ok); font-weight: 600; white-space: nowrap; }
.test-fill-expected[lang="ar"] { font-size: 1.15rem; }

/* per-question graded border */
.test-q.is-correct { border-left-color: var(--ed-ok); }
.test-q.is-wrong { border-left-color: var(--ed-bad); }
.test-q-feedback {
  margin: 0.7rem 0 0;
  padding: 0.5rem 0.75rem;
  font-size: 13.5px;
  font-style: italic;
  color: var(--fg-muted);
  background: rgba(212, 164, 55, 0.06);
  border-radius: 8px;
}

/* actions */
.test-actions { display: flex; gap: 0.6rem; flex-wrap: wrap; margin-top: 1.2rem; }
.test-submit, .test-retry {
  appearance: none;
  font: inherit;
  font-size: 14px;
  font-weight: 600;
  padding: 0.6rem 1.4rem;
  border-radius: 10px;
  cursor: pointer;
  transition: background 140ms ease, border-color 140ms ease, transform 120ms ease;
}
.test-submit {
  border: 1px solid var(--accent);
  background: var(--grad-gold, var(--accent));
  color: var(--on-accent, #1a1206);
}
.test-submit:hover { transform: translateY(-1px); }
.test-submit:focus-visible, .test-retry:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.test-retry {
  border: 1px solid rgba(212, 164, 55, 0.4);
  background: rgba(212, 164, 55, 0.08);
  color: var(--fg);
}
.test-retry:hover { background: rgba(212, 164, 55, 0.16); }

/* result */
.test-result {
  margin-top: 1.2rem;
  padding: 1.1rem 1.2rem;
  border-radius: 12px;
  border: 1px solid var(--rule);
  background: var(--surface-subtle);
}
.test-score {
  font-family: var(--font-display);
  font-size: 1.5rem;
  color: var(--fg);
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
}
.test-score-pct { font-size: 1rem; color: var(--fg-subtle); }
.test-verdict { margin: 0.4rem 0 0; font-size: 14px; }
.test-card.is-passed .test-result { border-color: var(--ed-ok); background: var(--ed-ok-bg); }
.test-card.is-passed .test-verdict { color: var(--ed-ok); }
.test-card.is-failed .test-result { border-color: var(--ed-bad); background: var(--ed-bad-bg); }
.test-card.is-failed .test-verdict { color: var(--ed-bad); }

@media (max-width: 560px) {
  .test-card { padding: 1.2rem 1rem 1.3rem; }
  .test-opts--tf { grid-auto-flow: row; }
}
/* On paper: show real radio circles, drop the interactive chrome. */
@media print {
  .test-actions, .test-result, .test-poll-nav { display: none !important; }
  .test-card--poll .test-q { display: block !important; }
  .test-opt input { position: static; opacity: 1; width: auto; height: auto; }
}

/* ── poll presentation (:::test mode=poll) — one question at a time ───────── */
.test-card--poll .test-submit { display: none; } /* poll drives its own footer */
.test-card--poll .test-q { display: none; }
.test-card--poll .test-q.is-active { display: block; animation: test-poll-in 220ms ease; }
/* final review: reveal every question with its correct/wrong state */
.test-card--poll.is-graded .test-q { display: block; }
.test-card--poll.is-graded .test-poll-nav { display: none; }
@keyframes test-poll-in {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: none; }
}
.test-card--poll .test-opt { padding: 0.8rem 1rem; } /* bigger tap targets */

.test-poll-nav { margin-top: 1.2rem; }
.test-poll-track {
  height: 6px;
  border-radius: 999px;
  background: var(--surface-subtle);
  overflow: hidden;
}
.test-poll-bar {
  display: block;
  height: 100%;
  width: 0;
  border-radius: 999px;
  background: var(--grad-gold, var(--accent));
  transition: width 280ms ease;
}
.test-poll-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.8rem;
  margin-top: 0.7rem;
}
.test-poll-step { font-size: 13px; color: var(--fg-subtle); font-variant-numeric: tabular-nums; }
.test-poll-next {
  appearance: none;
  font: inherit;
  font-size: 14px;
  font-weight: 600;
  padding: 0.6rem 1.4rem;
  border-radius: 10px;
  border: 1px solid var(--accent);
  background: var(--grad-gold, var(--accent));
  color: var(--on-accent, #1a1206);
  cursor: pointer;
  transition: transform 120ms ease, opacity 140ms ease;
}
.test-poll-next:hover { transform: translateY(-1px); }
.test-poll-next:disabled { opacity: 0.5; cursor: not-allowed; transform: none; }
.test-poll-next:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* fillblank */
.fb-prompt { font-size: 17px; line-height: 1.9; margin: 0; }
.fb-input {
  display: inline-block;
  min-width: 4.5em;
  padding: 0.15rem 0.5rem;
  margin: 0 0.15rem;
  border: 1px solid rgba(212, 164, 55, 0.35);
  border-bottom: 2px solid var(--accent);
  background: var(--surface-subtle);
  color: var(--fg);
  border-radius: 4px;
  font: inherit;
  font-size: 16px;
  text-align: center;
  transition: border-color 120ms ease, background 120ms ease;
}
.fb-input--ar { font-family: var(--font-arabic); font-size: 19px; }
.fb-input:focus { outline: none; background: rgba(212, 164, 55, 0.10); border-color: var(--accent-strong); }
.fb-input.is-ok  { border-color: var(--ed-ok); background: var(--ed-ok-bg); }
.fb-input.is-err { border-color: var(--ed-bad); background: var(--ed-bad-bg); }

/* match */
.match-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.6rem 1.2rem;
  margin-top: 0.4rem;
}
.match-col { display: flex; flex-direction: column; gap: 0.5rem; }
.match-chip {
  appearance: none;
  text-align: left;
  font: inherit;
  font-size: 15px;
  padding: 0.6rem 0.9rem;
  background: var(--surface-subtle);
  border: 1px solid var(--rule);
  border-radius: 10px;
  color: var(--fg);
  cursor: pointer;
  transition: background 120ms ease, border-color 120ms ease, transform 100ms ease;
}
.match-chip[lang="ar"] { font-family: var(--font-arabic); font-size: 18px; text-align: right; }
.match-chip:hover { background: rgba(212, 164, 55, 0.10); border-color: rgba(212, 164, 55, 0.35); }
.match-chip.is-selected { background: rgba(212, 164, 55, 0.18); border-color: var(--accent); }
.match-chip.is-matched  { background: var(--ed-ok-bg); border-color: var(--ed-ok); color: var(--fg-muted); cursor: default; }
.match-chip.is-wrong    { animation: matchShake 320ms ease; border-color: var(--ed-bad); }
@keyframes matchShake { 0%,100% { transform: translateX(0); } 25% { transform: translateX(-4px); } 75% { transform: translateX(4px); } }
@media (max-width: 560px) { .match-grid { grid-template-columns: 1fr; } }

/* order — sentence builder */
.order-bank, .order-target {
  display: flex; flex-wrap: wrap; gap: 0.45rem;
  padding: 0.65rem; min-height: 3em;
  border-radius: 10px;
  background: var(--surface-subtle);
  border: 1px dashed var(--rule-hi);
}
.order-target {
  margin-bottom: 0.6rem;
  border-style: solid;
  border-color: rgba(212, 164, 55, 0.30);
  background: rgba(212, 164, 55, 0.05);
}
.order-token {
  appearance: none;
  font: inherit; font-size: 16px;
  padding: 0.35rem 0.7rem;
  background: var(--bg-elev-hi);
  border: 1px solid var(--rule-hi);
  border-radius: 8px;
  color: var(--fg);
  cursor: pointer;
  transition: transform 100ms ease, background 120ms ease;
}
.order-token[lang="ar"] { font-family: var(--font-arabic); font-size: 19px; }
.order-token:hover { background: rgba(212, 164, 55, 0.12); }
.order-token.is-placed { opacity: 0.35; pointer-events: none; }
.order-block.is-ok  .order-target { border-color: var(--ed-ok); background: var(--ed-ok-bg); }
.order-block.is-err .order-target { border-color: var(--ed-bad); background: var(--ed-bad-bg); }
.order-translation { margin: 0.6rem 0 0; color: var(--fg-muted); font-style: italic; font-size: 14px; }

/* truefalse */
.tf-statement { font-size: 17px; line-height: 1.6; margin: 0.2rem 0 0.8rem; }
.tf-options { display: flex; gap: 0.6rem; flex-wrap: wrap; }
.tf-option {
  flex: 1 1 8em;
  appearance: none;
  font: inherit;
  font-size: 15px;
  padding: 0.7rem 1.1rem;
  background: var(--bg-elev-hi);
  border: 1px solid var(--rule-hi);
  border-radius: 10px;
  color: var(--fg);
  cursor: pointer;
  transition: background 120ms ease, border-color 120ms ease;
}
.tf-option:hover { background: rgba(212, 164, 55, 0.10); border-color: rgba(212, 164, 55, 0.35); }
.tf-option.is-ok    { background: var(--ed-ok-bg); border-color: var(--ed-ok); }
.tf-option.is-wrong { background: var(--ed-bad-bg); border-color: var(--ed-bad); }
.tf-option:disabled { cursor: default; }

/* table */
.aoa-table-wrap { margin: 1.6rem 0; overflow-x: auto; }
.aoa-table-caption { margin: 0 0 0.5rem; font-size: 13px; color: var(--fg-muted); text-transform: uppercase; letter-spacing: 0.08em; }
.aoa-table {
  width: 100%;
  border-collapse: collapse;
  background: var(--bg-elev);
  border-radius: 10px;
  overflow: hidden;
  font-size: 15px;
}
.aoa-table th, .aoa-table td {
  padding: 0.7rem 0.9rem;
  text-align: left;
  border-bottom: 1px solid var(--rule);
}
.aoa-table th { background: rgba(212, 164, 55, 0.10); color: var(--accent); font-weight: 600; text-transform: uppercase; font-size: 12px; letter-spacing: 0.08em; }
.aoa-table tbody tr:last-child td { border-bottom: 0; }
.aoa-table tbody tr:hover { background: rgba(212, 164, 55, 0.04); }
.aoa-table [lang="ar"] { font-family: var(--font-arabic); font-size: 17px; text-align: right; }

/* verse */
.verse-card {
  margin: 1.8rem 0;
  padding: 1.4rem 1.6rem;
  background: linear-gradient(180deg, rgba(212, 164, 55, 0.08), rgba(212, 164, 55, 0.02));
  border: 1px solid rgba(212, 164, 55, 0.28);
  border-radius: 14px;
  text-align: center;
}
.verse-ar { font-family: var(--font-ar-display); font-size: clamp(1.7rem, 1.2rem + 1.7vw, 2.5rem); line-height: 1.9; margin: 0 0 0.5rem; color: var(--fg); font-weight: 400; font-feature-settings: 'liga' 1, 'calt' 1, 'dlig' 1; }
.verse-tr { color: var(--fg-muted); font-style: italic; margin: 0 0 0.3rem; }
.verse-ru { color: var(--fg); margin: 0 0 0.4rem; }
.verse-source { color: var(--fg-subtle); font-size: 13px; margin: 0.5rem 0 0; letter-spacing: 0.04em; }

/* compare */
.compare-grid {
  margin: 1.6rem 0;
  display: grid;
  grid-template-columns: repeat(var(--compare-cols, 2), 1fr);
  gap: 0.9rem;
  position: relative;
}
.compare-kicker { grid-column: 1 / -1; font-size: 11px; letter-spacing: 0.12em; text-transform: uppercase; color: var(--accent); font-weight: 600; }
.compare-card {
  padding: 1rem 1.1rem;
  background: var(--bg-elev);
  border: 1px solid var(--rule);
  border-radius: 12px;
}
.compare-card--left  { border-left:  3px solid var(--accent); }
.compare-card--right { border-right: 3px solid #8b5cf6; }
.compare-card-title { margin: 0 0 0.4rem; font-size: 12px; letter-spacing: 0.08em; text-transform: uppercase; color: var(--fg-muted); font-weight: 600; }
.compare-card-body { margin: 0; font-size: 16px; line-height: 1.5; }
.compare-card-body[lang="ar"] { font-family: var(--font-arabic); font-size: 19px; }
@media (max-width: 600px) { .compare-grid { grid-template-columns: 1fr; } }

/* vocab */
.vocab-list {
  margin: 1.5rem 0;
  background: var(--bg-elev);
  border: 1px solid var(--rule);
  border-radius: 12px;
  overflow: hidden;
}
.vocab-title { margin: 0; padding: 0.7rem 1rem; background: rgba(212, 164, 55, 0.08); color: var(--accent); font-size: 12px; letter-spacing: 0.08em; text-transform: uppercase; font-weight: 600; border-bottom: 1px solid rgba(212, 164, 55, 0.16); }
.vocab-row {
  display: grid;
  grid-template-columns: 1.2fr 1fr 1.5fr;
  gap: 0.8rem;
  align-items: baseline;
  padding: 0.65rem 1rem;
  border-bottom: 1px solid var(--rule);
}
.vocab-row:last-child { border-bottom: 0; }
.vocab-row:hover { background: rgba(212, 164, 55, 0.04); }
.vocab-ar { font-family: var(--font-arabic); font-size: 19px; text-align: right; }
.vocab-tr { color: var(--fg-muted); font-style: italic; font-size: 14px; }
.vocab-ru { color: var(--fg); font-size: 15px; }
@media (max-width: 560px) { .vocab-row { grid-template-columns: 1fr; gap: 0.2rem; } .vocab-ar { text-align: left; } }

/* highlight (ruby annotations) */
.highlight-block {
  margin: 1.6rem 0;
  padding: 1.2rem 1.4rem;
  background: var(--bg-elev);
  border: 1px solid rgba(139, 92, 246, 0.28);
  border-radius: 12px;
  text-align: center;
}
.highlight-kicker { display: inline-block; font-size: 11px; letter-spacing: 0.12em; text-transform: uppercase; color: #a78bfa; font-weight: 600; margin-bottom: 0.6rem; }
.highlight-arabic { font-family: var(--font-ar-display); font-size: clamp(1.8rem, 1.3rem + 1.7vw, 2.6rem); line-height: 2.3; margin: 0.2rem 0 0.6rem; font-weight: 400; }
.hi-ruby { display: inline-block; margin: 0 0.25rem; }
.hi-piece { color: var(--fg); }
.hi-gloss { font-family: var(--font-body); font-size: 11px; color: #a78bfa; letter-spacing: 0.04em; }
.highlight-ru { margin: 0.4rem 0 0; color: var(--fg-muted); font-size: 14px; }
/* Light theme: the fixed light-purple ruby labels wash out on the cream card —
   darken the kicker/gloss so they stay legible on the paper skin. */
:root[data-theme="light"] .highlight-kicker,
:root[data-theme="light"] .hi-gloss,
html[data-sah-theme="light"] .highlight-kicker,
html[data-sah-theme="light"] .hi-gloss { color: #7c3aed; }

/* ----------------------------------------------------------------------
   Buttons + section CTA — base styles for the .prose (page/lesson) layout.

   The button family lives in landing.css, but the page (lesson/konspekt)
   layout does NOT load landing.css, so in-lesson CTAs like
   `<a class="btn btn-primary btn-lg">К неделе 2 →</a>` were rendering as
   bare gold link text ("all gold"). These rules mirror landing.css so the
   buttons look identical everywhere. On landing/site layouts landing.css
   loads after this file and simply restates the same values (no conflict).
---------------------------------------------------------------------- */
.btn {
  box-sizing: border-box;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5em;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 0.95rem;
  letter-spacing: 0.01em;
  line-height: 1.2;
  padding: 0.85em 1.6em;
  border-radius: var(--r-pill);
  border: 1px solid transparent;
  cursor: pointer;
  text-decoration: none;
  text-align: center;
  white-space: normal;
  overflow-wrap: anywhere;
  transition: transform 0.18s, box-shadow 0.18s, background 0.18s, color 0.18s, border-color 0.18s;
}
.btn-primary {
  background: var(--accent);
  color: var(--on-accent);
  box-shadow: 0 10px 30px var(--accent-dim);
}
.btn-primary:hover {
  background: var(--accent-strong);
  transform: translateY(-2px);
  box-shadow: 0 16px 44px var(--accent-glow);
}
.btn-ghost {
  background: var(--surface-subtle);
  color: var(--fg);
  border-color: var(--rule-hi);
}
.btn-ghost:hover {
  border-color: var(--accent);
  color: var(--accent);
  transform: translateY(-2px);
}
.btn-lg { font-size: 1.05rem; padding: 1em 2em; }
.btn-sm { font-size: 0.8rem; padding: 0.6em 1.2em; }

.section-cta {
  display: flex;
  flex-wrap: wrap;
  gap: 1em;
  justify-content: center;
  margin-top: 1.6rem;
}

/* ════════════════════════════════════════════════════════════════════════════
   SAHARA EDITORIAL ASSETS  (light / cream reader)  — premium component design
   ────────────────────────────────────────────────────────────────────────────
   v2: a real design pass, not a recolour. The old version just tinted every
   block beige-on-cream → flat, low-contrast, "plain". This re-skins each rich
   "asset" (callouts, tables, quizzes, graded tests, flashcards, dialogue, audio,
   vocab, timelines …) into a crisp card system: warm-WHITE surfaces that LIFT
   off the cream page with a soft layered shadow + inner highlight, gold-gradient
   badges / letters / numbers, refined icon treatment, calm semantic states.
   The flashcard already proved white-on-cream + shadow reads premium — this
   brings that language to everything else.

   ONE block, THREE surfaces — fires under BOTH theme roots:
     • :root[data-theme="light"]        → standalone /notes konspekt + CMS preview
     • html[data-reader-theme="light"]  → the in-app /learn lesson + track reader
   `:where()` keeps the prefix at ZERO specificity, so each rule matches its base
   rule's weight and wins purely on source order (this block is appended last).
   ════════════════════════════════════════════════════════════════════════════ */
:where(:root) {
  /* ── editorial NIGHT — the dark base. Warm "dark-paper" panels that lift off
     the near-black page, luminous gold badges that emit a soft halo, cream ink,
     calm green/terracotta states. The LIGHT block below re-skins to cream. Both
     skins drive the SAME component rules (token-driven), so the two modes stay
     in lock-step — duck.school by day, the same composition by night. ── */
  --ed-card:      #211e1a;                          /* warm charcoal panel — lifts off the flat #191817 Notion canvas (was #1b1611 for the old near-black page) */
  --ed-card-2:    #1c1916;                          /* recessed wells / zebra / code / sub-surfaces — a hair below the card, near the canvas */
  --ed-ink:       #f4ece0;
  --ed-ink-soft:  #b3a791;
  --ed-line:      rgba(255, 239, 214, 0.12);        /* warm hairline — visible on dark */
  --ed-line-2:    rgba(255, 239, 214, 0.06);
  --ed-gold:      #e6bd6a;
  --ed-gold-ink:  #ecc87c;                          /* label text glows on dark */
  --ed-grad:      linear-gradient(155deg, #f6db8e 0%, #d9a843 55%, #b07f1e 100%);
  --ed-gold-bg:   rgba(230, 189, 106, 0.11);
  --ed-gold-bg-2: rgba(230, 189, 106, 0.19);
  /* lift on near-black = card sits lighter than the page + a fine top highlight
     + a soft ambient shadow that just darkens the seam. */
  --ed-inset:     inset 0 1px 0 rgba(255, 244, 222, 0.05);
  --ed-lift:      0 16px 34px -24px rgba(0, 0, 0, 0.7), 0 2px 6px -3px rgba(0, 0, 0, 0.5);
  --ed-lift-sm:   0 10px 22px -18px rgba(0, 0, 0, 0.6);
  --ed-badge-sh:  0 4px 14px -3px rgba(230, 185, 96, 0.45);   /* gold halo under badges */
  --ed-ok:   #a7cf72;  --ed-ok-bg:   rgba(167, 207, 114, 0.14);  /* leaf green — clear on dark */
  --ed-bad:  #e58d63;  --ed-bad-bg:  rgba(229, 141, 99, 0.14);   /* warm terracotta */
}

/* ── editorial CREAM — the light override (the accepted duck.school day skin). ── */
/* 🔴 THREE signals, not two. The academy's Sahara shell stamps its theme on the
   `#sah` DIV as `data-theme` and mirrors it on <html> as `data-sah-theme` — so
   `:root[data-theme="light"]` NEVER matches on /blog or /archive, and every
   token-driven block (tables, vocab, checklists, quiz options) kept the dark
   --ed-* values on a cream page: dark slab, dark ink, unreadable. Matching
   `html[data-sah-theme="light"]` too is what makes the article blocks follow
   the site's own light/dark toggle. */
:where(:root[data-theme="light"],
       html[data-reader-theme="light"],
       html[data-sah-theme="light"]) {
  --ed-card:      #fffdf8;                          /* crisp warm white — clearly above the cream page */
  --ed-card-2:    #f8f2e6;                          /* inset wells / zebra / sub-surfaces */
  --ed-ink:       #1c160d;
  --ed-ink-soft:  #5c5343;
  --ed-line:      rgba(43, 30, 8, 0.12);            /* warm hairline */
  --ed-line-2:    rgba(43, 30, 8, 0.07);            /* faint inner rule */
  --ed-gold:      #b07f12;
  --ed-gold-ink:  #8a6510;                          /* label text on cream */
  --ed-grad:      linear-gradient(155deg, #edc25c 0%, #c6921a 55%, #a8790f 100%);
  --ed-gold-bg:   rgba(184, 135, 30, 0.08);
  --ed-gold-bg-2: rgba(184, 135, 30, 0.15);
  --ed-inset:     inset 0 1px 0 rgba(255, 255, 255, 0.6);
  --ed-lift:      0 1px 1px rgba(58, 40, 10, 0.05), 0 13px 28px -16px rgba(58, 40, 10, 0.22), 0 3px 8px -4px rgba(58, 40, 10, 0.10);
  --ed-lift-sm:   0 1px 1px rgba(58, 40, 10, 0.04), 0 8px 18px -12px rgba(58, 40, 10, 0.18);
  --ed-badge-sh:  0 3px 8px -2px rgba(120, 86, 12, 0.45);
  --ed-ok:   #5f8a3f;  --ed-ok-bg:   rgba(95, 138, 63, 0.13);   /* olive — reads on cream, no neon */
  --ed-bad:  #b4532f;  --ed-bad-bg:  rgba(180, 83, 47, 0.11);   /* terracotta */
}

/* — Kill leftover dark glows / depth text-shadows. — */
:where(:root) :is(
  .stat-card .stat-num, .root-tree-hub, .timeline-node .node-ar, .timeline-node .node-date,
  .flashcard .flashcard-arabic-hero, .flashcard .flashcard-back-arabic, .flashcard-russian
) { text-shadow: none; }

/* — Universal CARD LIFT: crisp white surface + soft shadow + inner highlight,
     no backdrop blur. Cream's glass tokens are dark in the in-app reader, so
     this both fixes that AND gives every asset the depth it was missing. — */
:where(:root) :is(
  .glass-panel, .dialogue, .audio-pill, .quiz-option, .root-branch, .stat-card,
  .todo-card, .prose details, .prose table, .flashcard-face, .test-card,
  .vocab-list, .highlight-block, .callout-definition
) {
  background: var(--ed-card);
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  border: 1px solid var(--ed-line);
  box-shadow: var(--ed-inset), var(--ed-lift-sm);
}

/* ── Callouts → white card, gold ICON BADGE, crisp kicker, gold spine ──────── */
:where(:root) .callout-definition {
  border-left: 3px solid var(--ed-gold);
  border-radius: 18px;
  padding: 1.4em 1.7em;
}
:where(:root) .callout-definition .logo {
  width: 2.1em; height: 2.1em;
  display: grid; place-items: center;
  border-radius: 50%;
  background: var(--ed-gold-bg-2);
  box-shadow: inset 0 0 0 1px var(--ed-line);
  font-size: 1.15em;
}
:where(:root) :is(
  .callout-definition .label, .markdown-alert-title, .callout-horizontal .h-label
) { color: var(--ed-gold-ink); }

/* note + vertical (rotated-label) callouts → white body card + crisp gold tab */
:where(:root) :is(.callout-vertical .v-body, .callout-note .n-body) {
  background: var(--ed-card);
  border: 1px solid var(--ed-line);
  box-shadow: var(--ed-inset), var(--ed-lift-sm);
  border-radius: 16px;
}
:where(:root) .callout-vertical .v-label {
  opacity: 1; color: var(--ed-gold-ink); font-weight: 800;
}
:where(:root) .callout-note .n-badge {
  border-color: var(--ed-line); color: var(--ed-gold-ink);
  background: var(--ed-gold-bg);
}
:where(:root) :is(
  .callout-vertical .v-rule, .callout-note .n-rule, .callout-horizontal .h-rule
) { background: var(--ed-grad); border-radius: 3px; }

/* horizontal + GitHub-alert → soft gold-tint pill card */
:where(:root) :is(.callout-horizontal, .markdown-alert) {
  background: var(--ed-gold-bg);
  border: 1px solid var(--ed-line);
  border-radius: 14px;
}

/* ── Tables → editorial: gold header band, zebra, rounded, lifted, and CENTRED.
     Covers both raw markdown tables (.prose table) and the :::table directive
     (.aoa-table). Content is centre-aligned (Ali's ask) so short comparison/vocab
     values sit in the middle of their column instead of floating at the left edge
     with a ragged gap. Cells are vertically centred too, so a one-word cell lines
     up with a code-chip cell in the same row. ── */
:where(:root) :is(.prose table, .aoa-table) {
  border-radius: 16px; overflow: hidden;
  background: var(--ed-card);
  border: 1px solid var(--ed-line);
  box-shadow: var(--ed-inset), var(--ed-lift-sm);
  table-layout: auto;
}
:where(:root) :is(.prose th, .aoa-table th),
:where(:root) :is(.prose td, .aoa-table td) {
  text-align: center;
  vertical-align: middle;
  padding: 0.8em 1.15em;
}
:where(:root) :is(.prose th, .aoa-table th) {
  background: var(--ed-gold-bg-2);
  color: var(--ed-gold-ink);
  border-bottom: 1px solid var(--ed-line);
  font-size: 0.74em;
  letter-spacing: 0.1em;
  text-wrap: balance;          /* keep multi-word headers like "Хиджази (Саудия)" balanced, not orphaned */
  white-space: normal;
}
:where(:root) :is(.prose td, .aoa-table td) { border-bottom: 1px solid var(--ed-line-2); }
/* First column reads as the row label — keep it centred but a touch stronger. */
:where(:root) :is(.prose tbody td, .aoa-table tbody td):first-child {
  font-weight: 600;
  color: var(--fg);
}
/* Arabic cells: keep right-to-left shaping but centre them in the column. */
:where(:root) :is(.prose, .aoa-table) :is(td, th):lang(ar),
:where(:root) :is(.prose, .aoa-table) [lang="ar"] {
  direction: rtl;
  text-align: center;
  font-family: var(--font-arabic);
}
:where(:root) :is(.prose tbody tr:nth-child(even), .aoa-table tbody tr:nth-child(even)) { background: var(--ed-card-2); }
:where(:root) :is(.prose tbody tr:hover, .aoa-table tbody tr:hover) { background: var(--ed-gold-bg); }

/* ── Code → warm paper, gold ink, chips don't break ── */
:where(:root) .prose :is(p, li, td, th) code {
  background: var(--ed-gold-bg); color: var(--ed-gold-ink);
  border: 1px solid var(--ed-line-2); white-space: nowrap;
}
:where(:root) .prose pre {
  background: var(--ed-card-2) !important;
  backdrop-filter: none; -webkit-backdrop-filter: none;
  border: 1px solid var(--ed-line);
}

/* ── Details / toggle → lifted card, gold chevron, gold hover ── */
:where(:root) .prose details > summary:hover { background: var(--ed-gold-bg); }

/* ── Editorial divider — centred gold lozenge ── */
:where(:root) .prose hr {
  border: 0; height: 1px; background: var(--ed-line);
  margin: 3.2em auto; max-width: 13rem; overflow: visible;
}
:where(:root) .prose hr::after {
  content: ""; display: block; width: 7px; height: 7px;
  margin: -4px auto 0; transform: rotate(45deg);
  background: var(--ed-grad); border-radius: 1px;
}

/* ── Figures → soft frame + serif caption ── */
:where(:root) .prose :is(img, figure img) {
  border: 1px solid var(--ed-line); box-shadow: var(--ed-lift-sm);
}
:where(:root) .prose figcaption { font-family: var(--font-display); color: var(--ed-ink-soft); }

/* ── Quiz → white chips that lift, gold-gradient letters, calm states ── */
:where(:root) .quiz-option { border-width: 1.5px; transition: border-color .18s, box-shadow .18s, transform .15s; }
:where(:root) .quiz-option:hover:not(.revealed) {
  border-color: var(--ed-gold); transform: translateY(-1px); box-shadow: var(--ed-inset), var(--ed-lift);
}
:where(:root) .quiz-option .quiz-letter {
  background: var(--ed-grad); color: #fff; box-shadow: var(--ed-badge-sh);
}
:where(:root) .quiz-option.revealed[data-correct="true"] { border-color: var(--ed-ok); background: var(--ed-ok-bg); box-shadow: var(--ed-lift-sm); }
:where(:root) .quiz-option.revealed[data-correct="true"] .quiz-letter { background: var(--ed-ok); }
:where(:root) .quiz-option.revealed[data-correct="false"] { border-color: var(--ed-bad); background: var(--ed-bad-bg); }
:where(:root) .quiz-feedback { color: var(--ed-gold-ink); }

/* ── Graded test (:::test) → lifted sheet, white wells, gradient letters ── */
:where(:root) .test-card { box-shadow: var(--ed-inset), var(--ed-lift); }
:where(:root) .test-q { background: var(--ed-card-2); border-color: var(--ed-line); border-left-color: var(--ed-line); }
:where(:root) .test-opt { background: var(--ed-card); border-color: var(--ed-line); }
:where(:root) .test-opt:hover { border-color: var(--ed-gold); }
:where(:root) .test-opt-letter { background: var(--ed-grad); color: #fff; box-shadow: var(--ed-badge-sh); }
:where(:root) .test-opt:has(input:checked) { border-color: var(--ed-gold); background: var(--ed-gold-bg); }
:where(:root) .test-opt.is-correct { border-color: var(--ed-ok); background: var(--ed-ok-bg); }
:where(:root) .test-opt.is-correct .test-opt-letter { background: var(--ed-ok); }
:where(:root) .test-opt.is-wrong { border-color: var(--ed-bad); background: var(--ed-bad-bg); }
:where(:root) .test-q.is-correct { border-left-color: var(--ed-ok); }
:where(:root) .test-q.is-wrong { border-left-color: var(--ed-bad); }
:where(:root) .test-fill { background: var(--ed-gold-bg); border-bottom-color: var(--ed-gold); }
:where(:root) .test-fill.is-correct { border-bottom-color: var(--ed-ok); background: var(--ed-ok-bg); }
:where(:root) .test-fill.is-wrong { border-bottom-color: var(--ed-bad); background: var(--ed-bad-bg); }
:where(:root) .test-fill-expected { color: var(--ed-ok); }
:where(:root) .test-result { background: var(--ed-card-2); border-color: var(--ed-line); }
:where(:root) .test-submit { background: var(--ed-grad); color: #fff; box-shadow: var(--ed-badge-sh); }
:where(:root) .test-card.is-passed .test-result { border-color: var(--ed-ok); background: var(--ed-ok-bg); }
:where(:root) .test-card.is-passed .test-verdict { color: var(--ed-ok); }
:where(:root) .test-card.is-failed .test-result { border-color: var(--ed-bad); background: var(--ed-bad-bg); }
:where(:root) .test-card.is-failed .test-verdict { color: var(--ed-bad); }
:where(:root) :is(.test-q-feedback, .test-poll-track) { background: var(--ed-gold-bg); }
:where(:root) .test-poll-bar { background: var(--ed-grad); }

/* ── Practice ── */
:where(:root) .practice-feedback.is-ok  { color: var(--ed-ok); }
:where(:root) .practice-feedback.is-err { color: var(--ed-bad); }
:where(:root) .practice-hint { background: var(--ed-gold-bg); border-left-color: var(--ed-gold); }

/* ── Dialogue → lifted chat card; bubble A on the recessed sheet, gold bubble B;
     ringed avatars. The container sits a step BELOW the bubbles so speaker A
     reads as a distinct card even when both share the surface family. ── */
:where(:root) .dialogue { background: var(--ed-card-2); box-shadow: var(--ed-inset), var(--ed-lift); }
:where(:root) .dialogue-bubble {
  background: var(--ed-card); border-color: var(--ed-line); box-shadow: var(--ed-lift-sm);
}
:where(:root) .dialogue-line.speaker-b .dialogue-bubble { background: var(--ed-gold-bg-2); border-color: var(--ed-line); }
:where(:root) .dialogue-avatar {
  background: var(--ed-card); border: 1.5px solid var(--ed-gold); color: var(--ed-gold-ink); box-shadow: var(--ed-lift-sm);
}
:where(:root) .dialogue-line.speaker-b .dialogue-avatar { border-color: var(--ed-line); color: var(--ed-ink-soft); }

/* ── Audio → lifted white pill, gold-gradient play, visible waveform ── */
:where(:root) .audio-pill .play-btn { background: var(--ed-grad); box-shadow: var(--ed-badge-sh); }
:where(:root) .audio-pill .play-btn:hover { box-shadow: 0 6px 16px -4px rgba(120, 86, 12, 0.55); }
:where(:root) .audio-pill .wave-bar { background: var(--ed-gold-bg-2); }
:where(:root) .audio-pill .wave-bar.active { background: var(--ed-gold); }

/* ── Stat card → gold-gradient hero number ── */
:where(:root) .stat-card .stat-num {
  background: var(--ed-grad);
  -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: transparent; color: transparent;
}

/* ── Root-tree / timeline → gradient hub, gold ring dots, ink+gold text ── */
:where(:root) .root-tree-hub {
  background: var(--ed-gold-bg-2); border-color: var(--ed-gold);
  box-shadow: var(--ed-inset), var(--ed-lift); color: var(--ed-gold-ink);
}
:where(:root) .root-branch:hover { border-color: var(--ed-gold); box-shadow: var(--ed-inset), var(--ed-lift); transform: translateY(-2px); }
:where(:root) .timeline-node .node-dot { background: var(--ed-card); border-color: var(--ed-gold); }
:where(:root) .timeline-node.active .node-dot { background: var(--ed-gold); box-shadow: none; animation: none; }
:where(:root) :is(.timeline-node .node-ar, .timeline-node .node-date) { color: var(--ed-gold-ink); }

/* ── Flashcards → richer lift + gold accents ── */
:where(:root) .flashcard-face { box-shadow: var(--ed-inset), var(--ed-lift); }
:where(:root) :is(
  .flashcard .flashcard-arabic-hero, .flashcard .flashcard-back-arabic, .flashcard-tag, .flashcard-back-section-label
) { color: var(--ed-gold-ink); }
:where(:root) .flashcard-dialect.is-active { background: var(--ed-grad); color: #fff; border-color: transparent; }
:where(:root) :is(.flashcard-back-example, .flashcard-back-note) { background: var(--ed-card-2); }
:where(:root) .flashcard-back-example { border-left-color: var(--ed-gold); }
:where(:root) .flashcardlist-nav { background: var(--ed-card); border-color: var(--ed-line); box-shadow: var(--ed-inset), var(--ed-lift-sm); }
:where(:root) .flashcardlist-nav:hover:not([disabled]) { background: var(--ed-grad); color: #fff; border-color: transparent; }
:where(:root) .flashcardlist-dots button.is-active { background: var(--ed-gold); border-color: var(--ed-gold); }

/* ── Vocab + highlight: dark hairlines were invisible on cream; repaint warm ── */
:where(:root) .vocab-title {
  background: var(--ed-gold-bg-2); color: var(--ed-gold-ink); border-bottom: 1px solid var(--ed-line);
}
:where(:root) .vocab-row { border-bottom: 1px solid var(--ed-line-2); }
:where(:root) .vocab-row:hover { background: var(--ed-gold-bg); }
:where(:root) .highlight-block { border-top: 3px solid var(--ed-gold); }
:where(:root) :is(.highlight-kicker, .hi-gloss) { color: var(--ed-gold-ink); }

/* ── Todo → lifted card, gold check ── */
:where(:root) .todo-item.is-done .todo-box,
:where(:root) .todo-box.is-checked { background: var(--ed-grad); border-color: transparent; }
:where(:root) .todo-item.is-interactive:hover { background: var(--ed-gold-bg); }

/* ── Pull-quote = the duck.school editorial statement (base, token-driven) —
     no light override needed; --accent/--fg/--fg-muted already theme it. The
     light editorial accent is just a slightly deeper gold for the em + mark.
     (Kept as the ORIGINAL bare centred statement — Ali prefers it un-framed.) ── */
:where(:root) .pullquote-em { color: var(--ed-gold-ink); }

/* ── Buttons → tame gold glow into a soft lift ── */
:where(:root) .btn-primary { background: var(--ed-grad); box-shadow: var(--ed-lift-sm); }
:where(:root) .btn-primary:hover { box-shadow: var(--ed-lift); transform: translateY(-2px); }

/* ── Verse (:::verse / :::ayah) → a centred "sacred plate": lifted card, gold
     hairline frame, big Arabic, serif translation, gold attribution. ── */
:where(:root) .verse-card {
  background: var(--ed-card);
  border: 1px solid var(--ed-line);
  border-radius: 20px;
  box-shadow: var(--ed-inset), var(--ed-lift);
  text-align: center;
  padding: 2.1em 1.9em;
  position: relative;
}
:where(:root) .verse-card::before {
  content: ""; position: absolute; inset: 0.5rem;
  border: 1px solid var(--ed-line-2); border-radius: 14px; pointer-events: none;
}
:where(:root) .verse-ar { color: var(--ed-gold-ink); text-shadow: none; }
:where(:root) .verse-tr { color: var(--ed-ink-soft); font-style: italic; }
:where(:root) .verse-ru { font-family: var(--font-display); }
:where(:root) .verse-source { color: var(--ed-gold-ink); letter-spacing: 0.12em; text-transform: uppercase; font-size: 0.74em; }

/* ── Compare (:::compare) → two lifted cards; the "right" card carries a gold
     wash so the contrast reads at a glance. ── */
:where(:root) .compare-card {
  background: var(--ed-card);
  border: 1px solid var(--ed-line);
  border-radius: 16px;
  box-shadow: var(--ed-inset), var(--ed-lift-sm);
}
:where(:root) .compare-card--right { background: var(--ed-gold-bg); border-color: var(--ed-line); }
:where(:root) .compare-card-title { color: var(--ed-gold-ink); }
:where(:root) .compare-kicker { color: var(--ed-gold-ink); }

/* ════════════════════════ end editorial assets ════════════════════════════ */

/* ════════════════════════════════════════════════════════════════════════════
   FLASHCARD REDESIGN — centered word card (both themes)
   The front pinned the Arabic top-right + the translation left + left a big
   empty lower half. Recompose as a calm CENTERED card: kicker → big centered
   Arabic hero → Playfair translation + translit → meta footer, vertically
   centred so it fills the card. Layout is theme-agnostic; colours come from the
   base rules + the light editorial block above. Appended last → wins on order.
   ════════════════════════════════════════════════════════════════════════════ */
.flashcard { max-width: 400px; }   /* narrower card (Ali); list-carousel cards keep their own width */
.flashcard-front {
  align-items: center;          /* was stretch */
  justify-content: center;      /* was flex-start → fills the empty lower half */
  text-align: center;           /* was left */
  gap: 0.5em;
  padding: 2.1em 1.8em;
}
.flashcard--has-dialects .flashcard-front { padding-top: 3.6em; }
.flashcard-text { align-items: center; text-align: center; width: 100%; gap: 0.15em; }
.flashcard-tag { margin: 0 0 0.2em; }
.flashcard-hero { text-align: center; margin: 0.05em 0 0.1em; }
/* Bigger Arabic — the hero of the card (doubled-class beats .prose :lang(ar)). */
.flashcard .flashcard-arabic-hero {
  text-align: center;
  font-size: clamp(3rem, 20cqi, 5.2rem);
  font-weight: 500;
  line-height: 1.12;
}
/* Translation in the editorial Didone serif (was Inter). */
.flashcard .flashcard-russian {
  font-family: 'Playfair Display', Georgia, serif;
  font-weight: 700;
  font-size: clamp(1.55rem, 3.6vw, 2.35rem);
  line-height: 1.1;
  text-align: center;
}
.flashcard-subtitle { text-align: center; }
.flashcard-meta { width: 100%; margin-top: 1.15em; }
/* Back: translation also picks up the editorial serif for consistency. */
.flashcard .flashcard-back-translation { font-family: 'Playfair Display', Georgia, serif; font-weight: 700; }
/* ════════════════════════ end flashcard redesign ════════════════════════════ */

/* ══════════════════════════════════════════════════════════════════════════
   The explainer trio — :::split · :::matrix · :::spectrum
   (preprocess.mjs → renderSplit / renderMatrix / renderSpectrum)

   All three are built from tokens only, so they re-skin with the theme and the
   per-level accent instead of carrying a palette of their own. Nothing here
   measures anything in JavaScript: the bars are percentage widths and the pips
   are a class, which is what lets the PDF capture look like the screen.
   ══════════════════════════════════════════════════════════════════════════ */

/* ── :::split — 100 %-stacked proportion bars ────────────────────────────── */
.split-chart { margin: 2rem 0; }
.split-title {
  margin: 0 0 0.35rem;
  font-family: var(--font-display), var(--font-body);
  font-size: clamp(1.02rem, 2.4vw, 1.2rem);
  font-weight: 600;
  letter-spacing: -0.01em;
}
.split-legend { display: flex; flex-wrap: wrap; gap: 1.1rem; margin: 0 0 1.05rem; }
.split-key {
  display: inline-flex; align-items: center; gap: 0.45rem;
  font-family: var(--font-mono, ui-monospace, monospace);
  font-size: 11px; letter-spacing: 0.07em; text-transform: uppercase;
  color: var(--fg-muted);
}
.split-swatch { width: 10px; height: 10px; border-radius: 2px; flex: none; }
.split-row { margin-bottom: 0.85rem; }
.split-head {
  display: flex; justify-content: space-between; align-items: baseline; gap: 1rem;
  margin-bottom: 0.32rem;
}
.split-label { font-size: 14.5px; line-height: 1.35; }
/* The numbers repeat the bar in text — the only version a screen reader or a
   greyscale print can actually read. */
.split-read {
  flex: none;
  font-family: var(--font-mono, ui-monospace, monospace);
  font-size: 11px; letter-spacing: 0.04em; color: var(--fg-subtle);
  font-variant-numeric: tabular-nums;
}
.split-bar {
  display: flex; height: 14px; border-radius: 3px; overflow: hidden;
  background: var(--bg-elev-hi);
}
.split-seg { display: block; height: 100%; min-width: 2px; }
.split-note {
  margin: 1rem 0 0;
  font-size: 12.5px; line-height: 1.6; color: var(--fg-subtle);
}
@media (max-width: 520px) {
  .split-head { flex-direction: column; gap: 0.1rem; }
  .split-read { align-self: flex-start; }
}

/* ── :::matrix — a grid of small dot ratings ─────────────────────────────── */
.matrix-wrap { margin: 2rem 0; }
.matrix-title {
  margin: 0 0 0.6rem;
  font-family: var(--font-display), var(--font-body);
  font-size: clamp(1.02rem, 2.4vw, 1.2rem);
  font-weight: 600;
  letter-spacing: -0.01em;
}
.matrix-scroll { overflow-x: auto; }
.matrix-table {
  width: 100%; border-collapse: collapse;
  font-size: 13.5px;
}
.matrix-table th, .matrix-table td {
  padding: 0.62rem 0.7rem;
  border-top: 1px solid var(--rule);
  text-align: center;
  vertical-align: middle;
}
.matrix-table thead th {
  border-top: 0;
  padding-top: 0;
  font-family: var(--font-mono, ui-monospace, monospace);
  font-size: 10.5px; font-weight: 400;
  letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--fg-muted);
  white-space: nowrap;
}
.matrix-table tbody th {
  text-align: left; font-weight: 600; white-space: nowrap;
}
.matrix-table tbody tr:hover { background: var(--accent-soft); }
.matrix-table [lang="ar"] { font-family: var(--font-arabic); font-size: 16px; }
.matrix-pips { display: inline-flex; gap: 4px; }
.matrix-pip {
  width: 8px; height: 8px; border-radius: 50%;
  background: var(--fg-faint);
}
.matrix-pip.is-on { background: var(--accent); }
.matrix-note {
  margin: 0.9rem 0 0;
  font-size: 12.5px; line-height: 1.6; color: var(--fg-subtle);
}

/* ── :::spectrum — one phrase across a register ──────────────────────────── */
.spectrum-block {
  margin: 2.4rem 0;
  padding: 1.6rem 1.5rem 1.5rem;
  background: var(--bg-elev);
  border: 1px solid var(--rule);
  border-radius: 12px;
}
.spectrum-kicker {
  margin: 0 0 0.4rem;
  font-family: var(--font-mono, ui-monospace, monospace);
  font-size: 10.5px; letter-spacing: 0.18em; text-transform: uppercase;
  color: var(--accent);
}
.spectrum-title {
  margin: 0 0 0.25rem;
  font-family: var(--font-display), var(--font-body);
  font-size: clamp(1.15rem, 3vw, 1.5rem); font-weight: 600;
  letter-spacing: -0.015em;
}
.spectrum-caption { margin: 0 0 1.3rem; font-size: 14.5px; color: var(--fg-muted); }
.spectrum-stage {
  background: var(--bg-code);
  border-radius: 8px;
  padding: 1.5rem 1.3rem;
}
.spectrum-stop + .spectrum-stop { margin-top: 1.5rem; padding-top: 1.5rem; border-top: 1px solid var(--rule); }
.spectrum-place {
  margin: 0 0 0.75rem;
  font-family: var(--font-mono, ui-monospace, monospace);
  font-size: 10.5px; letter-spacing: 0.16em; text-transform: uppercase;
  color: var(--fg-muted);
}
.spectrum-ar {
  margin: 0 0 0.4rem;
  font-family: var(--font-arabic);
  font-size: clamp(1.9rem, 7vw, 2.9rem); line-height: 1.55;
  color: var(--fg);
}
.spectrum-tr {
  margin: 0 0 0.7rem;
  font-family: var(--font-mono, ui-monospace, monospace);
  font-size: clamp(0.9rem, 2.6vw, 1.05rem);
  color: var(--accent);
}
.spectrum-note { margin: 0; font-size: 14.5px; line-height: 1.6; color: var(--fg-muted); max-width: 40em; }

/* Live mode: spectrum.js unhides the controls and adds .is-live, at which
   point the stage shows one stop at a time. Before that — and forever, if the
   script never runs — every stop is simply a readable block. */
.spectrum-block.is-live .spectrum-stop { display: none; }
.spectrum-block.is-live .spectrum-stop.is-active { display: block; }
.spectrum-block.is-live .spectrum-stop + .spectrum-stop {
  margin-top: 0; padding-top: 0; border-top: 0;
}
.spectrum-block.is-live .spectrum-stage { min-height: 13rem; }

.spectrum-controls { margin-top: 1.4rem; }
.spectrum-slider {
  -webkit-appearance: none; appearance: none;
  width: 100%; height: 26px; margin: 0;
  background: transparent; cursor: pointer;
}
.spectrum-slider::-webkit-slider-runnable-track { height: 2px; background: var(--rule-hi); border-radius: 2px; }
.spectrum-slider::-moz-range-track { height: 2px; background: var(--rule-hi); border-radius: 2px; }
.spectrum-slider::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 22px; height: 22px; margin-top: -10px;
  border-radius: 50%; background: var(--accent);
  border: 3px solid var(--bg); cursor: pointer;
}
.spectrum-slider::-moz-range-thumb {
  width: 22px; height: 22px;
  border-radius: 50%; background: var(--accent);
  border: 3px solid var(--bg); cursor: pointer;
}
.spectrum-slider:focus-visible { outline: 2px solid var(--accent); outline-offset: 6px; }
.spectrum-ticks { display: flex; gap: 6px; margin-top: 0.6rem; }
.spectrum-tick {
  flex: 1; min-width: 0;
  background: none; border: 0; padding: 0.2rem 0.1rem;
  font-family: var(--font-mono, ui-monospace, monospace);
  font-size: 10px; letter-spacing: 0.06em; text-transform: uppercase;
  line-height: 1.35; color: var(--fg-subtle);
  text-align: center; cursor: pointer;
}
.spectrum-tick:first-child { text-align: left; }
.spectrum-tick:last-child { text-align: right; }
.spectrum-tick.is-on { color: var(--accent); }
.spectrum-tick:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 3px; }

/* Print / PDF: a slider is meaningless on paper — show every stop. */
@media print {
  .spectrum-block.is-live .spectrum-stop { display: block; }
  .spectrum-block.is-live .spectrum-stop + .spectrum-stop {
    margin-top: 1.5rem; padding-top: 1.5rem; border-top: 1px solid var(--rule);
  }
  .spectrum-controls { display: none !important; }
  .spectrum-block, .matrix-wrap, .split-chart { break-inside: avoid; }
}
