/* /learn pilot (stage-1-v4) — Duolingo-style lessons guided by Nexa.
   Tokens only (theme.css). Lessons run in focus mode: the site nav and the
   feedback FAB hide so nothing covers the answer button (docs/UI-DESIGN.md). */
*, *::before, *::after { box-sizing: border-box; }
[hidden] { display: none !important; }
body.learn-page { margin: 0; background: var(--paper); color: var(--ink); font: 16px/1.6 'Hind Siliguri', 'Inter Tight', system-ui, sans-serif; }
:lang(en) { font-family: 'Inter Tight', 'Hind Siliguri', system-ui, sans-serif; }
button, input, textarea { font: inherit; color: inherit; }
button { cursor: pointer; }
button:disabled { cursor: default; }
:focus-visible { outline: 3px solid var(--coral); outline-offset: 3px; }
.sr-only { position: absolute; width: 1px; height: 1px; margin: -1px; overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; }
.skip-link { position: fixed; top: -80px; left: 16px; z-index: 300; background: var(--surface); padding: 8px 16px; border-radius: 8px; }
.skip-link:focus { top: 8px; }
.micro { font-family: 'JetBrains Mono', monospace; font-size: 11px; font-weight: 500; letter-spacing: .08em; }
.small { font-size: 13px; color: var(--muted); }
.center { text-align: center; }
.loading { padding: 64px 0; text-align: center; color: var(--muted); }
.notice { padding: 16px; border: 1px solid var(--rule); border-radius: 12px; background: var(--surface); margin: 16px 0; }
.link-btn { background: none; border: 0; padding: 8px 0; min-height: 44px; color: var(--muted); text-decoration: underline; display: inline-flex; align-items: center; gap: 8px; }
.ic { width: 24px; height: 24px; flex-shrink: 0; }

.learn-wrap { max-width: 640px; margin: 0 auto; padding: 0 16px 48px; }

/* ── Crawlable curriculum outline ───────────────────────────────────────────
   The pre-boot contents of #learn-app: what a crawler, a no-JS visitor and a
   slow connection read while learn.js fetches the 428 KB curriculum. render()
   replaces all of it, so nothing here needs a teardown. Generated by
   admin/refresh-learn-outline.js — see that file for why it exists. */
.learn-outline { padding: 32px 0 0; }
.learn-outline h1 { font-family: 'Literata', Georgia, serif; font-size: 30px; line-height: 1.25; margin: 0 0 12px; color: var(--charcoal); }
.learn-outline .lo-lead { margin: 0 0 8px; color: var(--muted); font-size: 15px; }
.learn-outline .loading { padding: 24px 0 8px; }
.lo-stages, .lo-levels { list-style: none; margin: 0; padding: 0; }
.lo-stage { border-top: 1px solid var(--rule); padding: 24px 0 8px; }
.lo-stage h2 { font-size: 19px; margin: 0 0 4px; color: var(--charcoal); display: flex; flex-wrap: wrap; align-items: baseline; gap: 8px; }
.lo-stage h2 .micro { color: var(--muted); }
.lo-stage > p { margin: 0 0 16px; color: var(--muted); font-size: 14px; }
.lo-level { padding: 8px 0 8px 16px; border-left: 2px solid var(--rule-soft); margin-bottom: 8px; }
.lo-level b { display: block; font-size: 15px; color: var(--ink); }
.lo-level .lo-meta { font-family: 'JetBrains Mono', monospace; font-size: 11px; letter-spacing: .08em; color: var(--muted); display: block; margin: 2px 0; }
.lo-level > span:last-child { font-size: 14px; color: var(--muted); }
/* The English gloss. It is the line Google matches on — 3 Bangla-script queries
   in 90 days against ~25K impressions — so it reads as real copy, not fine print. */
.lo-en { display: block; font-size: 14px; color: var(--ink); }
.lo-stage > p.lo-en { margin: 0 0 4px; font-size: 15px; }
.lo-level .lo-en { margin-bottom: 2px; }
.lo-stage h2 .lo-bn { font-weight: 500; color: var(--muted); font-size: 16px; }
body.learn-focus #site-nav, body.learn-focus #fbw-fab { display: none !important; }
body.learn-focus .learn-wrap { padding-bottom: 160px; }
/* The done screen's bar carries two stacked buttons, not the lesson's one, so
   it needs more room beneath the content than `learn-focus` reserves — without
   this the last thing on the page (the reward link, on a stage-final screen)
   sits under the bar and cannot be scrolled clear of it. */
body.learn-done .learn-wrap { padding-bottom: 200px; }

/* ── Buttons: chunky, pressable ─────────────────────────────────────────── */
.btn3d { --face: var(--coral); --edge: var(--coral-dark); --text: var(--surface);
  min-height: 52px; padding: 8px 24px; border: 0; border-radius: 12px; background: var(--face); color: var(--text);
  font-weight: 700; font-size: 17px; letter-spacing: .01em; box-shadow: 0 4px 0 var(--edge); transition: transform .08s, box-shadow .08s; }
.btn3d:active:not(:disabled) { transform: translateY(4px); box-shadow: 0 0 0 var(--edge); }
.btn3d:disabled { --face: var(--rule-soft); --edge: var(--rule); --text: var(--faint); }
.btn3d.wide { width: 100%; }
.btn3d.good { --face: var(--ok); --edge: var(--ok-text); }
.btn3d.bad { --face: var(--bad); --edge: var(--bad-text); }
.btn3d.ghost { --face: var(--surface); --edge: var(--rule); --text: var(--charcoal); border: 2px solid var(--rule); }

/* ── Nexa: the same living orb as the speaking examiner ─────────────────── */
.nx-orb { --size: 56px; position: relative; display: block; width: var(--size); height: var(--size); flex-shrink: 0; }
.nx-orb.big { --size: 128px; }
.nx-orb::before { content: ''; position: absolute; inset: -18%; border-radius: 50%; z-index: 0;
  background: radial-gradient(circle, color-mix(in srgb, var(--coral) 30%, transparent), transparent 70%); opacity: .6; }
.nx-core { position: absolute; inset: 0; border-radius: 50%; overflow: hidden; z-index: 1;
  background: radial-gradient(circle at 50% 60%, var(--coral), var(--coral-dark));
  box-shadow: 0 8px 24px -8px color-mix(in srgb, var(--coral) 60%, transparent), inset 0 0 16px color-mix(in srgb, var(--coral-dark) 50%, transparent);
  -webkit-mask: radial-gradient(circle, #000 83%, transparent 100%); mask: radial-gradient(circle, #000 83%, transparent 100%);
  transition: filter .2s, box-shadow .2s; }
.nx-core::after { content: ''; position: absolute; inset: -25%; border-radius: 50%;
  background: radial-gradient(circle at 34% 28%, color-mix(in srgb, var(--surface) 50%, transparent), transparent 48%); mix-blend-mode: screen; }
.nx-core i { position: absolute; filter: blur(4px); }
.nx-core .b1 { width: 96%; height: 96%; left: -10%; top: -4%; background: color-mix(in srgb, var(--coral) 80%, var(--surface)); opacity: .92; border-radius: 42% 58% 63% 37% / 45% 38% 62% 55%; }
.nx-core .b2 { width: 90%; height: 90%; right: -14%; bottom: -10%; background: var(--coral-dark); opacity: .85; border-radius: 62% 38% 40% 60% / 58% 62% 38% 42%; }
.nx-core .b3 { width: 58%; height: 58%; left: 18%; top: 14%; background: var(--coral-soft); opacity: .55; border-radius: 38% 62% 56% 44% / 52% 40% 60% 48%; }
.nx-core .b4 { width: 46%; height: 46%; right: 14%; top: 30%; background: color-mix(in srgb, var(--coral) 30%, var(--surface)); opacity: .6; border-radius: 55% 45% 48% 52% / 50% 55% 45% 50%; }
.nx-orb.speaking .nx-core { filter: brightness(1.12) saturate(1.2); box-shadow: 0 0 24px color-mix(in srgb, var(--coral) 70%, transparent); }
@media (prefers-reduced-motion: no-preference) {
  @keyframes nx-idle { 0%, 100% { opacity: .5; transform: scale(.95); } 50% { opacity: .9; transform: scale(1.06); } }
  @keyframes nx-morph { 0%, 100% { border-radius: 42% 58% 63% 37% / 45% 38% 62% 55%; } 33% { border-radius: 62% 38% 40% 60% / 58% 62% 38% 42%; } 66% { border-radius: 38% 62% 56% 44% / 52% 40% 60% 48%; } }
  @keyframes nx-spin { to { transform: rotate(360deg); } }
  @keyframes nx-talk { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.08); } }
  .nx-orb::before { animation: nx-idle 4.5s ease-in-out infinite; }
  .nx-core::after { animation: nx-spin 18s linear infinite; }
  .nx-core .b1 { animation: nx-morph 6s ease-in-out infinite, nx-spin 11s linear infinite; }
  .nx-core .b2 { animation: nx-morph 7.5s ease-in-out infinite reverse, nx-spin 15s linear infinite reverse; }
  .nx-core .b3 { animation: nx-morph 5s ease-in-out infinite, nx-spin 9s linear infinite; }
  .nx-core .b4 { animation: nx-morph 8s ease-in-out infinite reverse, nx-spin 13s linear infinite; }
  .nx-orb.speaking { animation: nx-talk .9s ease-in-out infinite; }
  .nx-orb.speaking .b1 { animation-duration: 3s, 6s; }
  .nx-orb.speaking .b2 { animation-duration: 3.5s, 7s; }
  .nx-orb.speaking .b3 { animation-duration: 2.5s, 5s; }
}
.nx-row { display: flex; align-items: flex-start; gap: 16px; margin: 16px 0 24px; }
.nx-btn { border: 0; background: none; padding: 0; border-radius: 50%; }
.nx-bubble { position: relative; margin: 0; padding: 16px; background: var(--surface); border: 2px solid var(--rule); border-radius: 16px;
  font-size: 17px; line-height: 1.7; color: var(--ink); box-shadow: var(--shadow-sm); }
.nx-bubble::before { content: ''; position: absolute; left: -10px; top: 20px; width: 16px; height: 16px; background: var(--surface);
  border-left: 2px solid var(--rule); border-bottom: 2px solid var(--rule); transform: rotate(45deg); }

/* ── Map ────────────────────────────────────────────────────────────────── */
.map { padding-top: 16px; }
/* The hero. This is the whole answer to "what is this and why should I spend
   five minutes on it", and it has one job: stop a candidate who already knows
   what IELTS is from reading the first line and concluding this is beginner
   material. It carries a CTA because the map's only way in used to be tapping a
   node, and 17 of the first 38 arrivals never pressed anything at all.
   Returning learners get `.compact` — they bought the pitch already. */
.hero { margin: 0 0 32px; padding: 24px; border: 2px solid var(--rule); border-radius: 16px;
  background: var(--surface); box-shadow: var(--shadow-sm); }
.hero-kick { display: block; margin-bottom: 8px; color: var(--coral); }
.hero h1 { margin: 0; font: 700 clamp(26px, 6vw, 38px)/1.25 'Hind Siliguri', 'Literata', serif; color: var(--charcoal); outline: none; }
.hero-lede { margin: 12px 0 0; max-width: 56ch; font-size: 17px; line-height: 1.7; color: var(--ink); }
.hero-lede b { color: var(--charcoal); }
/* The scale of the thing, at a glance — a pathway, not a leaflet. */
.hero-facts { display: flex; flex-wrap: wrap; gap: 8px; margin: 20px 0 0; padding: 0; list-style: none; }
.hero-facts li { display: inline-flex; align-items: baseline; gap: 6px; padding: 6px 14px; border-radius: 99px;
  background: var(--coral-soft); color: var(--coral-dark); font-size: 13px; }
.hero-facts b { font-size: 17px; font-weight: 700; }
.hero-both { margin: 20px 0 0; padding-top: 16px; border-top: 1px solid var(--rule-soft);
  font-size: 15px; line-height: 1.7; color: var(--muted); }
.hero-both b { color: var(--charcoal); }
.hero-cta { margin-top: 24px; }
.hero.compact { display: flex; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap; padding: 16px 24px; }
.hero.compact h1 { font-size: 20px; }
.hero.compact .hero-cta { margin-top: 0; }
.stats { display: flex; justify-content: flex-end; gap: 8px; flex-wrap: wrap; }
.stat { display: inline-flex; align-items: center; gap: 4px; padding: 4px 12px; border-radius: 99px; background: var(--surface); border: 2px solid var(--rule); font-size: 14px; color: var(--muted); }
.stat b { color: var(--charcoal); font-size: 16px; }
.stat .ic { width: 18px; height: 18px; }
.stat.hot .ic { color: var(--coral); fill: var(--coral-soft); }
.stat.gold .ic { color: var(--status-in); fill: var(--status-in); }
.stage-head { margin-bottom: 72px; }
.stage-banner { position: relative; padding: 24px; border-radius: 16px; background: var(--coral); color: var(--surface); box-shadow: 0 4px 0 var(--coral-dark); }
.stage-banner h2 { margin: 4px 0; font: 700 32px/1.3 'Hind Siliguri', 'Literata', serif; color: var(--surface); outline: none; }
.stage-banner p { margin: 0 0 16px; opacity: .92; }
.stage-progress { height: 12px; border-radius: 99px; background: color-mix(in srgb, var(--surface) 30%, transparent); overflow: hidden; }
.stage-progress span { display: block; height: 100%; border-radius: 99px; background: var(--surface); transition: width .6s ease; }
.stage-count { display: block; margin-top: 8px; font-size: 14px; }
/* Unpublished stage, visible to staff only — must never be mistaken for live. */
.stage-preview { display: inline-block; margin-left: 8px; padding: 0 8px; border-radius: 99px; background: var(--charcoal); color: var(--surface); font: 600 11px/20px 'JetBrains Mono', monospace; letter-spacing: .04em; vertical-align: middle; }

/* Which skills this stage covers, and how much of each — the answer to "what
   am I about to spend a week on?" before a single level is opened. */
.stage-skills { display: flex; flex-wrap: wrap; gap: 8px; margin: 16px 0 0; padding: 0; list-style: none; }
.stage-skills li { display: inline-flex; align-items: center; gap: 6px; padding: 4px 12px; border-radius: 99px;
  background: color-mix(in srgb, var(--surface) 22%, transparent); font: 600 13px/1.4 'Inter Tight', system-ui, sans-serif; }
.stage-skills .ic { width: 15px; height: 15px; }

/* What the stage gives you, grouped into the four skills and folded by default:
   it sits at the END of the stage, so it reads as the haul rather than a
   syllabus nobody asked for before level 1. The same list either way — goals
   while unfinished, a record once done, ticked line by line. */
.stage-goals { margin-top: 24px; border: 2px solid var(--rule); border-radius: 12px; background: var(--surface); overflow: hidden; }
.stage-goals > summary { padding: 16px; font-weight: 700; color: var(--charcoal); cursor: pointer; list-style: none;
  display: flex; align-items: center; gap: 12px; }
.stage-goals > summary::-webkit-details-marker { display: none; }
.stage-goals > summary::after { content: '▾'; color: var(--muted); transition: transform .2s; }
.stage-goals[open] > summary::after { transform: rotate(180deg); }
.goal-count { margin-left: auto; padding: 2px 10px; border-radius: 99px; background: var(--rule-soft); color: var(--muted);
  font: 700 13px/1.6 'Hind Siliguri', 'Inter Tight', system-ui, sans-serif; }
.goal-groups { padding: 0 16px; }
.goal-group { padding: 12px 0; border-top: 1px solid var(--rule-soft); }
.goal-group h3 { display: flex; align-items: center; gap: 8px; margin: 0 0 8px; color: var(--skill);
  font: 600 11px/1.6 'JetBrains Mono', ui-monospace, monospace; letter-spacing: .08em; text-transform: uppercase; }
.goal-group h3 .ic { width: 16px; height: 16px; }
.goal-group ul { margin: 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: 6px; }
.goal-group li { display: flex; gap: 10px; font-size: 14px; line-height: 1.5; color: var(--muted); }
.goal-group li.got { color: var(--ink); }
.goal-group li .ic { flex: none; width: 17px; height: 17px; margin-top: 3px; color: var(--rule); }
.goal-group li.got .ic { color: var(--status-done); }
.goal-boss { display: flex; align-items: flex-start; gap: 10px; margin: 0; padding: 12px 16px; border-top: 1px solid var(--rule-soft);
  font-size: 14px; line-height: 1.5; font-weight: 600; color: var(--charcoal); }
.goal-boss .ic { flex: none; width: 17px; height: 17px; margin-top: 3px; color: var(--coral); }
.stage-goals.is-locked { display: flex; align-items: center; gap: 12px; padding: 16px; color: var(--faint);
  background: var(--rule-soft); font-size: 14px; font-weight: 600; }
.stage-goals.is-locked .ic { flex: none; width: 18px; height: 18px; }
.stage-goals .traps-won { display: flex; align-items: center; gap: 8px; margin: 0; padding: 12px 16px; border-top: 2px solid var(--rule);
  background: var(--rule-soft); font-size: 14px; font-weight: 600; color: var(--charcoal); }

.path { list-style: none; margin: 0; padding: 0 0 16px; display: flex; flex-direction: column; align-items: center; gap: 24px; }
.node-wrap { position: relative; display: flex; flex-direction: column; align-items: center; transform: translateX(var(--x)); }
/* The circle carries STATE only — open / done / locked — which is what a map is
   for. Skill is carried by the headline underneath it, where a colour can sit
   next to words instead of competing with them. A skill ring around the circle
   was tried on 2026-09-21 and dropped: two coloured surfaces 4px apart read as
   noise, not as two facts. */
.node { width: 80px; height: 80px; border-radius: 50%; border: 0; display: grid; place-items: center; color: var(--surface);
  background: var(--coral); box-shadow: 0 8px 0 var(--coral-dark); transition: transform .1s, box-shadow .1s; }
/* Optically centred, not box-centred. The 8px 3D lip below the disc makes the
   element box taller than the circle a learner actually sees, and every skill
   glyph carries its ink low (headphone cups, book spine, bubble tail), so a
   box-centred icon reads as sitting in the bottom of the circle. Measured at
   ~1.6px; rounded to a whole pixel to stay crisp. */
.node .ic { width: 32px; height: 32px; transform: translateY(-1px); }
.node:active { transform: translateY(8px); box-shadow: 0 0 0 var(--coral-dark); }
.node.done { background: var(--status-in); box-shadow: 0 8px 0 color-mix(in srgb, var(--status-in) 70%, var(--ink)); }
.node.locked { background: var(--rule); color: var(--faint); box-shadow: 0 8px 0 color-mix(in srgb, var(--rule) 80%, var(--ink)); cursor: not-allowed; }
.node.current { outline: 8px solid var(--coral-soft); outline-offset: 4px; }
/* The current node's tip floats above it, so that node needs room or the tip
   lands on the previous level's name. */
.node-wrap:has(.node-tip) { margin-top: 16px; }
.node-tip { position: absolute; top: -48px; padding: 8px 16px; border-radius: 12px; background: var(--surface); border: 2px solid var(--rule);
  color: var(--coral); font-weight: 700; white-space: nowrap; z-index: 1; }
.node-tip::after { content: ''; position: absolute; left: 50%; bottom: -8px; width: 12px; height: 12px; background: var(--surface);
  border-right: 2px solid var(--rule); border-bottom: 2px solid var(--rule); transform: translateX(-50%) rotate(45deg); }
.node-stars { display: flex; gap: 2px; height: 20px; margin-top: 16px; }
.node-stars span .ic { width: 18px; height: 18px; color: var(--rule); fill: var(--rule); }
.node-stars span.on .ic { color: var(--status-in); fill: var(--status-in); }
/* "(১) IELTS-এর চার দরজা" — a neutral number chip, then the title in the skill's
   colour. The number is what a learner says out loud; the colour is what groups
   four skills down a long scrolling map without adding a second shape. */
.node-name { max-width: 200px; text-align: center; font-size: 14px; font-weight: 600; line-height: 1.5; color: var(--skill); }
.node-chip { display: inline-block; margin-right: 6px; padding: 1px 8px; border-radius: 99px; vertical-align: 1px;
  background: var(--skill); color: var(--surface);
  font: 700 12px/1.5 'Hind Siliguri', 'Inter Tight', system-ui, sans-serif; }
.node-wrap:has(.locked) .node-name { color: var(--faint); }
.node-wrap:has(.locked) .node-chip { background: var(--faint); }
.shake { animation: shake .35s; }
@keyframes shake { 25% { transform: translateX(-6px); } 75% { transform: translateX(6px); } }
@media (prefers-reduced-motion: no-preference) {
  @keyframes bob { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-6px); } }
  .node-tip { animation: bob 1.6s ease-in-out infinite; }
}
.reward { display: flex; gap: 16px; align-items: center; margin: 16px 0; padding: 16px; border-radius: 16px; text-decoration: none; color: var(--ink);
  background: var(--surface); border: 2px solid var(--status-in); box-shadow: 0 4px 0 var(--status-in); text-align: left; }
.reward strong { display: block; color: var(--charcoal); }
.reward span span { font-size: 14px; color: var(--muted); }
.reward-badge { display: grid; place-items: center; width: 56px; height: 56px; flex-shrink: 0; border-radius: 50%; background: var(--status-in); color: var(--surface); }
.reward-badge .ic { fill: var(--surface); }
.stage-next { display: flex; align-items: center; justify-content: center; gap: 8px; margin: 24px 0; padding: 16px; border: 2px dashed var(--rule); border-radius: 16px; color: var(--faint); font-weight: 600; text-align: center; }
.map-foot { border-top: 1px solid var(--rule); padding-top: 16px; font-size: 13px; color: var(--muted); }
.map-foot summary { cursor: pointer; min-height: 44px; display: flex; align-items: center; }
.map-foot details .link-btn { display: flex; }

/* bottom sheets (level card + answer feedback) */
.scrim { position: fixed; inset: 0; background: color-mix(in srgb, var(--charcoal) 45%, transparent); z-index: 90; }
.sheet { position: fixed; left: 0; right: 0; bottom: 0; z-index: 100; padding: 24px max(16px, calc((100vw - 640px) / 2)) calc(24px + env(safe-area-inset-bottom));
  background: var(--surface); border-radius: 16px 16px 0 0; box-shadow: var(--shadow-lg); }
@media (prefers-reduced-motion: no-preference) {
  .sheet { animation: rise .22s ease-out; }
  @keyframes rise { from { transform: translateY(40%); opacity: .4; } to { transform: none; opacity: 1; } }
}
.level-sheet h2 { margin: 8px 0; font-size: 24px; color: var(--charcoal); }
.level-sheet p { margin: 0 0 8px; color: var(--muted); }
.level-sheet .micro { color: var(--coral); }
.sheet-meta { font-weight: 600; margin-bottom: 16px !important; }

/* The trap collection: earned cards, plus the ones still out there. */
.trap-sheet { max-height: 82vh; overflow-y: auto; }
.trap-sheet h2 { margin: 8px 0; font-size: 24px; color: var(--charcoal); }
.trap-sheet > p { margin: 0 0 16px; color: var(--muted); }
.trap-sheet .micro { color: var(--coral); }
.sheet-x { position: absolute; top: 12px; right: 12px; }
.trap-list { margin: 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: 12px; }
.trap-card { display: flex; gap: 12px; padding: 16px; border-radius: 12px; border: 2px solid var(--rule);
  border-left: 6px solid var(--skill); background: var(--surface); }
.trap-card b { display: block; color: var(--charcoal); font-size: 15px; }
.trap-card .trap-text { display: block; margin-top: 4px; font-size: 14px; line-height: 1.5; color: var(--muted); }
.trap-card .goal-skill { margin-top: 8px; }
.trap-n { flex: none; width: 32px; height: 32px; border-radius: 50%; display: grid; place-items: center;
  background: var(--skill); color: var(--surface); font: 700 15px/1 'Hind Siliguri', 'Inter Tight', system-ui, sans-serif; }
.trap-card.hidden-trap { border-left-color: var(--rule); background: var(--rule-soft); }
.trap-card.hidden-trap .trap-n { background: var(--rule); color: var(--faint); }
.trap-card.hidden-trap b { color: var(--muted); font-weight: 600; }
.trap-card.hidden-trap b .ic { width: 14px; height: 14px; vertical-align: -2px; }
/* The way into the collection. It is deliberately NOT a `.stat`: as a fourth
   pill in that row it was indistinguishable from the three read-outs, and the
   row's own shape said "score", not "press me". Coral edge + a raised edge in
   the `.btn3d` idiom + a chevron, so all three of this system's pressable
   signals are on it. */
.trap-open { display: flex; align-items: center; gap: 12px; width: 100%; margin: 12px 0 0; padding: 12px 16px;
  text-align: left; border: 2px solid var(--coral); border-radius: 12px; background: var(--coral-soft);
  font: inherit; color: var(--charcoal); cursor: pointer;
  box-shadow: 0 3px 0 var(--coral); transition: transform .08s, box-shadow .08s; }
.trap-open:active { transform: translateY(3px); box-shadow: 0 0 0 var(--coral); }
.trap-open-ic { flex: none; display: grid; place-items: center; width: 32px; height: 32px; border-radius: 50%; background: var(--surface); }
.trap-open-ic .ic { width: 18px; height: 18px; color: var(--coral-dark); fill: var(--coral-soft); }
.trap-open-text { flex: 1; font-size: 15px; }
.trap-open-text b { font-size: 17px; }
.trap-open-rest { color: var(--muted); }
.trap-open-go { flex: none; display: inline-flex; align-items: center; gap: 2px; font-size: 14px; font-weight: 700; color: var(--coral-dark); }
.trap-open-go .ic { width: 16px; height: 16px; }
@media (max-width: 380px) { .trap-open-go span { display: none; } .trap-open-rest { display: none; } }

/* The done-screen moment: "a new card is in your collection". Same fix as
   `.trap-open` — it had a `--shadow-sm` card's resting shadow and a muted grey
   CTA line, so the one pressable thing on the screen bar the primary button
   read as a notice. */
.trap-caught { display: flex; align-items: center; gap: 12px; width: 100%; max-width: 420px; margin: 8px 0 16px; padding: 16px;
  text-align: left; border: 2px solid var(--rule); border-left: 6px solid var(--skill); border-radius: 12px; background: var(--surface);
  font: inherit; color: var(--ink); cursor: pointer;
  box-shadow: 0 3px 0 var(--rule); transition: transform .08s, box-shadow .08s; }
.trap-caught:active { transform: translateY(3px); box-shadow: 0 0 0 var(--rule); }
.trap-caught .trap-n { width: 40px; height: 40px; font-size: 18px; }
/* Bangla in the monospace micro-label face spreads its letters apart — that
   face is for Latin labels like STAGE 1 — so this one takes the body font. */
.trap-caught .micro { display: block; color: var(--coral); font-family: inherit; letter-spacing: 0; font-size: 12px; font-weight: 700; }
.trap-caught b { display: block; margin: 2px 0 4px; font-size: 16px; color: var(--charcoal); }
.trap-caught-cta { display: inline-flex; align-items: center; gap: 4px; font-size: 14px; font-weight: 700; color: var(--coral-dark); }
.trap-caught-cta .ic { width: 15px; height: 15px; }
.trap-caught-cta .ic:first-child { fill: var(--coral-soft); }

/* ── Lesson ─────────────────────────────────────────────────────────────── */
.lesson-bar { position: sticky; top: 0; z-index: 50; display: flex; align-items: center; gap: 16px;
  padding: calc(16px + env(safe-area-inset-top)) 0 8px; background: var(--paper); }
.icon-btn { width: 44px; height: 44px; display: grid; place-items: center; border: 0; border-radius: 12px; background: none; color: var(--faint); }
.icon-btn:hover { color: var(--charcoal); background: var(--rule-soft); }
.bar { flex: 1; height: 16px; border-radius: 99px; background: var(--rule); overflow: hidden; }
.bar span { display: block; height: 100%; border-radius: 99px; background: var(--coral); transition: width .45s cubic-bezier(.3, 1.4, .5, 1);
  box-shadow: inset 0 -4px 0 color-mix(in srgb, var(--coral-dark) 50%, transparent); }
.combo { display: inline-flex; align-items: center; gap: 2px; color: var(--coral); font-weight: 700; opacity: 0; transform: scale(.6); transition: opacity .2s, transform .2s; }
.combo.on { opacity: 1; transform: scale(1); }
.combo .ic { width: 20px; height: 20px; fill: var(--coral-soft); }
.lesson-body { padding-top: 8px; }
/* Which skill this lesson is, on every screen of it — the map says so too, but
   someone deep in a lesson should not have to go back to find out. */
.lesson-skill { display: inline-flex; align-items: center; gap: 6px; margin-top: 8px; color: var(--skill);
  font-family: 'JetBrains Mono', ui-monospace, monospace; font-size: 11px; font-weight: 600; letter-spacing: .08em; }
.lesson-skill .ic { width: 14px; height: 14px; }
.retry-tag { display: inline-block; margin-top: 8px; padding: 4px 12px; border-radius: 99px; background: var(--warn-bg); color: var(--warn-text); font-size: 13px; font-weight: 600; }
.prompt { margin: 0 0 16px; font-size: 22px; line-height: 1.4; font-weight: 700; color: var(--charcoal); outline: none; }
.prompt:lang(bn) { font-size: 21px; }
.hint-line { margin: -8px 0 16px; color: var(--muted); font-size: 14px; }
.lesson-foot { position: fixed; left: 0; right: 0; bottom: 0; z-index: 60; padding: 16px max(16px, calc((100vw - 640px) / 2)) calc(16px + env(safe-area-inset-bottom));
  background: var(--paper); border-top: 2px solid var(--rule); }
.lesson-foot p { margin: 8px 0; }

/* recordings */
.clip { display: flex; align-items: center; gap: 16px; margin: 8px 0 24px; }
.speaker { width: 72px; height: 72px; border-radius: 16px; border: 0; display: grid; place-items: center; background: var(--coral); color: var(--surface); box-shadow: 0 4px 0 var(--coral-dark); }
.speaker .ic { width: 36px; height: 36px; }
.speaker:active { transform: translateY(4px); box-shadow: none; }
.slow { height: 48px; padding: 0 16px; border-radius: 12px; border: 2px solid var(--rule); background: var(--surface); color: var(--charcoal); font-weight: 600; box-shadow: 0 3px 0 var(--rule); }
.clip-label { color: var(--muted); font-size: 14px; }
.clip.missing { display: block; padding: 16px; border: 2px dashed var(--rule); border-radius: 12px; }
.transcript p { margin: 8px 0 0; }

/* cards on talk screens */
.card { padding: 24px; border-radius: 16px; background: var(--surface); border: 2px solid var(--rule); }
.card p { margin: 8px 0 0; font-size: 18px; line-height: 1.6; }
.card-kick { font-weight: 700; color: var(--coral); font-size: 14px; letter-spacing: .02em; }
.card.trap { position: relative; border-color: var(--status-in); background: color-mix(in srgb, var(--status-in) 12%, var(--surface)); padding-left: 72px; }
.card.trap .card-kick { color: color-mix(in srgb, var(--status-in) 70%, var(--ink)); }
.trap-ic { position: absolute; left: 24px; top: 24px; width: 32px; height: 32px; border-radius: 50%; display: grid; place-items: center;
  background: var(--status-in); color: var(--surface); font-weight: 800; }
.card.fact { border-color: var(--charcoal); }
.card.fact .card-kick { color: var(--charcoal); }
.card.skills { display: grid; gap: 8px; padding: 8px; }
.skill-tile { display: flex; align-items: center; gap: 16px; padding: 8px; }
.skill-tile b { display: block; color: var(--charcoal); }
.skill-tile span span { font-size: 14px; color: var(--muted); }
/* How big the paper is — sits under what it asks of you, quieter than both.
   Its own line: the tile already reads name / task, and a third inline run
   would collide with the task on a phone. */
.skill-shape { display: block; margin-top: 2px; font-size: 13px; color: var(--faint); }
.skill-ic { width: 48px; height: 48px; flex-shrink: 0; display: grid; place-items: center; border-radius: 12px; background: var(--coral-soft); color: var(--coral); }
.skill-time { margin-left: auto; font-size: 13px; color: var(--muted); white-space: nowrap; }
.card.parts { display: flex; flex-wrap: wrap; gap: 8px; align-items: flex-start; }
.part { display: flex; flex-direction: column; gap: 4px; }
.part b { padding: 8px 16px; border-radius: 12px; background: var(--rule-soft); font-size: 20px; }
.part.tagged b { background: var(--coral-soft); color: var(--coral-dark); }
.part small { font-size: 13px; color: var(--muted); text-align: center; }
.card.recap ol { margin: 8px 0 0; padding-left: 24px; font-size: 18px; }

/* answers */
.options { display: grid; gap: 16px; }
.option-wrap { position: relative; }
.option { width: 100%; display: flex; align-items: center; gap: 16px; min-height: 64px; padding: 12px 16px; text-align: left;
  border: 2px solid var(--rule); border-radius: 12px; background: var(--surface); box-shadow: 0 4px 0 var(--rule); font-size: 17px; line-height: 1.5;
  transition: transform .08s, box-shadow .08s, border-color .1s, background .1s; }
.option-wrap:has(.mini-play) .option { padding-right: 72px; }
.option:active:not(:disabled) { transform: translateY(4px); box-shadow: 0 0 0 var(--rule); }
.option .key { width: 32px; height: 32px; flex-shrink: 0; display: grid; place-items: center; border-radius: 8px; border: 2px solid var(--rule); color: var(--faint); font-size: 14px; font-weight: 700; }
.option[aria-pressed="true"], .option.picked { border-color: var(--coral); background: var(--coral-soft); box-shadow: 0 4px 0 var(--coral); }
.option[aria-pressed="true"] .key { border-color: var(--coral); color: var(--coral); }
.option.right { border-color: var(--ok); background: var(--ok-bg); box-shadow: 0 4px 0 var(--ok); }
.option.wrong { border-color: var(--bad); background: var(--bad-bg); box-shadow: 0 4px 0 var(--bad); }
.mini-play { position: absolute; right: 12px; top: 50%; transform: translateY(-60%); width: 44px; height: 44px; border-radius: 50%; border: 0; display: grid; place-items: center; background: var(--coral); color: var(--surface); }
.mini-play .ic { width: 18px; height: 18px; fill: var(--surface); }
.passage { padding: 16px; border-radius: 12px; background: var(--surface); border: 2px solid var(--rule); margin-bottom: 24px; font-size: 18px; line-height: 1.9; }
.passage-title { display: block; margin-bottom: 8px; color: var(--charcoal); }
/* ── Plan: a drawn map for labelling questions ──────────────────────────── */
.plan { margin: 0 0 24px; padding: 16px; border: 2px solid var(--rule); border-radius: 12px; background: var(--surface); }
.plan-title { display: block; margin-bottom: 12px; font-weight: 700; color: var(--charcoal); }
/* Capped by HEIGHT, not width: a tall plan (3 wide, 4 deep) at full width runs
   past the fold on a phone, and half a map answers nothing. The second term
   keeps the whole thing inside 44vh by shrinking the width instead. */
.plan-grid { position: relative; display: grid; margin: 0 auto;
  width: min(100%, 340px, calc(38vh * var(--cols) / var(--rows)));
  grid-template-columns: repeat(var(--cols), 1fr);
  /* Rows must be 1fr too, not implicit/auto: the SVG backdrop is drawn in grid
     units, so auto rows sizing to their content silently slide the road and the
     arrow away from the cells they belong to. */
  grid-template-rows: repeat(var(--rows), 1fr); place-items: center; }
.plan-art { position: absolute; inset: 0; width: 100%; height: 100%; }
.pl-road { fill: var(--rule-soft); stroke: var(--rule); stroke-width: .012; }
.pl-water { fill: color-mix(in srgb, var(--status-done) 16%, var(--surface)); stroke: var(--rule); stroke-width: .012; }
.pl-feat { fill: var(--rule-soft); stroke: var(--rule); stroke-width: .012; }
.pl-arrow { fill: none; stroke: var(--coral); stroke-width: .06; stroke-linecap: round; stroke-linejoin: round; }
/* Overrides .seg's inline flow; the state colours on .seg still apply. */
.pl-spot { position: relative; display: flex; align-items: center; justify-content: center;
  width: 84%; min-height: 44px; padding: 4px; margin: 0; font-weight: 700; font-size: 18px; line-height: 1.2;
  background: var(--surface); box-shadow: var(--shadow-sm); }
.pl-label { position: relative; font-size: 12px; color: var(--muted); text-align: center; }
/* A room on the route, not an answer: same footprint as a spot so it reads as a
   place, dashed and muted so it is plainly not one of the lettered choices. */
.pl-room { position: relative; display: flex; align-items: center; justify-content: center;
  width: 84%; min-height: 36px; padding: 4px; border: 2px dashed var(--rule); border-radius: 8px;
  background: var(--paper); font-size: 12px; color: var(--muted); text-align: center; line-height: 1.2; }
.pl-entry { align-self: end; font-weight: 600; color: var(--coral-dark); }
@media (max-width: 420px) { .pl-spot { font-size: 16px; } .pl-label { font-size: 11px; } }

/* ── Chart: a drawn Task 1 visual (bar, line, table) ────────────────────── */
.chart { margin: 0 0 24px; padding: 16px; border: 2px solid var(--rule); border-radius: 12px; background: var(--surface); }
.chart-title { display: block; margin-bottom: 12px; font-weight: 700; color: var(--charcoal); }
/* Two columns (y labels | plot) and three rows (plot | x labels | x title).
   The y-axis title spans the plot's full height in its own leading column. */
.ch-frame { display: grid; grid-template-columns: auto auto 1fr; grid-template-rows: 1fr auto auto;
  align-items: center; column-gap: 6px; }
.ch-axis-y { grid-area: 1/1; writing-mode: vertical-rl; rotate: 180deg; justify-self: center;
  font-size: 11px; color: var(--muted); text-align: center; }
/* Ticks are absolutely placed from the bottom by the same fraction the gridline
   uses, so label and line cannot drift apart at any width. */
.ch-ticks { grid-area: 1/2; position: relative; align-self: stretch; min-width: 18px; }
.ch-tick { position: absolute; right: 0; transform: translateY(50%); font-size: 11px; color: var(--muted); line-height: 1; }
/* Pinned to the SVG's own viewBox ratio so it scales uniformly — a marker drawn
   as a circle has to stay a circle. */
.ch-plot { grid-area: 1/3; aspect-ratio: 80 / 50; }
.ch-plot svg { display: block; width: 100%; height: 100%; overflow: visible; }
.ch-grid { stroke: var(--rule); stroke-width: .15; }
.ch-axis { fill: none; stroke: var(--faint); stroke-width: .3; }
.ch-bar { stroke: none; }
.ch-line { fill: none; stroke-width: .6; stroke-linecap: round; stroke-linejoin: round; }
.ch-mark { fill: var(--surface); stroke-width: .45; }
.ch-cats { grid-area: 2/3; display: grid; grid-template-columns: repeat(var(--cats), 1fr);
  justify-items: center; padding-top: 4px; }
.ch-axis-x { grid-area: 3/3; justify-self: center; padding-top: 2px; font-size: 11px; color: var(--muted); }
.ch-lab { font-size: 12px; color: var(--charcoal); text-align: center; line-height: 1.2; }
/* A tappable label is a real button, so it needs a real target: 44px tall, and
   it overrides .seg's inline flow the way .pl-spot does. */
.ch-tap { display: flex; align-items: center; justify-content: center; min-height: 44px;
  min-width: 40px; padding: 4px 6px; margin: 0; font-weight: 700; font-size: 14px; background: var(--surface); }
.ch-legend { display: flex; flex-wrap: wrap; gap: 4px 16px; margin: 12px 0 0; padding: 0; list-style: none; }
.ch-legend li { display: flex; align-items: center; gap: 6px; }
.ch-key { flex: none; display: block; height: 12px; }
.ch-key svg { display: block; height: 12px; width: auto; }
.ch-table-wrap { overflow-x: auto; }
.ch-table { width: 100%; border-collapse: collapse; font-size: 14px; }
.ch-table th, .ch-table td { padding: 6px 8px; border: 1px solid var(--rule); text-align: right; }
.ch-table thead th, .ch-table tbody th { background: var(--rule-soft); font-weight: 700; color: var(--charcoal); }
.ch-table tbody th { text-align: left; }
/* A table cell's button fills its cell, so the whole number is the target
   rather than a pill floating inside it. */
.ch-table td .ch-tap { width: 100%; min-height: 40px; }
@media (max-width: 420px) {
  .ch-frame { column-gap: 4px; }
  .ch-lab { font-size: 11px; }
  .ch-tap { font-size: 13px; min-width: 32px; }
}

.seg { display: inline; padding: 4px 8px; margin: 2px 0; border: 2px solid var(--rule); border-radius: 8px; background: var(--rule-soft); line-height: 2; font-size: 17px; text-align: left; box-decoration-break: clone; -webkit-box-decoration-break: clone; }
.seg[aria-pressed="true"], .seg.picked { border-color: var(--coral); background: var(--coral-soft); }
.seg.right, .seg.right[aria-pressed="true"] { border-color: var(--ok); background: var(--ok-bg); box-shadow: 0 3px 0 var(--ok); }
.seg.wrong, .seg.wrong[aria-pressed="true"] { border-color: var(--bad); background: var(--bad-bg); box-shadow: 0 3px 0 var(--bad); }
.gap-line { display: block; font-size: 20px; line-height: 2.2; }
.gap { width: 8em; max-width: 100%; margin: 0 8px; padding: 4px 8px; border: 0; border-bottom: 3px solid var(--coral); background: var(--surface); border-radius: 8px 8px 0 0; font-size: 20px; text-align: center; }
.gap:focus { outline: none; background: var(--coral-soft); }
.gap.right { border-color: var(--ok); background: var(--ok-bg); }
.gap.wrong { border-color: var(--bad); background: var(--bad-bg); }
.build { min-height: 72px; display: flex; flex-wrap: wrap; align-content: flex-start; gap: 8px; padding: 8px 0 16px; margin-bottom: 24px;
  border-bottom: 2px solid var(--rule); }
.build:empty::before { content: 'শব্দে tap করে sentence বানান'; color: var(--faint); padding: 8px; }
.build.right .tile { border-color: var(--ok); }
.build.wrong .tile { border-color: var(--bad); }
.bank { display: flex; flex-wrap: wrap; gap: 8px; justify-content: center; }
.tile { min-height: 48px; padding: 8px 16px; border: 2px solid var(--rule); border-radius: 12px; background: var(--surface); box-shadow: 0 3px 0 var(--rule); font-size: 17px; }
.tile.used { color: transparent; background: var(--rule-soft); box-shadow: none; }
.match { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.mcol { display: grid; gap: 16px; align-content: start; }
.mtile { min-height: 64px; padding: 8px 12px; border: 2px solid var(--rule); border-radius: 12px; background: var(--surface); box-shadow: 0 4px 0 var(--rule); font-size: 16px; line-height: 1.4; }
.mtile.picked { border-color: var(--coral); background: var(--coral-soft); box-shadow: 0 4px 0 var(--coral); }
.mtile.matched { border-color: var(--ok); background: var(--ok-bg); color: var(--ok-text); box-shadow: none; opacity: .55; }
.mtile.wrong { border-color: var(--bad); background: var(--bad-bg); animation: shake .35s; }
.write-box { display: block; width: 100%; padding: 16px; border: 2px solid var(--rule); border-radius: 12px; background: var(--surface); font-size: 18px; resize: vertical; }
.write-box:focus { outline: none; border-color: var(--coral); }
.checklist { list-style: none; padding: 0; margin: 16px 0; display: grid; gap: 8px; }
.checklist li { display: flex; align-items: center; gap: 8px; color: var(--muted); }
.checklist li .ic { width: 24px; height: 24px; padding: 3px; border-radius: 50%; background: var(--rule); color: var(--surface); transition: background .2s; }
.checklist li.ok { color: var(--ok-text); font-weight: 600; }
.checklist li.ok .ic { background: var(--ok); }
.checklist.tickable label { display: flex; align-items: center; gap: 12px; min-height: 44px; }
.checklist.tickable input { width: 24px; height: 24px; accent-color: var(--ok); }
.rec { margin: 16px 0; }
.rec-btn { display: inline-flex; align-items: center; gap: 8px; min-height: 52px; padding: 8px 24px; border-radius: 99px; border: 2px solid var(--coral); background: var(--surface); color: var(--coral); font-weight: 700; }
.rec-btn.on { background: var(--coral); color: var(--surface); }
@media (prefers-reduced-motion: no-preference) {
  .rec-btn.on { animation: pulse 1.4s infinite; }
  @keyframes pulse { 0% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--coral) 50%, transparent); } 70% { box-shadow: 0 0 0 16px transparent; } }
}

/* answer feedback */
.feedback.right, .feedback.model { background: var(--ok-bg); }
.feedback.wrong { background: var(--bad-bg); }
.sheet-head { display: flex; align-items: center; gap: 12px; margin-bottom: 8px; }
.sheet-head strong { font-size: 22px; }
.feedback.right strong, .feedback.model strong { color: var(--ok-text); }
.feedback.wrong strong { color: var(--bad-text); }
.sheet-ic { width: 40px; height: 40px; border-radius: 50%; display: grid; place-items: center; background: var(--surface); }
.feedback.right .sheet-ic, .feedback.model .sheet-ic { color: var(--ok); }
.feedback.wrong .sheet-ic { color: var(--bad); }
.sheet-answer { margin: 0 0 8px; font-size: 18px; font-weight: 700; color: var(--ink); }
.sheet-kick { margin: 0; font-size: 14px; color: var(--muted); }
.sheet-why { margin: 0 0 16px; color: var(--ink); line-height: 1.6; }
.sheet-transcript { margin: 0 0 16px; font-size: 14px; }
.sheet-transcript summary { cursor: pointer; min-height: 32px; color: var(--muted); }
.feedback .btn3d { margin-top: 8px; }

/* ── Level complete ─────────────────────────────────────────────────────── */
/* SECTION.done — the lesson-completion screen. The element selector is load-
   bearing: `.done` alone also matches `button.node.done` on the map and
   `p.save-note.done`, at equal specificity but later in the file, so it won
   and gave every finished map node `display:flex` + `padding-top:48px` —
   which is why their icon sat in the bottom third of the circle. Never
   reintroduce a bare `.done` rule here. */
section.done { position: relative; display: flex; flex-direction: column; align-items: center; text-align: center; padding-top: 48px; }
.confetti { position: fixed; inset: 0; width: 100vw; height: 100vh; pointer-events: none; z-index: 200; }
.done h1 { margin: 24px 0 8px; font: 700 36px/1.2 'Hind Siliguri', 'Literata', serif; color: var(--charcoal); outline: none; }
.done-stars { display: flex; gap: 8px; margin: 8px 0 16px; }
.done-stars .ic { width: 48px; height: 48px; color: var(--rule); fill: var(--rule); }
.done-stars .on .ic { color: var(--status-in); fill: var(--status-in); }
@media (prefers-reduced-motion: no-preference) {
  .done-stars .on { animation: pop .5s cubic-bezier(.3, 1.6, .5, 1) both; animation-delay: var(--d); }
  @keyframes pop { from { transform: scale(0) rotate(-40deg); opacity: 0; } to { transform: none; opacity: 1; } }
}
.can-do { max-width: 480px; font-size: 18px; line-height: 1.6; margin: 0 0 24px; }
.tiles { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; width: 100%; margin-bottom: 16px; }
.tile-stat { border-radius: 12px; border: 2px solid; overflow: hidden; }
.tile-stat span { display: block; padding: 4px; font-size: 12px; font-weight: 700; color: var(--surface); }
.tile-stat b { display: block; padding: 8px 4px; font-size: 20px; background: var(--surface); }
.tile-stat.xp { border-color: var(--status-in); } .tile-stat.xp span { background: var(--status-in); } .tile-stat.xp b { color: color-mix(in srgb, var(--status-in) 70%, var(--ink)); }
.tile-stat.acc { border-color: var(--ok); } .tile-stat.acc span { background: var(--ok); } .tile-stat.acc b { color: var(--ok-text); }
.tile-stat.time { border-color: var(--charcoal); } .tile-stat.time span { background: var(--charcoal); } .tile-stat.time b { color: var(--charcoal); }
.streak-line { display: flex; align-items: center; gap: 8px; font-weight: 700; color: var(--coral); }
.streak-line .ic { fill: var(--coral-soft); }
.useful { margin: 8px 0 16px; }
.useful p { margin: 0 0 8px; }
.useful-row { display: flex; gap: 8px; flex-wrap: wrap; justify-content: center; }
.chip { min-height: 44px; padding: 8px 16px; border-radius: 99px; border: 2px solid var(--rule); background: var(--surface); font-weight: 600; }
.chip.on { border-color: var(--coral); background: var(--coral-soft); color: var(--coral-dark); }
/* The done screen's actions are a foot bar, like the lesson's. Measured at
   390×664 — the smallest real phone — the SHORTEST done screen there is runs
   998px, so "পরের level" sat at y=718 with the page unscrolled: you finish a
   level and the one thing you are meant to do next is off-screen. Trimming
   cannot fix that (delete the orb entirely and it is still 870), and a
   stage-final screen adds goals, a reward and the usefulness prompt on top. */
.done-actions { position: fixed; left: 0; right: 0; display: grid; gap: 12px; width: 100%; margin: 0; z-index: 60;
  padding: 12px max(16px, calc((100vw - 640px) / 2)) calc(12px + env(safe-area-inset-bottom));
  background: var(--paper); border-top: 2px solid var(--rule); }

/* Bottom-anchored layers hang off the DYNAMIC viewport, not the layout one.
   `bottom: 0` resolves against the layout viewport, whose height on a phone is
   the height with the browser's URL bar HIDDEN — so while the bar is showing,
   the bottom of a fixed element is behind it, and the only way to reach the
   button is to scroll, which collapses the bar. That is exactly the report:
   "comes out of the screen and I have to scroll down to click", on both the
   level sheet and the done screen. Pinning the TOP edge at 100dvh and pulling
   the element up by its own height puts its bottom on the visible bottom in
   both states, with no wrapper element and no JS. */
.sheet, .lesson-foot, .done-actions { top: 100vh; bottom: auto; translate: 0 -100%; }
@supports (top: 100dvh) { .sheet, .lesson-foot, .done-actions { top: 100dvh; } }
/* Nothing may be taller than the viewport it is pinned to, or its own top is
   what goes off-screen instead — with no way to scroll a fixed element. */
.sheet, .done-actions { max-height: 100vh; overflow-y: auto; }
@supports (max-height: 100dvh) { .sheet, .done-actions { max-height: 100dvh; } }
/* The collection keeps its shorter cap — it is a long list, and a sheet that
   fills the screen edge to edge stops reading as a sheet. Restated after the
   rule above because that one is later in the file and would otherwise win. */
.trap-sheet { max-height: 82vh; }
@supports (max-height: 82dvh) { .trap-sheet { max-height: 82dvh; } }

dialog { max-width: 400px; width: calc(100% - 32px); border: 0; border-radius: 16px; padding: 24px; background: var(--surface); color: var(--ink); box-shadow: var(--shadow-lg); }
dialog::backdrop { background: color-mix(in srgb, var(--charcoal) 55%, transparent); }
dialog h2 { margin: 0 0 8px; font-size: 20px; color: var(--charcoal); }
.dialog-actions { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin-top: 24px; }

@media (max-width: 420px) {
  .nx-orb { --size: 48px; }
  .nx-bubble { font-size: 16px; padding: 12px 16px; }
  .prompt { font-size: 20px; }
  .path { gap: 24px; }
  .node { width: 72px; height: 72px; }
  .node-wrap { transform: translateX(calc(var(--x) * .7)); }
  .speaker { width: 64px; height: 64px; }
  .match { gap: 8px; }
  .mcol { gap: 8px; }
  .mtile { font-size: 15px; }
}

/* Save-your-progress offer on the map. Guest progress lives in one browser, so
   this says it plainly, once, and only when the learner has something to lose.
   Deliberately not red: nothing has gone wrong and they did nothing wrong. */
.save-card {
  margin: 24px 0 8px; padding: 16px; border-radius: 12px;
  background: color-mix(in srgb, var(--coral) 7%, var(--paper));
  border: 1px solid color-mix(in srgb, var(--coral) 24%, transparent);
}
.save-card p { margin: 0 0 8px; font-size: 14px; line-height: 1.55; color: var(--ink); }
.save-card p:last-of-type { color: color-mix(in srgb, var(--ink) 78%, var(--paper)); }
.save-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 16px; margin-top: 16px; }
/* These two are <a>, not <button>, so they inherit the page's link underline. */
.save-actions .btn3d { text-decoration: none; }
.save-actions .link-btn { text-decoration: underline; text-underline-offset: 2px; }
.save-note {
  display: flex; align-items: center; gap: 8px; margin: 24px 0 8px;
  font-size: 13px; line-height: 1.5; color: color-mix(in srgb, var(--ink) 70%, var(--paper));
}
.save-note .ic { width: 16px; height: 16px; flex: 0 0 auto; }
.save-note.done .ic { color: var(--coral); }

/* ── Reporting a problem from inside a lesson ─────────────────────────────── */
/* Small and low on the answer sheet — it must never compete with Continue. */
.report-link { display: flex; margin: 0 0 8px auto; min-height: 36px; padding: 4px 0; font-size: 13px; }
.report-link .ic { width: 15px; height: 15px; }
/* It opens OVER the answer sheet, so both layers sit above it. */
.report-scrim { z-index: 105; }
.report-sheet { z-index: 110; }
.report-sheet h2 { display: flex; align-items: center; gap: 8px; margin: 0 0 16px; font-size: 20px; color: var(--charcoal); }
.report-sheet h2 .ic { color: var(--status-done); }
.report-reasons { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 16px; }
.report-reasons .chip.on { border-color: var(--coral); background: var(--coral-soft); color: var(--charcoal); }
.report-field { display: flex; flex-direction: column; gap: 4px; margin-bottom: 12px; font-size: 14px; color: var(--muted); }
.report-field textarea, .report-field input { width: 100%; padding: 12px; border: 2px solid var(--rule); border-radius: 12px;
  background: var(--surface); color: var(--ink); font: inherit; }
.report-field textarea:focus, .report-field input:focus { border-color: var(--coral); outline: none; }
.report-error { margin: 0 0 12px; color: var(--warn-text); font-size: 14px; font-weight: 600; }
.report-actions { display: flex; gap: 12px; }
.report-actions .btn3d { flex: 1; }

/* ── Coming back: today's goal and the daily review (2026-10-01) ─────────────
   The goal row is a READ-OUT with one small text control, like the stats; the
   review card is the pressable thing, so it borrows `.trap-open`'s three press
   signals (coloured edge, raised edge, chevron). A review that is done or not
   yet due is plain text — never a button that does nothing. */
.today { display: grid; gap: 8px; margin: 12px 0 0; }
.today-goal { display: grid; grid-template-columns: 1fr auto; align-items: center; gap: 4px 12px; padding: 12px 16px;
  border: 2px solid var(--rule); border-radius: 12px; background: var(--surface); }
.today-goal.met { border-color: var(--ok); background: var(--ok-bg); }
.today-label { font-weight: 700; color: var(--charcoal); display: inline-flex; align-items: center; gap: 4px; }
.today-goal.met .today-label { color: var(--ok-text); }
.today-label .ic { width: 16px; height: 16px; }
.today-count { justify-self: end; font-size: 14px; color: var(--muted); }
.today-count b { font-size: 16px; color: var(--charcoal); }
.today-goal .goal-bar { grid-column: 1 / -1; }
.goal-change { grid-column: 1 / -1; justify-self: start; padding: 0; font-size: 14px; }
.goal-bar { height: 8px; border-radius: 99px; background: var(--rule); overflow: hidden; }
.goal-bar span { display: block; height: 100%; border-radius: 99px; background: var(--coral); transition: width .6s ease; }
.met .goal-bar span { background: var(--ok); }

.review-card { display: flex; align-items: center; gap: 12px; width: 100%; margin: 0; padding: 12px 16px;
  text-align: left; border: 2px solid var(--rule); border-radius: 12px; background: var(--surface); font: inherit; color: var(--ink); }
.review-card > span:not(.review-ic):not(.trap-open-go) { flex: 1; display: grid; gap: 2px; }
.review-card b { font-size: 16px; color: var(--charcoal); }
.review-card b + span { font-size: 14px; color: var(--muted); }
.review-card > .ic { flex: none; width: 24px; height: 24px; color: var(--muted); }
.review-card.ready { border-color: var(--charcoal); cursor: pointer; box-shadow: 0 3px 0 var(--charcoal); transition: transform .08s, box-shadow .08s; }
.review-card.ready:active { transform: translateY(3px); box-shadow: 0 0 0 var(--charcoal); }
.review-card.done > .ic { color: var(--ok-text); }
.review-ic { flex: none; display: grid; place-items: center; width: 32px; height: 32px; border-radius: 50%; background: var(--charcoal); }
.review-ic .ic { width: 18px; height: 18px; color: var(--surface); }
.review-card.ready .trap-open-go { color: var(--charcoal); }
@media (max-width: 380px) { .review-card .trap-open-go span { display: none; } }

/* Done screen: where today stands, and — once — the commitment. */
.goal-done { width: 100%; max-width: 420px; margin: 0 0 16px; display: grid; gap: 8px; }
.goal-line { margin: 0; display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: 4px 8px; }
.goal-done.met .goal-line b { color: var(--ok-text); display: inline-flex; align-items: center; gap: 4px; }
.goal-line .ic { width: 16px; height: 16px; }
.goal-pick { display: grid; gap: 8px; margin-top: 8px; text-align: left; }
.goal-ask { margin: 0; line-height: 1.6; }
.goal-options { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; }
.goal-option { display: grid; gap: 2px; min-height: 64px; padding: 8px; border: 2px solid var(--rule); border-radius: 12px;
  background: var(--surface); font: inherit; color: var(--ink); text-align: center; cursor: pointer; }
.goal-option b { font-size: 15px; color: var(--charcoal); }
.goal-option span { font-size: 12px; color: var(--muted); line-height: 1.4; }
.goal-option.on { border-color: var(--coral); background: var(--coral-soft); }
.goal-option.on b { color: var(--coral-dark); }
.goal-saved { margin: 0; display: flex; align-items: center; gap: 4px; font-weight: 700; color: var(--ok-text); }
.goal-saved .ic { width: 16px; height: 16px; }
.goal-sheet h2 { margin: 8px 0 16px; font-size: 24px; color: var(--charcoal); }
.goal-sheet .micro { color: var(--coral); }
.goal-sheet .small { margin: 16px 0 0; color: var(--muted); }

.review-traps { list-style: none; margin: 0 0 16px; padding: 0; display: flex; flex-wrap: wrap; justify-content: center; gap: 8px; max-width: 480px; }
.review-traps li { display: inline-flex; align-items: center; gap: 4px; padding: 4px 12px; border-radius: 99px;
  border: 2px solid var(--rule); background: var(--surface); font-size: 13px; font-weight: 600; }
.review-traps .ic { width: 14px; height: 14px; color: var(--coral-dark); }

/* Worked example → hint → alone (§3 → The learning curve). The hint is a
   written nudge under the question; it reads as help, not as the question. */
.hint-line.hint { display: flex; align-items: flex-start; gap: 8px; margin: -8px 0 16px; padding: 8px 12px; border-radius: 8px;
  background: var(--coral-soft); color: var(--charcoal); font-size: 14px; line-height: 1.5; }
.hint-line.hint .ic { flex: none; width: 16px; height: 16px; margin-top: 2px; color: var(--coral-dark); }
.screen-talk .transcript { margin: 0 0 16px; }

/* A real Cambridge part inside the path (rule 6). */
.real-card { border-left: 6px solid var(--charcoal); }
.real-title { display: block; margin: 4px 0 8px; font-size: 18px; color: var(--charcoal); }
.real-facts { margin: 8px 0 0; padding-left: 20px; }
.real-facts li { margin: 4px 0; }
.real-foot { display: grid; gap: 4px; justify-items: center; }
.real-foot .btn3d { text-align: center; text-decoration: none; }

/* Today's review as a node on the path, just before the current level — where a
   returning learner lands. Charcoal, so it never reads as one more level. */
.node.review { background: var(--charcoal); box-shadow: 0 8px 0 color-mix(in srgb, var(--charcoal) 70%, var(--ink)); }
.node.review:active { box-shadow: 0 0 0 var(--charcoal); }
.review-wrap .node-name { color: var(--charcoal); }
.review-wrap .node-tip { color: var(--charcoal); }
.node-wrap.review-wrap { margin-top: 32px; }

/* An exam limit inside a prompt — ONE NUMBER, NO MORE THAN TWO WORDS. */
.prompt .limit { color: var(--coral-dark); background: var(--coral-soft); padding: 0 4px; border-radius: 4px; }

/* The wrong sheet: what they wrote, then the answer key in the OK colour so it
   stands apart from the red of the mistake. */
.sheet-given { margin: 0 0 8px; font-size: 14px; color: var(--muted); }
.sheet-given s { color: var(--bad-text); }
.answer-key { display: grid; gap: 4px; margin: 0 0 12px; padding: 12px 16px; border-radius: 12px;
  background: var(--surface); border: 2px solid var(--ok); font-size: 18px; line-height: 1.6; color: var(--ink); }
.answer-key-label { font-size: 12px; font-weight: 700; color: var(--ok-text); letter-spacing: .02em; }
.answer-key mark { padding: 0 8px; border-radius: 4px; background: var(--ok-bg); color: var(--ok-text); font-weight: 800;
  box-shadow: inset 0 -2px 0 var(--ok); }
.answer-key b { color: var(--ok-text); }
