/* ==========================================================================
   LearnEU — mobile-first, no build step, no external assets.
   Colour system: EU blue + gold, with high-contrast feedback greens/reds.
   ========================================================================== */

:root {
  --eu-blue: #003399;
  --eu-blue-dark: #00256e;
  --eu-blue-light: #2a5fd0;
  --eu-gold: #ffcc00;

  /* Green means "you got that right" and nothing else — the primary action is
     EU blue. Keeping the two apart is what stops the app reading as a clone of
     the obvious reference. */
  --green: #10855c;
  --green-dark: #0a6144;
  --green-bg: #d9f2e6;
  --green-text: #0a6144;

  --red: #d92d20;
  --red-dark: #a81f15;
  --red-bg: #fde4e2;
  --red-text: #b42318;

  /* Callout tints on the teach page. Gold and blue need a background and a
     text shade the base palette does not carry. */
  --gold-bg: #fff6d6;
  --gold-text: #7a5c00;
  --blue-bg: #eaf0fd;
  --blue-text: #1e46a0;

  --bg: #ffffff;
  --surface: #ffffff;
  --raised: #f7f9fc;
  --text: #14202e;
  --muted: #6b7a8d;
  --border: #dde3ec;
  --border-strong: #c3ccda;

  --radius: 14px;
  --shadow-btn: 0 3px 0;
  --safe-top: env(safe-area-inset-top, 0px);
  --safe-bottom: env(safe-area-inset-bottom, 0px);

  --font: "Nunito", "Segoe UI", system-ui, -apple-system, "Helvetica Neue", sans-serif;
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #10161f;
    --surface: #161e2a;
    --raised: #1c2534;
    --text: #eaf0f8;
    --muted: #93a3b8;
    --border: #2a3547;
    --border-strong: #3a4761;
    --green-bg: #0e2a20;
    --green-text: #5fd3a3;
    --red-bg: #351a1a;
    --red-text: #ff9d95;
    --eu-blue-light: #5a8ae8;
    --gold-bg: #32290f;
    --gold-text: #ffd75e;
    --blue-bg: #16223a;
    --blue-text: #9dbcf8;
  }
}

* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }

html, body {
  margin: 0;
  height: 100%;
  overscroll-behavior-y: none;
}

body {
  font-family: var(--font);
  background: var(--bg);
  color: var(--text);
  font-size: 16px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

button { font-family: inherit; font-size: inherit; cursor: pointer; border: none; background: none; color: inherit; }

.app { min-height: 100%; display: flex; flex-direction: column; }
.boot { padding: 4rem 1rem; text-align: center; color: var(--muted); }
.hidden { display: none !important; }

/* Present to a screen reader, absent to everyone else. Used for the live region
   that announces a verdict, and for labels the layout says visually but the
   markup otherwise leaves unsaid. */
.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
}

/* One focus ring for the whole app.
   `:focus-visible` rather than `:focus` so a tapped button does not keep a ring
   a touch user never asked for, while anyone on a keyboard can always see where
   they are. Two colours because the blue ring vanishes against a blue button. */
:focus-visible {
  outline: 3px solid var(--eu-blue-light);
  outline-offset: 2px;
  border-radius: 4px;
}
.btn-primary:focus-visible,
.btn-blue:focus-visible,
.btn-danger:focus-visible,
.node-btn:focus-visible,
.unit-lock:focus-visible {
  outline-color: var(--eu-gold);
}

/* ---------------------------------------------------------------- buttons */

.btn {
  display: block;
  width: 100%;
  padding: 0.9rem 1rem;
  border-radius: var(--radius);
  font-weight: 800;
  font-size: 1rem;
  letter-spacing: 0;
  transition: transform .06s ease, filter .12s ease;
}
.btn:active:not(:disabled) { transform: translateY(3px); box-shadow: none !important; }
.btn:disabled { opacity: 1; cursor: default; }

.btn-primary { background: var(--eu-blue); color: #fff; box-shadow: var(--shadow-btn) var(--eu-blue-dark); }
.btn-blue { background: var(--eu-blue); color: #fff; box-shadow: var(--shadow-btn) var(--eu-blue-dark); }
.btn-danger { background: var(--red); color: #fff; box-shadow: var(--shadow-btn) var(--red-dark); }
.btn-ghost { background: transparent; color: var(--muted); border: 2px solid var(--border); box-shadow: none; }
.btn-muted { background: var(--border); color: var(--muted); box-shadow: var(--shadow-btn) var(--border-strong); }
.btn:disabled.btn-muted { cursor: not-allowed; }

/* ------------------------------------------------------------------- home */

.topbar {
  position: sticky; top: 0; z-index: 20;
  padding: calc(var(--safe-top) + 0.75rem) 1rem 0.6rem;
  background: var(--bg);
  border-bottom: 2px solid var(--border);
}
.topbar-row { display: flex; align-items: center; gap: 1rem; }
.topbar .brand { font-weight: 900; font-size: 1.1rem; color: var(--eu-blue-light); letter-spacing: -.3px; margin-right: auto; }

.quick-bar { display: flex; gap: .5rem; margin-top: .6rem; }
.quick-btn {
  flex: 1; padding: .7rem .4rem; min-height: 44px;
  border: 2px solid var(--border); border-bottom-width: 3px; border-radius: 12px;
  background: var(--raised); color: var(--text);
  font-weight: 800; font-size: .78rem; white-space: nowrap;
}
.quick-btn:active { transform: translateY(2px); border-bottom-width: 2px; }
/* Dimmed, but full contrast on the label: it has to stay legible enough to
   tell someone what they would be buying. */
.quick-btn.is-locked { border-style: dashed; color: var(--muted); }
.stat { display: flex; align-items: center; gap: .3rem; font-weight: 800; font-size: .95rem; }
.stat .ico { font-size: 1.15rem; line-height: 1; }
.stat.streak { color: #ff9600; }
.stat.ready {
  color: var(--eu-blue-light); font-size: .85rem; letter-spacing: .01em;
  font-variant-numeric: tabular-nums;
}
/* `?god=1` only. Deliberately loud and deliberately ugly: it should never be
   mistaken for part of the app. */
.stat.godmode {
  color: #b26b00; background: #ffe9b0; border-radius: .5rem;
  padding: .05rem .4rem; font-size: .7rem; text-transform: uppercase;
  letter-spacing: .06em;
}

/* overflow-x must be pinned: `overflow-y: auto` alone computes overflow-x to
   `auto` too, and the translateX on .node-row would then let the whole path
   drag sideways by 5.5rem into empty space. */
.scroll { flex: 1; overflow-y: auto; overflow-x: hidden; padding-bottom: calc(var(--safe-bottom) + 3rem); }

.unit-banner {
  margin: 1.25rem 1rem 0.5rem;
  padding: 1rem 1.15rem;
  border-radius: var(--radius);
  color: #fff;
  display: flex; align-items: center; gap: .9rem;
  /* The unfolding banner is a <button>; the locked one is a <div> wrapping its
     own lock button. Both need to look identical, so the button is reset to the
     block the div already was rather than the two being styled apart. */
  width: calc(100% - 2rem);
  text-align: left;
}
.unit-banner .u-icon { font-size: 1.9rem; }
.unit-banner h2 { margin: 0; font-size: 1.05rem; font-weight: 900; }
.unit-banner p { margin: .15rem 0 0; font-size: .82rem; opacity: .9; line-height: 1.35; }

/* The count line. A folded unit shows nothing else about its contents, so this
   is where "how much is in here, and how much is behind me" has to live. */
.unit-summary { font-weight: 800; opacity: .78 !important; font-size: .76rem !important; }

/* Points down when the unit is open and right when it is folded, which is the
   one convention people already read without being told. */
.unit-chevron {
  margin-left: auto; flex: none;
  font-size: 1rem;
  transition: transform .18s ease;
}
.unit-banner[aria-expanded="false"] .unit-chevron { transform: rotate(-90deg); }

/* `hidden` alone loses to `.path`'s own `display: flex`. */
.path[hidden] { display: none; }

/* A locked unit is dimmed, never hidden: the syllabus is what is being sold, so
   the title and subtitle have to stay readable through the lock. It never
   unfolds — there is nothing behind it to show, and its lesson titles are not
   in the bundle at all on the web demo. */
.unit-banner.is-locked { filter: saturate(.45); }
.unit-banner.is-locked h2, .unit-banner.is-locked p { opacity: .85; }
.unit-banner .unit-lock {
  margin-left: auto; flex: none;
  min-width: 44px; min-height: 44px;   /* stays a real tap target */
  font-size: 1.2rem; line-height: 1;
  border-radius: 999px;
  background: rgba(0, 0, 0, .22);
  color: #fff;
}
.unit-banner .unit-lock:active { transform: translateY(1px); }

/* The lesson path comes in two layouts, chosen by PATH_STYLE in js/app.js:
   `.winding` is the original alternating S-curve, `.line` a straight column
   with an arrow between each step. Only the selected class is ever applied. */

.path { display: flex; flex-direction: column; align-items: center; padding: .5rem 0 1rem; }
.node-row { display: flex; justify-content: center; width: 100%; margin: .35rem 0; }

.path.winding .node-row[data-off="-2"] { transform: translateX(-5.5rem); }
.path.winding .node-row[data-off="-1"] { transform: translateX(-3rem); }
.path.winding .node-row[data-off="1"]  { transform: translateX(3rem); }
.path.winding .node-row[data-off="2"]  { transform: translateX(5.5rem); }

/* Straight column, laid out as a timeline: circles run down one continuous
   track with their titles beside them rather than underneath. Centred labels
   would break the track into stubs between every pair of nodes, which is what
   makes a connector read as decoration instead of structure. The track is
   filled where the path has been reached, so the line carries the progress.
   Drawn on the row, so the opaque circles paint over it. */
.path.line { align-items: stretch; padding: .5rem 1rem 1rem; }
.path.line .node-row {
  position: relative;
  justify-content: flex-start; align-items: center;
  margin: 0; padding: .45rem 0;
}
/* Every node shares one 56px column so its centre lands on the track.
   Checkpoints and milestones keep their squarer corners, which is what
   distinguishes them from a lesson — they just stop being wider. */
.path.line .node-btn { width: 56px; height: 56px; font-size: 1.45rem; }
.path.line .node.checkpoint .node-btn,
.path.line .node.milestone .node-btn { width: 56px; height: 56px; }
.path.line .node-row::before {
  content: "";
  position: absolute; left: 28px; margin-left: -1.5px;
  top: 0; bottom: 0; width: 3px;
  background: var(--border);
}
.path.line .node-row.reached::before { background: var(--eu-blue-light); }
.path.line .node-row:first-child::before { top: 50%; }
.path.line .node-row:last-child::before { bottom: 50%; }

/* The track is absolutely positioned, so the node has to be positioned too or
   the line paints straight over the circles. */
.path.line .node {
  position: relative;
  flex-direction: row; align-items: center;
  gap: 1rem; width: 100%;
}
.path.line .node-label {
  flex: 0 1 auto; text-align: left;
  font-size: .88rem; line-height: 1.3;
}
/* The auto margin goes after the crowns, so they sit against the title rather
   than drifting to the far edge of the row. */
.path.line .crown { margin-right: auto; font-size: .75rem; }
.path.line .node-label:last-child { margin-right: auto; }

.node { display: flex; flex-direction: column; align-items: center; gap: .35rem; width: 7rem; }
.node-btn {
  width: 68px; height: 62px;
  border-radius: 50%;
  background: var(--border);
  box-shadow: 0 4px 0 var(--border-strong);
  display: grid; place-items: center;
  font-size: 1.7rem;
  transition: transform .07s ease;
}
.node-btn:active { transform: translateY(3px); box-shadow: 0 1px 0 var(--border-strong); }
.node.available .node-btn { background: var(--eu-blue); box-shadow: 0 4px 0 var(--eu-blue-dark); }
.node.done .node-btn { background: var(--eu-gold); box-shadow: 0 4px 0 #b38f00; }
.node.locked .node-btn { opacity: .55; }
/* Paid-locked reads differently from sequence-locked. A lesson waiting on the
   previous one is simply dim; one waiting on a purchase gets the dashed outline
   the rest of the app uses for "there is something here", so it invites a tap
   rather than looking broken. */
.node.paid .node-btn {
  opacity: .8;
  background: transparent;
  border: 2px dashed var(--border-strong);
  box-shadow: none;
}
.node.paid .node-btn:active { transform: translateY(2px); box-shadow: none; }
.node-label { font-size: .74rem; font-weight: 800; color: var(--muted); text-align: center; line-height: 1.2; }
.node.available .node-label, .node.done .node-label { color: var(--text); }
.crown { font-size: .7rem; letter-spacing: 1px; }

.home-footer { text-align: center; padding: 1.5rem 1rem; color: var(--muted); font-size: .8rem; }
.home-footer button { text-decoration: underline; font-weight: 700; padding: .75rem 1rem; min-height: 44px; }
/* The one call to action in the app. Not underlined like the footer's other
   controls — it is a button, not a link. */
.home-footer .unlock-cta {
  display: block; width: 100%; max-width: 22rem; margin: 0 auto 1.4rem;
  text-decoration: none; font-size: .92rem;
}

/* What replaces the unlock button once it has been bought. Quiet on purpose —
   it is a receipt, not an offer — but it occupies the same spot, so the answer
   to "did my purchase land on this phone?" is where the buying used to be. */
.home-footer .owned-note {
  display: block; width: 100%; max-width: 22rem; margin: 0 auto 1.4rem;
  font-size: .82rem; font-weight: 700; color: var(--muted); text-align: center;
  text-decoration: none;
}
.home-footer .owned-ref {
  display: block; margin-top: .25rem;
  font-size: .72rem; font-weight: 400; opacity: .8;
  overflow-wrap: anywhere; /* store ids are long and must not push the layout */
}
/* The only part that looks like a link, because it is the only part worth
   tapping — the two lines above it are a receipt. */
.home-footer .owned-support {
  display: block; margin-top: .45rem;
  font-size: .72rem; font-weight: 700; color: var(--eu-blue-light);
  text-decoration: underline;
}

/* A setting, not a call to action: it sits quietly under the unlock and states
   its current value, so the answer to "am I being reminded?" is on the screen
   rather than behind a tap. */
.home-footer .reminder-cta {
  display: block; margin: 0 auto 1rem; text-decoration: none;
  font-weight: 700; color: var(--muted);
}

.disclaimer {
  font-size: .72rem; line-height: 1.5; color: var(--muted);
  max-width: 30rem; margin: 0 auto 1rem; text-align: left;
  border-left: 3px solid var(--border-strong); padding-left: .8rem;
}
.center-screen .disclaimer { margin-top: 1.6rem; text-align: left; }

/* ==========================================================================
   Choosing a language

   The first screen of a first run, so it has to read as a decision rather than
   a settings list: one tappable row per language, each naming itself in its own
   language first. The English name sits underneath only where it differs — for
   English itself, or a language whose endonym is already the word we would use,
   the second line would be the same word twice.
   ========================================================================== */

.lang-list {
  display: flex; flex-direction: column; gap: .6rem;
  width: 100%; max-width: 22rem; margin: 1.4rem auto 0;
}

.lang-option {
  display: flex; flex-direction: column; align-items: flex-start; gap: .15rem;
  width: 100%; padding: .85rem 1rem; min-height: 56px;
  background: var(--surface); color: var(--text);
  border: 2px solid var(--border); border-radius: var(--radius);
  text-align: left; font: inherit; cursor: pointer;
}
.lang-option:hover { border-color: var(--eu-blue-light); }
.lang-option:active { transform: translateY(1px); }

.lang-native { font-size: 1.05rem; font-weight: 800; }
.lang-name { font-size: .8rem; color: var(--muted); }

/* Said out loud rather than hidden behind an asterisk: someone choosing a
   language that is part-finished should learn that here, not four lessons in. */
.lang-partial {
  margin-top: .2rem; padding: .1rem .45rem;
  font-size: .7rem; font-weight: 700;
  color: var(--gold-text); background: var(--gold-bg); border-radius: 999px;
}

/* A language that is announced but not written yet. Dimmed rather than left
   out, so the answer to "is my language coming?" is in the one place anybody
   asks it. It is a real <button disabled>, so it is skipped by the tab order and
   announced as unavailable, and the cursor says so before the tap does. */
.lang-option-soon { cursor: default; opacity: .55; }
.lang-option-soon:hover { border-color: var(--border); }
.lang-option-soon:active { transform: none; }

.lang-soon {
  margin-top: .2rem; padding: .1rem .45rem;
  font-size: .7rem; font-weight: 700;
  color: var(--muted); background: var(--border); border-radius: 999px;
}

/* The same list, one line long, at the foot of the language switcher. */
.lang-soon-note { font-size: .8rem; color: var(--muted); }

/* placement advice */
.placement-h { font-size: 1.5rem; font-weight: 900; margin: 0 0 .4rem; }
.placement-row { border-left-width: 6px; }
.placement-row.priority { border-left-color: var(--red); }
.placement-row.review { border-left-color: #ff9600; }
.placement-row.skim { border-left-color: var(--green); }
.placement-row.untested { border-left-color: var(--border-strong); }
.placement-go { margin-top: .6rem; padding: .5rem; font-size: .75rem; }
.concept-pill.priority { background: var(--red-bg); border-color: var(--red); color: var(--red-text); }
.concept-pill.review { border-color: #ff9600; color: #ff9600; }
.concept-pill.skim { background: var(--green-bg); border-color: var(--green); color: var(--green-text); }
.concept-pill.untested { border-color: var(--border-strong); color: var(--muted); }

/* ------------------------------------------------------------ lesson chrome */

.lesson-top {
  padding: calc(var(--safe-top) + 0.75rem) 1rem 0.75rem;
  display: flex; align-items: center; gap: .85rem;
}
/* 48px minimum on anything you tap: Material asks for 48dp, Apple for 44pt,
   and the close button is the escape hatch on every screen. Visual weight
   still comes from font-size, not from the box. */
/* The way out of the web demo, back to the marketing page it was opened from.
   Rendered only there — see `siteHome()` in js/content.js — so nothing about it
   is ever shipped in the APK.

   Quieter than the stats it sits beside: it is an exit, and an exit that
   competes with the streak for attention is an exit being advertised. It comes
   up to full contrast on hover and focus, and the 44px box is the tap target
   the glyph alone would not give. */
.exit-site {
  display: grid; place-items: center;
  min-width: 44px; min-height: 44px;
  margin-right: -.5rem; /* optically aligns the glyph to the edge, as .icon-btn does */
  font-size: 1.35rem; font-weight: 700; line-height: 1;
  color: var(--muted); text-decoration: none;
  opacity: .7;
}
.exit-site:hover, .exit-site:focus-visible { opacity: 1; color: var(--text); }

/* The welcome screen has no bar to sit in, so this one floats in the corner.
   Inset by the safe area because on a notched phone in a browser the top of the
   viewport is not the top of the screen. */
.exit-site-float {
  position: absolute;
  top: calc(var(--safe-top) + .25rem); right: .25rem;
  z-index: 5;
}

.icon-btn {
  font-size: 1.6rem; color: var(--muted); line-height: 1; font-weight: 700;
  min-width: 48px; min-height: 48px;
  display: grid; place-items: center;
  margin-left: -.5rem; /* keeps the glyph optically aligned to the edge */
}

.progress { flex: 1; height: 14px; background: var(--border); border-radius: 99px; overflow: hidden; }
.progress > i { display: block; height: 100%; background: var(--eu-blue-light); border-radius: 99px; transition: width .35s cubic-bezier(.2,.8,.3,1); }

.stage { flex: 1; overflow-y: auto; padding: .5rem 1rem 1rem; max-width: 640px; width: 100%; margin: 0 auto; }
.q-kicker { font-size: .78rem; font-weight: 900; text-transform: uppercase; letter-spacing: 1px; color: var(--muted); margin: .5rem 0 .6rem; }
.q-prompt { font-size: 1.35rem; font-weight: 800; line-height: 1.3; margin: 0 0 1.25rem; }

/* choice buttons */
.choices { display: flex; flex-direction: column; gap: .65rem; }
.choice {
  width: 100%; text-align: left;
  padding: .85rem 1rem;
  border: 2px solid var(--border);
  border-bottom-width: 4px;
  border-radius: 14px;
  background: var(--surface);
  font-weight: 600; line-height: 1.35;
  display: flex; align-items: center; gap: .75rem;
  transition: background .1s ease, border-color .1s ease;
}
.choice .key {
  flex: none; width: 26px; height: 26px; border-radius: 8px;
  border: 2px solid var(--border-strong); color: var(--muted);
  display: grid; place-items: center; font-size: .78rem; font-weight: 800;
}
.choice.selected { background: color-mix(in srgb, var(--eu-blue) 12%, transparent); border-color: var(--eu-blue-light); }
.choice.selected .key { border-color: var(--eu-blue-light); color: var(--eu-blue-light); }
.choice.right { background: var(--green-bg); border-color: var(--green); color: var(--green-text); }
.choice.wrong { background: var(--red-bg); border-color: var(--red); color: var(--red-text); }
.choice:disabled { cursor: default; }

/* A mark as well as a colour. Roughly one European man in twelve cannot reliably
   separate the green from the red, and a graded answer is exactly the moment the
   distinction has to land. Pseudo-elements, so the button's text — which the
   smoke test matches on — stays what it was. */
.choice.right::after,
.choice.wrong::after,
.chip.right::after,
.chip.wrong::after {
  padding-left: .5rem;
  font-weight: 900;
}
/* Pushed to the trailing edge in the stacked answer list; left beside the text
   in the match grid and the order tray, which centre their tiles. */
.choices .choice.right::after,
.choices .choice.wrong::after { margin-left: auto; }
.choice.right::after, .chip.right::after { content: "✓"; }
.choice.wrong::after, .chip.wrong::after { content: "✗"; }

/* match */
.match-grid { display: grid; grid-template-columns: 1fr 1fr; gap: .6rem; }
.match-grid .choice { justify-content: center; text-align: center; min-height: 4.4rem; font-size: .9rem; }
.match-grid .choice.cleared { opacity: .25; pointer-events: none; }

/* order */
.order-tray { min-height: 3rem; display: flex; flex-wrap: wrap; gap: .5rem; padding: .6rem; border: 2px dashed var(--border); border-radius: 14px; margin-bottom: .9rem; }
.order-bank { display: flex; flex-wrap: wrap; gap: .5rem; }
.chip {
  padding: .5rem .8rem; border-radius: 12px;
  border: 2px solid var(--border); border-bottom-width: 4px;
  background: var(--surface); font-weight: 700; font-size: .88rem;
}
.chip .n { color: var(--muted); font-size: .75rem; margin-right: .35rem; }
.chip.right { background: var(--green-bg); border-color: var(--green); color: var(--green-text); }
.chip.wrong { background: var(--red-bg); border-color: var(--red); color: var(--red-text); }

/* fill */
.fill-input {
  width: 100%; padding: .85rem 1rem; font-size: 1.05rem; font-weight: 700;
  border: 2px solid var(--border); border-bottom-width: 4px; border-radius: 14px;
  background: var(--surface); color: var(--text); font-family: inherit;
}
/* Keeps the global focus ring — an input that removes its outline and offers
   only a border tint leaves a keyboard user guessing. */
.fill-input:focus { border-color: var(--eu-blue-light); }

/* ------------------------------------------------------------- feedback bar */

.footer-bar {
  position: sticky; bottom: 0;
  padding: .9rem 1rem calc(var(--safe-bottom) + .9rem);
  border-top: 2px solid var(--border);
  background: var(--bg);
}
.footer-bar.right { background: var(--green-bg); border-top-color: transparent; }
.footer-bar.wrong { background: var(--red-bg); border-top-color: transparent; }
.footer-inner { max-width: 640px; margin: 0 auto; }

.verdict { display: flex; align-items: center; gap: .6rem; font-weight: 900; font-size: 1.15rem; margin-bottom: .35rem; }
.footer-bar.right .verdict { color: var(--green-text); }
.footer-bar.wrong .verdict { color: var(--red-text); }
.explain { font-size: .88rem; line-height: 1.45; margin: 0 0 .85rem; }
.footer-bar.right .explain { color: var(--green-text); }
.footer-bar.wrong .explain { color: var(--red-text); }
.source { display: block; margin-top: .4rem; font-size: .78rem; opacity: .8; font-style: italic; }

/* --------------------------------------------------------------- teach page */

.teach {
  max-width: 38rem; margin: 0 auto; padding: 0 1.1rem 1.5rem;
  font-size: 1.02rem; line-height: 1.68;
}

/* The lesson title, then the sections inside it. Each h3 opens a new section,
   so it carries the rule that breaks a long page into readable chunks. */
.teach h2 { font-size: 1.6rem; line-height: 1.25; font-weight: 900; margin: 1.3rem 0 .7rem; }
.teach h3 {
  font-size: 1.15rem; line-height: 1.3; font-weight: 900; color: var(--eu-blue-light);
  margin: 2.2rem 0 .6rem; padding-top: 1.3rem; border-top: 1px solid var(--border);
}
/* A page opens with its own section heading, so the divider that separates
   sections in a scroll would just be a stray line under the progress bar. */
.teach > h3:first-child { margin-top: .5rem; padding-top: 0; border-top: none; }
.teach h4 { font-size: .98rem; font-weight: 800; margin: 1.3rem 0 .4rem; }
.teach p { margin: 0 0 .95rem; }

/* The opening paragraph sets up the lesson; give it a little more presence. */
.teach h2:first-child + p { font-size: 1.1rem; color: var(--text); margin-bottom: 1.2rem; }

.teach ul, .teach ol { margin: 0 0 1rem; padding-left: 1.2rem; }
.teach li { margin-bottom: .45rem; }
.teach ul { list-style: none; padding-left: .3rem; }
.teach ul > li { position: relative; padding-left: 1.05rem; }
.teach ul > li::before {
  content: ""; position: absolute; left: .1rem; top: .68em;
  width: .36rem; height: .36rem; border-radius: 50%; background: var(--eu-blue-light);
}
.teach ol > li::marker { color: var(--eu-blue-light); font-weight: 800; }

.teach code { background: var(--raised); padding: .1rem .35rem; border-radius: 6px; font-size: .9em; }
.teach blockquote {
  margin: 1.1rem 0; padding: .75rem 1rem;
  border-left: 4px solid var(--eu-gold); background: var(--raised);
  border-radius: 0 10px 10px 0; font-style: italic;
}
.teach hr { border: none; border-top: 2px solid var(--border); margin: 1.6rem 0; }
.teach a { color: var(--eu-blue-light); }
.teach blockquote > :last-child { margin-bottom: 0; }

/* ------------------------------------------------------------- callouts */
/* `> [!trap]` and friends in the lesson markdown. Each type sets three
   custom properties and the shared rule does the rest. */

.teach .callout {
  margin: 1.2rem 0; padding: .85rem 1rem .9rem;
  background: var(--accent-bg);
  border-left: 5px solid var(--accent);
  border-radius: 0 12px 12px 0;
}
.teach .callout-label {
  margin: 0 0 .35rem;
  font-size: .72rem; font-weight: 900; letter-spacing: .07em; text-transform: uppercase;
  color: var(--accent-text);
}
.teach .callout > :last-child { margin-bottom: 0; }
.teach .callout ul, .teach .callout ol { margin-bottom: 0; }
.teach .callout ul > li::before { background: var(--accent); }

.teach .callout-trap   { --accent: var(--red);           --accent-bg: var(--red-bg);  --accent-text: var(--red-text); }
.teach .callout-exam   { --accent: var(--eu-gold);       --accent-bg: var(--gold-bg); --accent-text: var(--gold-text); }
.teach .callout-memory { --accent: var(--eu-blue-light); --accent-bg: var(--blue-bg); --accent-text: var(--blue-text); }
.teach .callout-note   { --accent: var(--border-strong); --accent-bg: var(--raised);  --accent-text: var(--muted); }

/* ------------------------------------------------------------ page footer */
/* The teach page is paged at each `##`, so the footer carries the position and
   a way back as well as the button forward. */

.teach-count {
  margin: 0 0 .55rem;
  text-align: center;
  font-size: .75rem; font-weight: 800; letter-spacing: .08em; text-transform: uppercase;
  color: var(--muted);
}
.teach-nav { display: flex; gap: .6rem; }
.teach-nav .btn { flex: 1; }
.teach-nav .teach-back { flex: 0 1 auto; padding-left: 1.5rem; padding-right: 1.5rem; }

/* ---------------------------------------------------------------- tables */

.table-wrap { overflow-x: auto; margin: 1.1rem 0; -webkit-overflow-scrolling: touch; }
.teach table { border-collapse: collapse; width: 100%; font-size: .88rem; line-height: 1.5; min-width: 22rem; }
.teach th, .teach td { border: 1px solid var(--border); padding: .55rem .65rem; text-align: left; vertical-align: top; }
.teach th { background: var(--raised); font-weight: 800; }

/* Below this width a row scrolled sideways is unreadable, so each row becomes
   its own card: first cell as the heading, the rest labelled with the column
   name that lesson.js stamped onto the cell. */
@media (max-width: 34rem) {
  .teach table { min-width: 0; border: none; font-size: .9rem; }
  .teach table, .teach tbody, .teach tr, .teach td { display: block; }
  .teach thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }

  .teach tbody tr {
    margin: 0 0 .7rem; border: 1px solid var(--border); border-radius: 12px;
    background: var(--surface); overflow: hidden;
  }
  .teach tbody td { border: none; padding: .5rem .8rem; }
  .teach tbody td:first-child {
    font-weight: 900; background: var(--raised);
    padding: .6rem .8rem; border-bottom: 1px solid var(--border);
  }
  .teach tbody td[data-label]:not(:first-child)::before {
    content: attr(data-label);
    display: block; margin-bottom: .1rem;
    font-size: .68rem; font-weight: 800; letter-spacing: .05em; text-transform: uppercase;
    color: var(--muted);
  }
}

/* ------------------------------------------------------------ result screen */

.center-screen {
  flex: 1; display: flex; flex-direction: column; justify-content: center; align-items: center;
  text-align: center; padding: 2rem 1.5rem; gap: .6rem;
  /* The containing block for `.exit-site-float`, which the welcome screen
     parks in the corner because there is no bar here to put it in. */
  position: relative;
}
.center-screen .big { font-size: 4rem; line-height: 1; }
.center-screen h2 { font-size: 1.7rem; font-weight: 900; margin: .4rem 0 0; }
.center-screen p { color: var(--muted); margin: 0; max-width: 22rem; }

.score-row { display: flex; gap: .75rem; margin: 1.4rem 0 .5rem; width: 100%; max-width: 22rem; }
.score-card { flex: 1; border: 3px solid var(--eu-gold); border-radius: 14px; overflow: hidden; }
.score-card.acc { border-color: var(--eu-blue-light); }
.score-card .cap { background: var(--eu-gold); color: #4a3b00; font-size: .68rem; font-weight: 900; text-transform: uppercase; letter-spacing: 1px; padding: .2rem; }
.score-card.acc .cap { background: var(--eu-blue-light); color: #fff; }
.score-card .val { padding: .5rem; font-size: 1.45rem; font-weight: 900; }

/* `margin-inline: auto` so this also centres inside a plain .scroll, not just
   inside .center-screen where align-items already handles it. */
.actions {
  width: 100%; max-width: 22rem;
  display: flex; flex-direction: column; gap: .6rem;
  margin: 1.2rem auto 0; padding: 0 1rem 1.5rem;
}

/* --------------------------------------------------------------- mock exam */

.exam-btn { font-size: 1.25rem; line-height: 1; padding: .15rem .3rem; }

.exam-card {
  margin: .5rem 1rem 0; padding: 1.1rem 1.15rem;
  border: 2px solid var(--border); border-radius: var(--radius);
  background: var(--raised);
}
.exam-card h3 { margin: 0 0 .3rem; font-size: 1.05rem; font-weight: 900; }
.exam-card p { margin: 0 0 .9rem; font-size: .84rem; color: var(--muted); line-height: 1.4; }
.exam-card-meta { font-weight: 800; color: var(--eu-blue-light) !important; }

.exam-title { font-weight: 900; font-size: 1.05rem; }

.exam-intro { padding: .5rem 1.15rem 0; max-width: 640px; margin: 0 auto; }
.exam-intro h2 { font-size: 1.5rem; font-weight: 900; margin: .3rem 0 .7rem; }
.exam-intro ul { margin: 0 0 1.2rem; padding-left: 1.1rem; color: var(--muted); font-size: .88rem; }
.exam-intro li { margin-bottom: .35rem; }

.exam-preset {
  display: flex; align-items: center; gap: 1rem;
  width: calc(100% - 2rem); margin: 0 1rem .7rem; padding: .95rem 1.1rem;
  border: 2px solid var(--border); border-bottom-width: 4px; border-radius: 14px;
  background: var(--surface); text-align: left;
}
.exam-preset:active { transform: translateY(2px); border-bottom-width: 2px; }
.exam-preset-main { flex: 1; }
.exam-preset-main h3 { margin: 0; font-size: 1rem; font-weight: 900; }
.exam-preset-main p { margin: .1rem 0 0; font-size: .8rem; color: var(--muted); }
.exam-preset-meta { text-align: right; display: flex; flex-direction: column; line-height: 1.3; }
.exam-preset-meta strong { font-size: 1.05rem; font-weight: 900; color: var(--eu-blue-light); }
.exam-preset-meta span { font-size: .78rem; color: var(--muted); font-weight: 700; }
.exam-short { color: var(--eu-gold) !important; }

.exam-pool, .exam-empty { padding: 0 1.15rem; font-size: .8rem; color: var(--muted); max-width: 640px; margin: 0 auto; }
.exam-section { padding: .8rem 1.15rem 0; margin: 0; font-size: .95rem; font-weight: 900; max-width: 640px; margin: 0 auto; }
.exam-history { padding: .4rem 1rem 1rem; max-width: 640px; margin: 0 auto; }
.exam-history-row {
  display: flex; align-items: baseline; gap: .8rem;
  padding: .5rem .8rem; margin-bottom: .4rem;
  border-radius: 12px; background: var(--raised);
  border-left: 5px solid var(--border-strong);
}
.exam-history-row.pass { border-left-color: var(--green); }
.exam-history-row.fail { border-left-color: var(--red); }
.eh-score { font-weight: 900; font-size: 1.05rem; min-width: 3rem; }
.exam-history-row.pass .eh-score { color: var(--green-text); }
.exam-history-row.fail .eh-score { color: var(--red-text); }
.eh-detail { font-size: .8rem; color: var(--muted); }

/* exam chrome */
.exam-top {
  padding: calc(var(--safe-top) + 0.75rem) 1rem 0.75rem;
  display: flex; align-items: center; gap: .85rem;
  border-bottom: 2px solid var(--border);
}
.exam-timer {
  flex: 1; text-align: center;
  font-variant-numeric: tabular-nums; font-weight: 900; font-size: 1.25rem;
  letter-spacing: .5px;
}
.exam-timer.warn { color: #ff9600; }
.exam-timer.danger { color: var(--red); }
.exam-count { font-weight: 800; font-size: .85rem; color: var(--muted); min-width: 3.5rem; text-align: right; }

.exam-stage { padding-top: 1rem; }
.exam-nav { display: flex; gap: .5rem; margin-bottom: .6rem; }
.nav-btn, .flag-btn {
  flex: 1; padding: .75rem .5rem; min-height: 48px;
  border: 2px solid var(--border); border-radius: 12px;
  font-weight: 800; font-size: .82rem; color: var(--text);
  background: var(--surface);
}
.nav-btn:disabled { opacity: .4; cursor: default; }
.flag-btn.flagged { background: color-mix(in srgb, var(--eu-gold) 22%, transparent); border-color: var(--eu-gold); }

/* review palette */
.palette-wrap { padding: 1rem 1.15rem; max-width: 640px; margin: 0 auto; }
.palette-wrap h2 { font-size: 1.4rem; font-weight: 900; margin: 0 0 .3rem; }
.palette-wrap > p { color: var(--muted); font-size: .88rem; margin: 0 0 1rem; }
.palette { display: grid; grid-template-columns: repeat(auto-fill, minmax(3rem, 1fr)); gap: .5rem; }
.pal-item {
  aspect-ratio: 1; border-radius: 12px; font-weight: 900; font-size: .95rem;
  border: 2px solid var(--border-strong); color: var(--muted); background: var(--surface);
}
.pal-item.answered { background: var(--eu-blue); border-color: var(--eu-blue); color: #fff; }
.pal-item.flagged { border-color: var(--eu-gold); border-width: 3px; }
.palette-key { margin-top: 1rem; font-size: .76rem; color: var(--muted); }

.exam-note { font-size: .8rem; color: var(--muted); margin-top: .8rem !important; }

/* answers review */
.review-item {
  margin: 0 1rem .9rem; padding: .9rem 1rem;
  border: 2px solid var(--border); border-radius: 14px;
  border-left-width: 6px;
}
.review-item.right { border-left-color: var(--green); }
.review-item.wrong { border-left-color: var(--red); }
.review-item.skipped { border-left-color: var(--border-strong); }
.review-head { display: flex; justify-content: space-between; align-items: center; margin-bottom: .2rem; }
.review-n { font-weight: 900; font-size: .8rem; color: var(--muted); }
.review-tag { font-size: .72rem; font-weight: 900; text-transform: uppercase; letter-spacing: .5px; }
.review-item.right .review-tag { color: var(--green-text); }
.review-item.wrong .review-tag { color: var(--red-text); }
.review-item.skipped .review-tag { color: var(--muted); }
.review-item .q-kicker { display: none; }
.review-item .q-prompt { font-size: 1rem; margin-bottom: .8rem; }
.review-item .choice { padding: .6rem .8rem; font-size: .88rem; }
.review-item .explain { margin: .8rem 0 0; color: var(--muted); }

/* ---------------------------------------------------------------- dialogs */

.modal-backdrop {
  position: fixed; inset: 0; z-index: 100;
  background: rgba(6, 11, 20, .62);
  display: flex; align-items: center; justify-content: center;
  padding: 1.5rem;
  animation: fade-in .12s ease;
}
.modal {
  width: 100%; max-width: 22rem;
  background: var(--surface); color: var(--text);
  border: 2px solid var(--border); border-radius: var(--radius);
  padding: 1.25rem 1.25rem 1rem;
  box-shadow: 0 18px 40px rgba(0, 0, 0, .35);
  animation: pop-in .14s cubic-bezier(.2, .9, .3, 1.2);
}
.modal h3 { margin: 0 0 .4rem; font-size: 1.15rem; font-weight: 900; }

/* The upsell: taller than a confirm dialog and scrollable, because on a small
   phone in landscape the list of what you get would otherwise run off-screen
   and take the buttons with it. */
.modal.modal-sheet { max-width: 24rem; max-height: 82vh; overflow-y: auto; }
.paywall-h {
  margin: .2rem 0 .5rem; font-size: .78rem; font-weight: 900;
  text-transform: uppercase; letter-spacing: .06em; color: var(--muted);
}
.paywall-list { margin: 0 0 1rem; padding-left: 1.1rem; }
.paywall-list li { font-size: .88rem; line-height: 1.45; margin-bottom: .35rem; }
.modal .paywall-price {
  margin: 0 0 .5rem; color: var(--text); font-weight: 900; font-size: 1rem;
}
.modal .paywall-note { margin: 0 0 1rem; font-size: .78rem; }
.modal p { margin: 0 0 1.1rem; color: var(--muted); font-size: .92rem; line-height: 1.45; }

/* The reminder's hour. A native time control rather than a custom one: it is
   the thing every phone already knows how to present, in the learner's own
   locale and clock format, and it is reachable with a screen reader. */
.reminder-row {
  display: flex; align-items: center; justify-content: space-between; gap: .75rem;
  margin: 0 0 1.1rem; color: var(--text); font-size: .92rem; font-weight: 700;
}
.reminder-time {
  font: inherit; color: var(--text); background: var(--raised);
  border: 2px solid var(--border-strong); border-radius: .6rem;
  padding: .5rem .6rem; min-height: 44px;
}

/* The rest nudge. Smaller than the dialog's own text: this is the reasoning
   under the headline, and it should not shout as loudly as the headline. */
.modal .pace-note { margin: 0 0 .9rem; font-size: .84rem; }
.modal-actions { display: flex; flex-direction: column; gap: .5rem; }
.modal-actions .btn { padding: .75rem 1rem; }

@keyframes fade-in { from { opacity: 0; } }
@keyframes pop-in { from { transform: scale(.94); opacity: 0; } }
@media (prefers-reduced-motion: reduce) {
  .modal-backdrop, .modal { animation: none; }
}

/* --------------------------------------------- checkpoint & milestone nodes */

/* Three kinds of node, three levels of label. A lesson gets a plain title; a
   checkpoint and a milestone are assessments, so their labels read as category
   markers — uppercase and tracked rather than shrunk, since making type smaller
   to say "different" only says "less important". The milestone, being the
   cumulative paper, also takes the gold. */
.node.checkpoint .node-label,
.node.milestone .node-label {
  font-size: .76rem; font-weight: 900;
  text-transform: uppercase; letter-spacing: .09em;
}
.node.milestone:not(.locked) .node-label { color: var(--eu-gold); }

.node.checkpoint .node-btn { border-radius: 18px; width: 74px; }
.node.checkpoint.available .node-btn { background: var(--eu-gold); box-shadow: 0 4px 0 #b38f00; }
.node.checkpoint.passed .node-btn { background: var(--green); box-shadow: 0 4px 0 var(--green-dark); }
.node.checkpoint.due .node-btn {
  background: #ff9600; box-shadow: 0 4px 0 #b36900;
  animation: nudge 2.4s ease-in-out infinite;
}
.node.checkpoint.due .node-label { color: #ff9600; }
@keyframes nudge {
  0%, 88%, 100% { transform: translateY(0); }
  92% { transform: translateY(-4px); }
  96% { transform: translateY(0); }
}
@media (prefers-reduced-motion: reduce) {
  .node.checkpoint.due .node-btn { animation: none; }
}

.node.milestone { width: 9rem; }
.node.milestone .node-btn {
  border-radius: 20px; width: 82px; height: 68px;
  background: var(--eu-blue-light); box-shadow: 0 4px 0 var(--eu-blue-dark);
}
.node.milestone.locked .node-btn { background: var(--border); box-shadow: 0 4px 0 var(--border-strong); }
.node.milestone.passed .node-btn { background: var(--eu-gold); box-shadow: 0 4px 0 #b38f00; }
.node.milestone.due .node-btn { background: #ff9600; box-shadow: 0 4px 0 #b36900; }
/* Due beats the gold, so a paper that has fallen due reads the same urgent
   orange as a due checkpoint. */
.node.milestone.due .node-label { color: #ff9600; }

/* --------------------------------------------------------- concept map */

.concept-summary { padding: 1rem 1.15rem .4rem; max-width: 640px; margin: 0 auto; }
.concept-summary p { margin: 0 0 .9rem; color: var(--muted); font-size: .9rem; }

.concept-unit {
  margin: 1.2rem 0 .5rem; padding: 0 1.15rem;
  font-size: .8rem; font-weight: 900; text-transform: uppercase; letter-spacing: 1px;
  color: var(--muted); max-width: 640px; margin-inline: auto;
}

.concept-row {
  margin: 0 1rem .7rem; padding: .75rem .9rem;
  border: 2px solid var(--border); border-radius: 14px; background: var(--surface);
}
/* Stands in for a paid unit's concept rows: one line, same footprint, so the
   shape of the syllabus still reads down the page. */
.concept-locked {
  display: block; width: calc(100% - 2rem);
  margin: 0 1rem .7rem; padding: .75rem .9rem;
  border: 2px dashed var(--border-strong); border-radius: 14px;
  background: transparent; color: var(--muted);
  font-weight: 800; font-size: .86rem; text-align: left; min-height: 44px;
}
.concept-locked:active { transform: translateY(1px); }
.concept-head { display: flex; align-items: center; justify-content: space-between; gap: .6rem; margin-bottom: .5rem; }
.concept-title { font-weight: 800; font-size: .92rem; }
.concept-pill {
  flex: none; font-size: .66rem; font-weight: 900; text-transform: uppercase; letter-spacing: .5px;
  padding: .15rem .5rem; border-radius: 99px; border: 2px solid var(--border-strong); color: var(--muted);
}
.concept-pill.solid { background: var(--green-bg); border-color: var(--green); color: var(--green-text); }
.concept-pill.shaky { background: var(--red-bg); border-color: var(--red); color: var(--red-text); }
.concept-pill.learning { border-color: var(--eu-blue-light); color: var(--eu-blue-light); }

.concept-bar { display: flex; height: 10px; border-radius: 99px; overflow: hidden; background: var(--border); gap: 2px; }
.concept-bar .seg { display: block; }
.concept-bar .seg.known { background: var(--green); }
.concept-bar .seg.learning { background: var(--eu-blue-light); }
.concept-bar .seg.weak { background: var(--red); }
.concept-bar .seg.unseen { background: var(--border-strong); }
.concept-counts { margin: .45rem 0 0; font-size: .74rem; color: var(--muted); }

.checkpoint-lede { color: var(--muted); font-size: .9rem; margin: 0 0 .8rem; }
.checkpoint-returning { color: var(--red-text); font-weight: 700; }

@media (min-width: 720px) {
  .app { max-width: 560px; margin: 0 auto; box-shadow: 0 0 0 2px var(--border); }
}
