/* =========================================================================
   ROBOTICS CORE - Cybernetic Diagnostic Interface (light mode, compact)
   Light lab-report surfaces, neon-cyan framing accents (borders/bars only),
   sharp corners + corner brackets, monospaced labels. Fully offline.
   Audited (Impeccable): no side-stripe accents, text >=4.5:1, focus-visible,
   one h1/page, >=44px primary controls. Page canvas stays LIGHT.
   ========================================================================= */
:root{
  --bg:#faf9fb; --surface-low:#f5f3f6; --surface:#efedf0; --surface-high:#e7e6ea;
  --deep:#16181d; --deep-2:#0f172a; --paper:#f6fbfb;
  --ink:#1b1c1e; --ink-soft:#3a494b; --outline:#54636a; --outline-dim:#b0c0c1;
  --primary:#00696f; --ink-accent:#046a70;
  --cyan:#00b9c4; --cyan-bright:#00e0ec; --cyan-glow:rgba(0,185,196,.35);
  --amber:#6e4f00; --amber-bright:#feb700; --green:#2a7d00; --green-bright:#2ae500;
  --pink:#c200c2; --error:#a8101a; --error-ink:#7a0008;
  /* Correct / incorrect washes behind a graded option or a self-check row.
     Tokens, not literals, because the dark theme has to restate them. */
  --wash-bad:#f7e4e4; --wash-good:#e7f4e2;
  --font-head:"Space Mono","JetBrains Mono",ui-monospace,"Cascadia Mono",Consolas,monospace;
  --font-body:"Hanken Grotesk","Inter",system-ui,-apple-system,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
  --font-mono:"JetBrains Mono",ui-monospace,"Cascadia Code",Consolas,Menlo,monospace;
  --gutter:20px; --maxw:1080px; --bar:48px;
  /* Height of the fixed bottom bar. .foot, .shell's padding-bottom and .side's bottom MUST
     agree or content ends up underneath the bar; it used to be 28px hard-coded in all three.
     Below 620px the footer's three groups wrap to two lines, so the strip has to grow. */
  --foot-h:28px;
}
@media(max-width:620px){:root{--foot-h:46px}}
*{box-sizing:border-box}
html,body{margin:0;padding:0}
body{background:var(--bg);color:var(--ink);font-family:var(--font-body);
  font-size:15px;line-height:1.55;-webkit-font-smoothing:antialiased}
a{color:var(--primary);text-decoration:none}
a:hover{color:var(--ink-accent);text-decoration:underline}
code{font-family:var(--font-mono);font-size:.88em}
::selection{background:var(--cyan-bright);color:var(--deep)}
:focus-visible{outline:2px solid var(--primary);outline-offset:2px}
.page-h1{font-family:var(--font-head);font-size:24px;color:var(--primary);margin:0 0 16px;letter-spacing:-.01em}
.lead{color:var(--ink-soft);max-width:74ch;margin:-8px 0 18px}

/* ---- top bar / footer ---- */
.topbar{position:fixed;top:0;left:0;right:0;height:var(--bar);z-index:50;display:flex;
  align-items:center;justify-content:space-between;gap:14px;padding:0 var(--gutter);
  background:var(--bg);border-bottom:2px solid var(--outline-dim);box-shadow:0 1px 0 var(--cyan)}
.tb-left{display:flex;align-items:center;gap:14px;min-width:0}
.navtoggle{font-size:18px;line-height:1;background:none;border:1px solid var(--outline-dim);
  color:var(--ink-soft);width:34px;height:32px;cursor:pointer}
.navtoggle:hover{border-color:var(--cyan);color:var(--primary)}
.brand{font-family:var(--font-head);font-weight:700;font-size:16px;color:var(--primary);letter-spacing:-.02em;white-space:nowrap}
.brand b{color:var(--ink-accent)}
.crumbs{font-family:var(--font-mono);font-size:11px;color:var(--ink-soft);display:flex;gap:7px;align-items:center;overflow:hidden;white-space:nowrap}
.crumbs .sep{opacity:.5}.crumbs .hot{color:var(--ink-accent);font-weight:700}
/* The kind chip (LESSON / COURSE / ERROR LOG) is the load-bearing crumb: never shrink it.
   The trailing topic name ellipsizes instead of being cut mid-word ("… / Py"), and below
   560px it drops out entirely rather than leaving a 2-character stub. `:not(.hot)` matters:
   on non-lesson pages the chip is itself the last child and must stay visible. */
.crumbs>.hot{flex:0 0 auto}
.crumbs>span:not(.hot):not(.sep){min-width:0;overflow:hidden;text-overflow:ellipsis}
@media(max-width:560px){.crumbs>.sep,.crumbs>span:not(.hot):not(.sep){display:none}}
.status{font-family:var(--font-mono);font-size:10px;color:var(--green);display:flex;align-items:center;gap:7px;text-transform:uppercase;letter-spacing:.1em}
.dot{width:7px;height:7px;border-radius:50%;background:var(--green-bright);box-shadow:0 0 7px var(--green-bright);animation:pulse 2s infinite}
@keyframes pulse{50%{opacity:.4}}
.foot{position:fixed;bottom:0;left:0;right:0;height:var(--foot-h);z-index:50;display:flex;align-items:center;
  justify-content:space-between;gap:10px;padding:0 var(--gutter);background:var(--surface-low);
  border-top:1px solid var(--outline-dim);font-family:var(--font-mono);font-size:10px;color:var(--ink-soft);
  line-height:1.35}
.foot .live{color:var(--green);display:flex;align-items:center;gap:7px;text-transform:uppercase;letter-spacing:.08em}

/* ---- layout + collapsible sidebar ---- */
.shell{display:flex;min-height:100vh;padding-top:var(--bar);padding-bottom:var(--foot-h)}
.side{position:fixed;top:var(--bar);bottom:var(--foot-h);left:0;width:230px;z-index:40;background:var(--surface-low);
  border-right:2px solid var(--outline-dim);display:flex;flex-direction:column;overflow:hidden;transition:transform .18s ease-out}
.side-head{padding:12px 16px;border-bottom:1px solid var(--outline-dim);background:var(--surface)}
.side-row{display:flex;justify-content:space-between;font-family:var(--font-mono);font-size:10px;
  color:var(--ink-soft);text-transform:uppercase;letter-spacing:.1em;margin-bottom:6px}
.side-row .v{color:var(--ink-accent);font-weight:700}
.track{height:5px;background:var(--surface-high);border:1px solid var(--outline-dim);overflow:hidden}
.track .fill{height:100%;background:var(--cyan)}
.side-nav{flex:1;overflow-y:auto;padding:6px 0}
.side-nav a{display:block;padding:7px 16px;font-family:var(--font-mono);font-size:12px;color:var(--ink-soft)}
.side-nav a:hover{background:var(--surface);color:var(--primary);text-decoration:none}
.side-nav a.on{background:var(--surface-high);color:var(--primary);font-weight:700}
.nav-phase{padding:10px 16px 4px;font-family:var(--font-mono);font-size:10px;letter-spacing:.14em;
  text-transform:uppercase;color:var(--outline)}
.side-foot{padding:10px 16px;border-top:1px solid var(--outline-dim);font-family:var(--font-mono);font-size:10px;color:var(--ink-soft)}
/* min-width:0 is load-bearing: .main is a flex item, so its default min-width:auto is its
   CONTENT's min-content width. One wide code block or table therefore stretched .main past
   the viewport (measured: 732px inside a 390px shell) and the whole document scrolled
   sideways on mobile. With 0 it shrinks to the shell and the pre/table scroll internally. */
.main{flex:1;min-width:0;margin-left:230px;padding:26px var(--gutter) 56px;background:var(--bg)}
.wrap{max-width:var(--maxw);margin:0 auto}
body.nav-collapsed .side{transform:translateX(-100%)}
body.nav-collapsed .main{margin-left:0}
/* Under 880px the sidebar stops being a column and becomes an overlay DRAWER, closed by
   default. It needs its own state class: `nav-collapsed` means "hidden" (desktop, where
   the default is open), so reusing it here would invert the mobile default and, worse,
   the rule below already hid .side unconditionally - the topbar toggle flipped a class
   that changed nothing and the drawer could not be opened at all. `nav-open` means
   "shown", is only meaningful at this width, and is NOT persisted (a drawer should not
   survive navigation). See the matchMedia branch in the topbar toggle. */
@media(max-width:880px){
  .side{transform:translateX(-100%);box-shadow:none}
  body.nav-open .side{transform:none;box-shadow:0 0 0 100vmax rgba(11,26,33,.45)}
  .main{margin-left:0}
}

/* =====================  DASHBOARD  ===================== */
.metrics{display:grid;grid-template-columns:repeat(5,1fr);gap:10px;margin-bottom:14px}
.metric{border:1px solid var(--outline-dim);background:var(--surface-low);padding:10px 12px;position:relative}
.metric::before{content:"";position:absolute;top:-1px;left:-1px;width:8px;height:8px;border-top:2px solid var(--cyan);border-left:2px solid var(--cyan)}
.m-val{display:block;font-family:var(--font-head);font-size:22px;color:var(--primary);line-height:1.1}
.m-lab{display:block;font-family:var(--font-mono);font-size:10px;text-transform:uppercase;letter-spacing:.1em;color:var(--ink-soft);margin-top:3px}
.m-sub{display:block;font-family:var(--font-mono);font-size:10px;color:var(--outline)}
/* 5 metrics in a 2-column grid left the last bordered tile orphaned at half width with a
   visible empty cell beside it. An odd last child spans the row instead. */
@media(max-width:760px){.metrics{grid-template-columns:repeat(2,1fr)}
  .metrics>:last-child:nth-child(odd){grid-column:1/-1}}

.current{display:flex;align-items:center;justify-content:space-between;gap:14px;
  border:1px solid var(--outline);background:var(--surface);padding:10px 14px;margin-bottom:14px}
.cur-lab{font-family:var(--font-mono);font-size:10px;letter-spacing:.12em;color:var(--ink-soft);text-transform:uppercase}
.cur-title{display:block;font-family:var(--font-head);font-size:15px;color:var(--primary);margin-top:2px}
/* .btn-primary carries align-self:flex-start (it is usually the last thing in a column),
   which overrode this row's align-items:center and pinned OPEN to the top of the bar —
   measured 0px above / 12.5px below. Put it back on the centre line. */
.current .btn-primary{align-self:center}

.queue{border:1px solid var(--amber);background:var(--surface-high);padding:12px 14px;margin-bottom:20px;position:relative}
.queue::before{content:"";position:absolute;top:-1px;left:-1px;width:10px;height:10px;border-top:2px solid var(--amber-bright);border-left:2px solid var(--amber-bright)}
.qh{margin:0 0 8px;font-family:var(--font-mono);font-size:11px;letter-spacing:.1em;text-transform:uppercase;color:var(--amber);display:flex;gap:8px;align-items:center}
.qh .count{background:var(--amber-bright);color:var(--deep);padding:0 7px;font-weight:700}
.qitem{display:flex;justify-content:space-between;align-items:center;gap:10px;padding:7px 0;border-top:1px solid var(--outline-dim)}
.qitem:first-of-type{border-top:none}
.qname{font-family:var(--font-mono);font-size:11px;color:var(--ink)}.qname b{color:var(--primary)}
.ratings{display:flex;gap:5px}
.ratings button{min-height:30px;padding:0 9px;cursor:pointer;border:none;font-family:var(--font-mono);font-size:10px;font-weight:700;color:var(--deep);text-transform:uppercase}
.r-again{background:#e88b82}.r-hard{background:var(--amber-bright)}.r-good{background:var(--green-bright)}.r-easy{background:var(--cyan-bright)}
.ratings button:hover{filter:brightness(1.08)}
.q-more{display:inline-block;margin-top:6px;font-family:var(--font-mono);font-size:11px}

.turn{margin:26px 0}
/* flex-wrap so the tagline can drop to its own line instead of squeezing .tprog; without
   the nowrap on .tprog, "7/82 · 9%" broke after the separator and left it dangling. */
.turn-head{display:flex;flex-wrap:wrap;align-items:center;gap:12px;margin-bottom:10px;border-bottom:1px solid var(--outline-dim);padding-bottom:8px}
.turn-tag{font-family:var(--font-head);font-size:15px;font-weight:700;color:var(--deep);background:var(--cyan);padding:2px 10px}
.turn-head.t2 .turn-tag{background:var(--primary);color:var(--paper)}
.turn-head .tagline{font-family:var(--font-mono);font-size:11px;color:var(--ink-soft)}
.turn-head .tprog{margin-left:auto;font-family:var(--font-mono);font-size:11px;color:var(--ink-accent);font-weight:700;white-space:nowrap}
.phase-label{font-family:var(--font-mono);font-size:10px;letter-spacing:.12em;text-transform:uppercase;
  color:var(--outline);margin:14px 0 6px}

/* collapsible topic node */
.node{border:1px solid var(--outline-dim);background:var(--surface-low);margin-bottom:7px}
.node[open]{border-color:var(--outline)}
.node>summary{list-style:none;cursor:pointer;display:flex;align-items:center;gap:12px;padding:9px 13px}
.node>summary::-webkit-details-marker{display:none}
.node>summary::before{content:"+";font-family:var(--font-mono);color:var(--ink-accent);font-weight:700;width:10px}
.node[open]>summary::before{content:"-"}
.node>summary:hover{background:var(--surface)}
.n-name{flex:1;font-family:var(--font-head);font-size:14px;color:var(--ink)}
.n-bar{width:120px;height:7px;background:var(--surface-high);border:1px solid var(--outline-dim);overflow:hidden}
.n-seg{display:block;height:100%;background:var(--cyan)}
.n-pct{font-family:var(--font-mono);font-size:11px;color:var(--ink-soft);min-width:40px;text-align:right}
.n-body{border-top:1px solid var(--outline-dim)}
.lrow,.final-row{display:flex;align-items:center;gap:11px;padding:8px 13px 8px 16px;border-top:1px solid var(--surface-high);color:var(--ink)}
.lrow:first-child{border-top:none}
.lrow:hover,.final-row:hover{background:var(--surface);text-decoration:none}
.lnum{font-family:var(--font-mono);font-size:11px;color:var(--outline);width:18px;flex:0 0 auto;text-align:center}
.lrow.done .lnum,.final-row.done .lnum{color:var(--green)}
.lrow.locked,.final-row.locked{opacity:.5}
.ltitle{flex:1;font-size:13px}
.lrow.done .ltitle{color:var(--ink-soft)}
.lkind{font-family:var(--font-mono);font-size:10px;letter-spacing:.08em;color:var(--outline);text-transform:uppercase}
.final-row{border:1px solid var(--amber);background:var(--surface-high);margin-top:7px}

/* tracks page (reuses .node / .lrow) */
.trk-body{padding:2px 0 10px}
.trk-note{font-size:13px;color:var(--ink-soft);line-height:1.5;margin:12px 16px 6px;max-width:74ch}
.trk-actions{display:flex;align-items:center;gap:14px;flex-wrap:wrap;margin:0 16px 8px}
.trk-rem{font-family:var(--font-mono);font-size:11px;color:var(--outline)}
.trk-body .phase-label{display:flex;align-items:baseline;gap:8px;padding:0 16px;margin:16px 0 4px}
.trk-st-pct{margin-left:auto;font-family:var(--font-mono);font-size:10px;color:var(--outline);text-transform:none}
.lmin{font-family:var(--font-mono);font-size:10px;color:var(--outline);flex:0 0 auto;width:34px;text-align:right}
.trk-tag{margin-left:8px;border-color:var(--primary);color:var(--primary)}
.trk-tag.t-robotics{border-color:var(--cyan);color:var(--ink-accent)}
.trk-tag.t-embedded{border-color:var(--green);color:var(--green)}
.trk-tag.t-chip{border-color:var(--outline-dim);color:var(--outline)}

/* soft "upcoming" cue (access is never blocked) + ahead-of-order note */
.lrow.upcoming .ltitle{color:var(--ink-soft)}
.lrow.upcoming .lnum{color:var(--outline-dim)}
.final-row.upcoming{border-color:var(--outline-dim)}
.ahead-note{font-family:var(--font-mono);font-size:11px;line-height:1.5;color:var(--ink-soft);
  border:1px solid var(--outline-dim);background:var(--surface-low);padding:9px 12px;margin:0 0 16px;
  display:flex;gap:9px;align-items:baseline}
.ahead-note .an-tag{color:var(--ink-accent);font-weight:700;letter-spacing:.08em;text-transform:uppercase;flex:0 0 auto}
/* A bridge build reaches outside its course on purpose (courses.BRIDGE_BUILDS): amber,
   like the other "read this before you start" cues, never red — it is not a problem. */
.bridge-note{border-color:var(--amber)}
.bridge-note .an-tag{color:var(--amber)}
.ahead-note.final-note{border-color:var(--amber)}
.ahead-note.final-note .an-tag{color:var(--amber)}

.danger{margin-top:40px}
.danger summary{font-family:var(--font-mono);font-size:11px;color:var(--ink-soft);cursor:pointer;text-transform:uppercase;letter-spacing:.08em}
.danger button{margin-top:10px;font-family:var(--font-mono);font-weight:700;font-size:11px;min-height:34px;padding:0 14px;border:1px solid var(--error);background:transparent;color:var(--error-ink);cursor:pointer;text-transform:uppercase}
.danger button:hover{background:var(--error);color:var(--paper)}

/* =====================  LESSON  ===================== */
.lhead{padding:0 0 14px;margin-bottom:18px;border-bottom:2px solid var(--outline-dim)}
.lhead .kicker{font-family:var(--font-mono);font-size:11px;letter-spacing:.14em;text-transform:uppercase;color:var(--ink-accent);display:flex;gap:9px;align-items:center;margin-bottom:8px}
.lhead .kicker .bit{width:13px;height:7px;background:var(--cyan)}
.lhead h1{font-family:var(--font-head);font-size:29px;line-height:1.1;letter-spacing:-.02em;color:var(--primary);margin:0}
.lhead .pills{margin-top:11px;display:flex;gap:7px;flex-wrap:wrap;align-items:center}
.pill{font-family:var(--font-mono);font-size:10px;line-height:1;display:inline-flex;align-items:center;
  padding:4px 9px;border:1px solid var(--outline-dim);color:var(--ink-soft);text-transform:uppercase;letter-spacing:.07em}
.pill.turn{border-color:var(--primary);color:var(--primary)}

.lesson{font-size:15px}
.lesson>p,.lesson li{max-width:74ch}
.lesson p{line-height:1.62}
.sec-head{display:flex;align-items:center;gap:11px;margin:38px 0 16px}
.sec-head .badge{flex:0 0 auto;width:25px;height:25px;display:flex;align-items:center;justify-content:center;font-family:var(--font-mono);font-weight:700;font-size:13px;color:var(--deep);background:var(--cyan)}
.sec-head .t{font-family:var(--font-head);font-size:17px;color:var(--ink);text-transform:uppercase;letter-spacing:.02em}
.sec-head .line{flex:1;height:2px;background:var(--outline-dim)}
.sec-c .badge{background:var(--primary);color:var(--paper)}.sec-d .badge{background:var(--green-bright)}.sec-e .badge{background:var(--amber-bright)}.sec-f .badge{background:var(--error);color:var(--paper)}.sec-g .badge{background:var(--pink);color:var(--paper)}
.lesson h2:not(.sec-head){font-family:var(--font-head);font-size:18px;color:var(--ink);margin:1.8em 0 .6em;padding-bottom:.3em;border-bottom:1px solid var(--outline-dim)}
.lesson h3{font-family:var(--font-head);font-size:15px;color:var(--primary);margin:1.5em 0 .5em}
.lesson ul,.lesson ol{padding-left:1.3em}.lesson li{margin:.28em 0}
.lesson strong{color:var(--ink)}
.lesson blockquote{border:1px solid var(--outline-dim);background:var(--surface);margin:1.2em 0;padding:.7em 1em;color:var(--ink-soft)}
.lesson :not(pre)>code{background:var(--surface-high);color:var(--primary);padding:1px 5px;border:1px solid var(--outline-dim)}
.lesson table{border-collapse:collapse;width:100%;margin:1.1em 0;font-size:13px;display:block;overflow-x:auto}
.lesson th,.lesson td{border:1px solid var(--outline-dim);padding:7px 10px;text-align:left}
.lesson th{background:var(--surface-high);font-family:var(--font-mono);font-size:11px;text-transform:uppercase;letter-spacing:.05em;color:var(--ink-soft)}
.codehilite{background:var(--deep-2);border:1px solid var(--outline);margin:1.2em 0;overflow:hidden}
.codehilite pre{margin:0;padding:14px 16px;overflow-x:auto;font-family:var(--font-mono);font-size:12.5px;line-height:1.55;color:#cdd6e4}
.lesson pre{background:var(--deep-2);border:1px solid var(--outline);padding:14px 16px;overflow-x:auto;color:#cdd6e4;font-family:var(--font-mono);font-size:12.5px}

/* recall cards + self-scoring */
details.card{background:var(--deep);border:1px solid var(--cyan);margin:12px 0;position:relative}
details.card::before,details.card::after{content:"";position:absolute;width:9px;height:9px;pointer-events:none}
details.card::before{top:-1px;left:-1px;border-top:2px solid var(--cyan);border-left:2px solid var(--pink)}
details.card::after{bottom:-1px;right:-1px;border-bottom:2px solid var(--cyan);border-right:2px solid var(--pink)}
details.card summary{list-style:none;cursor:pointer;padding:14px 16px;color:#e3e2e5;display:flex;flex-direction:column;gap:6px}
details.card summary::-webkit-details-marker{display:none}
.card-q-label{font-family:var(--font-mono);font-size:10px;letter-spacing:.18em;color:var(--cyan-bright)}
details.card summary p{margin:0;color:#eef3f6;font-size:14px}
.card-cue{align-self:flex-start;font-family:var(--font-mono);font-size:10px;letter-spacing:.16em;color:#ff6bff;border:1px solid #ff6bff;padding:2px 7px;margin-top:3px}
details.card[open] .card-cue{display:none}
details.card[open]{box-shadow:inset 0 0 0 1px var(--cyan)}
details.card .answer{border-top:1px dashed var(--outline);padding:14px 16px;color:#e1e8ec}
details.card strong,details.card summary strong{color:#eef3f6}
.card-a-label{display:block;font-family:var(--font-mono);font-size:10px;letter-spacing:.18em;color:var(--green-bright);margin-bottom:7px}
details.card .answer :not(pre)>code{background:#0c1530;color:#86ecff;border-color:#1c3a5e}
details.card[data-rated="1"]{border-color:var(--green-bright)}
details.card[data-rated="0"]{border-color:#ff6bff}
.card-rate{display:flex;align-items:center;gap:8px;margin-top:12px;padding-top:10px;border-top:1px dashed var(--outline)}
.cr-label{font-family:var(--font-mono);font-size:10px;color:#9fb0bb;margin-right:auto;text-transform:uppercase;letter-spacing:.08em}
.card-rate button{cursor:pointer;font-family:var(--font-mono);font-size:11px;font-weight:700;padding:5px 11px;border:1px solid var(--outline);background:transparent}
.cr-got{color:var(--green-bright)}.cr-miss{color:#ff8fb0}
.cr-got.sel{background:var(--green-bright);color:var(--deep);border-color:var(--green-bright)}
.cr-miss.sel{background:#ff6bff;color:var(--deep);border-color:#ff6bff}

/* stored free-text answer boxes (sections B / C / E / G) */
.qcard{margin:12px 0 30px}
.qcard+.qcard{border-top:1px solid var(--surface-high);padding-top:22px}
.qcard details.card{margin:0}
.respond{margin:14px 0 32px}
.respond+.respond{border-top:1px solid var(--surface-high);padding-top:22px}
.resp-q{margin:0 0 7px}
.resp-q-label{display:block;font-family:var(--font-mono);font-size:10px;letter-spacing:.16em;color:var(--ink-accent);text-transform:uppercase;margin-bottom:3px}
.resp-q p{margin:0;color:var(--ink);font-size:14px}
.qresp{width:100%;font-family:var(--font-body);font-size:14px;color:var(--ink);background:var(--bg);
  border:1px solid var(--outline-dim);padding:8px 10px;margin:6px 0;resize:vertical;display:block}
.qresp:focus{outline:none;border-color:var(--primary)}
.respond details.card{margin:4px 0 0}

/* answer-box tools: MATH symbol strip + CODE editor mode */
.qtools{display:flex;gap:6px;justify-content:flex-end;align-items:center;flex-wrap:wrap;margin:12px 0 6px}
/* practice problems: hug each question to its answer box, separate the pairs */
.lesson p:has(+ .qtools){margin-bottom:4px}
.qtool{font-family:var(--font-mono);font-size:9px;font-weight:700;letter-spacing:.1em;cursor:pointer;
  padding:3px 9px;border:1px solid var(--outline-dim);background:var(--surface-low);color:var(--ink-soft)}
.qtool:hover{border-color:var(--primary);color:var(--primary)}
.qtool.on{border-color:var(--primary);color:var(--primary);background:var(--surface-high)}
.qtool[disabled]{opacity:.4;cursor:default}
.qsyms{display:flex;flex-wrap:wrap;gap:4px;margin:0 auto 0 0}   /* sits IN the toolbar row, buttons pushed right */
.qsyms[hidden]{display:none}   /* the class rule would otherwise beat the UA [hidden] rule */
.qsym{min-width:28px;height:28px;cursor:pointer;font-size:14px;line-height:1;color:var(--ink);
  border:1px solid var(--outline-dim);background:var(--surface-low)}
.qsym:hover{border-color:var(--cyan);background:var(--surface)}
.qresp.code-mode{font-family:var(--font-mono);font-size:13px;line-height:20px;letter-spacing:0;tab-size:4;
  font-kerning:none;font-variant-ligatures:none;
  /* pin the box metrics so they ALWAYS equal .code-wrap>pre.code-hl - the review
     page's .rv-input sets padding:11px 13px, which would drift the overlay */
  padding:8px 10px;
  background:#161a1f;color:#d7e3e6;border-color:var(--outline);min-height:110px;
  white-space:pre-wrap;overflow-wrap:break-word;word-break:normal;overflow:hidden}
.qresp.code-mode:focus{border-color:var(--cyan)}

/* syntax-highlight overlay: <pre> painted behind a transparent-text textarea.
   Metrics (font/size/line-height/letter-spacing/padding/border/tab-size/white-space)
   MUST match .qresp.code-mode exactly - use INTEGER px for font-size & line-height,
   or textarea vs block subpixel rounding makes the overlay drift below the caret. */
.code-wrap{position:relative;margin:6px 0}
.code-wrap>.qresp{margin:0}
/* selector is deliberately .code-wrap>pre.code-hl (0,2,1): it must OUTRANK the
   .lesson pre code-panel rule (0,1,1), which otherwise restyles the overlay
   (14px padding / 12.5px font) and shifts the highlight ~6px below the caret */
.code-wrap>pre.code-hl{position:absolute;top:0;left:0;right:0;bottom:0;margin:0;overflow:hidden;pointer-events:none;
  padding:8px 10px;border:1px solid transparent;background:#161a1f;color:#d7e3e6;
  font-family:var(--font-mono);font-size:13px;line-height:20px;letter-spacing:0;tab-size:4;
  font-kerning:none;font-variant-ligatures:none;
  white-space:pre-wrap;overflow-wrap:break-word;word-break:normal}
.code-wrap>pre.code-hl>code{font:inherit;font-size:13px;line-height:20px;letter-spacing:0;background:none;
  padding:0;margin:0;border:none;color:inherit;white-space:inherit;display:inline}
.code-wrap:not(.on) .code-hl{display:none}
.code-wrap.on>.qresp.code-mode{background:transparent;color:transparent;-webkit-text-fill-color:transparent;
  caret-color:#e8f2f4;position:relative;z-index:1;resize:none}
  /* wrapping mode + JS autosize: the textarea NEVER scrolls (no internal scrollbars at
     all - a horizontal scrollbar inside the textarea but not the overlay is exactly
     what desynced them). Long lines soft-wrap identically in both layers. */
.code-wrap.on>.qresp.code-mode::selection{background:rgba(0,185,196,.30);color:transparent}
/* token colors - the Pygments "material" palette the rendered code panels use */
.tk-kw{color:#c792ea}
.tk-str{color:#c3e88d}
.tk-num{color:#f78c6c}
.tk-fn{color:#82aaff}
.tk-com{color:#7d8b99;font-style:italic}

/* per-problem practice boxes + the solution self-check rows */
.qresp-practice{margin:0 0 60px}
/* a practice box in CODE mode is wrapped in .code-wrap, whose margin would
   otherwise replace the 60px rhythm - carry it on the wrapper instead */
.code-wrap:has(>.qresp-practice){margin:0 0 60px}
.sol-check{margin:4px 0 18px;border:1px solid var(--outline-dim);background:var(--surface-low)}

/* Feynman sourced from section E (no double entry) */
.gate-fey-note{font-family:var(--font-mono);font-size:11px;color:var(--ink-soft);line-height:1.5}

/* finish + mastery gate */
.finish{margin-top:36px;border-top:2px solid var(--outline-dim);padding-top:20px;display:flex;flex-direction:column;gap:16px}
.gatebox{display:flex;flex-direction:column;gap:14px;background:var(--surface-low);border:1px solid var(--outline);padding:16px 18px}
.gatebox .sec-head{margin:0 0 4px}
.gate-item{display:flex;align-items:center;gap:10px;font-size:14px;color:var(--ink)}
.gate-item.col{flex-direction:column;align-items:stretch;gap:6px}
.gi-lab{font-family:var(--font-mono);font-size:11px;letter-spacing:.02em;color:var(--ink-soft)}
.gate-item.check{cursor:pointer}.gate-item.check input{width:18px;height:18px;accent-color:var(--primary)}
.recall-live{font-family:var(--font-mono);font-size:13px;color:var(--amber);font-weight:700}
.recall-live.ok{color:var(--green)}
.gatebox textarea,.errform textarea,.pform textarea,.pform input,.pform select,.errform input{
  width:100%;font-family:var(--font-body);font-size:14px;color:var(--ink);background:var(--bg);
  border:1px solid var(--outline-dim);padding:8px 10px}
.gatebox textarea:focus,.errform input:focus,.errform textarea:focus,.pform textarea:focus,.pform input:focus,.pform select:focus{outline:none;border-color:var(--primary)}
.gate-status{font-family:var(--font-mono);font-size:12px;color:var(--ink-soft);margin:0}
.btn-primary{font-family:var(--font-mono);font-weight:700;text-transform:uppercase;letter-spacing:.08em;font-size:13px;
  min-height:46px;padding:0 22px;background:var(--cyan);color:var(--deep);border:none;cursor:pointer;align-self:flex-start;display:inline-flex;align-items:center}
.btn-primary.sm{min-height:36px;font-size:11px;padding:0 16px}
.btn-primary:hover{background:var(--cyan-bright)}
.btn-primary:disabled{background:var(--surface-high);color:var(--outline);cursor:not-allowed}
.okmsg{font-family:var(--font-mono);font-size:13px;color:var(--green)}
.lnav{display:flex;justify-content:space-between;font-family:var(--font-mono);font-size:12px}

/* =====================  ERROR LOG  ===================== */
.errform{border:1px solid var(--outline);background:var(--surface-low);padding:14px 16px;margin-bottom:18px}
.ef-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:12px;margin-bottom:12px}
.ef-grid label,.pform label{display:flex;flex-direction:column;gap:4px;font-family:var(--font-mono);font-size:11px;text-transform:uppercase;letter-spacing:.05em;color:var(--ink-soft)}
@media(max-width:680px){.ef-grid{grid-template-columns:1fr}}
.datatable{width:100%;border-collapse:collapse;font-size:13px}
.datatable th,.datatable td{border:1px solid var(--outline-dim);padding:8px 10px;text-align:left;vertical-align:top}
.datatable th{background:var(--surface-high);font-family:var(--font-mono);font-size:10px;text-transform:uppercase;letter-spacing:.06em;color:var(--ink-soft)}
.datatable td.num{text-align:center;font-family:var(--font-mono);white-space:nowrap}
.datatable .empty{color:var(--ink-soft);text-align:center;padding:20px}
/* The Error Log is 6 prose columns; its min-content width is ~490px, so below 760px it used
   to push the whole document sideways, and merely letting it scroll crushed every column to
   ~60px (4-line-tall cells reading "Confused / P with I / gain"). Below 620px each row
   becomes its own card with the column name as a leading label, which is what the data
   actually is: one mistake per entry. Desktop keeps the real table. Labels come from
   data-label on each <td> (set in coursedash.py and app.py). */
@media(max-width:620px){
  .datatable,.datatable tbody,.datatable tr,.datatable td{display:block;width:100%}
  .datatable thead{display:none}
  .datatable tr{border:1px solid var(--outline-dim);margin-bottom:10px;padding:4px 0}
  .datatable td{border:none;padding:5px 12px;text-align:left}
  .datatable td[data-label]::before{content:attr(data-label);display:block;
    font-family:var(--font-mono);font-size:10px;text-transform:uppercase;letter-spacing:.06em;
    color:var(--ink-soft);margin-bottom:2px}
  .datatable td.num{text-align:left}
  .datatable td:empty{display:none}            /* the delete-button cell when it has no form */
  .datatable .empty{border:none;text-align:center}
}
.xbtn{cursor:pointer;border:1px solid var(--outline-dim);background:transparent;color:var(--error-ink);font-family:var(--font-mono);padding:2px 8px}
.xbtn:hover{background:var(--error);color:var(--paper);border-color:var(--error)}

/* =====================  PORTFOLIO  ===================== */
.pgrid{display:grid;grid-template-columns:repeat(2,1fr);gap:12px}
@media(max-width:760px){.pgrid{grid-template-columns:1fr}}
.pcard{border:1px solid var(--outline-dim);background:var(--surface-low)}
.pcard>summary{list-style:none;cursor:pointer;display:flex;flex-direction:column;gap:3px;padding:12px 14px}
.pcard>summary::-webkit-details-marker{display:none}
.pcard>summary:hover{background:var(--surface)}
.p-status{font-family:var(--font-mono);font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.08em;align-self:flex-start;padding:1px 8px;color:var(--deep)}
.pcard.s0 .p-status{background:var(--surface-high);color:var(--ink-soft)}
.pcard.s1 .p-status{background:var(--amber-bright)}
.pcard.s2 .p-status{background:var(--green-bright)}
.pcard.s2{border-color:var(--green)}
.p-title{font-family:var(--font-head);font-size:14px;color:var(--ink)}
.p-topic{font-family:var(--font-mono);font-size:10px;color:var(--outline)}
.pform{display:flex;flex-direction:column;gap:10px;padding:0 14px 14px;border-top:1px solid var(--outline-dim)}
.pform label{padding-top:10px}

/* ---- sidebar: Curriculum collapsible group ---- */
.nav-sep{height:1px;background:var(--outline-dim);margin:8px 14px}
.nav-curric{margin-top:2px}
.nav-curric>summary{list-style:none;cursor:pointer;display:flex;align-items:center;gap:8px;
  padding:8px 16px;font-family:var(--font-mono);font-size:11px;letter-spacing:.1em;
  text-transform:uppercase;color:var(--ink-accent)}
.nav-curric>summary::-webkit-details-marker{display:none}
.nav-curric>summary::before{content:"+";font-weight:700;width:10px}
.nav-curric[open]>summary::before{content:"-"}
.nav-curric>summary:hover{background:var(--surface);color:var(--primary)}
.nc-t{flex:1}
.nc-cue{font-size:10px;color:var(--outline);letter-spacing:.08em}
.nav-curric-body{padding-bottom:4px}

/* ---- review queue: spaced-repetition action plan ---- */
.q-note{font-family:var(--font-mono);font-size:11px;color:var(--ink-soft);margin:0 0 10px;max-width:84ch;line-height:1.5}
.qitem{align-items:flex-start}
.qmain{display:flex;flex-direction:column;gap:4px;min-width:0;flex:1}
.qact{display:flex;gap:9px;align-items:baseline;font-size:12.5px;color:var(--ink-soft);max-width:72ch}
.qday{font-family:var(--font-mono);font-size:10px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;
  color:var(--amber);background:var(--surface-low);border:1px solid var(--amber);padding:1px 7px;flex:0 0 auto}
.qact .qtext{line-height:1.42}
.ratings{flex:0 0 auto}

/* ---- collapsible Solutions (anti-spoiler) ---- */
.solutions{border:1px solid var(--outline-dim);background:var(--surface-low);margin:16px 0}
.solutions>summary{list-style:none;cursor:pointer;display:flex;align-items:center;gap:10px;padding:10px 14px;
  font-family:var(--font-mono);font-size:11px;letter-spacing:.08em;text-transform:uppercase;color:var(--ink-accent)}
.solutions>summary::-webkit-details-marker{display:none}
.solutions>summary::before{content:"+";font-weight:700;width:10px}
.solutions[open]>summary::before{content:"-"}
.solutions[open]>summary{border-bottom:1px solid var(--outline-dim)}
.solutions>summary:hover{background:var(--surface)}
.sol-t{font-weight:700}
.sol-cue{margin-left:auto;color:var(--outline);font-size:10px;letter-spacing:.06em}
.sol-body{padding:2px 16px 14px}
.sol-body>*:first-child{margin-top:.7em}

/* ---- per-module quiz ---- */
.quiz{border:1px solid var(--primary);background:var(--surface-low);padding:14px 16px;margin:20px 0;position:relative}
.quiz::before{content:"";position:absolute;top:-1px;left:-1px;width:9px;height:9px;border-top:2px solid var(--cyan);border-left:2px solid var(--cyan)}
.quiz-head{display:flex;align-items:baseline;gap:10px;margin-bottom:12px}
.quiz-label{font-family:var(--font-mono);font-size:11px;font-weight:700;letter-spacing:.16em;color:var(--deep);background:var(--cyan);padding:2px 9px}
.quiz-sub{font-family:var(--font-mono);font-size:10px;color:var(--ink-soft);letter-spacing:.04em}
.quiz-qs{list-style:none;margin:0;padding:0;counter-reset:qz}
.quiz-q{margin:0 0 24px;padding:0}
.quiz-q+.quiz-q{border-top:1px solid var(--surface-high);padding-top:20px}
.quiz-q .qq{margin:0 0 8px;font-size:14px;color:var(--ink);font-weight:600;max-width:74ch}
.quiz-q .qq::before{counter-increment:qz;content:"Q" counter(qz) ". ";font-family:var(--font-mono);color:var(--ink-accent);font-weight:700}
.qopts{display:flex;flex-direction:column;gap:6px}
.qopt{display:flex;align-items:flex-start;gap:9px;cursor:pointer;border:1px solid var(--outline-dim);background:var(--bg);padding:8px 11px;font-size:14px;color:var(--ink);max-width:74ch}
.qopt:hover{border-color:var(--primary)}
.qopt input{margin:2px 0 0;width:16px;height:16px;accent-color:var(--primary);flex:0 0 auto}
.qtext{flex:1}
.qmark{display:none;font-family:var(--font-mono);font-weight:700;font-size:12px;flex:0 0 auto}
.quiz-q.graded .qopt{cursor:default}
.qopt.correct{border-color:var(--green);background:#e7f4e2}
.qopt.wrong{border-color:var(--error);background:var(--wash-bad)}
.qopt.correct .qmark,.qopt.wrong .qmark{display:inline}
.qopt.correct .qmark::after{content:"OK";color:var(--green)}
.qopt.wrong .qmark::after{content:"X";color:var(--error-ink)}
.quiz-foot{display:flex;align-items:center;gap:14px;margin-top:8px;padding-top:18px;
  border-top:1px solid var(--outline-dim);flex-wrap:wrap}
.quiz-result{font-family:var(--font-mono);font-size:13px;font-weight:700}
.quiz-result.ok{color:var(--green)}.quiz-result.mid{color:var(--amber)}.quiz-result.bad{color:var(--error-ink)}

/* ---- missed-item -> Error Log ---- */
.misslog{display:flex;align-items:center;gap:12px;flex-wrap:wrap;border:1px dashed var(--amber);background:var(--surface-low);padding:10px 14px}
.ml-text{font-family:var(--font-mono);font-size:11px;color:var(--amber)}
.ml-done{font-family:var(--font-mono);font-size:11px;color:var(--green)}
.btn-ghost{font-family:var(--font-mono);font-weight:700;text-transform:uppercase;letter-spacing:.06em;font-size:11px;
  min-height:34px;padding:0 14px;background:transparent;border:1px solid var(--primary);color:var(--primary);cursor:pointer}
.btn-ghost.sm{min-height:32px;font-size:10px;padding:0 12px}
.btn-ghost:hover{background:var(--primary);color:var(--paper)}

/* ---- practice self-check (Missed -> Error Log) ---- */
.selfcheck{border:1px solid var(--outline-dim);background:var(--surface-low);padding:12px 14px;margin:18px 0}
.sc-h{font-family:var(--font-head);font-size:14px;color:var(--primary);margin:0 0 4px}
.sc-note{font-family:var(--font-mono);font-size:11px;color:var(--ink-soft);margin:0 0 10px;line-height:1.5;max-width:80ch}
.sc-row{display:flex;align-items:center;gap:10px;padding:8px 6px;border-top:1px solid var(--surface-high);flex-wrap:wrap}
.sc-row:first-of-type{border-top:none}
.sc-row[data-result=miss]{background:var(--wash-bad)}
.sc-row[data-result=got]{background:var(--wash-good)}
.sc-id{font-family:var(--font-mono);font-size:11px;font-weight:700;color:var(--ink-accent);flex:0 0 auto;min-width:24px}
.sc-prob{flex:1;font-size:13px;color:var(--ink);min-width:200px;line-height:1.4}
.sc-btns{display:flex;gap:6px;flex:0 0 auto}
.sc-got,.sc-miss{cursor:pointer;font-family:var(--font-mono);font-size:11px;font-weight:700;padding:5px 11px;border:1px solid var(--outline);background:transparent}
.sc-got{color:var(--green)}.sc-miss{color:var(--error-ink)}
.sc-got.sel{background:var(--green-bright);color:var(--deep);border-color:var(--green-bright)}
.sc-miss.sel{background:var(--error);color:var(--paper);border-color:var(--error)}
.sc-logged{font-family:var(--font-mono);font-size:10px;color:var(--error-ink);flex:0 0 auto}

h1,h2,h3{font-weight:700}

/* ============================================================= *
 * Roboteca web platform (webapp/) — storefront, dashboard, gates.
 * Uses the shared design tokens; no side-stripes; cyan for borders/
 * bars/glows only; light canvas preserved. Harmless to the app.
 * ============================================================= */
.clerk-auth{display:inline-flex;align-items:center;margin-left:10px;vertical-align:middle}
.nav-link{font-family:var(--font-mono);font-size:11px;color:var(--ink-soft);text-decoration:none;margin-left:14px}
.nav-link:hover{color:var(--ink-accent)}
.signin-btn,.buy-btn{font-family:var(--font-mono);font-size:12px;font-weight:700;
  letter-spacing:.04em;text-transform:uppercase;cursor:pointer;border-radius:0;
  background:var(--primary);color:#fff;border:1px solid var(--primary);padding:9px 16px;min-height:40px}
.signin-btn{padding:6px 12px;min-height:32px;background:transparent;color:var(--ink-accent)}
.signin-btn:hover{border-color:var(--cyan);box-shadow:0 0 0 2px var(--cyan-glow)}
.buy-btn:hover{background:var(--ink-accent);box-shadow:0 0 0 2px var(--cyan-glow)}
.buy-btn.bundle{background:transparent;color:var(--ink-accent)}
.buy-soon{font-family:var(--font-mono);font-size:11px;color:var(--ink-soft)}
.course-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:16px;margin:18px 0}
.course-card{border:1px solid var(--outline-dim);background:var(--surface-low);padding:18px 18px 16px;
  position:relative}
.course-card::before,.course-card::after{content:"";position:absolute;width:12px;height:12px;
  border:2px solid var(--cyan);pointer-events:none}
.course-card::before{top:-1px;left:-1px;border-right:0;border-bottom:0}
.course-card::after{bottom:-1px;right:-1px;border-left:0;border-top:0}
.course-card h2{font-size:16px;margin:0 0 6px}
.course-card .mods{font-family:var(--font-mono);font-size:11px;color:var(--ink-soft);margin:8px 0 12px}
.card-cta{display:flex;gap:12px;align-items:center;flex-wrap:wrap;margin-top:6px}
.card-cta a{font-family:var(--font-mono);font-size:11px;color:var(--ink-accent)}
/* The line under a buy row that states what the alternative costs. Quiet: it explains
   the button above it, it is not a third thing to click. */
.cta-note{font-family:var(--font-mono);font-size:11.5px;line-height:1.55;
  color:var(--ink-soft);max-width:70ch;margin:10px 0 0}
.bundle-band{border:1px solid var(--outline-dim);background:var(--surface-low);padding:22px;margin:24px 0;text-align:center}
.bundle-band h2{font-size:18px;margin:0 0 6px}
.outline{list-style:none;padding:0;margin:14px 0}
.outline li{padding:6px 0;border-bottom:1px solid var(--outline-dim);font-size:14px}
.outline .locked{font-family:var(--font-mono);font-size:10px;color:var(--ink-soft)}
.owned-note{font-family:var(--font-mono);font-size:12px;color:var(--green)}
/* public syllabus page: composition pills, "inside" chips, recommended path */
.syl-pills{margin:0 0 22px}
.syl-chips{display:flex;flex-wrap:wrap;gap:8px;margin:10px 0 4px}
.syl-chip{font-family:var(--font-mono);font-size:11px;letter-spacing:.04em;color:var(--ink);
  border:1px solid var(--outline-dim);background:var(--surface-low);padding:7px 12px}
.syl-rec{max-width:74ch}
/* NOTE: the platform pages render the SAME markup as the legacy app (.metrics/.metric
   with m-val/m-lab/m-sub, .node > summary with n-name/n-bar/n-seg/n-pct/n-body, .lrow
   with lnum/ltitle/lkind), so they MUST inherit the legacy rules above verbatim. Do not
   re-declare .metrics/.metric/.node/.lrow here — an override further down the file wins
   on equal specificity (and `.node .lrow` beats `.lrow` outright), which is exactly how
   the dashboard drifted from the app. Platform-only variants belong on modifier classes,
   like .metrics-fit below. */
/* Webapp-only surfaces (hub dashboard, track page) show 2-4 metrics, not the app
   dashboard's 5, so the fixed 5-column grid would leave dead columns. */
.metrics.metrics-fit{grid-template-columns:repeat(auto-fit,minmax(160px,1fr))}
.done-note{font-family:var(--font-mono);font-size:11px;color:var(--green)}
/* The sign-in gate is a whole page whose only job is one action, so it is centred in the
   column and vertically weighted instead of hugging the top-left like a content page.
   min-height keeps the footer at the bottom of tall viewports without a jump. */
.signin-page{max-width:460px;margin:0 auto;padding:6vh 0 4vh;text-align:center;
  min-height:calc(100vh - var(--bar) - var(--foot-h) - 90px);
  display:flex;flex-direction:column;align-items:center;justify-content:center}
.signin-page .page-h1{margin:10px 0 6px}
.signin-page .lead{max-width:42ch;margin:0 0 4px}
.eyebrow-chip{display:inline-flex;align-items:center;gap:8px;font-family:var(--font-mono);
  font-size:11px;letter-spacing:.16em;text-transform:uppercase;color:var(--ink-accent)}
.eyebrow-chip::before{content:"";width:14px;height:8px;background:var(--cyan)}
.signin-alt{font-family:var(--font-mono);font-size:11px;color:var(--ink-soft);margin-top:22px}
/* Clerk's card centres itself inside this slot rather than sitting left-aligned. The
   width cap matters on a phone: Clerk's card carries its own min-width, and without a
   ceiling it pushed the document sideways at 360-390px. */
.clerk-signin{margin:18px 0;display:flex;justify-content:center;width:100%;max-width:100%}
.clerk-signin>*{max-width:100%}
/* Placeholder while ClerkJS is in flight. The slot is 0px tall until the widget mounts,
   so without this the page was a heading above empty space for as long as the network
   took. Shape only - it mimics the form's footprint so nothing jumps on mount. */
.clerk-skel{width:100%;max-width:360px;border:1px solid var(--outline-dim);
  background:var(--surface-low);padding:20px 18px;display:flex;flex-direction:column;
  align-items:stretch;gap:12px}
.clerk-skel .sk-line,.clerk-skel .sk-btn{display:block;background:var(--surface-high);
  animation:skpulse 1.5s ease-in-out infinite}
.clerk-skel .sk-line{height:38px;border:1px solid var(--outline-dim)}
.clerk-skel .sk-a{animation-delay:.1s}
.clerk-skel .sk-b{animation-delay:.25s}
.clerk-skel .sk-btn{height:40px;background:var(--outline-dim);animation-delay:.4s}
.clerk-skel .sk-note{font-family:var(--font-mono);font-size:11px;color:var(--ink-soft);
  text-align:center;letter-spacing:.04em}
@keyframes skpulse{0%,100%{opacity:.55}50%{opacity:1}}
@media (prefers-reduced-motion:reduce){.clerk-skel .sk-line,.clerk-skel .sk-btn{animation:none}}
/* Shown only if ClerkJS has not mounted the sign-in widget after a few seconds, so a
   blocked vendor script leaves a usable page instead of a blank one (frontend.signin_gate_body). */
.signin-fallback{display:none;margin-top:20px;border:1px solid var(--outline-dim);
  background:var(--surface-low);padding:14px 16px}
.signin-fallback.show{display:block}
.signin-fallback .lead{margin-top:0}
/* The page is centred, so the fallback's actions were the one left-aligned thing on it,
   and the 44px Retry button sat shoulder-to-shoulder with a 17px text link. Centre them,
   and stack them on a phone so each is a full-width target instead of two odd slivers. */
.signin-fallback .card-cta{justify-content:center}
.signin-fallback .card-cta .cta-alt{font-family:var(--font-mono);font-size:11px}
@media (max-width:620px){
  .signin-fallback{padding:16px 14px}
  .signin-fallback .card-cta{flex-direction:column;align-items:stretch;gap:10px}
  .signin-fallback .card-cta .btn-primary{width:100%;justify-content:center}
  .signin-fallback .card-cta .cta-alt{text-align:center;min-height:32px;
    display:inline-flex;align-items:center;justify-content:center}
}
.fallback-note{font-family:var(--font-mono);font-size:11px;color:var(--ink-soft);margin:12px 0 0}

/* ---- in-lesson section navigation: right-side collapsible menu + scroll-spy ---- */
html{scroll-behavior:smooth}
.lesson h2,.gatebox .sec-head{scroll-margin-top:64px}
body.lnav-open .main{padding-right:214px}
.lesson-nav{position:fixed;right:0;top:calc(var(--bar) + 16px);z-index:35;width:194px;
  display:flex;flex-direction:column;max-height:calc(100vh - var(--bar) - 54px);
  background:var(--surface-low);border:1px solid var(--outline-dim);border-right:none;
  box-shadow:-1px 1px 0 var(--cyan)}
.lesson-nav::before{content:"";position:absolute;top:-1px;left:-1px;width:9px;height:9px;
  border-left:2px solid var(--cyan);border-top:2px solid var(--cyan)}
.ln-toggle{display:flex;align-items:center;justify-content:space-between;gap:8px;width:100%;
  box-sizing:border-box;padding:9px 12px;background:var(--surface);border:none;
  border-bottom:1px solid var(--outline-dim);cursor:pointer;font-family:var(--font-mono);
  font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.08em;color:var(--outline)}
.ln-toggle:hover{color:var(--primary)}
.ln-caret{font-size:10px;transition:transform .15s ease-out}
.lesson-nav.collapsed{width:auto}
.lesson-nav.collapsed .ln-title{display:none}
.lesson-nav.collapsed .ln-caret{transform:rotate(180deg)}
.lesson-nav.collapsed .ln-list{display:none}
.ln-list{display:flex;flex-direction:column;gap:2px;padding:6px;overflow-y:auto}
.ln-chip{display:flex;align-items:center;gap:8px;width:100%;box-sizing:border-box;padding:6px 8px;
  border:1px solid transparent;font-family:var(--font-body);font-size:12px;color:var(--ink-soft);
  text-decoration:none;line-height:1.2}
.ln-chip:hover{background:var(--surface);color:var(--primary);text-decoration:none}
.ln-chip.on{background:var(--surface-high);border-color:var(--outline-dim);color:var(--ink);font-weight:700}
.ln-badge{flex:0 0 auto;width:19px;height:19px;display:flex;align-items:center;justify-content:center;
  font-family:var(--font-mono);font-weight:700;font-size:11px;color:var(--deep);background:var(--cyan)}
.ln-badge.ln-plain{background:var(--outline-dim)}
.ln-chip.sec-c .ln-badge{background:var(--primary);color:var(--paper)}
.ln-chip.sec-d .ln-badge{background:var(--green-bright);color:var(--deep)}
.ln-chip.sec-e .ln-badge{background:var(--amber-bright);color:var(--deep)}
.ln-chip.sec-f .ln-badge{background:var(--error);color:var(--paper)}
.ln-chip.sec-g .ln-badge{background:var(--pink);color:var(--paper)}
.ln-chip.sec-m .ln-badge{background:var(--ink-accent);color:var(--paper)}
/* Chips stay equal WIDTH (the design choice) but a long section name now wraps to a second
   line instead of ellipsing: at 194px the only casualty was "Error Analysis Framework",
   which truncated to "Error Analysis Framew..." with 20px of free gutter beside the rail. */
.ln-lab{flex:1;min-width:0;overflow-wrap:break-word}
@media(max-width:1180px){.lesson-nav{width:176px}body.lnav-open .main{padding-right:196px}}
@media(max-width:980px){.lesson-nav{display:none}body.lnav-open .main{padding-right:var(--gutter)}}

/* ---- spaced-repetition review sessions (a due review asks questions) ---- */
.nav-due{margin-left:7px;font-family:var(--font-mono);font-size:10px;font-weight:700;
  background:var(--amber-bright);color:var(--deep);padding:0 6px}
.rv-start{margin:2px 0 14px}
.q-roll{display:block;margin-top:4px;font-family:var(--font-mono);font-size:10px;color:var(--amber)}
/* the queue chip reuses the existing .qact/.qday/.qtext rules above - do NOT
   redeclare a bare .qtext here, it would clobber the quiz's .qtext{flex:1} */
.q-more{color:var(--ink-soft)}

.rv-rail{margin:0 0 18px}
.rv-track{height:6px;background:var(--surface-high);border:1px solid var(--outline-dim)}
.rv-fill{height:100%;background:var(--cyan);box-shadow:0 0 6px var(--cyan-glow)}
.rv-meta{display:flex;flex-wrap:wrap;align-items:baseline;gap:12px;margin-top:7px;
  font-family:var(--font-mono);font-size:11px;color:var(--ink-soft)}
.rv-count{font-weight:700;color:var(--ink)}
.rv-owner{color:var(--primary);font-weight:700}
.rv-day{border:1px solid var(--outline-dim);padding:0 7px;letter-spacing:.08em;text-transform:uppercase}
.rv-src{color:var(--ink-accent)}

.rv-card{border:1px solid var(--primary);background:var(--surface-low);padding:18px 20px;
  margin:0 0 20px;position:relative;display:flex;flex-direction:column;gap:16px;max-width:78ch}
.rv-card::before,.rv-card::after{content:"";position:absolute;width:10px;height:10px;pointer-events:none}
.rv-card::before{top:-1px;left:-1px;border-top:2px solid var(--cyan);border-left:2px solid var(--cyan)}
.rv-card::after{bottom:-1px;right:-1px;border-bottom:2px solid var(--cyan);border-right:2px solid var(--cyan)}
.rv-prompt{font-size:15px;color:var(--ink)}
.rv-prompt p{margin:0 0 .7em}.rv-prompt p:last-child{margin-bottom:0}
.rv-prompt code{background:var(--surface-high);color:var(--primary);padding:1px 5px;border:1px solid var(--outline-dim)}
.rv-opts{display:flex;flex-direction:column;gap:7px}
.rv-opt{display:flex;align-items:flex-start;gap:10px;cursor:pointer;border:1px solid var(--outline-dim);
  background:var(--bg);padding:11px 13px;font-size:14px;color:var(--ink);min-height:44px}
.rv-opt:hover{border-color:var(--primary)}
.rv-opt input{margin:2px 0 0;width:17px;height:17px;accent-color:var(--primary);flex:0 0 auto}
.rv-otext{flex:1}
.rv-mark{display:none;font-family:var(--font-mono);font-weight:700;font-size:12px;flex:0 0 auto;min-width:16px}
.rv-opt.shown{cursor:default}
.rv-opt.shown .rv-mark{display:inline}
.rv-opt.right{border-color:var(--green);background:#e7f4e2}
.rv-opt.right .rv-mark::after{content:"OK";color:var(--green)}
.rv-opt.wrong{border-color:var(--error);background:#f7e4e4}
.rv-opt.wrong .rv-mark::after{content:"X";color:var(--error-ink)}
.rv-tag{font-family:var(--font-mono);font-size:10px;letter-spacing:.1em;text-transform:uppercase;
  color:var(--ink-soft);flex:0 0 auto;align-self:center}
.rv-verdict{font-family:var(--font-mono);font-size:12px;font-weight:700;letter-spacing:.08em;
  text-transform:uppercase;padding:7px 11px;border:1px solid var(--outline-dim)}
.rv-verdict.got{color:var(--green);border-color:var(--green);background:#e7f4e2}
.rv-verdict.miss{color:var(--error-ink);border-color:var(--error);background:#f7e4e4}
.rv-input{width:100%;font-family:var(--font-body);font-size:14px;line-height:1.5;color:var(--ink);
  background:var(--bg);border:1px solid var(--outline-dim);padding:11px 13px;resize:vertical}
.rv-input:focus{border-color:var(--primary);outline:2px solid var(--primary);outline-offset:1px}
.rv-lab{display:block;font-family:var(--font-mono);font-size:10px;letter-spacing:.16em;
  text-transform:uppercase;color:var(--ink-accent);margin-bottom:5px}
.rv-yours{border:1px solid var(--outline-dim);background:var(--bg);padding:11px 13px}
.rv-yourtext{white-space:pre-wrap;font-size:14px;color:var(--ink)}
.rv-model{border:1px solid var(--primary);background:var(--deep);color:#e6f7f8;padding:14px 16px}
.rv-model .rv-lab{color:var(--cyan-bright)}
.rv-model p{margin:0 0 .7em}.rv-model p:last-child{margin-bottom:0}
.rv-model code{font-family:var(--font-mono);font-size:.88em;color:var(--cyan-bright)}
.rv-model pre{background:var(--deep-2);border:1px solid var(--outline);padding:11px 13px;overflow-x:auto}
.rv-model a.rv-link{color:var(--cyan-bright);text-decoration:underline}
.rv-rate{display:flex;flex-wrap:wrap;align-items:center;gap:10px;padding-top:14px;
  border-top:1px dashed var(--outline-dim)}
.rv-rlab{font-family:var(--font-mono);font-size:11px;color:var(--ink-soft)}
.rv-got,.rv-miss{cursor:pointer;font-family:var(--font-mono);font-size:12px;font-weight:700;
  letter-spacing:.06em;text-transform:uppercase;min-height:44px;padding:0 18px;border:1px solid var(--outline)}
.rv-got{color:var(--green);background:transparent}
.rv-got:hover{background:var(--green-bright);color:var(--deep);border-color:var(--green-bright)}
.rv-miss{color:var(--error-ink);background:transparent}
.rv-miss:hover{background:var(--error);color:var(--paper);border-color:var(--error)}

.rv-results{margin:0 0 22px}
.rv-res{display:flex;flex-wrap:wrap;justify-content:space-between;align-items:center;gap:12px;
  border:1px solid var(--outline-dim);background:var(--surface-low);padding:12px 14px;margin-bottom:8px}
.rv-resmain{display:flex;flex-wrap:wrap;align-items:baseline;gap:12px;min-width:0}
.rv-restitle{font-family:var(--font-head);font-size:14px;color:var(--primary)}
.rv-resscore{font-family:var(--font-mono);font-size:12px;font-weight:700;color:var(--ink)}
.rv-resnext{font-family:var(--font-mono);font-size:11px;color:var(--ink-soft)}
.rv-override{display:flex;gap:5px;flex:0 0 auto}
.rv-override .rr{min-height:32px;padding:0 10px;cursor:pointer;border:1px solid var(--outline-dim);
  background:transparent;font-family:var(--font-mono);font-size:10px;font-weight:700;
  text-transform:uppercase;color:var(--ink-soft)}
.rv-override .rr:hover{border-color:var(--primary);color:var(--primary)}
.rv-override .rr.on{color:var(--deep);border-color:transparent}
.rv-override .rr.again.on{background:#e88b82}
.rv-override .rr.hard.on{background:var(--amber-bright)}
.rv-override .rr.good.on{background:var(--green-bright)}
.rv-override .rr.easy.on{background:var(--cyan-bright)}

/* ---- Roboteca web: dashboard hub + error log (webapp only) ---- */
.dsec{font-family:var(--font-head);font-size:15px;color:var(--ink);margin:22px 0 10px;
  padding-bottom:6px;border-bottom:1px solid var(--outline-dim)}
.dgrid{display:grid;grid-template-columns:repeat(2,1fr);gap:12px}
@media(max-width:760px){.dgrid{grid-template-columns:1fr}}
/* A course you OWN is a resume affordance, not a shop item: give it a full-width row so the
   progress bar has room to read as a measurement and the actions sit on one line (this is
   how the Dashboard.png reference lays it out). Unowned courses stay in the 2-up grid. */
.dgrid.dgrid-owned{grid-template-columns:1fr}
/* Unowned courses are the storefront row: auto-fit so three of them sit 3-up like the
   Dashboard.png reference, without hard-coding a column count that breaks at 1, 2 or 4. */
.dgrid.dgrid-more{grid-template-columns:repeat(auto-fit,minmax(250px,1fr))}
/* Course names wrap to one or two lines depending on width, so the blurb under them
   started at a different y in each card (measured: 21px apart at 820px). Subgrid makes
   every card in the row share one set of row tracks, so title / blurb / footer line up.
   THREE children per card - keep in sync with main.dashboard's more_cards. */
@supports (grid-template-rows: subgrid){
  .dgrid.dgrid-more{grid-auto-rows:auto}
  .dgrid.dgrid-more>.dcard{grid-row:span 3;display:grid;grid-template-rows:subgrid;gap:12px}
}
.dgrid-more .dcard{padding:18px 18px 16px;gap:12px}
.dgrid-more .dcard-name{font-size:17px;line-height:1.25}
.dgrid-more .dcard-top{align-items:flex-start}
.dgrid-more .dcard-sub{white-space:nowrap;padding-top:3px}
.dgrid-more .dcard-blurb{font-size:13px}
/* The all-access panel: centred call to action, full border (never a side stripe). */
.bundle-cta{margin-top:16px;padding:26px 22px;text-align:center;background:var(--surface-low)}
.bundle-h{font-family:var(--font-head);font-size:19px;color:var(--primary);margin:0 0 6px;letter-spacing:-.01em}
.bundle-p{color:var(--ink-soft);font-size:14px;max-width:62ch;margin:0 auto 16px}
.dgrid-owned .dcard-top{align-items:baseline}
.dgrid-owned .dcard-name{font-size:19px}
@media(min-width:761px){
  .dgrid-owned .dcard-foot{gap:20px}
  .dgrid-owned .dcard-sub{flex:0 0 auto}
}
.dcard{padding:16px 16px 14px;display:flex;flex-direction:column;gap:10px}
/* .n-bar is 120px because inside a topic row it sits beside a "4/26" counter. As a direct
   child of a course card it is a standalone progress bar and has to span the card, or it
   reads as a broken 120px stub in a 318px column. */
.dcard>.n-bar{width:auto}
.dcard-top{display:flex;align-items:baseline;justify-content:space-between;gap:10px}
.dcard-name{font-family:var(--font-head);font-size:16px;color:var(--primary);letter-spacing:-.01em}
.dcard-name:hover{color:var(--ink-accent);text-decoration:none}
.dcard-pct{font-family:var(--font-mono);font-size:12px;color:var(--primary);font-weight:700}
.dcard-sub{font-family:var(--font-mono);font-size:10px;color:var(--ink-soft);text-transform:uppercase;letter-spacing:.08em}
.dcard-blurb{font-size:13.5px;color:var(--ink-soft);margin:0;flex:1}
.dcard-foot{display:flex;align-items:center;justify-content:space-between;gap:10px;margin-top:auto}
.dcard-cta{display:flex;align-items:center;gap:10px}
.dcard.locked{background:var(--surface-low)}
.errlog{display:flex;flex-direction:column;gap:8px}
.err-item{border:1px solid var(--outline-dim);background:var(--surface-low);padding:11px 14px;position:relative}
.err-item::before{content:"";position:absolute;top:-1px;left:-1px;width:8px;height:8px;border-top:2px solid var(--error);border-left:2px solid var(--error)}
.err-top{display:flex;align-items:baseline;justify-content:space-between;gap:12px}
.err-mistake{font-family:var(--font-head);font-size:14px;color:var(--ink)}
.err-freq{font-family:var(--font-mono);font-size:11px;color:var(--amber);font-weight:700}
.err-line{font-size:13px;color:var(--ink-soft);margin-top:5px}
.err-line b{color:var(--ink-accent);font-family:var(--font-mono);font-size:11px;text-transform:uppercase;letter-spacing:.06em;margin-right:6px}

/* ---- Roboteca web: per-account licence watermark (spacing + size) ---- */
.wm-footer{font-family:var(--font-mono);font-size:11px;line-height:1.55;color:var(--ink-soft);
  background:var(--surface-low);border:1px solid var(--outline-dim);padding:8px 12px;margin:0 0 34px}
.wm-footer strong{color:var(--ink-accent)}
/* Sits just above the bottom bar, so it has to track --foot-h (it was a hard-coded 34px =
   28 + 6, which put it INSIDE the taller wrapped footer on phones). max-width keeps the
   licence line from spanning the column and printing across body text on narrow screens. */
.wm-overlay{position:fixed;right:12px;bottom:calc(var(--foot-h) + 6px);z-index:5;pointer-events:none;
  max-width:min(64ch,calc(100vw - 24px));text-align:right;
  font-family:var(--font-mono);font-size:10px;letter-spacing:.03em;color:var(--outline);opacity:.42}
@media print{.wm-overlay{display:none}}

/* ---- free-sample lesson: the CTA above the lesson (webapp only) ----
   A lesson is a long page. A reader who arrives from the email, decides in the first
   screen that this is for them, and finds nothing to click until they have scrolled the
   whole thing is a lost sale - so the offer appears at the top as well as the bottom.
   Compact on purpose: it must not push the lesson itself below the fold. */
/* A CLOSED box, with corner brackets like every other framed element here. It was a
   full-bleed rule (border-left/right:0, no side padding) and read as a band someone had
   cut the ends off, because nothing else on the page is open at the sides. */
.free-top{display:flex;align-items:center;gap:16px;flex-wrap:wrap;position:relative;
  border:1px solid var(--outline-dim);
  background:var(--surface-low);padding:14px 18px;margin:0 0 22px}
.free-top::before,.free-top::after{content:"";position:absolute;width:10px;height:10px;
  pointer-events:none}
.free-top::before{top:-1px;left:-1px;border-top:2px solid var(--cyan);
  border-left:2px solid var(--cyan)}
.free-top::after{bottom:-1px;right:-1px;border-bottom:2px solid var(--cyan);
  border-right:2px solid var(--cyan)}
/* No max-width. A 52ch cap wrapped the sentence onto two lines while a third of the band
   sat empty - the line length here is already bounded by the button beside it. min-width:0
   lets the flex item shrink below its min-content width instead of forcing the band wider. */
.free-top-t{margin:0;font-size:13px;color:var(--ink-soft);flex:1 1 auto;min-width:0}
.free-top-t b{color:var(--ink);font-weight:600}
/* margin-left:auto, not space-between: with one CTA the button pins to the right edge,
   and with several they stay grouped instead of spreading across the band. */
.free-top-cta{display:flex;gap:10px;flex-wrap:wrap;margin-left:auto}
.free-top-cta .btn,.free-top-cta .buy-btn{font-size:11px;padding:9px 14px;min-height:40px}
@media (max-width:620px){
  .free-top{gap:10px}
  .free-top-cta{width:100%;margin-left:0}
  .free-top-cta .btn,.free-top-cta .buy-btn{flex:1 1 auto;justify-content:center;
    min-height:44px;text-align:center}
}

/* ---- project build walkthrough + hidden solution (see docs/project-format-rework) ---- */
.build{border:1px solid var(--outline-dim);background:var(--surface-low);margin:22px 0;position:relative}
.build::before,.build::after{content:"";position:absolute;width:10px;height:10px;pointer-events:none}
.build::before{top:-1px;left:-1px;border-top:2px solid var(--cyan);border-left:2px solid var(--cyan)}
.build::after{bottom:-1px;right:-1px;border-bottom:2px solid var(--cyan);border-right:2px solid var(--cyan)}
.build-head{display:flex;align-items:center;gap:10px;padding:10px 14px;
  border-bottom:1px solid var(--outline-dim);background:var(--surface)}
.build-t{font-family:var(--font-mono);font-size:11px;font-weight:700;letter-spacing:.08em;
  text-transform:uppercase;color:var(--ink-accent)}
.build-count{margin-left:auto;font-family:var(--font-mono);font-size:11px;color:var(--outline)}
.bstep{border-bottom:1px solid var(--outline-dim)}
.bstep:last-child{border-bottom:none}
.bstep-head{display:flex;align-items:flex-start;gap:10px;padding:11px 14px;cursor:pointer;
  min-height:44px;background:none;border:none;width:100%;text-align:left}
.bstep-head:hover{background:var(--surface)}
.bstep-tick{margin:3px 0 0;width:17px;height:17px;flex:0 0 auto;accent-color:var(--primary);cursor:pointer}
.bstep-n{font-family:var(--font-mono);font-size:11px;color:var(--outline);flex:0 0 auto;padding-top:2px}
.bstep-t{font-weight:600;color:var(--ink);flex:1}
.bstep-t code{font-weight:400}
.bstep.done .bstep-t{color:var(--outline)}
.bstep.done .bstep-n::after{content:" OK";color:var(--primary);font-weight:700}
.bstep-body{padding:0 16px 14px 51px}
.bstep-body>*:first-child{margin-top:0}
.bstep-body>*:last-child{margin-bottom:0}
@media (max-width:640px){.bstep-body{padding-left:16px}}
/* the hidden full solution reuses .solutions, with a louder cue */
.build-sol{border-color:var(--primary)}
.build-sol>summary{color:var(--primary)}
.gate-note{font-size:13px;color:var(--ink-soft);margin:2px 0 10px}

/* =====================  TOUCH TARGETS (small screens only)  =====================
   Measured on a 360-390px viewport, every control was under the 44px guideline and
   several were under the 24px WCAG 2.5.8 floor: prev/next 18.6px, MATH/CODE 18px,
   Got/Missed 25px, lesson rows 37px, footer legal links 11px, topbar nav 17px.
   Only sizing/padding is touched here (no colours, no font families), and the whole
   block is scoped to <=900px so the deliberately dense desktop layout is unchanged.
   This is LAST in the file on purpose: media queries add no specificity, so it has to
   come after the base rules (and after the webapp block) to win. */
@media(max-width:900px){
  /* primary controls -> 44px */
  .lrow,.final-row{min-height:44px}
  .lnav{gap:14px}
  .lnav a{display:inline-flex;align-items:center;min-height:44px;padding:0 2px}
  .cr-got,.cr-miss,.sc-got,.sc-miss{min-height:44px;display:inline-flex;align-items:center;
    justify-content:center}
  .btn-primary,.btn-primary.sm,.btn-ghost,.btn-ghost.sm{min-height:44px}
  .qopt{min-height:44px;align-items:center}
  .xbtn{min-height:44px;padding:0 12px}
  .navtoggle{width:44px;height:44px}
  .nav-link{display:inline-flex;align-items:center;min-height:44px}
  .brand{display:inline-flex;align-items:center;min-height:44px}
  input:not([type=checkbox]):not([type=radio]),select{min-height:44px}
  .side-nav a{min-height:44px;display:flex;align-items:center}
  .node summary,.nav-curric>summary,.solutions>summary{min-height:44px}
  /* a bare <summary> is display:list-item with no padding — 17px tall, and it gates a
     destructive action ("Reset my progress"), so it is the worst target on the page */
  .danger summary{display:flex;align-items:center;min-height:44px}
  /* secondary utility chips -> comfortably over the 24px AA floor without 22 fat
     buttons per lesson (one MATH + one CODE sits above every answer box) */
  .qtool{min-height:32px;padding:0 12px;display:inline-flex;align-items:center;justify-content:center}
  /* inside the 46px bottom bar, so 44 will not fit — 26px clears WCAG 2.5.8 */
  .foot-legal a{display:inline-flex;align-items:center;min-height:26px;padding:0 2px}
}

/* ---- dark theme -------------------------------------------------------------------
   Three states, not two. An explicit choice stamps data-theme on <html>; the DEFAULT
   ("auto") stamps nothing, and only prefers-color-scheme separates light from dark - so
   the media query is guarded with :not([data-theme="light"]) and the palette is repeated
   under [data-theme="dark"] so the toggle wins in both directions.

   Only TOKENS are redefined here. Nothing below this block may declare a colour that
   exists only inside a media or [data-theme] rule; that is how a page ends up rendering
   one theme's text on the other theme's ground.

   --deep and --deep-2 deliberately do NOT change. They carry two jobs at once: the
   background of the surfaces that are dark in BOTH themes (recall cards, code panels, the
   code editor) and the ink on cyan / green / amber fills. Lightening them in dark mode
   would have turned every button label from near-black to mid-grey on a bright fill.
   In dark mode the cards therefore sit slightly ABOVE the page tone rather than below it,
   which is the right way round for a dark UI - and their cyan border carries the edge. */
@media (prefers-color-scheme:dark){
  :root:not([data-theme="light"]){
    --bg:#0e1116; --surface-low:#151a21; --surface:#1b222b; --surface-high:#232d37;
    --paper:#eafcff;
    --ink:#e8eef1; --ink-soft:#a2b4b9; --outline:#7f9198; --outline-dim:#31404b;
    --primary:#37c7cf; --ink-accent:#74e2e9;
    --cyan:#00b9c4; --cyan-bright:#45e7f1; --cyan-glow:rgba(0,185,196,.45);
    --amber:#f2c14e; --amber-bright:#f4b000; --green:#57cf4b; --green-bright:#2ae500;
    --pink:#e46ae4; --error:#c9313b; --error-ink:#ff9d94;
    --wash-bad:rgba(201,49,59,.20); --wash-good:rgba(42,229,0,.15);
  }
}
:root[data-theme="dark"]{
  --bg:#0e1116; --surface-low:#151a21; --surface:#1b222b; --surface-high:#232d37;
  --paper:#eafcff;
  --ink:#e8eef1; --ink-soft:#a2b4b9; --outline:#7f9198; --outline-dim:#31404b;
  --primary:#37c7cf; --ink-accent:#74e2e9;
  --cyan:#00b9c4; --cyan-bright:#45e7f1; --cyan-glow:rgba(0,185,196,.45);
  --amber:#f2c14e; --amber-bright:#f4b000; --green:#57cf4b; --green-bright:#2ae500;
  --pink:#e46ae4; --error:#c9313b; --error-ink:#ff9d94;
  --wash-bad:rgba(201,49,59,.20); --wash-good:rgba(42,229,0,.15);
}
/* The theme control: three states in one button, because "follows your system" is a real
   state a two-way switch cannot express. Text, not an icon - the UI carries no icon font
   and a sun/moon glyph would be the only one on the site. */
.themetoggle{font-family:var(--font-mono);font-size:10px;font-weight:700;letter-spacing:.1em;
  text-transform:uppercase;min-height:32px;padding:0 10px;cursor:pointer;
  background:none;border:1px solid var(--outline-dim);color:var(--ink-soft)}
.themetoggle:hover{border-color:var(--cyan);color:var(--primary)}
/* A half-filled circle, drawn in CSS. NOT a glyph: the mono stacks are latin-subset
   first, and U+25D0 would render as tofu wherever JetBrains Mono is the face that
   resolves. Same reason the rest of the UI carries no icon font. */
.themetoggle .tt-k{width:10px;height:10px;border-radius:50%;display:inline-block;
  vertical-align:-1px;margin-right:5px;border:1px solid currentColor;
  background:linear-gradient(90deg,currentColor 0 50%,transparent 50% 100%)}
/* The 900px touch-target block sits EARLIER in this file, and a media query adds no
   specificity - so a floor declared up there would lose to the 32px above. It has to
   be restated here, after the base rule. */
@media(max-width:900px){.themetoggle{min-height:44px}}
@media(max-width:620px){.themetoggle{padding:0 8px;letter-spacing:.06em}}
