/* Number Theory Frontier: no build step, everything in this file.
   The one external dependency is the Google Fonts link in each page's head
   (Space Grotesk + IBM Plex Mono); the system stacks below keep the app
   readable offline. Dark theme on purpose: the graph is the demo, and glowing
   nodes on a dark field read from the back of a room. */

:root {
  /* --locked-fill / --frontier / --mastered: keep in sync with fillFor()
     in graph.js; the fill ramp duplicates these hexes on purpose. */
  --bg: #08090D;
  --panel: #0A0C10;
  --panel-2: #10141B;
  --inset: #161C26;
  --line: #222A36;
  --text: #E7EDF5;
  --text-2: #A7B3C4;
  --muted: #7C8899;
  --dim: #5C6775;
  --mastered: #45C4B0;
  --mastered-glyph: #7DDCCB;
  --mastered-fill: #0D1B17;
  --inferred: #45C4B0;
  --frontier: #A3C585;
  --frontier-fill: #141A10;
  --frontier-label: #C9DBB2;
  --locked-fill: #10141B;
  --locked-edge: #2E394A;
  --danger: #E8879C;
  --accent: #6366F1;
  --link: #A5B4FC;
  --violet: #A78BFA;
  --amber: #E8C170;   /* hints: a warm nudge, not an alarm */
  --sky: #7EA6E0;     /* key results: reference material, calm and cool */
  --mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  /* dungeon-only fire palette (challenges page); the rest of the site
     never uses these, and cosmetic name colors stay on the tokens above */
  --ember: #FF5C3C;
  --ember-soft: #FDBA8C;
  --heart: #FF5C7A;
  --depth-cyan: #22D3EE;
  --streak-lime: #B8F135;
}

* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
body {
  background: var(--bg);
  /* faint warm, indigo and teal washes: atmosphere, never foreground */
  background-image:
    radial-gradient(1000px 480px at 50% -6%, rgba(255, 196, 150, .05), transparent 70%),
    radial-gradient(820px 460px at 12% 60%, rgba(99, 102, 241, .07), transparent 70%),
    radial-gradient(760px 420px at 88% 70%, rgba(69, 196, 176, .05), transparent 70%);
  background-attachment: fixed;
  color: var(--text);
  font: 16px/1.55 "Space Grotesk", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  /* animating percentages must not jitter sideways as digits change */
  font-variant-numeric: tabular-nums;
}

header {
  display: flex; justify-content: space-between; align-items: center;
  flex-wrap: wrap; row-gap: 8px;
  padding: 16px 24px; border-bottom: 1px solid var(--inset);
}
a.brand {
  display: flex; align-items: center; gap: 12px;
  color: var(--text); text-decoration: none;
}
.wordmark { font-size: 14px; font-weight: 600; letter-spacing: .3em; }
.topnav { display: flex; gap: 4px; }
.topnav a {
  color: var(--muted); text-decoration: none; font-size: 14.5px;
  padding: 6px 12px; border-radius: 8px;
}
.topnav a:hover { color: var(--text); background: var(--panel-2); }
.topnav a.active { color: var(--link); background: var(--inset); }
.head-right { display: flex; align-items: center; gap: 14px; }
.context { font-family: var(--mono); font-size: 12px; color: var(--muted); }
.avatar {
  width: 34px; height: 34px; border-radius: 50%;
  border: 1px solid var(--locked-edge);
  display: flex; align-items: center; justify-content: center;
}
.who { color: var(--muted); font-size: 13.5px; font-family: var(--mono); }
.who a { color: var(--link); text-decoration: none; margin-left: 8px; }

section { padding: 22px; }
.card {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 12px;
  padding: 20px;
}
h1 { font-size: 26px; margin: 0 0 8px; }
h2 { font-size: 17px; margin: 0 0 10px; }
.muted { color: var(--muted); }
.tiny { font-size: 13px; }
b.fr { color: var(--frontier); }

/* ---- home: hero + two directions ---- */
.home {
  position: relative; overflow: hidden;
  min-height: calc(100vh - 70px);   /* fills the viewport under the header */
  display: flex; flex-direction: column;
}
.home-wrap {
  max-width: 980px; width: 100%; margin: 8vh auto 0; padding: 0 20px;
  text-align: center; position: relative;
}
/* entrance: hero, then cards, then progress, each rising into place */
.rise { animation: rise-in .9s ease-out both; }
.rise-2 { animation-delay: .15s; }
.rise-3 { animation-delay: .3s; }
@keyframes rise-in {
  from { opacity: 0; transform: translateY(14px); }
  to   { opacity: 1; transform: translateY(0); }
}
.hero-title {
  font-size: clamp(30px, 6vw, 56px); font-weight: 500;
  letter-spacing: .38em; text-indent: .38em;
  margin: 0; color: #F2F5FA; text-shadow: 0 0 44px rgba(165, 180, 252, .22);
}
.hero-rule {
  width: 34px; height: 2px; background: var(--accent);
  border-radius: 2px; margin: 22px auto;
}
.hero-tag { margin: 0; font-size: 20px; color: var(--text-2); }
.hl-indigo { color: var(--link); }
.hl-teal { color: var(--mastered-glyph); }

.dir-cards {
  display: grid; grid-template-columns: 1fr 1fr; gap: 24px;
  margin-top: 56px; text-align: left;
}
a.dir-card {
  color: inherit; text-decoration: none;
  display: flex; flex-direction: column; gap: 14px;
  padding: 28px; background: rgba(16, 20, 27, .74);
  border-radius: 16px; backdrop-filter: blur(10px);
  transition: border-color .3s, box-shadow .3s, transform .2s;
}
.dir-card:hover { transform: translateY(-2px); }
#dir-learn:hover { border-color: #3D437E; box-shadow: 0 0 46px rgba(99, 102, 241, .14); }
#dir-practice:hover { border-color: #5C6B4E; box-shadow: 0 0 46px rgba(163, 197, 133, .09); }
.dir-top {
  display: flex; justify-content: space-between; align-items: flex-start;
  gap: 16px; flex-wrap: wrap;
}
.dir-name { font-size: 24px; font-weight: 600; letter-spacing: .2em; margin: 0; }
.dir-rule { width: 26px; height: 2px; border-radius: 2px; }
.dir-rule.indigo { background: var(--accent); }
.dir-rule.sage { background: var(--frontier); }
.dir-card > p { margin: 0; max-width: 36ch; line-height: 1.6; }
.dir-arrow {
  align-self: flex-end; width: 44px; height: 44px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
}
.dir-arrow.indigo { border: 1.4px solid var(--accent); box-shadow: 0 0 18px rgba(99, 102, 241, .18); }
.dir-arrow.sage { border: 1.4px solid #7E9E60; box-shadow: 0 0 18px rgba(163, 197, 133, .14); }

/* the tilted "from a lesson" mini-card on the LESSONS direction */
.mini-example {
  width: 176px; max-width: 100%; flex: none;
  background: #0C0F15; border: 1px solid #1D2532; border-radius: 10px;
  padding: 13px 14px 15px; transform: rotate(1.6deg);
  box-shadow: 0 8px 26px rgba(0, 0, 0, .35);
  font-family: var(--mono); font-size: 11px; color: var(--muted); line-height: 1.75;
}
.mini-example .t {
  text-transform: uppercase; letter-spacing: .13em; font-size: 9.5px;
  margin-bottom: 6px;
}
.mini-example .hi { color: var(--link); }
.mini-example .r { color: var(--mastered-glyph); }

/* the ascending bars on the CHALLENGES direction */
.bars { display: flex; align-items: flex-end; gap: 7px; padding: 6px 4px 2px; opacity: .85; }
.bars i { width: 14px; border-radius: 3px; background: var(--inset); }
.bars i:nth-child(1) { height: 16px; }
.bars i:nth-child(2) { height: 26px; background: var(--locked-edge); }
.bars i:nth-child(3) { height: 38px; background: #3A4A5E; }
.bars i:nth-child(4) {
  height: 52px; background: linear-gradient(180deg, #A3C585, #7E9E60);
  box-shadow: 0 0 16px rgba(163, 197, 133, .25);
}

/* mastery progress, always visible; home.js fills in the real numbers */
.home-progress {
  margin: 60px auto 48px;
  display: flex; flex-direction: column; align-items: center; gap: 13px;
}
.prog-label { font-size: 15px; color: var(--text-2); }
.prog-label .num { font-family: var(--mono); font-weight: 600; color: var(--mastered-glyph); }
.prog-bar {
  width: min(480px, 80vw); height: 6px; border-radius: 4px;
  background: var(--inset); overflow: hidden;
}
.prog-fill {
  height: 100%; border-radius: 4px; width: 0;
  background: linear-gradient(90deg, var(--accent), var(--mastered));
  box-shadow: 0 0 14px rgba(69, 196, 176, .35);
  transition: width .6s ease;
}

/* floating corner sketches: decoration is the subject, not stock art.
   They live behind the content at low opacity, so at any window size they
   keep the page alive without ever fighting the text for attention. */
.decor { position: absolute; inset: 0; pointer-events: none; }
.decor svg { position: absolute; animation: drift 18s ease-in-out infinite; }
.decor .d1 { top: 70px; left: 24px; }
.decor .d2 { top: 80px; right: 28px; animation-delay: 2s; animation-duration: 21s; }
.decor .d3 { bottom: 24px; left: 40px; animation-delay: 1s; animation-duration: 20s; }
.decor .d4 { bottom: 40px; right: 44px; animation-delay: 3s; }
@keyframes drift {
  0%, 100% { transform: translateY(0); opacity: .55; }
  50% { transform: translateY(-10px); opacity: .9; }
}
/* the ring and triangle draw themselves in on load */
.sketch-line { stroke-dasharray: 420; animation: sketch-draw 3.2s ease-out .3s both; }
.d2 .sketch-line { animation-delay: .8s; animation-duration: 3.4s; }
@keyframes sketch-draw {
  from { stroke-dashoffset: 420; } to { stroke-dashoffset: 0; }
}
@media (max-width: 700px) { .decor { opacity: .25; } }
@media (max-width: 1000px) { .dir-cards { grid-template-columns: 1fr; } }
@media (max-width: 640px) { .context { display: none; } }

/* ---- learn: sticky skill-map column + open lesson ---- */
.learn-wrap { max-width: 1360px; margin: 0 auto; }
.learn-layout {
  display: grid; grid-template-columns: 540px minmax(0, 1fr);
  gap: 40px; align-items: start;
}
.learn-side {
  position: sticky; top: 20px;
  max-height: calc(100vh - 40px); overflow-y: auto; padding-right: 4px;
}
@media (max-width: 1280px) {
  .learn-layout { grid-template-columns: 440px minmax(0, 1fr); gap: 28px; }
}
@media (max-width: 1100px) {
  .learn-layout { grid-template-columns: 380px minmax(0, 1fr); }
}

.learn-main { max-width: 820px; min-width: 0; }
.lesson-title { font-size: 36px; font-weight: 600; letter-spacing: -0.025em; margin: 4px 0 14px; }
.lesson-meta { display: flex; align-items: center; gap: 12px; margin-bottom: 20px; }
.lesson-meta .pill { margin-bottom: 0; }
.card.beyond { margin-top: 28px; }
.card.beyond p { margin: 6px 0 0; }
.prereq-line .chip { text-decoration: none; color: var(--text); }
.prereq-line .chip:hover { border-color: var(--accent); }
.lede { font-size: 17px; line-height: 1.65; max-width: 62ch; }
.lsec { margin-top: 18px; }

@media (max-width: 900px) {
  .learn-layout { grid-template-columns: 1fr; gap: 20px; }
  .learn-side { position: static; max-height: none; overflow: visible; }
  /* stacked: cap the map so the lesson isn't pushed below the fold */
  .learn-side #graph-holder { max-height: 45vh; }
}
.eyebrow {
  font-family: var(--mono); font-size: 11px; letter-spacing: .14em;
  text-transform: uppercase; color: var(--muted); margin-bottom: 5px;
}
.lsec p { margin: 0; line-height: 1.65; max-width: 62ch; }
/* the math boxes read as mono insets, like the mockup's code blocks */
.lsec.keyres p {
  background: #0C0F15; border: 1px solid #1D2532; border-left: 3px solid var(--sky);
  border-radius: 8px; padding: 12px 16px;
  font-family: var(--mono); font-size: 13.5px; line-height: 1.8; color: var(--text-2);
}
.lsec.worked p {
  background: #0C0F15; border: 1px solid #1D2532; border-left: 3px solid var(--mastered);
  border-radius: 8px; padding: 12px 16px;
  font-family: var(--mono); font-size: 13.5px; line-height: 1.8; color: var(--text-2);
}
.lsec.mistakes p {
  background: var(--inset); border-left: 3px solid var(--danger);
  border-radius: 6px; padding: 12px 14px; color: var(--text-2);
}
.lsec.asked p { color: var(--muted); font-size: 14px; }
.lesson-cta { margin-top: 22px; }
.btn-link {
  display: inline-block; background: var(--accent); color: #08090D;
  border-radius: 8px; padding: 10px 16px; font-size: 15px; font-weight: 600;
  text-decoration: none;
}
.btn-link:active { transform: translateY(1px); }
.lesson-nav {
  display: flex; justify-content: space-between; gap: 12px; margin-top: 26px;
  padding-top: 14px; border-top: 1px solid var(--line); font-size: 14px;
}
.lesson-nav a { color: var(--accent); text-decoration: none; }
.lesson-nav a:hover { text-decoration: underline; }
.lesson sup { font-size: .72em; }
.round-card { margin-top: 28px; }

/* ---- login ---- */
.login-card { max-width: 440px; margin: 10vh auto 0; text-align: center; }
.login-card form { display: flex; gap: 10px; margin-top: 18px; }

/* ---- inputs & buttons ---- */
input[type="text"] {
  flex: 1; min-width: 0;
  background: var(--inset); color: var(--text);
  border: 1px solid var(--line); border-radius: 8px;
  padding: 10px 12px; font-size: 15px; font-family: var(--mono);
}
input[type="text"]:focus {
  outline: none; border-color: var(--accent);
  box-shadow: 0 0 0 3px rgba(99, 102, 241, .25);
}
button {
  border: 0; border-radius: 8px; padding: 10px 16px;
  font-size: 15px; font-weight: 600; cursor: pointer;
}
button.primary { background: var(--accent); color: #08090D; }
button.primary:active { transform: translateY(1px); }
/* answering at the frontier is the sage action; login/diagnostic stay indigo */
#prac-form button.primary { background: var(--frontier); }
button.ghost {
  background: transparent; color: var(--muted);
  border: 1px solid var(--line);
}
button.ghost:hover { color: var(--text); }

/* ---- question cards ---- */
.q-card { max-width: 560px; margin: 8vh auto 0; }
.q-top { display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px; }
.tag {
  background: var(--inset); border: 1px solid #3A4B7B;
  color: var(--link); border-radius: 999px;
  font-family: var(--mono); font-size: 12px; padding: 3px 12px;
}
.prompt { font-size: 19px; margin: 10px 0 4px; }
.prompt sup { font-size: 12px; }
#diag-form, #prac-form, .answer-form { display: flex; gap: 10px; margin-top: 14px; }
/* four feedback kinds, four colours: correct=teal, wrong=rose,
   unparseable=neutral (it costs nothing, it must not look like a miss),
   mastered=violet (the model talking) */
.feedback { min-height: 22px; margin: 10px 0 0; font-weight: 600; }
.feedback.good { color: var(--mastered); }
.feedback.bad { color: var(--danger); }
.feedback.info { color: var(--text-2); }
.feedback.mastered { color: var(--violet); }
.hint {
  margin-top: 10px; padding: 10px 12px; font-size: 14px;
  background: var(--inset); border-left: 3px solid var(--amber);
  border-radius: 6px; color: var(--text-2); line-height: 1.5;
}

/* ---- give up / reveal (PROJECT.md 5.3: the cost is the streak) ---- */
button.ghost.reveal { margin-top: 10px; font-weight: 400; }
button.ghost.reveal .cost { color: var(--muted); font-size: 13px; }
button.ghost.reveal.armed {
  color: var(--danger); border-color: var(--danger); font-weight: 600;
}
/* the reveal ladder is Python-computed: numbered, mono, final line teal;
   aligned arithmetic reads as rigour */
.steps {
  margin-top: 10px; display: flex; flex-direction: column; gap: 6px;
  counter-reset: step;
}
.steps .step {
  position: relative; padding: 8px 12px 8px 34px;
  font-family: var(--mono); font-size: 13.5px; line-height: 1.6;
  background: var(--panel-2); border-left: 3px solid var(--accent);
  border-radius: 6px;
}
.steps .step::before {
  counter-increment: step; content: counter(step);
  position: absolute; left: 12px; color: var(--dim);
}
.steps .step:last-child {
  border-left-color: var(--mastered); color: var(--mastered-glyph); font-weight: 600;
}
.steps .step sup { font-size: 11px; }

/* ---- pips (the round: one per problem, teal right, rose wrong) ---- */
.pips { display: inline-flex; gap: 6px; }
.pip {
  width: 14px; height: 14px; border-radius: 50%;
  border: 2px solid var(--muted); display: inline-block;
  transition: background .25s, border-color .25s;
}
.pip.good { background: var(--mastered); border-color: var(--mastered); }
.pip.bad { background: var(--danger); border-color: var(--danger); }
.pips.shake { animation: shake .45s; }
@keyframes shake {
  20% { transform: translateX(-4px); } 40% { transform: translateX(4px); }
  60% { transform: translateX(-3px); } 80% { transform: translateX(3px); }
}

/* ---- graph screen: map region left, sticky inspector right ---- */
.graph-wrap {
  display: grid; grid-template-columns: minmax(0, 1fr) 330px;
  gap: 40px; max-width: 1360px; margin: 0 auto;
  align-items: start;
}
.map-head {
  display: flex; justify-content: space-between; align-items: baseline;
  gap: 16px; margin-bottom: 14px;
}
.map-head .eyebrow { margin-bottom: 6px; }
.map-sub { font-size: 14px; color: #8B96AD; line-height: 1.5; }
.map-count {
  font-family: var(--mono); font-size: 12px; color: var(--muted); flex: none;
}
.graph-pane {
  padding: 12px; border-radius: 18px; border-color: var(--inset);
  /* the integer lattice: the decoration is literally the subject */
  background-color: #090B10;
  background-image: radial-gradient(#141C2A 1px, transparent 1px);
  background-size: 24px 24px;
}
#graph-holder { max-height: 76vh; overflow-y: auto; }
#graph-holder svg { width: 100%; height: auto; display: block; }
.legend {
  display: flex; flex-wrap: wrap; gap: 18px;
  margin-top: 14px; font-size: 12.5px; color: var(--muted);
}
.dot {
  width: 10px; height: 10px; border-radius: 50%;
  display: inline-block; margin-right: 6px; vertical-align: -1px;
}
.dot.mastered { background: var(--mastered); }
.dot.inferred { background: transparent; border: 2px dashed var(--inferred); opacity: .6; }
.dot.frontier { background: var(--frontier); }
.dot.locked { background: transparent; border: 1px solid var(--locked-edge); }
.dot.gate {
  border-radius: 2px; background: rgba(232, 135, 156, .6);
  transform: rotate(45deg);
}
/* the continuous fill scale: navy 0% -> sage 50% -> teal 95%+ */
.dot.grad {
  width: 30px; border-radius: 6px;
  background: linear-gradient(90deg, #182130, var(--frontier) 55%, var(--mastered));
}
.side-pane {
  display: flex; flex-direction: column; gap: 14px;
  position: sticky; top: 24px;
}
.sum-head {
  display: flex; justify-content: space-between; align-items: center;
  gap: 12px; margin-bottom: 8px;
}
.sum-head h2 { margin: 0; }

@media (max-width: 980px) {
  .graph-wrap { grid-template-columns: 1fr; gap: 20px; }
  .side-pane { position: static; }
  #graph-holder { max-height: 55vh; }
}

/* ---- SVG nodes ---- */
.halos circle { transition: opacity .35s; }
g.node circle { transition: fill .35s, stroke .35s, fill-opacity .35s, r .35s; }
g.node text {
  fill: var(--text-2); font-size: 12px; text-anchor: middle;
  font-family: var(--mono); letter-spacing: .02em;
  pointer-events: none;
}
g.node.locked circle { fill: var(--locked-fill); stroke: var(--locked-edge); stroke-width: 1.5; }
g.node.locked text { fill: var(--dim); }
g.node.mastered circle { fill: var(--mastered-fill); stroke: rgba(69, 196, 176, .55); stroke-width: 1.5; }
g.node.mastered text { fill: var(--mastered-glyph); }
/* inference is a guess: dashed and dimmer, never presented as measured */
g.node.mastered.inferred circle {
  fill: var(--mastered-fill); fill-opacity: .45;
  stroke: var(--inferred); stroke-opacity: .55;
  stroke-dasharray: 5 4; stroke-width: 2;
}
g.node.frontier circle {
  fill: var(--frontier-fill); stroke: var(--frontier); stroke-width: 3;
  filter: drop-shadow(0 0 8px rgba(163, 197, 133, .35));
  animation: pulse 2.2s ease-in-out infinite;
}
g.node.frontier text { fill: var(--frontier-label); }
g.node { cursor: pointer; }
g.node.clickable:hover circle { stroke-width: 4; }
/* fog: locked nodes wear a "?" until the frontier reaches them */
g.node text.glyph {
  font-size: 14px; fill: var(--dim); opacity: 0;
  transition: opacity .35s;
}
g.node.locked text.glyph { opacity: 1; }
/* the RSA mastery gate: rose while it is still out of reach */
g.node.gate.locked circle { stroke: rgba(232, 135, 156, .5); }
g.node.gate.locked text.glyph { fill: #F0A8B8; }
/* the inspected node: indigo ring, independent of status styling */
g.node circle.ring {
  fill: none; stroke: var(--accent); stroke-width: 2;
  opacity: 0; transition: opacity .2s;
  animation: none; filter: none;
}
g.node.selected circle.ring { opacity: 1; }
@keyframes pulse {
  0%, 100% { stroke-opacity: 1; } 50% { stroke-opacity: .45; }
}
g.node.unlocking circle {
  transform-box: fill-box; transform-origin: center;
  animation: pop .7s ease-out;
}
/* the unlock beat: scale pop with a sage stroke flash, the one place the
   accent gets spent at full strength */
@keyframes pop {
  0% { transform: scale(1); }
  45% { transform: scale(1.8); stroke: var(--frontier); stroke-opacity: 1; }
  100% { transform: scale(1); }
}

/* ---- knowledge ripples: nodes one answer just moved, sized by |delta|.
   These come after the state rules on purpose: equal specificity, later
   wins, so a rippling frontier node pauses its idle pulse for the beat. */
g.node.moved-sm circle, g.node.moved-md circle, g.node.moved-lg circle {
  transform-box: fill-box; transform-origin: center;
}
g.node.moved-sm circle { animation: ripple-sm .9s ease-out; }
g.node.moved-md circle { animation: ripple-md .9s ease-out; }
g.node.moved-lg circle { animation: ripple-lg .9s ease-out; }
/* a drop reads red for its beat: the cost of a wrong answer or a reveal */
g.node.down circle { stroke: var(--danger); }
@keyframes ripple-sm {
  0% { transform: scale(1); } 35% { transform: scale(1.15); stroke-width: 3; }
  100% { transform: scale(1); }
}
@keyframes ripple-md {
  0% { transform: scale(1); } 35% { transform: scale(1.32); stroke-width: 4; }
  100% { transform: scale(1); }
}
@keyframes ripple-lg {
  0% { transform: scale(1); } 35% { transform: scale(1.55); stroke-width: 5; }
  100% { transform: scale(1); }
}

/* ---- SVG edges ---- */
path.edge { fill: none; stroke: #1B2331; stroke-width: 1.6; transition: stroke .35s; }
path.edge.to-frontier {
  stroke: rgba(163, 197, 133, .4); stroke-width: 2; stroke-dasharray: 4 4;
}
path.edge.to-mastered { stroke: rgba(69, 196, 176, .3); }
/* propagation beat: evidence flows down the edges into each moved node.
   Placed after the state tints: equal specificity, later wins for the beat. */
path.edge.pulse-up, path.edge.pulse-down, path.edge.unlock-edge {
  stroke-dasharray: 6 10; stroke-width: 2;
  animation: edge-flow .9s linear;
}
path.edge.pulse-up { stroke: var(--mastered); }
path.edge.pulse-down { stroke: var(--danger); }
path.edge.unlock-edge { stroke: var(--frontier); }
@keyframes edge-flow {
  from { stroke-dashoffset: 32; } to { stroke-dashoffset: 0; }
}

/* ---- toast ---- */
#toast {
  position: fixed; left: 50%; bottom: 28px; transform: translateX(-50%);
  background: var(--panel-2); border: 1px solid var(--frontier);
  color: var(--text); border-radius: 10px; padding: 12px 20px;
  font-size: 15px; box-shadow: 0 8px 30px rgba(0,0,0,.5);
  animation: rise .4s ease-out;
}
@keyframes rise {
  from { opacity: 0; transform: translate(-50%, 12px); }
  to   { opacity: 1; transform: translate(-50%, 0); }
}

/* ---- wrong-origin warning ---- */
.warn-card { text-align: left; border-color: var(--danger); }
.warn-card h1 { font-size: 21px; color: var(--danger); }
code {
  font: 14px var(--mono);
  background: var(--panel-2); border: 1px solid var(--line);
  border-radius: 6px; padding: 1px 7px;
}
pre {
  background: var(--panel-2); border: 1px solid var(--line);
  border-radius: 8px; padding: 10px 14px; overflow-x: auto;
}
pre code { background: transparent; border: 0; padding: 0; }
.warn-card a { color: var(--accent); }

/* ---- diagnostic progress dots ---- */
.diag-right { display: inline-flex; align-items: center; gap: 10px; }
.diag-dots { display: inline-flex; gap: 5px; }
.ddot {
  width: 9px; height: 9px; border-radius: 50%;
  border: 1px solid var(--locked-edge); display: inline-block;
}
.ddot.done { background: var(--mastered); border-color: var(--mastered); }
.ddot.now { border-color: var(--link); box-shadow: 0 0 0 2px rgba(99, 102, 241, .25); }

/* ---- selected-skill card ---- */
.skill-name { font-size: 22px; letter-spacing: -0.01em; margin: 4px 0 10px; }
.pill {
  display: inline-block; font-family: var(--mono); font-size: 10.5px;
  padding: 3px 10px; border-radius: 20px; margin-bottom: 12px;
}
.pill.mastered { color: var(--mastered-glyph); background: rgba(69, 196, 176, .08); border: 1px solid rgba(69, 196, 176, .25); }
.pill.frontier { color: var(--frontier-label); background: rgba(163, 197, 133, .08); border: 1px solid rgba(163, 197, 133, .25); }
.pill.locked { color: var(--muted); background: rgba(124, 136, 153, .08); border: 1px solid rgba(124, 136, 153, .22); }
/* lesson-path states mirror the graph pair: complete = teal, ready = sage */
.pill.complete { color: var(--mastered-glyph); background: rgba(69, 196, 176, .08); border: 1px solid rgba(69, 196, 176, .25); }
.pill.ready { color: var(--frontier-label); background: rgba(163, 197, 133, .08); border: 1px solid rgba(163, 197, 133, .25); }

/* ---- the locked-lesson panel (/learn hard lock) ---- */
.locked-list { margin-top: 22px; }
.locked-row {
  display: flex; align-items: center; justify-content: space-between;
  gap: 14px; padding: 11px 2px; border-bottom: 1px solid #1D2532;
}
.locked-row:last-child { border-bottom: none; }
.locked-row-name { font-weight: 600; font-size: 14.5px; }
.skill-desc { margin: 0 0 14px; font-size: 14.5px; line-height: 1.6; }
.skill-prereqs { border-top: 1px solid var(--inset); padding-top: 12px; margin-bottom: 16px; }
.skill-prereqs p { margin: 4px 0 0; }
.prereq-row {
  display: flex; gap: 10px; align-items: center;
  font-size: 14px; color: var(--text-2); margin: 7px 0;
}
.prereq-mark {
  flex: none; width: 16px; height: 16px; border-radius: 50%;
  border: 1px solid var(--locked-edge);
  display: flex; align-items: center; justify-content: center;
  font-size: 9px; color: var(--mastered-glyph);
}
.prereq-row.done { color: var(--muted); }
.prereq-row.done .prereq-mark {
  background: rgba(69, 196, 176, .12); border-color: rgba(69, 196, 176, .4);
}
button.cta { width: 100%; font-size: 14.5px; }
button.cta.frontier { background: var(--frontier); color: #08090D; }
button.cta.mastered { background: transparent; color: var(--link); border: 1px solid #3D437E; }
button.cta.locked {
  background: transparent; color: var(--muted);
  border: 1px solid var(--line); cursor: default;
}

/* ---- diagnostic summary ---- */
.sum-row { margin: 6px 0; font-size: 14px; }
.sum-row .n { color: var(--muted); }
.chip {
  display: inline-block; background: var(--panel-2); border: 1px solid var(--line);
  border-radius: 999px; padding: 1px 10px; font-size: 12.5px; margin: 2px 3px 2px 0;
}
.chip.pass { border-color: var(--mastered); color: var(--mastered); }
.chip.fail { border-color: var(--danger); color: var(--danger); }

/* ---- reduced motion: state changes stay, oscillation goes ---- */
@media (prefers-reduced-motion: reduce) {
  g.node.frontier circle,                       /* static sage ring remains */
  g.node.unlocking circle,
  g.node.moved-sm circle, g.node.moved-md circle, g.node.moved-lg circle,
  path.edge.pulse-up, path.edge.pulse-down, path.edge.unlock-edge,
  .pips.shake, .decor svg, .sketch-line, .rise, #toast {
    animation: none;
  }
}

/* ---- dungeon (challenges page) ---- */
/* the header's earned identity: color ids the server hands out map to the
   existing palette tokens here; no hex ever travels through the API */
.who-title { color: var(--muted); font-size: 12px; margin-right: 6px; }
.name-sage   { color: var(--frontier); }
.name-teal   { color: var(--mastered); }
.name-sky    { color: var(--sky); }
.name-violet { color: var(--violet); }
.name-amber  { color: var(--amber); }
.name-rose   { color: var(--danger); }

/* the page: everything sits above one fixed cave backdrop; body.boss-mode
   retints that backdrop (and the HUD/card below) without touching layout */
body.dungeon { background-color: #070A12; }
body.dungeon header { position: relative; z-index: 2; }
body.dungeon section { position: relative; z-index: 1; }
/* several dungeon elements are display:flex, which beats the UA's default
   [hidden] rule; this puts hidden back in charge */
body.dungeon [hidden] { display: none !important; }

.dungeon-bg { position: fixed; inset: 0; z-index: 0; pointer-events: none; }
.dungeon-bg::before {
  content: ""; position: absolute; inset: 0;
  background:
    radial-gradient(900px 460px at 50% -18%, rgba(99, 102, 241, .13), transparent 70%),
    radial-gradient(1100px 420px at 50% 118%, rgba(255, 92, 60, .09), transparent 68%);
}
.cave-top { position: absolute; top: 0; left: 0; width: 100%; height: 110px; }
.cave-floor { position: absolute; bottom: 0; left: 0; width: 100%; height: 80px; }
.cave-back { fill: rgba(10, 14, 26, .9); }
.cave-front { fill: #03050A; }
.vignette {
  position: absolute; inset: 0;
  background:
    linear-gradient(to right, rgba(2, 3, 8, .8), transparent 9%),
    linear-gradient(to left, rgba(2, 3, 8, .8), transparent 9%),
    radial-gradient(120% 90% at 50% 45%, transparent 58%, rgba(2, 3, 8, .6) 100%);
}
.ember {
  position: absolute; width: 220px; height: 280px;
  background: radial-gradient(closest-side, rgba(255, 150, 80, .10), transparent 70%);
  animation: flicker 4.2s ease-in-out infinite;
}
.ember-l { left: -40px; top: 32%; }
.ember-r { right: -40px; top: 50%; animation-duration: 5.6s; animation-direction: reverse; }
.floor-glow {
  position: absolute; left: 0; right: 0; bottom: 0; height: 160px;
  background: linear-gradient(to top, rgba(255, 120, 60, .06), transparent);
  animation: emberpulse 7s ease-in-out infinite;
}
.sketch { position: absolute; opacity: .55; }
.sketch-1 { left: 5%; top: 22%; animation: drift 26s ease-in-out infinite; }
.sketch-2 { right: 5%; bottom: 16%; animation: drift2 30s ease-in-out infinite; }

/* boss mode: the same cave, lit from below in red */
body.boss-mode .dungeon-bg::before {
  background:
    radial-gradient(800px 400px at 50% -15%, rgba(99, 102, 241, .10), transparent 70%),
    radial-gradient(1200px 600px at 50% 120%, rgba(255, 70, 40, .16), transparent 70%);
}
body.boss-mode .cave-back { fill: rgba(26, 12, 14, .9); }
body.boss-mode .cave-front { fill: #0A0406; }
body.boss-mode .ember {
  background: radial-gradient(closest-side, rgba(255, 92, 60, .16), transparent 70%);
  animation-duration: 3.4s;
}
body.boss-mode .floor-glow {
  height: 220px;
  background: linear-gradient(to top, rgba(255, 92, 60, .10), transparent);
  animation-duration: 5s;
}

@keyframes drift { 0% { transform: translate(0, 0); } 50% { transform: translate(8px, 12px); } 100% { transform: translate(0, 0); } }
@keyframes drift2 { 0% { transform: translate(0, 0); } 50% { transform: translate(-10px, -10px); } 100% { transform: translate(0, 0); } }
@keyframes emberpulse { 0%, 100% { opacity: .5; } 50% { opacity: .9; } }
@keyframes sigilspin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
@keyframes flicker { 0%, 100% { opacity: .55; } 30% { opacity: .85; } 60% { opacity: .6; } 80% { opacity: .9; } }

/* ---- dungeon: centered screens (login, placement, lobby, death) ---- */
.dungeon .stage {
  min-height: calc(100vh - 150px);
  display: flex; align-items: center; justify-content: center;
}
.dg-card {
  background: rgba(9, 11, 17, .9); border-color: #1D2536;
  border-radius: 18px; padding: 34px 38px;
}
.stage .login-card { margin: 0; }
.ember-text { color: var(--ember-soft); }
.dg-card .eyebrow { margin-bottom: 12px; letter-spacing: .18em; }

.lobby-row {
  display: flex; gap: 28px; flex-wrap: wrap;
  align-items: stretch; justify-content: center;
}
.lobby-card { width: 480px; max-width: 100%; }
.lobby-card h1 { font-size: 36px; }
.trophy-card { width: 330px; max-width: 100%; background: rgba(9, 11, 17, .7); }
button.wide { width: 100%; padding: 13px; font-size: 15.5px; }

.records-strip {
  display: flex; border: 1px solid #1D2536; border-radius: 12px;
  overflow: hidden; margin: 20px 0 24px; background: rgba(9, 11, 17, .6);
}
.records-strip .rec { flex: 1; padding: 16px 10px; text-align: center; }
.records-strip .rec + .rec { border-left: 1px solid var(--inset); }
.rec-num { font-size: 26px; font-weight: 700; }
.rec-num.cyan { color: var(--depth-cyan); }
.rec-num.peach { color: var(--ember-soft); }
.rec-label {
  font-family: var(--mono); color: var(--muted); font-size: 10.5px;
  text-transform: uppercase; letter-spacing: .12em; margin-top: 4px;
}

.cos-label {
  font-family: var(--mono); font-size: 10.5px; letter-spacing: .14em;
  text-transform: uppercase; color: var(--dim); margin: 16px 0 8px;
}
.cos-row { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; }
.swatch {
  width: 24px; height: 24px; border-radius: 50%; padding: 0;
  border: 2px solid transparent; cursor: pointer;
}
.swatch.inline { display: inline-block; width: 14px; height: 14px; vertical-align: -2px; }
.swatch-sage   { background: var(--frontier); }
.swatch-teal   { background: var(--mastered); }
.swatch-sky    { background: var(--sky); }
.swatch-violet { background: var(--violet); }
.swatch-amber  { background: var(--amber); }
.swatch-rose   { background: var(--danger); }
.swatch.locked { opacity: .25; cursor: default; }
.swatch.worn { border-color: var(--text); box-shadow: 0 0 0 3px var(--panel-2); }
.title-pill {
  background: transparent; border: 1px solid var(--locked-edge); color: var(--text-2);
  font-family: var(--mono); border-radius: 999px; padding: 4px 12px;
  font-size: 11.5px; cursor: pointer;
}
.title-pill.locked { opacity: .35; cursor: default; }
.title-pill.worn { border-color: var(--mastered); color: var(--mastered); }

/* ---- dungeon: the run HUD ---- */
.dungeon-wrap { max-width: 680px; margin: 0 auto; }
.run-hud {
  display: flex; align-items: stretch; padding: 0; overflow: hidden;
  border-radius: 14px; background: rgba(9, 11, 17, .88); border-color: #1D2536;
  font-family: var(--mono); margin-bottom: 16px;
}
.hud-cell { display: flex; align-items: center; gap: 10px; padding: 12px 16px; }
.hud-cell + .hud-cell { border-left: 1px solid var(--inset); }
.hud-hearts { display: inline-flex; gap: 6px; }
.heart svg { width: 18px; height: 16px; fill: var(--heart); display: block; }
.heart.lost svg { fill: var(--inset); }
.hud-hearts.shake { animation: shake .35s; }
.hud-room-num {
  font-family: "Space Grotesk", sans-serif; font-size: 22px; font-weight: 600;
  letter-spacing: -0.02em; color: var(--text);
}
.hud-key {
  font-size: 10.5px; letter-spacing: .12em; text-transform: uppercase;
  color: var(--muted);
}
.hud-val { font-size: 14px; color: var(--text); }
.hud-val.lime { color: var(--streak-lime); }
.hud-countdown { flex: 1; gap: 6px; }
/* countdown pips: one square per non-boss room in this block, then the
   boss diamond at the end of the row */
.cd { width: 8px; height: 8px; border-radius: 2px; border: 1px solid var(--locked-edge); }
.cd.on { background: var(--accent); border-color: var(--accent); }
.cd-boss {
  width: 9px; height: 9px; border: 1px solid var(--ember);
  transform: rotate(45deg); margin-left: 2px;
}
.cd-text { font-size: 11px; color: var(--ember-soft); margin-left: 8px; }
.boss-word {
  font-size: 11px; letter-spacing: .18em; text-transform: uppercase;
  color: var(--ember-soft);
}
/* the gauntlet pips ride the shared .pip classes but shape up as diamonds */
#boss-pips { display: inline-flex; gap: 7px; }
#boss-pips .pip {
  width: 10px; height: 10px; border-radius: 0;
  border: 1px solid var(--ember); background: transparent;
  transform: rotate(45deg);
}
#boss-pips .pip.good { background: var(--mastered); border-color: var(--mastered); }
#boss-pips .pip.bad { background: var(--danger); border-color: var(--danger); }
body.boss-mode .run-hud { border-color: #33222A; background: rgba(13, 9, 12, .88); }
body.boss-mode .hud-cell + .hud-cell { border-left-color: #221720; }

/* ---- dungeon: boss intro ---- */
#boss-intro { display: flex; align-items: center; gap: 24px; padding: 22px 8px 6px; }
.sigil { position: relative; width: 96px; height: 96px; flex: none; }
.sigil svg { position: absolute; inset: 0; }
.sigil-spin { animation: sigilspin 40s linear infinite; }
.boss-name { font-size: 32px; font-weight: 700; letter-spacing: -0.02em; line-height: 1.1; }
.boss-note { font-family: var(--mono); font-size: 12px; color: var(--muted); margin-top: 8px; }

/* ---- dungeon: the room card ---- */
#room-card {
  margin-top: 16px; border-color: #1D2536; border-radius: 16px;
  background: linear-gradient(180deg, #090B11, #0C0B0F);
  padding: 30px 34px 22px;
  box-shadow: 0 30px 80px rgba(10, 14, 30, .5);
}
#room-card .prompt { font-size: 24px; line-height: 1.4; margin: 14px 0 6px; }
.room-floor { font-family: var(--mono); font-size: 11.5px; color: var(--muted); }
#room-card.boss {
  border-color: #33222A;
  background: linear-gradient(180deg, #0D090C, #0B0A10);
  box-shadow: 0 30px 90px rgba(255, 92, 60, .08);
}
#room-card.boss .tag {
  background: transparent; color: var(--ember-soft);
  border-color: rgba(255, 92, 60, .35);
}
#room-card.boss button.primary {
  background: linear-gradient(135deg, #FF5C3C, #E23A5A); color: #fff;
}

.reward-card { border-color: var(--amber); margin-top: 14px; animation: rise .4s ease-out; }
.reward-card p { margin-top: 8px; }

.flee-row { margin: 14px 0 0; text-align: right; }
.flee-row a { color: var(--muted); text-decoration: underline dotted; }
.flee-row a.armed { color: var(--danger); }

/* ---- dungeon: death ---- */
.death-card { width: 520px; max-width: 100%; text-align: center; }
.death-hearts { display: inline-flex; gap: 8px; margin-bottom: 18px; }
.death-card h1 { font-size: 34px; }
.new-best { font-family: var(--mono); font-size: 12.5px; color: var(--streak-lime); }
.death-actions { display: flex; gap: 12px; justify-content: center; margin-top: 4px; }

@media (prefers-reduced-motion: reduce) {
  .hud-hearts.shake, .reward-card,
  .ember, .floor-glow, .sketch, .sigil-spin { animation: none; }
}
