/* ==========================================================================
   ITWars design system — "NOC at 3am"
   Not generic neon cyberpunk: the visual language of network operations.
   Syslog severity colours, rack spacing, terminal phosphor, cable states.
   ========================================================================== */

:root {
  /* surfaces — cool, desaturated, like an unlit ops room */
  --bg: #070a0e;
  --bg-elevated: #0b1016;
  --surface: #0f151d;
  --surface-2: #151d27;
  --surface-3: #1c2632;
  --border: #202b38;
  --border-bright: #2c3a4a;

  --text: #e6edf5;
  --text-dim: #8fa1b5;
  --text-faint: #56687d;

  /* syslog severity — these are the semantic colours, used everywhere */
  --sev-emerg: #ff4060;      /* 0-2 critical */
  --sev-warn: #ffb020;       /* 4 warning */
  --sev-notice: #4d9dff;     /* 5 notice */
  --sev-info: #2f8fff;       /* 6 informational / success */
  --sev-debug: #8b6cff;      /* 7 debug */

  /* Accent is themeable (see [data-theme] blocks below). --accent-rgb exists
     because most of this sheet needs the accent at partial alpha, and you
     cannot derive "rgba of a hex var" in plain CSS -- so the channels are
     carried separately and every glow/tint reads rgba(var(--accent-rgb), x).
     Any new theme must set all four or it will only half-apply. */
  --accent: #2f8fff;
  --accent-rgb: 47,143,255;
  --accent-bright: #5fb2ff;
  --accent-dim: #1568c9;
  --accent-ink: #04101f;
  --danger: var(--sev-emerg);
  /* Right/wrong is not a brand colour: it stays green and red in every theme,
     otherwise "correct" would be blue here and amber two themes over -- and
     blue already means "the option you picked". */
  --ok: #24d17e;
  --ok-rgb: 36,209,126;
  --warn: var(--sev-warn);
  --info: var(--sev-notice);

  /* Archivo is the faceplate/label grotesque — the register of silkscreened
     rack legends. Inter carries dense UI data. JetBrains Mono is the console. */
  --font-display: 'Archivo', 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
  --font-body: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  --font-mono: 'JetBrains Mono', 'SFMono-Regular', Consolas, monospace;

  --radius: 10px;
  --radius-sm: 6px;
  --shadow: 0 2px 4px rgba(0,0,0,.4), 0 8px 24px rgba(0,0,0,.35);
  --glow-accent: 0 0 0 1px rgba(var(--accent-rgb),.25), 0 0 28px -6px rgba(var(--accent-rgb),.45);
  --glow-danger: 0 0 0 1px rgba(255,64,96,.3), 0 0 28px -6px rgba(255,64,96,.5);

  --u: 8px;  /* rack unit — all spacing is a multiple */
}

/* ---------- Themes ----------
   Only the accent ramp changes. The surface palette stays put on purpose:
   these are re-skins of one ops room, not four different products. */
[data-theme="amber"] {
  --accent: #ffb020; --accent-rgb: 255,176,32; --accent-bright: #ffc95e;
  --accent-dim: #b87a08; --accent-ink: #1a1002;
}
[data-theme="phosphor"] {
  --accent: #1fe6a8; --accent-rgb: 31,230,168; --accent-bright: #2bffc0;
  --accent-dim: #12a87a; --accent-ink: #04140f;
}
[data-theme="magenta"] {
  --accent: #b06cff; --accent-rgb: 176,108,255; --accent-bright: #c894ff;
  --accent-dim: #7b3fd1; --accent-ink: #12051f;
}

* { box-sizing: border-box; }
html { color-scheme: dark; scroll-behavior: smooth; }

body {
  margin: 0;
  background:
    radial-gradient(1100px 520px at 12% -12%, rgba(var(--accent-rgb),.06), transparent 62%),
    radial-gradient(900px 460px at 100% -4%, rgba(77,157,255,.05), transparent 58%),
    var(--bg);
  background-attachment: fixed;
  color: var(--text);
  font-family: var(--font-body);
  font-size: 16px;
  line-height: 1.6;
  min-height: 100vh;
  -webkit-font-smoothing: antialiased;
}

/* faint scanline texture — reads as CRT without being a costume */
body::before {
  content: '';
  position: fixed; inset: 0; pointer-events: none; z-index: 1;
  background: repeating-linear-gradient(180deg, rgba(255,255,255,.012) 0 1px, transparent 1px 3px);
  mix-blend-mode: overlay;
}

/* Type scale: 1.25 modular. Display sits tight and heavy; body stays at a
   comfortable 16px+ with the measure capped wherever prose runs long. */
h1, h2, h3, h4 {
  font-family: var(--font-display); font-weight: 700; line-height: 1.1;
  margin: 0 0 .5em; letter-spacing: -.022em; text-wrap: balance;
}
h1 { font-size: clamp(2.4rem, 1.6rem + 3vw, 4rem); }
h2 { font-size: clamp(1.6rem, 1.2rem + 1.5vw, 2.4rem); }
h3 { font-size: clamp(1.15rem, 1.05rem + .4vw, 1.35rem); font-weight: 600; line-height: 1.2; }
h4 { font-size: 1.02rem; font-weight: 600; }
p { margin: 0 0 1em; color: var(--text-dim); }
/* 65ch keeps the return sweep short enough to land on the right line. */
p, .prose { max-width: 68ch; }
.center p, .card p, td p, .panel p { max-width: none; }

a { color: var(--accent); text-decoration: none; }
/* Underline links that sit inside running prose. Structural links -- cards,
   nav, tags, buttons -- are already obviously interactive and an underline on
   them just makes the page look broken on hover. */
p > a, li > a, .prose a, .link {
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: .18em;
  text-decoration-color: rgba(var(--accent-rgb),.45);
  transition: text-decoration-color .15s, color .15s;
}
p > a:hover, li > a:hover, .prose a:hover, .link:hover { text-decoration-color: currentColor; }

/* A <button class="link"> has to have its native chrome removed, or it renders
   as an OS button with a border, a grey background and the platform UI font --
   the underline above was the only thing being applied. This was scoped to the
   notification bell, so every other button.link on the site looked broken. */
button.link {
  background: none; border: 0; padding: 0; margin: 0;
  font: inherit; color: var(--accent); cursor: pointer;
}
button.link:hover { color: var(--accent-bright); }
code, .mono { font-family: var(--font-mono); }
small { font-size: .82rem; }
hr { border: none; border-top: 1px solid var(--border); margin: calc(var(--u)*3) 0; }

:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px; }

::selection { background: rgba(var(--accent-rgb),.28); color: #fff; }

::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: var(--bg); }
::-webkit-scrollbar-thumb { background: var(--surface-3); border-radius: 6px; border: 2px solid var(--bg); }
::-webkit-scrollbar-thumb:hover { background: var(--border-bright); }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important; animation-iteration-count: 1 !important;
    transition-duration: .01ms !important; scroll-behavior: auto !important;
  }
}

.container { max-width: 1120px; margin: 0 auto; padding: 0 24px; position: relative; z-index: 2; }
.container-narrow { max-width: 640px; margin: 0 auto; padding: 0 24px; position: relative; z-index: 2; }
.stack > * + * { margin-top: calc(var(--u)*2); }
.row { display: flex; align-items: center; gap: var(--u); flex-wrap: wrap; }
.row-between { display: flex; align-items: center; justify-content: space-between; gap: var(--u); flex-wrap: wrap; }
.spacer { flex: 1; }
.muted { color: var(--text-dim); }
.faint { color: var(--text-faint); }
.center { text-align: center; }
.nowrap { white-space: nowrap; }

/* ---------- Nav ---------- */
.nav {
  position: sticky; top: 0; z-index: 50;
  display: flex; align-items: center; justify-content: space-between;
  padding: 18px 32px;
  background: rgba(7,10,14,.85);
  backdrop-filter: blur(12px) saturate(140%);
  border-bottom: 1px solid var(--border);
}
.nav-logo {
  font-family: var(--font-mono); font-weight: 700; font-size: 1.35rem;
  color: var(--text); letter-spacing: -.03em; display: flex; align-items: center; gap: 8px;
}
.nav-logo .prompt { color: var(--accent); text-shadow: 0 0 14px rgba(var(--accent-rgb),.5); }
.nav-links { display: flex; align-items: center; gap: 12px; }
.nav-links .btn { font-size: 1rem; padding: 11px 20px; }
@media (max-width: 640px) {
  .nav { padding: 14px 18px; }
  .nav-logo { font-size: 1.15rem; }
  .nav-links .btn { font-size: .9rem; padding: 9px 14px; }
}
.nav-links a.plain { color: var(--text-dim); font-size: .92rem; }
.nav-links a.plain:hover { color: var(--text); text-decoration: none; }

/* ---------- Buttons ---------- */
.btn {
  font-family: var(--font-body); font-weight: 600; font-size: .92rem;
  padding: 10px 18px; border-radius: var(--radius-sm);
  border: 1px solid var(--border-bright);
  background: linear-gradient(180deg, var(--surface-2), var(--surface));
  color: var(--text); cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  transition: transform .12s cubic-bezier(.2,.8,.2,1), border-color .15s, background .15s, box-shadow .15s;
  text-decoration: none; white-space: nowrap;
}
.btn:hover { border-color: var(--accent-dim); transform: translateY(-1px); text-decoration: none; }
.btn:active { transform: translateY(0) scale(.985); }
.btn-primary {
  background: linear-gradient(180deg, var(--accent-bright), var(--accent));
  border-color: var(--accent); color: var(--accent-ink);
  box-shadow: 0 0 20px -8px rgba(var(--accent-rgb),.7);
}
.btn-primary:hover { box-shadow: 0 0 26px -6px rgba(var(--accent-rgb),.9); border-color: var(--accent-bright); }
.btn-danger { background: linear-gradient(180deg, #ff5a78, var(--danger)); border-color: var(--danger); color: #21050b; }
.btn-ghost { background: transparent; border-color: var(--border); }
.btn-ghost:hover { background: var(--surface); }
.btn-block { width: 100%; }
.btn-sm { padding: 6px 12px; font-size: .8rem; }
.btn-lg { padding: 14px 26px; font-size: 1rem; }
.btn[disabled] { opacity: .45; cursor: not-allowed; transform: none; box-shadow: none; }
.btn .kbd { font-family: var(--font-mono); font-size: .72rem; opacity: .65; }

/* ---------- Cards / panels ---------- */
.card {
  background: linear-gradient(180deg, var(--surface), var(--bg-elevated));
  border: 1px solid var(--border); border-radius: var(--radius);
  padding: calc(var(--u)*2.5); position: relative;
}
.card-hover { transition: transform .18s cubic-bezier(.2,.8,.2,1), border-color .18s, box-shadow .18s; }
.card-hover:hover, .card-hover:focus-visible { transform: translateY(-3px); border-color: var(--accent-dim); box-shadow: var(--shadow), var(--glow-accent); }
.card-tight { padding: calc(var(--u)*1.5); }

/* rack-mount panel: a card with the little screw-hole rails */
.panel {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); overflow: hidden;
}
.panel-head {
  display: flex; align-items: center; justify-content: space-between; gap: var(--u);
  padding: 10px 16px; background: var(--bg-elevated);
  border-bottom: 1px solid var(--border);
  font-family: var(--font-mono); font-size: .78rem;
  text-transform: uppercase; letter-spacing: .08em; color: var(--text-faint);
}
.panel-body { padding: calc(var(--u)*2); }

.grid { display: grid; gap: 18px; }
.grid-2 { grid-template-columns: repeat(2, 1fr); }
.grid-3 { grid-template-columns: repeat(3, 1fr); }
.grid-4 { grid-template-columns: repeat(4, 1fr); }
.grid-auto { grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); }
@media (max-width: 900px) { .grid-3, .grid-4 { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 640px) { .grid-2, .grid-3, .grid-4 { grid-template-columns: 1fr; } }

/* ---------- Tags / status pills ---------- */
.tag {
  font-family: var(--font-mono); font-size: .7rem; text-transform: uppercase; letter-spacing: .06em;
  padding: 3px 8px; border-radius: 4px; border: 1px solid var(--border);
  color: var(--text-dim); display: inline-flex; align-items: center; gap: 5px;
  background: var(--bg-elevated);
}
.tag-accent { color: var(--accent); border-color: rgba(var(--accent-rgb),.35); background: rgba(var(--accent-rgb),.08); }
.tag-warn   { color: var(--warn);   border-color: rgba(255,176,32,.35); background: rgba(255,176,32,.08); }
.tag-danger { color: var(--danger); border-color: rgba(255,64,96,.35);  background: rgba(255,64,96,.08); }
.tag-info   { color: var(--info);   border-color: rgba(77,157,255,.35); background: rgba(77,157,255,.08); }
.tag-locked { color: var(--text-faint); }

/* ---------- Pricing ---------- */
/* Stretch, not start: two cards of different heights read as a layout bug. */
.plan-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 20px; align-items: stretch; }
@media (max-width: 760px) { .plan-grid { grid-template-columns: 1fr; } }

.plan {
  position: relative; overflow: hidden;
  display: flex; flex-direction: column;
  background: linear-gradient(180deg, var(--surface), var(--bg-elevated));
  border: 1px solid var(--border); border-radius: var(--radius);
  padding: calc(var(--u)*3);
  transition: transform .28s cubic-bezier(.2,.8,.2,1), border-color .28s, box-shadow .28s;
}
/* The list absorbs the height difference so both buttons sit on one line. */
.plan .plan-list { flex: 1; }
/* The card is a flex column, so an inline-flex tag would stretch edge to edge. */
.plan > .tag { align-self: flex-start; }
/* A sweep of phosphor across the top edge on hover — the card "comes up". */
.plan::before {
  content: ''; position: absolute; inset: 0 0 auto 0; height: 2px;
  background: linear-gradient(90deg, transparent, var(--accent), transparent);
  transform: translateX(-100%); transition: transform .55s cubic-bezier(.2,.8,.2,1);
}
.plan:hover { transform: translateY(-6px); border-color: var(--accent-dim); box-shadow: var(--shadow), var(--glow-accent); }
.plan:hover::before { transform: translateX(100%); }
.plan-pro { border-color: rgba(var(--accent-rgb),.4); box-shadow: 0 0 40px -22px rgba(var(--accent-rgb),.8); }

/* A plain pill, not a clipped diagonal ribbon. It reads at a glance and does
   not fight the card corner. */
.plan-flag {
  position: absolute; top: calc(var(--u)*3); right: calc(var(--u)*3);
  background: var(--accent); color: var(--accent-ink);
  font-family: var(--font-mono); font-size: .64rem; font-weight: 700;
  letter-spacing: .1em; text-transform: uppercase;
  padding: 4px 10px; border-radius: 999px;
}
.plan-price {
  font-family: var(--font-display); font-weight: 700; letter-spacing: -.04em;
  font-size: 3.2rem; line-height: 1; margin: 12px 0 2px; color: var(--text);
}
.plan-price .per { font-family: var(--font-mono); font-size: .95rem; font-weight: 400; color: var(--text-faint); letter-spacing: 0; }
.plan-sub { font-size: .9rem; color: var(--text-faint); margin: 0 0 20px; min-height: 2.6em; }

.plan-list { list-style: none; padding: 0; margin: 0 0 22px; }
.plan-list li {
  position: relative; padding-left: 26px; margin-bottom: 10px;
  font-size: .92rem; line-height: 1.5; color: var(--text-dim);
}
.plan-list li::before {
  content: ''; position: absolute; left: 4px; top: .48em;
  width: 9px; height: 9px; border-radius: 2px;
  border: 1px solid var(--accent-dim); background: rgba(var(--accent-rgb),.18);
}
.plan-list li.off { color: var(--text-faint); }
.plan-list li.off::before { border-color: var(--border-bright); background: transparent; }
.plan-list li strong { color: var(--text); font-weight: 600; }
.plan .btn { width: 100%; }
.plan-note { font-size: .78rem; color: var(--text-faint); margin: 10px 0 0; max-width: none; }

.led { width: 7px; height: 7px; border-radius: 50%; background: var(--text-faint); flex-shrink: 0; }
.led-up { background: var(--accent); box-shadow: 0 0 8px var(--accent); }
.led-down { background: var(--danger); box-shadow: 0 0 8px var(--danger); animation: blink 1.1s steps(1) infinite; }
.led-warn { background: var(--warn); box-shadow: 0 0 8px var(--warn); }
@keyframes blink { 50% { opacity: .25; } }

/* ---------- Terminal ---------- */
.terminal {
  background: #04070a; border: 1px solid var(--border); border-radius: var(--radius);
  font-family: var(--font-mono); font-size: .86rem; line-height: 1.55;
  color: #c8f7e6; padding: 16px 18px; overflow: auto;
  text-shadow: 0 0 6px rgba(var(--accent-rgb),.18);
}
.terminal .dim { color: var(--text-faint); text-shadow: none; }
.terminal .err { color: var(--danger); text-shadow: 0 0 6px rgba(255,64,96,.3); }
.terminal .warn { color: var(--warn); }
.terminal .ok { color: var(--accent); }
.terminal .cmd { color: #fff; }
.terminal .prompt-str { color: var(--sev-notice); }
.prompt-label::before { content: '$ '; color: var(--accent); }

.term-shell { display: flex; flex-direction: column; height: 336px; }
.term-out { flex: 1; overflow-y: auto; white-space: pre-wrap; word-break: break-word; }
.term-inputline { display: flex; align-items: center; gap: 8px; border-top: 1px solid var(--border); padding-top: 10px; margin-top: 10px; }
.term-inputline .prompt-str { flex-shrink: 0; }
.term-input {
  flex: 1; background: transparent; border: none; outline: none;
  color: #fff; font-family: var(--font-mono); font-size: .86rem; caret-color: var(--accent);
}
.cursor-block { display: inline-block; width: 8px; height: 1.05em; background: var(--accent); vertical-align: text-bottom; animation: blink 1.05s steps(1) infinite; }

/* ---------- Forms ---------- */
.field { margin-bottom: 16px; text-align: left; }
.field label { display: block; font-size: .82rem; color: var(--text-dim); margin-bottom: 6px; font-family: var(--font-mono); text-transform: uppercase; letter-spacing: .05em; }
.field input, .field select, .field textarea {
  width: 100%; background: var(--bg); border: 1px solid var(--border); color: var(--text);
  padding: 11px 13px; border-radius: var(--radius-sm);
  font-family: var(--font-body); font-size: .95rem; transition: border-color .15s, box-shadow .15s;
}
.field input:focus, .field textarea:focus, .field select:focus {
  border-color: var(--accent-dim); outline: none; box-shadow: 0 0 0 3px rgba(var(--accent-rgb),.1);
}
.field-mono input, .field-mono textarea { font-family: var(--font-mono); }
.form-error { color: var(--danger); font-size: .85rem; min-height: 1.2em; margin: -6px 0 12px; }

/* ---------- Hero / landing ---------- */
.hero { padding: 104px 0 62px; }
.hero-eyebrow {
  font-family: var(--font-mono); color: var(--accent); font-size: .84rem;
  margin-bottom: 18px; display: inline-flex; align-items: center; gap: 9px;
  letter-spacing: .01em;
}
.hero p.lede { font-size: clamp(1.1rem, 1rem + .5vw, 1.3rem); max-width: 60ch; line-height: 1.6; }
.hero-actions { display: flex; gap: 14px; margin-top: 34px; flex-wrap: wrap; min-height: 52px; }
.boot-line { font-family: var(--font-mono); font-size: .85rem; }

/* Section headers carry a syslog facility tag — the product's own vocabulary
   doing the job an eyebrow usually fakes. */
.section-eyebrow {
  display: inline-block; font-family: var(--font-mono); font-size: .72rem;
  letter-spacing: .12em; color: var(--text-faint); margin-bottom: 10px;
}
.section-lede { font-size: 1.05rem; max-width: 62ch; }

/* Signature: the incident runbook. A real ordered sequence, so the timestamps
   carry information rather than decorating. */
/* Rail geometry, kept in one place so the line and the dots cannot drift apart:
   time column 68px, then a 34px gutter with the rail down its centre. */
.runbook {
  --rb-col: 68px; --rb-gutter: 34px;
  --rb-rail: calc(var(--rb-col) + var(--rb-gutter) / 2);   /* 85px */
  list-style: none; padding: 0; margin: 30px 0 0; position: relative;
}
.runbook::before {
  content: ''; position: absolute; left: var(--rb-rail); top: 12px; bottom: 12px; width: 1px;
  background: linear-gradient(180deg, transparent, var(--border-bright) 12%, var(--border-bright) 88%, transparent);
}
.runbook li {
  display: grid; grid-template-columns: var(--rb-col) 1fr; gap: 0 var(--rb-gutter);
  padding: 0 0 32px; position: relative;
}
.runbook li:last-child { padding-bottom: 0; }
.rb-time {
  font-family: var(--font-mono); font-size: .82rem; color: var(--text-faint);
  text-align: right; padding-top: 4px;
}
/* Dot is positioned against the li, centred on the rail. */
.runbook li::before {
  content: ''; position: absolute; left: calc(var(--rb-rail) - 4px); top: 10px;
  width: 9px; height: 9px; border-radius: 50%;
  background: var(--bg-elevated); border: 1px solid var(--border-bright);
}
.runbook li:last-child .rb-time { color: var(--accent); }
.runbook li:last-child::before {
  border-color: var(--accent); background: var(--accent);
  box-shadow: 0 0 12px rgba(var(--accent-rgb),.7);
}
.rb-body h3 { margin: 0 0 6px; }
.rb-body p { margin: 0; max-width: 64ch; }
.rb-body code { font-size: .86em; color: var(--text); background: var(--bg); padding: 1px 5px; border-radius: 4px; border: 1px solid var(--border); }
@media (max-width: 640px) {
  .runbook::before { display: none; }
  .runbook li { grid-template-columns: 1fr; gap: 4px; }
  .runbook li::before { display: none; }
  .rb-time { text-align: left; }
}

/* ---------- Meters ---------- */
.meter { height: 7px; border-radius: 4px; background: var(--surface-2); overflow: hidden; }
.meter-fill { height: 100%; background: linear-gradient(90deg, var(--accent-dim), var(--accent)); border-radius: 4px; transition: width .5s cubic-bezier(.2,.8,.2,1); }
.meter-fill.warn { background: linear-gradient(90deg, #c07c10, var(--warn)); }
.meter-fill.danger { background: linear-gradient(90deg, #c02840, var(--danger)); }

.stat { display: flex; flex-direction: column; gap: 2px; }
.stat-value { font-family: var(--font-mono); font-size: 1.7rem; font-weight: 700; line-height: 1.1; color: var(--text); }
.stat-label { font-family: var(--font-mono); font-size: .7rem; text-transform: uppercase; letter-spacing: .07em; color: var(--text-faint); }
.stat-accent .stat-value { color: var(--accent); }
.stat-danger .stat-value { color: var(--danger); }

/* big readiness gauge */
.gauge { display: flex; align-items: baseline; gap: 10px; }
.gauge-score { font-family: var(--font-mono); font-size: 3.4rem; font-weight: 700; line-height: 1; letter-spacing: -.03em; }
.gauge-score.pass { color: var(--accent); text-shadow: 0 0 30px rgba(var(--accent-rgb),.35); }
.gauge-score.fail { color: var(--warn); }
.gauge-scale { position: relative; height: 10px; background: var(--surface-2); border-radius: 5px; margin-top: 14px; overflow: visible; }
.gauge-bar { height: 100%; border-radius: 5px; background: linear-gradient(90deg, var(--sev-emerg), var(--warn) 55%, var(--accent)); transition: width .8s cubic-bezier(.2,.8,.2,1); }
.gauge-pass-mark { position: absolute; top: -5px; bottom: -5px; width: 2px; background: var(--text); opacity: .7; }
.gauge-pass-mark::after { content: 'PASS'; position: absolute; top: -17px; left: 50%; transform: translateX(-50%); font-family: var(--font-mono); font-size: .6rem; letter-spacing: .08em; color: var(--text-dim); }

/* ---------- Questions ---------- */
.question-body { font-size: 1.12rem; margin-bottom: 20px; line-height: 1.55; }
.option-list { display: flex; flex-direction: column; gap: 9px; }
.option {
  text-align: left; padding: 13px 16px; border-radius: var(--radius-sm);
  border: 1px solid var(--border); background: var(--bg-elevated); color: var(--text);
  cursor: pointer; font-family: var(--font-body); font-size: .96rem;
  display: flex; align-items: flex-start; gap: 12px;
  transition: border-color .12s, background .12s, transform .12s;
}
.option:hover:not(:disabled) { border-color: var(--accent-dim); background: var(--surface-2); }
.option:disabled { cursor: default; }
.option .opt-key {
  font-family: var(--font-mono); font-size: .72rem; color: var(--text-faint);
  border: 1px solid var(--border); border-radius: 4px; padding: 1px 6px; flex-shrink: 0; margin-top: 2px;
}
.option.selected { border-color: var(--accent); background: rgba(var(--accent-rgb),.08); }
.option.correct { border-color: var(--ok); background: rgba(var(--ok-rgb),.14); }
.option.correct .opt-key { color: var(--ok); border-color: var(--ok); }
.option.incorrect { border-color: var(--danger); background: rgba(255,64,96,.12); }
.option.incorrect .opt-key { color: var(--danger); border-color: var(--danger); }
.option.decoy { border-color: var(--sev-debug); box-shadow: 0 0 0 1px rgba(139,108,255,.4); }

.timer-bar { height: 3px; background: var(--surface-2); border-radius: 3px; overflow: hidden; margin-bottom: 20px; }
.timer-fill { height: 100%; background: var(--accent); }
.timer-fill.warn { background: var(--warn); }
.timer-fill.danger { background: var(--danger); }

/* confidence selector */
.confidence { display: flex; gap: 8px; margin-bottom: 16px; }
.conf-btn {
  flex: 1; padding: 8px; border-radius: var(--radius-sm); border: 1px solid var(--border);
  background: var(--bg-elevated); color: var(--text-dim); cursor: pointer;
  font-family: var(--font-mono); font-size: .74rem; text-transform: uppercase; letter-spacing: .05em;
  transition: all .12s;
}
.conf-btn:hover { border-color: var(--border-bright); color: var(--text); }
.conf-btn.active { border-color: var(--accent); color: var(--accent); background: rgba(var(--accent-rgb),.1); }
.conf-btn[data-level="2"].active { border-color: var(--sev-info); }
.conf-btn[data-level="0"].active { border-color: var(--text-faint); color: var(--text-dim); background: var(--surface-2); }

/* ---------- Flashcard ---------- */
.flash-card {
  min-height: 240px; display: flex; align-items: center; justify-content: center; text-align: center;
  padding: 36px; cursor: pointer; font-size: 1.18rem; line-height: 1.5;
  transition: transform .35s cubic-bezier(.2,.8,.2,1);
  transform-style: preserve-3d;
}
.flash-card .side-label { position: absolute; top: 14px; left: 18px; font-family: var(--font-mono); font-size: .7rem; color: var(--text-faint); letter-spacing: .08em; }
.flash-card .flip-hint { position: absolute; bottom: 14px; left: 0; right: 0; font-family: var(--font-mono); font-size: .7rem; color: var(--text-faint); }

/* ---------- Arena ---------- */
.vs-row { display: flex; align-items: center; justify-content: center; gap: 20px; margin: 20px 0; }
.vs-player { flex: 1; text-align: center; min-width: 0; }
.vs-player .name { font-family: var(--font-mono); font-size: .92rem; overflow: hidden; text-overflow: ellipsis; }
.vs-score { font-family: var(--font-mono); font-size: 2.4rem; font-weight: 700; color: var(--accent); line-height: 1; }
.vs-divider { font-family: var(--font-mono); color: var(--text-faint); font-size: 1.1rem; }
.progress-dots { display: flex; gap: 5px; justify-content: center; margin-top: 8px; }
.dot { width: 9px; height: 9px; border-radius: 50%; background: var(--surface-3); transition: background .25s, box-shadow .25s, transform .25s; }
.dot.done { background: var(--accent); box-shadow: 0 0 8px rgba(var(--accent-rgb),.6); }
.dot.hidden-state { background: var(--sev-debug); box-shadow: 0 0 8px rgba(139,108,255,.5); }
.dot.miss { background: var(--danger); box-shadow: 0 0 8px rgba(255,64,96,.6); }
.dot.pop { transform: scale(1.45); }

.meter-charge { display: flex; align-items: center; gap: 10px; margin: 14px 0; }
.charge-track { flex: 1; height: 6px; background: var(--surface-2); border-radius: 3px; overflow: hidden; }
.charge-fill { height: 100%; background: linear-gradient(90deg, var(--sev-debug), var(--sev-notice)); transition: width .3s; }
.charge-fill.ready { background: linear-gradient(90deg, var(--warn), var(--danger)); animation: chargePulse 1.2s ease-in-out infinite; }
@keyframes chargePulse { 50% { filter: brightness(1.45); } }

.sabotage-bar { display: flex; gap: 8px; flex-wrap: wrap; margin: 12px 0; }
.sab-btn {
  padding: 7px 11px; border-radius: var(--radius-sm); border: 1px solid var(--border);
  background: var(--bg-elevated); color: var(--text-dim); cursor: pointer;
  font-family: var(--font-mono); font-size: .74rem; transition: all .12s;
}
.sab-btn:not([disabled]):hover { border-color: var(--danger); color: var(--danger); box-shadow: var(--glow-danger); }
.sab-btn[disabled] { opacity: .35; cursor: not-allowed; }

/* sabotage screen effects */
.fx-redact { filter: blur(0); }
.fx-redact .redacted { background: var(--sev-emerg); color: transparent; border-radius: 2px; animation: flicker .35s steps(2) infinite; }
@keyframes flicker { 50% { opacity: .55; } }
.fx-latency .option { pointer-events: none; opacity: .55; transition: opacity .2s; }
.storm-overlay {
  position: fixed; inset: 0; z-index: 900; pointer-events: none; overflow: hidden;
  font-family: var(--font-mono); font-size: .74rem; color: rgba(255,64,96,.5);
}
.storm-line { position: absolute; white-space: nowrap; animation: stormScroll linear forwards; }
@keyframes stormScroll { from { transform: translateX(100vw); } to { transform: translateX(-100%); } }

.pulse { animation: pulse 1.5s ease-in-out infinite; }
@keyframes pulse { 0%,100% { opacity: 1; } 50% { opacity: .48; } }

/* ---------- Topology renderer ---------- */
/* Full column width: the topology is the instrument you debug against, so it
   gets the room. min-width keeps it readable inside the horizontal scroller
   on narrow screens rather than crushing the labels together. */
.topo-wrap {
  background: #04070a; border: 1px solid var(--border); border-radius: var(--radius);
  padding: 12px; overflow-x: auto;
}
.topo-svg { display: block; width: 100%; height: auto; min-width: 520px; }

/* Device glyphs. dev-body is the chassis, dev-top the lit upper face,
   dev-port the port row / drive bays, dev-mark the stroked arrows. Gradients
   are defined per-SVG in topo.js and referenced by id here. */
.topo-node .dev-body {
  fill: url(#topoFace); stroke: var(--border-bright); stroke-width: 1.3;
  transition: stroke .3s, fill .3s;
}
.topo-node .dev-body.no-stroke { stroke: none; }
.topo-node .dev-top {
  fill: url(#topoTop); stroke: var(--border-bright); stroke-width: 1.1;
  transition: stroke .3s, fill .3s;
}
.topo-node .dev-top.no-stroke { stroke: none; }
.topo-node .dev-screen { fill: url(#topoScreen); stroke: var(--border); stroke-width: .8; }
.topo-node .dev-seam { stroke: var(--border-bright); stroke-width: 1; fill: none; opacity: .55; }
.topo-node .dev-port { fill: var(--bg); stroke: none; opacity: .85; }
.topo-node .dev-led { fill: var(--sev-info); }
.topo-node .dev-mark {
  fill: none; stroke: var(--text-dim); stroke-width: 1.5;
  stroke-linecap: round; stroke-linejoin: round;
}

/* A device that is down recolours its whole chassis, so a broken box is
   findable in the diagram without reading a single label. */
.topo-node.down .dev-body,
.topo-node.down .dev-top { stroke: var(--danger); fill: rgba(255,64,96,.14); }
.topo-node.down .dev-mark, .topo-node.down .dev-seam { stroke: var(--danger); }
.topo-node.down .dev-led { fill: var(--danger); }
.topo-node.down .dev-name { fill: var(--danger); }

.topo-node text { font-family: var(--font-mono); font-size: 11px; fill: var(--text); }
.topo-node .dev-name { font-size: 11px; font-weight: 500; }
.topo-node .sub { font-size: 9px; fill: var(--text-faint); }

/* ---------- Cables ----------
   Three pieces per link: a casing in the page background so crossing cables
   read as over/under instead of merging, the cable itself, and a pip at each
   end carrying that end's state. */
.topo-casing { stroke: #04070a; stroke-width: 6; stroke-linecap: round; }
.topo-link { stroke: var(--border-bright); stroke-width: 2.4; stroke-linecap: round; transition: stroke .3s; }
.topo-pip { fill: var(--border-bright); transition: fill .3s; }

.topo-link-g.up .topo-link { stroke: var(--accent-dim); }
.topo-link-g.up .topo-pip { fill: var(--accent); }
.topo-link-g.down .topo-link { stroke: var(--danger); stroke-dasharray: 6 5; animation: dash 1s linear infinite; }
.topo-link-g.down .topo-pip { fill: var(--danger); }
/* A cut cable is physically absent, not just administratively down. */
.topo-link-g.cut .topo-link { stroke: var(--text-faint); stroke-dasharray: 2 6; opacity: .7; }
.topo-link-g.cut .topo-pip { fill: var(--text-faint); }
@keyframes dash { to { stroke-dashoffset: -22; } }

.topo-link-label { font-family: var(--font-mono); font-size: 9px; fill: var(--text-dim); paint-order: stroke; stroke: #04070a; stroke-width: 3px; stroke-linejoin: round; }
.topo-packet { fill: var(--accent); filter: drop-shadow(0 0 6px var(--accent)); }

/* Lab/incident console: control column + a fixed-width objectives rail on
   desktop, one column on anything narrower than the rail can comfortably sit
   beside the console. Pure CSS -- the old version set this via inline style
   plus a one-shot `innerWidth < 900` JS check at render time, which doesn't
   react to resize and can race the viewport during navigation. */
.lab-grid { display: grid; grid-template-columns: 1fr 300px; gap: 18px; align-items: start; }
/* Grid items default to min-width:auto (content-based) same as flex items --
   without this, the console column refuses to shrink below the terminal's
   content width and the 1fr track blows past the container. */
.lab-grid > * { min-width: 0; }
@media (max-width: 860px) { .lab-grid { grid-template-columns: 1fr; } }

/* ---------- Objectives checklist ---------- */
.objectives { display: flex; flex-direction: column; gap: 8px; }
.objective { display: flex; align-items: flex-start; gap: 10px; padding: 10px 12px; border-radius: var(--radius-sm); background: var(--bg-elevated); border: 1px solid var(--border); transition: all .3s; }
.objective.passed { border-color: var(--accent-dim); background: rgba(var(--accent-rgb),.07); }
.objective .obj-box { width: 16px; height: 16px; border-radius: 3px; border: 1.5px solid var(--border-bright); flex-shrink: 0; margin-top: 3px; display: grid; place-items: center; font-size: 11px; color: var(--accent-ink); }
.objective.passed .obj-box { background: var(--accent); border-color: var(--accent); }
/* min-width:0 overrides the flex-item default of min-width:auto (content-based).
   Without it, a long objective label refuses to shrink below its own text
   width and pushes the whole card wider than the viewport on mobile. */
.objective .obj-label { flex: 1; min-width: 0; font-size: .92rem; }
.objective .obj-points { font-family: var(--font-mono); font-size: .72rem; color: var(--text-faint); }

/* ---------- Device tabs (lab console) ---------- */
.device-tabs { display: flex; gap: 4px; flex-wrap: wrap; margin-bottom: 10px; }
.device-tab {
  padding: 6px 12px; border-radius: var(--radius-sm) var(--radius-sm) 0 0;
  border: 1px solid var(--border); border-bottom: none;
  background: var(--bg-elevated); color: var(--text-dim); cursor: pointer;
  font-family: var(--font-mono); font-size: .78rem; display: flex; align-items: center; gap: 6px;
}
.device-tab.active { background: #04070a; color: var(--accent); border-color: var(--border-bright); }

/* ---------- Tables ---------- */
.table { width: 100%; border-collapse: collapse; font-size: .9rem; }
.table th {
  text-align: left; font-family: var(--font-mono); font-size: .7rem; text-transform: uppercase;
  letter-spacing: .07em; color: var(--text-faint); padding: 8px 10px; border-bottom: 1px solid var(--border);
  font-weight: 500;
}
.table td { padding: 9px 10px; border-bottom: 1px solid var(--border); }
.table tr:last-child td { border-bottom: none; }
.table tbody tr:hover { background: var(--surface); }
.table .num { font-family: var(--font-mono); text-align: right; }
.table-scroll { overflow-x: auto; }
.rank { font-family: var(--font-mono); color: var(--text-faint); width: 2.5em; }
.rank-1 { color: #ffd24a; } .rank-2 { color: #cfd8e3; } .rank-3 { color: #d99a5c; }

/* ---------- Toast ---------- */
.toast-host { position: fixed; bottom: 20px; right: 20px; z-index: 1000; display: flex; flex-direction: column; gap: 8px; align-items: flex-end; }
.toast {
  background: var(--surface-2); border: 1px solid var(--border-bright); border-left: 3px solid var(--accent);
  border-radius: var(--radius-sm); padding: 11px 16px; font-size: .88rem; box-shadow: var(--shadow);
  max-width: 340px; font-family: var(--font-mono);
}
.toast.err { border-left-color: var(--danger); }
.toast.warn { border-left-color: var(--warn); }
.toast.info { border-left-color: var(--info); }

/* ---------- Command palette ---------- */
/* ---------- Generic modal (upgrade prompt today, reusable later) ---------- */
.modal-backdrop {
  position: fixed; inset: 0; background: rgba(4,7,10,.72); backdrop-filter: blur(4px);
  z-index: 1100; display: grid; place-items: center; padding: 5vh 20px; overflow-y: auto;
}
.modal {
  position: relative; width: min(680px, 100%); max-height: 90vh; overflow-y: auto;
  background: var(--surface); border: 1px solid var(--border-bright); border-radius: var(--radius);
  box-shadow: var(--shadow); padding: calc(var(--u)*3.5);
}
.modal-close {
  position: absolute; top: 14px; right: 14px; width: 30px; height: 30px; padding: 0;
  display: flex; align-items: center; justify-content: center;
  border-radius: var(--radius-sm); border: 1px solid var(--border); background: var(--bg-elevated);
  color: var(--text-dim); font-size: 1.1rem; line-height: 1; cursor: pointer;
}
.modal-close:hover { color: var(--text); border-color: var(--border-bright); }
.upgrade-reason { color: var(--text-dim); font-size: .92rem; margin: 0 0 18px; max-width: 52ch; }
.upgrade-note { font-size: .8rem; color: var(--text-faint); margin: 16px 0 0; text-align: center; }
.modal .plan-grid { margin-top: 4px; }
.modal .plan { padding: calc(var(--u)*2.5); }
.modal .plan-price { font-size: 2.4rem; }

.palette-backdrop { position: fixed; inset: 0; background: rgba(4,7,10,.72); backdrop-filter: blur(4px); z-index: 1100; display: grid; place-items: start center; padding-top: 12vh; }
.palette { width: min(560px, 92vw); background: var(--surface); border: 1px solid var(--border-bright); border-radius: var(--radius); box-shadow: var(--shadow); overflow: hidden; }
.palette input {
  width: 100%; background: var(--bg-elevated); border: none; border-bottom: 1px solid var(--border);
  color: var(--text); padding: 15px 18px; font-family: var(--font-mono); font-size: .95rem; outline: none;
}
.palette-list { max-height: 340px; overflow-y: auto; }
.palette-item { padding: 11px 18px; display: flex; align-items: center; gap: 10px; cursor: pointer; font-size: .9rem; }
.palette-item.active { background: rgba(var(--accent-rgb),.1); color: var(--accent); }
.palette-item .pi-hint { margin-left: auto; font-family: var(--font-mono); font-size: .72rem; color: var(--text-faint); }

/* ---------- Feedback FX ---------- */
.screen-flash { position: fixed; inset: 0; pointer-events: none; z-index: 950; opacity: 0; }
.streak-frame {
  position: fixed; inset: 0; pointer-events: none; z-index: 940;
  box-shadow: inset 0 0 0 0 rgba(var(--accent-rgb),0);
  transition: box-shadow .4s ease-out;
}
.combo-badge {
  position: fixed; top: 18px; left: 50%; transform: translateX(-50%);
  font-family: var(--font-mono); font-weight: 700; font-size: 1.1rem;
  color: var(--accent); text-shadow: 0 0 16px rgba(var(--accent-rgb),.7);
  z-index: 960; pointer-events: none; opacity: 0;
}
.syslog-line {
  font-family: var(--font-mono); font-size: .78rem; padding: 6px 0;
  border-left: 2px solid var(--danger); padding-left: 10px; color: var(--danger); margin-bottom: 10px;
}
.syslog-line.ok { border-left-color: var(--accent); color: var(--accent); }

/* ---------- App shell ---------- */
.app-shell { display: flex; min-height: 100vh; align-items: flex-start; position: relative; z-index: 2; }
/* Fixed rather than sticky: a sticky 100vh column stops painting where the
   viewport ends, leaving a visible seam down a long page. */
#sidebar, .sidebar {
  width: var(--sidebar-w, 300px); flex-shrink: 0; position: fixed; inset: 0 auto 0 0; overflow-y: auto;
  border-right: 1px solid var(--border); background: var(--bg-elevated);
  display: flex; flex-direction: column; padding: 22px 18px 18px; gap: 3px;
  /* State is binary (expanded/collapsed, see shell.js) so this transition
     always animates a single clean flip, never a fight against a live drag. */
  transition: width .16s cubic-bezier(.2,.8,.2,1);
}
.app-shell > .app-main { margin-left: var(--sidebar-w, 300px); transition: margin-left .16s cubic-bezier(.2,.8,.2,1); }

/* Drag handle: a hairline hit target on the sidebar's right edge. Widening on
   hover/drag rather than sizing it for the drag target up front keeps the
   resting state visually quiet. */
.sidebar-resize-handle {
  position: absolute; top: 0; right: -3px; width: 6px; height: 100%;
  cursor: col-resize; z-index: 5; background: transparent;
}
.sidebar-resize-handle:hover, .sidebar-resize-handle.dragging { background: var(--accent-dim); opacity: .5; }

/* Icon-only rail: dragged narrow enough that labels would wrap or truncate,
   so they're dropped and only the glyphs stay -- the track picker and user
   block need running text to mean anything, so those go too. */
.sidebar.icon-only .sidebar-section-label,
.sidebar.icon-only .side-link span,
.sidebar.icon-only .side-badge,
.sidebar.icon-only .sidebar-cert,
.sidebar.icon-only .sidebar-user,
.sidebar.icon-only .logo-text { display: none; }
/* Fixed 84px rail (see SIDEBAR_ICON_W in shell.js): tighter side padding than
   the expanded state so a 24px icon sits centered with room to breathe
   instead of nearly touching the rail edges. */
.sidebar.icon-only { padding-left: 10px; padding-right: 10px; }
.sidebar.icon-only .side-link { justify-content: center; padding-left: 8px; padding-right: 8px; }
.sidebar.icon-only .sidebar-logo { justify-content: center; padding-left: 0; padding-right: 0; }

/* Bar (logo + hamburger) is always in flow; the toggle only shows up once the
   mobile media query below flips it visible. Body holds everything else and
   is what the toggle collapses on narrow screens -- on desktop it's just a
   plain flex column continuing the sidebar's own layout. */
.sidebar-bar { display: flex; align-items: center; justify-content: space-between; }
.sidebar-body { display: flex; flex-direction: column; flex: 1; min-height: 0; gap: 2px; }
.sidebar-toggle {
  display: none; align-items: center; justify-content: center;
  width: 34px; height: 34px; padding: 0; border-radius: var(--radius-sm);
  border: 1px solid var(--border); background: var(--bg); color: var(--text-dim); cursor: pointer;
}
.sidebar-toggle svg { width: 18px; height: 18px; stroke: currentColor; fill: none; stroke-width: 1.8; stroke-linecap: round; }
.sidebar-toggle:hover { color: var(--text); border-color: var(--border-bright); }

.sidebar-logo {
  font-family: var(--font-mono); font-weight: 700; font-size: 1.3rem; color: var(--text);
  padding: 2px 10px 16px; display: flex; align-items: center; gap: 8px; text-decoration: none;
  letter-spacing: -.03em;
}
.sidebar-logo .prompt { color: var(--accent); text-shadow: 0 0 12px rgba(var(--accent-rgb),.5); }
.sidebar-section-label {
  font-family: var(--font-mono); font-size: .7rem; text-transform: uppercase;
  letter-spacing: .12em; color: var(--text-faint); padding: 16px 11px 7px;
}
.sidebar-nav { display: flex; flex-direction: column; gap: 3px; }
.side-link {
  display: flex; align-items: center; gap: 14px; padding: 11px 13px; border-radius: var(--radius-sm);
  color: var(--text-dim); font-size: 1.02rem; line-height: 1.3; text-decoration: none;
  transition: background .12s, color .12s;
}
.side-link:hover { background: var(--surface-2); color: var(--text); text-decoration: none; }
.side-link.active { background: rgba(var(--accent-rgb),.1); color: var(--accent); }

/* Nav glyphs: one stroke icon per mode, quiet until the row is active. */
.side-icon {
  width: 24px; height: 24px; flex-shrink: 0; stroke: currentColor; fill: none;
  stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round;
  opacity: .55; transition: opacity .12s;
}
.side-link:hover .side-icon { opacity: .85; }
.side-link.active .side-icon { opacity: 1; filter: drop-shadow(0 0 6px currentColor); }
.side-badge {
  margin-left: auto; font-family: var(--font-mono); font-size: .68rem; color: var(--accent);
  background: rgba(var(--accent-rgb),.12); padding: 1px 7px; border-radius: 999px;
}
.sidebar-cert {
  margin: 6px 2px; padding: 9px 11px; border-radius: var(--radius-sm); border: 1px solid var(--border);
  background: var(--bg); color: var(--text); font-family: var(--font-mono); font-size: .85rem; width: calc(100% - 4px);
}

/* ---------- Custom track picker ---------- */
.cert-select { position: relative; margin: 6px 2px 2px; }
.cert-trigger {
  width: 100%; display: flex; align-items: center; justify-content: space-between; gap: 8px;
  padding: 11px 13px; border-radius: var(--radius-sm); border: 1px solid var(--border);
  background: var(--bg); color: var(--text); font-family: var(--font-mono); font-size: .9rem;
  cursor: pointer; text-align: left; transition: border-color .15s;
}
.cert-trigger:hover { border-color: var(--border-bright); }
.cert-select.open .cert-trigger { border-color: var(--accent-dim); }
.cert-caret { width: 16px; height: 16px; flex-shrink: 0; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; opacity: .6; transition: transform .15s; }
.cert-select.open .cert-caret { transform: rotate(180deg); }
.cert-current { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.cert-menu {
  display: none; position: absolute; top: calc(100% + 4px); left: 0; right: 0; z-index: 40;
  background: var(--surface); border: 1px solid var(--border-bright); border-radius: var(--radius-sm);
  box-shadow: var(--shadow); overflow: hidden; padding: 4px;
}
.cert-select.open .cert-menu { display: block; }
.cert-option {
  width: 100%; display: flex; align-items: center; justify-content: space-between; gap: 8px;
  padding: 9px 11px; border: none; border-radius: 4px; background: transparent;
  color: var(--text-dim); font-family: var(--font-mono); font-size: .86rem;
  cursor: pointer; text-align: left; transition: background .12s, color .12s;
}
.cert-option:hover { background: var(--surface-2); color: var(--text); }
/* Pro tracks read as reachable, not disabled -- clicking one opens the
   upgrade prompt, so greying it out would be a lie about what it does. */
.cert-option.locked { color: var(--accent); }
.cert-option.locked:hover { background: rgba(var(--accent-rgb),.14); color: var(--accent); }
/* Selected track: white label on a blue wash with an accent edge, rather than
   blue text. Blue text is the locked/Pro signal in this menu, so using it for
   "current" too made the two states read as the same thing. Declared after
   .locked so it wins if a Pro user has a Pro track selected. */
.cert-option.active,
.cert-option.active:hover {
  color: var(--text);
  background: rgba(var(--accent-rgb),.16);
  box-shadow: inset 2px 0 0 var(--accent);
}
.cert-pro {
  font-size: .62rem; letter-spacing: .1em; font-weight: 700;
  color: var(--accent-ink); background: var(--accent);
  padding: 2px 6px; border-radius: 999px; flex-shrink: 0;
}
.sidebar.icon-only .cert-select { display: none; }
.sidebar-spacer { flex: 1; min-height: 10px; }

/* ---------- Sidebar identity (top) ----------
   Avatar + name + plan sit directly under the wordmark: it is the context the
   whole rest of the column is read through, and it doubles as the link to
   your own profile. */
.sidebar-identity {
  display: flex; align-items: center; gap: 11px;
  padding: 10px; margin-bottom: 8px; border-radius: var(--radius-sm);
  border: 1px solid var(--border); background: var(--bg);
  text-decoration: none; color: var(--text); min-width: 0;
  transition: border-color .15s, background .15s;
}
.sidebar-identity:hover { border-color: var(--border-bright); background: var(--surface); text-decoration: none; }
.sidebar-identity .si-text { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.sidebar-identity .si-name {
  font-family: var(--font-mono); font-size: .95rem; color: var(--text);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.sidebar-identity .si-meta {
  display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
  font-size: .72rem; color: var(--text-faint);
}

.sidebar-upgrade {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  width: 100%; padding: 10px 12px; margin-bottom: 4px; border-radius: var(--radius-sm);
  border: 1px solid rgba(var(--accent-rgb),.28); background: rgba(var(--accent-rgb),.07);
  color: var(--accent); font-size: .84rem; text-decoration: none;
  font-family: inherit; cursor: pointer; text-align: left;
  transition: background .15s, border-color .15s;
}
.sidebar-upgrade:hover { background: rgba(var(--accent-rgb),.14); border-color: var(--accent-dim); text-decoration: none; }
.sidebar-upgrade .price { font-family: var(--font-mono); font-size: .76rem; flex-shrink: 0; }
.sidebar-upgrade .su-icon {
  display: none; width: 18px; height: 18px; flex-shrink: 0;
  fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round;
}
.streak-flame { color: var(--warn); }

/* Settings sits with the navigation, not with the utilities: it is a page you
   go to, not a control you press. Pinned to the bottom of the list so the
   ordering still reads "the things you do, then the thing you configure". */
.sidebar-nav-tail { margin-top: 4px; }

/* ---------- Sidebar utility row (bottom) ----------
   Command palette, sound and log out as equal icon buttons. These are pressed
   rarely and act in place; as full-width labelled rows they would cost three
   lines of prime real estate for nothing. */
.sidebar-utility {
  display: flex; align-items: center; gap: 6px;
  border-top: 1px solid var(--border); padding-top: 10px; margin-top: 8px;
}
.util-btn {
  flex: 1; display: flex; align-items: center; justify-content: center;
  height: 36px; padding: 0; border-radius: var(--radius-sm);
  border: 1px solid var(--border); background: transparent;
  color: var(--text-dim); cursor: pointer; text-decoration: none;
  transition: background .12s, color .12s, border-color .12s;
}
.util-btn svg {
  width: 18px; height: 18px; fill: none; stroke: currentColor;
  stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round;
}
.util-btn:hover { background: var(--surface-2); color: var(--text); border-color: var(--border-bright); text-decoration: none; }
.util-btn.active { color: var(--accent); border-color: rgba(var(--accent-rgb),.4); background: rgba(var(--accent-rgb),.1); }
.util-btn.muted-off { color: var(--text-faint); }
.util-btn.util-danger:hover { color: var(--danger); border-color: rgba(255,64,96,.4); background: rgba(255,64,96,.08); }

/* Collapsed rail: identity keeps just the avatar, upgrade keeps just the
   price, and the utility row stacks so four icons still fit at 84px. */
.sidebar.icon-only .sidebar-identity { justify-content: center; padding: 8px; }
.sidebar.icon-only .sidebar-identity .si-text { display: none; }
/* Collapsed: a padlock glyph replaces the label and price -- "$9.99/mo" does
   not fit in an 84px rail and spilled over the edge. */
.sidebar.icon-only .sidebar-upgrade { justify-content: center; padding: 9px 4px; }
.sidebar.icon-only .sidebar-upgrade .su-text,
.sidebar.icon-only .sidebar-upgrade .price { display: none; }
.sidebar.icon-only .sidebar-upgrade .su-icon { display: block; }
.sidebar.icon-only .sidebar-utility { flex-wrap: wrap; }
.sidebar.icon-only .util-btn { flex: 0 0 calc(50% - 3px); }
/* Collapsed rail: the Settings row keeps its icon and drops the label, exactly
   like every other nav row above it. */
.sidebar.icon-only .sidebar-nav-tail .side-link { justify-content: center; }

.app-main { flex: 1; min-width: 0; padding: 34px 38px 80px; }
.app-main > .container, .app-main > .container-narrow { padding: 0; margin: 0; max-width: 780px; }
.page-head { margin-bottom: 26px; }
.page-head h1 { font-size: 1.9rem; margin-bottom: 6px; }
.page-head .sub { color: var(--text-dim); font-size: .94rem; }

footer.site-footer { border-top: 1px solid var(--border); margin-top: 80px; padding: 28px 0; color: var(--text-faint); font-size: .85rem; position: relative; z-index: 2; }

@media (max-width: 900px) {
  .app-shell { flex-direction: column; }
  #sidebar, .sidebar {
    width: 100%; position: relative; inset: auto; min-height: auto; overflow: visible;
    border-right: none; border-bottom: 1px solid var(--border);
    flex-direction: column; padding: 10px 14px; gap: 0;
  }
  /* min-width:0 alone doesn't stop a column-direction flex child's cross size
     (width) from being pulled wide by an unshrinkable descendant somewhere
     deep in the tree; an explicit width pins it to the container regardless. */
  .app-shell > .app-main { margin-left: 0; width: 100%; }
  .sidebar-logo { padding: 4px 0; }

  /* Collapsed by default: a page opens on its own content, not a wall of nav.
     The toggle button only exists at this width. */
  .sidebar-toggle { display: flex; }
  .sidebar-body { display: none; padding-top: 8px; }
  .sidebar.nav-open .sidebar-body { display: flex; }
  .sidebar.nav-open { padding-bottom: 12px; }

  .sidebar-spacer { display: none; }
  .sidebar-resize-handle { display: none; }
  .app-main { padding: 22px 18px 60px; }
  .term-shell { height: 272px; }
}

/* ---------- Avatars ---------- */
.avatar {
  width: 32px; height: 32px; border-radius: 50%; flex-shrink: 0;
  object-fit: cover; background: var(--surface-2);
  border: 1px solid var(--border-bright); display: inline-block; vertical-align: middle;
}
.avatar-sm { width: 24px; height: 24px; }
.avatar-lg { width: 72px; height: 72px; }
.avatar-xl { width: 104px; height: 104px; }
/* Fallback initial when a user has no picture. */
.avatar-fallback {
  display: inline-flex; align-items: center; justify-content: center;
  font-family: var(--font-mono); font-weight: 700; color: var(--accent);
  background: rgba(var(--accent-rgb),.12); text-transform: uppercase;
}
.avatar-lg.avatar-fallback { font-size: 1.9rem; }
.leader-user { display: flex; align-items: center; gap: 9px; }

/* ---------- Onboarding ---------- */
.onb-steps { display: flex; gap: 6px; margin-bottom: 22px; }
.onb-dot { flex: 1; height: 3px; border-radius: 2px; background: var(--surface-3); transition: background .3s; }
.onb-dot.done { background: var(--accent); }
.onb-choices { display: grid; gap: 10px; margin: 18px 0 4px; }
.onb-choice {
  display: block; width: 100%; text-align: left; cursor: pointer;
  padding: 15px 17px; border-radius: var(--radius-sm);
  border: 1px solid var(--border); background: var(--bg-elevated); color: var(--text);
  font-family: inherit; font-size: .96rem; transition: border-color .12s, background .12s, transform .12s;
}
.onb-choice:hover { border-color: var(--accent-dim); background: var(--surface-2); transform: translateY(-1px); }
.onb-choice.selected { border-color: var(--accent); background: rgba(var(--accent-rgb),.09); }
.onb-choice .oc-title { font-weight: 600; display: block; margin-bottom: 3px; }
.onb-choice .oc-sub { font-size: .85rem; color: var(--text-dim); }

/* ---------- Theme swatches ---------- */
.theme-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 12px; }
.theme-card {
  cursor: pointer; padding: 14px; border-radius: var(--radius-sm);
  border: 1px solid var(--border); background: var(--bg-elevated); color: var(--text);
  font-family: inherit; text-align: left; transition: border-color .12s, transform .12s;
}
.theme-card:hover { transform: translateY(-2px); border-color: var(--border-bright); }
.theme-card.selected { border-color: var(--accent); box-shadow: var(--glow-accent); }
.theme-swatch { display: flex; gap: 5px; margin-bottom: 9px; }
.theme-swatch i { width: 22px; height: 22px; border-radius: 5px; display: block; }

/* ---------- Device list ---------- */
.device-row {
  display: flex; align-items: center; gap: 13px;
  padding: 12px 0; border-bottom: 1px solid var(--border);
}
.device-row:last-child { border-bottom: none; }
.device-icon {
  width: 22px; height: 22px; flex-shrink: 0; fill: none; stroke: var(--text-dim);
  stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round;
}
.device-meta { flex: 1; min-width: 0; }
.device-name { font-size: .92rem; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }

/* ==========================================================================
   Profile / statistics visual language
   Stat *cards* were the problem: eight identical boxes give every number the
   same weight and shape, so nothing reads as more important than anything
   else and the page feels like a spreadsheet. These components each encode
   their number differently -- a ring for progress, a strip for consistency,
   a bar for comparison -- so shape carries meaning before you read a label.
   ========================================================================== */

/* ---------- Player hero ---------- */
.hero-player {
  display: flex; align-items: center; gap: 26px; flex-wrap: wrap;
  padding: 26px 28px; margin-bottom: 22px; border-radius: var(--radius);
  border: 1px solid var(--border);
  background:
    radial-gradient(520px 200px at 0% 0%, rgba(var(--accent-rgb),.10), transparent 70%),
    linear-gradient(180deg, var(--surface), var(--bg-elevated));
  position: relative; overflow: hidden;
}
/* Avatar sits inside a progress ring: the ring IS the level bar, so identity
   and progression are one object instead of a picture next to a number. */
.hero-ring { position: relative; width: 116px; height: 116px; flex-shrink: 0; }
.hero-ring svg { position: absolute; inset: 0; transform: rotate(-90deg); }
.hero-ring .ring-track { fill: none; stroke: var(--surface-3); stroke-width: 5; }
.hero-ring .ring-fill {
  fill: none; stroke: var(--accent); stroke-width: 5; stroke-linecap: round;
  filter: drop-shadow(0 0 6px rgba(var(--accent-rgb),.55));
  transition: stroke-dashoffset 1s cubic-bezier(.2,.8,.2,1);
}
.hero-ring .avatar { position: absolute; top: 6px; left: 6px; width: 104px; height: 104px; }
.hero-ring .avatar-fallback { font-size: 2.6rem; }
/* Level chip pinned to the ring, like a rank flash on a uniform. */
.hero-level {
  position: absolute; bottom: -4px; left: 50%; transform: translateX(-50%);
  background: var(--accent); color: var(--accent-ink);
  font-family: var(--font-mono); font-size: .72rem; font-weight: 700;
  letter-spacing: .06em; padding: 3px 11px; border-radius: 999px;
  border: 2px solid var(--bg-elevated); white-space: nowrap;
}
.hero-main { flex: 1; min-width: 260px; }
.hero-name { font-family: var(--font-display); font-size: 1.9rem; font-weight: 700; letter-spacing: -.02em; line-height: 1.1; margin: 0; }
.hero-title { color: var(--accent); font-family: var(--font-mono); font-size: .84rem; letter-spacing: .06em; margin-top: 2px; }
.hero-sub { color: var(--text-faint); font-size: .84rem; margin-top: 8px; display: flex; gap: 14px; flex-wrap: wrap; align-items: center; }
.hero-xpbar { margin-top: 14px; max-width: 420px; }
.hero-xpbar .meter { height: 8px; }
.hero-xpline { display: flex; justify-content: space-between; font-family: var(--font-mono); font-size: .74rem; color: var(--text-faint); margin-bottom: 5px; }

/* Presence as a pill in the meta row, not a bare dot floating by the avatar. */
.presence { display: inline-flex; align-items: center; gap: 6px; font-family: var(--font-mono); font-size: .74rem; padding: 3px 9px; border-radius: 999px; border: 1px solid var(--border); }
.presence.on { color: var(--accent); border-color: rgba(var(--accent-rgb),.4); background: rgba(var(--accent-rgb),.09); }
.presence.off { color: var(--text-faint); }

/* ---------- Figure row: numbers that differ in shape, not just value ------ */
.figure-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 0; border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; background: var(--bg-elevated); }
.figure {
  padding: 16px 18px; border-right: 1px solid var(--border);
  display: flex; flex-direction: column; gap: 3px; min-width: 0;
}
.figure:last-child { border-right: none; }
.figure-label { font-family: var(--font-mono); font-size: .66rem; letter-spacing: .1em; text-transform: uppercase; color: var(--text-faint); }
.figure-value { font-family: var(--font-mono); font-size: 1.6rem; font-weight: 700; line-height: 1.1; color: var(--text); }
.figure-value.accent { color: var(--accent); }
.figure-value.warn { color: var(--warn); }
.figure-value.danger { color: var(--danger); }
.figure-note { font-size: .76rem; color: var(--text-faint); }
@media (max-width: 640px) { .figure { border-right: none; border-bottom: 1px solid var(--border); } }

/* ---------- Activity strip (12 weeks) ---------- */
.heat { display: grid; grid-auto-flow: column; grid-template-rows: repeat(7, 11px); gap: 3px; }
.heat i { width: 11px; height: 11px; border-radius: 2px; background: var(--surface-2); display: block; }
.heat i[data-l="1"] { background: rgba(var(--accent-rgb),.28); }
.heat i[data-l="2"] { background: rgba(var(--accent-rgb),.5); }
.heat i[data-l="3"] { background: rgba(var(--accent-rgb),.75); }
.heat i[data-l="4"] { background: var(--accent); box-shadow: 0 0 6px rgba(var(--accent-rgb),.5); }
.heat-wrap { overflow-x: auto; padding-bottom: 4px; }
.heat-legend { display: flex; align-items: center; gap: 5px; font-family: var(--font-mono); font-size: .7rem; color: var(--text-faint); margin-top: 10px; }
.heat-legend i { width: 11px; height: 11px; border-radius: 2px; display: block; background: var(--surface-2); }

/* ---------- Column chart (hours / weekdays) ---------- */
.colchart { display: flex; align-items: flex-end; gap: 3px; height: 96px; }
.colchart .col { flex: 1; display: flex; flex-direction: column; justify-content: flex-end; height: 100%; min-width: 0; }
.colchart .col b {
  display: block; width: 100%; background: linear-gradient(180deg, var(--accent), var(--accent-dim));
  border-radius: 2px 2px 0 0; min-height: 2px; transition: height .6s cubic-bezier(.2,.8,.2,1);
}
.colchart .col.peak b { box-shadow: 0 0 10px rgba(var(--accent-rgb),.55); }
.colchart-axis { display: flex; gap: 3px; margin-top: 6px; font-family: var(--font-mono); font-size: .62rem; color: var(--text-faint); }
.colchart-axis span { flex: 1; text-align: center; min-width: 0; overflow: hidden; }

/* ---------- Sparkline ---------- */
.spark { width: 100%; height: 54px; display: block; overflow: visible; }
.spark path.line { fill: none; stroke: var(--accent); stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; }
.spark path.area { fill: rgba(var(--accent-rgb),.14); stroke: none; }
.spark circle { fill: var(--accent); }

/* ---------- Split bar (win/loss/tie, confidence bands) ---------- */
.splitbar { display: flex; height: 26px; border-radius: var(--radius-sm); overflow: hidden; border: 1px solid var(--border); }
.splitbar span { display: flex; align-items: center; justify-content: center; font-family: var(--font-mono); font-size: .72rem; color: var(--accent-ink); min-width: 0; transition: flex-grow .6s; }
.splitbar .s-win { background: var(--accent); }
.splitbar .s-lose { background: var(--danger); color: #21050b; }
.splitbar .s-tie { background: var(--surface-3); color: var(--text-dim); }
.splitbar .s-empty { background: var(--surface-2); flex: 1; color: var(--text-faint); }

/* ---------- Ranked bar list ---------- */
.rankbar { display: flex; flex-direction: column; gap: 11px; }
.rankbar-row .rb-top { display: flex; justify-content: space-between; gap: 10px; font-size: .9rem; margin-bottom: 5px; }
.rankbar-row .rb-val { font-family: var(--font-mono); font-size: .78rem; color: var(--text-faint); flex-shrink: 0; }

/* ---------- Admin ops board ----------
   The four numbers that are true *this second*, styled like a rack status
   panel rather than another grid of identical cards. */
.ops-board {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  border: 1px solid var(--border-bright); border-radius: var(--radius);
  overflow: hidden; background: var(--bg-elevated);
}
.ops-live {
  display: flex; align-items: center; gap: 10px; padding: 18px 20px;
  border-right: 1px solid var(--border);
}
.ops-live:last-child { border-right: none; }
.ops-live-n { font-family: var(--font-mono); font-size: 1.9rem; font-weight: 700; line-height: 1; color: var(--text); }
.ops-live-l { font-family: var(--font-mono); font-size: .68rem; letter-spacing: .09em; text-transform: uppercase; color: var(--text-faint); }
@media (max-width: 640px) { .ops-live { border-right: none; border-bottom: 1px solid var(--border); } }

/* ==========================================================================
   Social: notification bell, challenge popup, friend rows
   ========================================================================== */

.bell-wrap { position: relative; margin-left: auto; }
.bell-btn {
  position: relative; display: flex; align-items: center; justify-content: center;
  width: 40px; height: 40px; border-radius: var(--radius-sm);
  border: 1px solid var(--border); background: var(--bg-elevated);
  color: var(--text-dim); cursor: pointer; transition: color .12s, border-color .12s;
}
.bell-btn:hover { color: var(--text); border-color: var(--border-bright); }
.bell-btn svg { width: 19px; height: 19px; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
.bell-badge {
  position: absolute; top: -5px; right: -5px; min-width: 18px; height: 18px;
  padding: 0 5px; border-radius: 999px; background: var(--danger); color: #fff;
  font-family: var(--font-mono); font-size: .66rem; font-weight: 700;
  display: flex; align-items: center; justify-content: center;
  border: 2px solid var(--bg);
}

/* Anchored under the bell rather than a full modal: it is a glance, not a
   destination. */
.bell-panel {
  display: none; position: absolute; top: calc(100% + 8px); right: 0; z-index: 120;
  width: min(360px, calc(100vw - 32px));
  background: var(--surface); border: 1px solid var(--border-bright);
  border-radius: var(--radius); box-shadow: var(--shadow); overflow: hidden;
}
.bell-wrap.open .bell-panel { display: block; }
.bell-head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 11px 14px; border-bottom: 1px solid var(--border);
  font-family: var(--font-mono); font-size: .72rem; text-transform: uppercase;
  letter-spacing: .08em; color: var(--text-faint);
}
.bell-head .link { font-size: .72rem; }
.bell-list { max-height: 380px; overflow-y: auto; }
.bell-item {
  display: flex; align-items: flex-start; gap: 11px; padding: 12px 14px;
  border-bottom: 1px solid var(--border); text-decoration: none; color: var(--text);
  transition: background .12s;
}
.bell-item:last-child { border-bottom: none; }
.bell-item:hover { background: var(--surface-2); text-decoration: none; }
.bell-item.unread { background: rgba(var(--accent-rgb),.06); }
.bell-item.unread .bi-title { font-weight: 600; }
.bell-icon { width: 16px; height: 16px; flex-shrink: 0; margin-top: 2px; fill: none; stroke: var(--accent); stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
.bi-text { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.bi-title { font-size: .88rem; line-height: 1.35; }
.bi-body { font-size: .8rem; color: var(--text-dim); line-height: 1.4; }
.bi-time { font-family: var(--font-mono); font-size: .7rem; color: var(--text-faint); flex-shrink: 0; }
.bell-empty { padding: 26px 14px; text-align: center; color: var(--text-faint); font-size: .88rem; }

/* ---------- Challenge popup ---------- */
.challenge-modal { max-width: 420px; text-align: center; }
.chal-ring {
  width: 76px; height: 76px; margin: 0 auto; border-radius: 50%;
  border: 3px solid var(--accent); display: grid; place-items: center;
  font-family: var(--font-mono); font-size: 1.9rem; font-weight: 700; color: var(--accent);
  box-shadow: 0 0 26px -6px rgba(var(--accent-rgb),.7);
  animation: chalPulse 1s ease-in-out infinite;
}
@keyframes chalPulse { 50% { transform: scale(1.05); } }

/* ---------- Friend rows ---------- */
.person-row {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  padding: 11px 0; border-bottom: 1px solid var(--border);
}
.person-row:last-child { border-bottom: none; }
.person-main { display: flex; align-items: center; gap: 11px; flex: 1; min-width: 0; text-decoration: none; color: var(--text); }
.person-main:hover { text-decoration: none; }
.person-main:hover .person-name { color: var(--accent); }
.person-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.person-name { font-size: .95rem; transition: color .12s; }
.person-sub { display: flex; align-items: center; gap: 6px; font-size: .74rem; color: var(--text-faint); }
.person-actions { display: flex; gap: 6px; flex-wrap: wrap; }

/* ==========================================================================
   Mobile pass for the lab / troubleshooting console
   Flashcards and exams were already usable on a phone; the lab console was
   not. The problems were concrete: a fixed-height terminal that left no room
   for the keyboard, device tabs that wrapped into four rows, a topology with
   a 520px floor forcing a horizontal scroll, and an objectives rail stacked
   below a full-height console so you could never see both.
   ========================================================================== */
@media (max-width: 860px) {
  /* Objectives first: on a phone you need to know what you are being asked
     before you get a console. The console is the tall element, so it goes
     last and the checklist is reachable without scrolling past it. */
  /* align-items must be reset here. The desktop rule sets `align-items: start`
     for the two-column grid, which is right there -- but this rule turns the
     container into a *column* flex box, where the cross axis is horizontal, so
     `start` stops stretching children to the container width and sizes them to
     their content instead. The console's widest child (a 562px device-tab
     strip) then set the width of the whole page, and every phone got a
     sideways scroll on the two pages people actually type into. */
  .lab-grid { display: flex; flex-direction: column; gap: 14px; align-items: stretch; }
  .lab-grid > :first-child { order: 2; }
  .lab-grid > :last-child { order: 1; }

  .topo-wrap { padding: 8px; }
  .topo-svg { min-width: 0; }        /* was 520px: forced a sideways scroll */

  /* Horizontal strip instead of a wrapping block of buttons. */
  .device-tabs {
    flex-wrap: nowrap; overflow-x: auto; gap: 5px;
    padding-bottom: 4px; -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
  }
  .device-tabs::-webkit-scrollbar { display: none; }
  .device-tab { flex: 0 0 auto; font-size: .74rem; padding: 6px 10px; }

  /* Viewport-relative so the on-screen keyboard cannot bury the prompt. */
  .term-shell { height: min(46vh, 300px); }
  .terminal { font-size: .8rem; padding: 12px 13px; }
  .term-input { font-size: 16px; }   /* < 16px makes iOS zoom on focus */

  /* Full-width action buttons beat three cramped ones on a 360px screen. */
  .lab-actions { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
  .lab-actions .btn { width: 100%; }
  .lab-actions .btn:first-child { grid-column: 1 / -1; }

  .objective { padding: 9px 11px; }
  .objective .obj-label { font-size: .88rem; }
}

@media (max-width: 560px) {
  .hero-player { gap: 16px; padding: 18px; }
  .hero-ring { width: 92px; height: 92px; }
  .hero-ring .avatar { width: 82px; height: 82px; top: 5px; left: 5px; }
  .hero-name { font-size: 1.5rem; }
  .figure { padding: 13px 14px; }
  .figure-value { font-size: 1.35rem; }
  .person-actions { width: 100%; }
  .person-actions .btn { flex: 1; }
  .bell-panel { position: fixed; top: 64px; left: 16px; right: 16px; width: auto; }
}

/* ==========================================================================
   Marketing chrome: nav actions, footer, legal pages
   ========================================================================== */

/* Matched to the logout button beside it (34px). A CTA and its adjacent icon
   button reading as two different heights is the thing the eye catches first. */
.nav-console { height: 34px; padding-top: 0; padding-bottom: 0; line-height: 1; }

.nav-logout {
  display: flex; align-items: center; justify-content: center;
  width: 34px; height: 34px; padding: 0; border-radius: var(--radius-sm);
  border: 1px solid var(--border); background: var(--bg-elevated);
  color: var(--text-dim); cursor: pointer; transition: color .12s, border-color .12s, background .12s;
}
.nav-logout svg { width: 17px; height: 17px; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
.nav-logout:hover { color: var(--danger); border-color: rgba(255,64,96,.4); background: rgba(255,64,96,.08); }

/* Hero CTA pair.
   These two were always pixel-identical in box size -- measured equal at every
   viewport. What made them read as misaligned was optical, not geometric: the
   primary carries an outer glow that spills past its border, so its silhouette
   looked taller than the outline button sitting flush beside it. Dropping the
   spill (keeping a tight inner glow) and giving both an explicit shared height
   makes the pair read as one unit. */
.hero-actions { align-items: center; }
.hero-actions .btn { height: 56px; padding-top: 0; padding-bottom: 0; box-shadow: none; }
.hero-actions .btn-primary:hover { box-shadow: 0 0 22px -10px rgba(var(--accent-rgb),.9); }
.hero-actions .btn-ghost { border-color: var(--border-bright); background: var(--bg-elevated); }
.hero-actions .btn-ghost:hover { border-color: var(--accent-dim); background: var(--surface); }

/* ---------- Footer ---------- */
.footer-grid {
  display: grid; grid-template-columns: 2fr 1fr 1fr; gap: 32px; align-items: start;
}
@media (max-width: 700px) { .footer-grid { grid-template-columns: 1fr; gap: 24px; } }
.footer-note { max-width: 42ch; margin: 10px 0 0; font-size: .84rem; color: var(--text-faint); }
.footer-links { display: flex; flex-direction: column; gap: 8px; }
.footer-head {
  font-family: var(--font-mono); font-size: .68rem; letter-spacing: .12em;
  text-transform: uppercase; color: var(--text-faint); margin-bottom: 2px;
}
.footer-links a { color: var(--text-dim); font-size: .88rem; text-decoration: none; width: fit-content; }
.footer-links a:hover { color: var(--accent); }
.footer-bottom {
  border-top: 1px solid var(--border); margin-top: 26px; padding-top: 18px;
  display: flex; justify-content: space-between; gap: 14px; flex-wrap: wrap;
  font-size: .8rem; color: var(--text-faint);
}

/* ---------- Legal pages ---------- */
.legal { max-width: 780px; padding-top: 46px; padding-bottom: 70px; }
.legal-back { font-size: .86rem; color: var(--text-dim); display: inline-block; margin-bottom: 22px; }
.legal-back:hover { color: var(--accent); }
.legal h1 { font-size: clamp(2rem, 1.5rem + 2vw, 2.8rem); margin-bottom: 6px; }
.legal-updated { font-size: .78rem; color: var(--text-faint); margin-bottom: 30px; }
.legal-lede { font-size: 1.05rem; color: var(--text-dim); margin-bottom: 34px; }
.legal h2 {
  font-size: 1.12rem; margin: 34px 0 10px; padding-top: 20px;
  border-top: 1px solid var(--border);
}
.legal p, .legal li { color: var(--text-dim); line-height: 1.65; }
.legal ul { padding-left: 20px; margin: 0 0 16px; }
.legal li { margin-bottom: 7px; }
.legal a { color: var(--accent); }
.legal-table { margin: 6px 0 18px; }
.legal-table td { vertical-align: top; }
.legal-table td:first-child { white-space: nowrap; color: var(--text); font-weight: 500; }
.legal-callout {
  border: 1px solid rgba(var(--accent-rgb),.35); background: rgba(var(--accent-rgb),.07);
  border-radius: var(--radius); padding: 16px 18px; margin-bottom: 30px;
  color: var(--text-dim); font-size: .94rem; line-height: 1.6;
}
.legal-callout strong { color: var(--accent); }

/* ---------- Landing stats strip ---------- */
.stat-strip {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--bg-elevated); overflow: hidden;
}
.stat-strip .ss {
  padding: 20px 22px; border-right: 1px solid var(--border);
  display: flex; flex-direction: column; gap: 4px;
}
.stat-strip .ss:last-child { border-right: none; }
.ss-n { font-family: var(--font-mono); font-size: 1.8rem; font-weight: 700; line-height: 1; color: var(--accent); }
.ss-l { font-family: var(--font-mono); font-size: .68rem; letter-spacing: .1em; text-transform: uppercase; color: var(--text-faint); }
@media (max-width: 640px) { .stat-strip .ss { border-right: none; border-bottom: 1px solid var(--border); } }

/* ---------- FAQ ---------- */
.faq { border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; }
.faq details { border-bottom: 1px solid var(--border); }
.faq details:last-child { border-bottom: none; }
.faq summary {
  padding: 16px 20px; cursor: pointer; font-size: 1rem; color: var(--text);
  list-style: none; display: flex; align-items: center; justify-content: space-between; gap: 12px;
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after { content: '+'; font-family: var(--font-mono); color: var(--accent); font-size: 1.2rem; flex-shrink: 0; }
.faq details[open] summary::after { content: '\2212'; }
.faq summary:hover { background: var(--surface); }
.faq .faq-body { padding: 0 20px 18px; color: var(--text-dim); font-size: .93rem; line-height: 1.65; }
.faq .faq-body p { margin: 0; max-width: none; }

/* Answer reveal: the beat between answering and the next question. */
.answer-reveal {
  margin-top: 16px; padding: 14px 16px; border-radius: var(--radius-sm);
  border: 1px solid var(--border); background: var(--bg-soft);
}
.answer-reveal.is-correct { border-color: var(--ok); background: rgba(var(--ok-rgb), .07); }
.answer-reveal.is-wrong { border-color: var(--danger); background: rgba(255,64,96,.07); }
.answer-reveal .ar-verdict { font-weight: 700; font-size: 1.02rem; margin-bottom: 4px; }
.answer-reveal.is-correct .ar-verdict { color: var(--ok); }
.answer-reveal.is-wrong .ar-verdict { color: var(--danger); }
.answer-reveal .ar-why { margin: 0 0 10px; font-size: .9rem; color: var(--text-dim); }
.answer-reveal .ar-next { font-size: .74rem; color: var(--text-faint); display: flex; align-items: center; gap: 8px; }
.answer-reveal .ar-bar { height: 2px; width: 100%; max-width: 120px; background: currentColor; opacity: .5; display: block; }

/* Table rows that open something. */
.row-click { cursor: pointer; transition: background .12s; }
.row-click:hover { background: rgba(var(--accent-rgb), .07); }
.hero-actions { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }

/* Pager strip under a paged table. */
.pager { margin-top: 12px; padding-top: 10px; border-top: 1px solid var(--border); }
.field-hint { display: block; margin-top: 5px; font-size: .76rem; color: var(--text-faint); }

/* ============================================================================
   MOBILE
   ----------------------------------------------------------------------------
   The desktop layout already reflows at these widths; what it did not do was
   become *touchable*. Everything below is about the hand, not the grid:
   44px targets, text you can read without pinching, inputs that do not make
   iOS zoom, and tables that stop pretending to be tables.
   ========================================================================== */

@media (max-width: 640px) {

  /* ---- Reachability -------------------------------------------------------
     44px is the Apple/Material floor for a finger. Buttons were 29-38px, which
     is fine for a mouse cursor and a coin-flip for a thumb. */
  .btn {
    min-height: 44px; padding: 11px 18px; font-size: .95rem;
  }
  .btn-sm { min-height: 40px; padding: 9px 15px; font-size: .88rem; }
  .btn-lg { min-height: 52px; font-size: 1rem; }
  .btn-block { width: 100%; }

  /* Icon-only buttons need to be square, not just tall. */
  .nav-logout, .sidebar-toggle, .bell-btn, .sfx-toggle {
    min-width: 44px; min-height: 44px;
  }

  /* Bare text links inside prose and footers get vertical padding so the tap
     box is bigger than the glyphs, without moving the text visually. */
  .legal-back, .bell-item, .leader-user, .nav-logo,
  .footer-links a, .footer-col a, .footer-bottom a {
    display: inline-flex; align-items: center; min-height: 40px;
  }
  .footer-links a, .footer-col a, .footer-bottom a { padding: 3px 0; }

  /* Links inside running prose must not break the line box, so they get their
     reach from padding rather than by becoming flex items -- a display:flex
     link mid-sentence would jump onto its own line. */
  .legal-body a, .faq-body a, .prose a, p a, button.link {
    display: inline; padding: 7px 0; min-height: 0;
  }

  /* ---- Legibility ---------------------------------------------------------
     Nothing readable should sit under 12px on a phone. These were 10.6-11.2px. */
  .tag { font-size: .76rem; padding: 4px 9px; }
  .table th { font-size: .78rem; }
  .figure-label, .ss-l, .footer-head, .stat-label, .bi-time,
  .plan-flag, .flash-card .side-label, .flash-card .flip-hint { font-size: .76rem; }
  .faint, .panel-head span:last-child { font-size: .82rem; }

  /* ---- Inputs -------------------------------------------------------------
     iOS zooms the whole page when a focused input is under 16px. That zoom
     never undoes itself and leaves the user scrolled sideways on a layout
     that had no horizontal overflow to begin with -- so 16px is a layout fix,
     not a typography preference. */
  .field input, .field select, .field textarea,
  input[type="text"], input[type="password"], input[type="number"],
  input[type="date"], input[type="email"], select, textarea {
    font-size: 16px; min-height: 46px; padding: 12px 14px;
  }
  .field label { font-size: .85rem; }

  /* ---- Rhythm -------------------------------------------------------------
     Desktop padding eats a phone's width. */
  .app-main { padding: 18px 14px 90px; }
  .card { padding: 16px 14px; }
  .panel-body { padding: 14px; }
  .panel-head { padding: 11px 14px; }
  h1, .page-head h1 { font-size: 1.45rem; }
  .page-sub, #page-sub { font-size: .88rem; }

  /* Rows of buttons stack instead of squeezing to unreadable widths. */
  .row { flex-wrap: wrap; gap: 8px; }
  .onb-actions { flex-direction: column-reverse; align-items: stretch; gap: 10px; }
  .onb-actions .row { width: 100%; }
  .onb-actions .btn { width: 100%; }

  /* ---- Safe areas ---------------------------------------------------------
     Notch and home-indicator territory on modern phones. */
  .app-main { padding-bottom: calc(90px + env(safe-area-inset-bottom)); }
  .sidebar { padding-top: calc(10px + env(safe-area-inset-top)); }
  .modal { padding-bottom: calc(20px + env(safe-area-inset-bottom)); }
}

/* ---- Tables become cards -------------------------------------------------
   A 7-column table on a 390px screen is a sideways-scrolling strip: you drag
   right to read a row, drag back to read the next, and never see two rows
   compared. Stacked cards trade the column alignment (which was already lost
   to the scroll) for rows you can actually read.

   data-label is stamped onto every cell by watchTables() in main.js. */
@media (max-width: 640px) {
  .table-scroll { overflow-x: visible; }

  .table, .table thead, .table tbody, .table tr, .table td { display: block; width: 100%; }
  /* Headers move into each cell via data-label, so the real thead is dead
     weight -- clip it rather than display:none, which would drop it from the
     accessibility tree along with the table semantics. */
  .table thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }

  .table tr {
    margin-bottom: 10px; padding: 4px 12px;
    border: 1px solid var(--border); border-radius: var(--radius-sm);
    background: var(--bg-elevated);
  }
  .table tr:last-child { margin-bottom: 0; }
  .table tbody tr:hover { background: var(--bg-elevated); }

  .table td {
    display: flex; align-items: center; justify-content: space-between; gap: 14px;
    padding: 9px 0; text-align: right;
    border-bottom: 1px solid var(--border);
  }
  .table tr td:last-child { border-bottom: none; }
  .table td::before {
    content: attr(data-label);
    font-family: var(--font-mono); font-size: .72rem; letter-spacing: .04em;
    text-transform: uppercase; color: var(--text-faint);
    text-align: left; flex: 0 0 auto;
  }
  /* A cell with no header (action buttons, spacer columns) spans the card. */
  .table td:not([data-label]) { justify-content: stretch; text-align: left; }
  .table td:not([data-label])::before { content: none; }
  .table .num { text-align: right; }

  /* Leaderboard reads as a ranking, not a spreadsheet: keep it inline. */
  .table.table-flat tr { display: flex; align-items: center; gap: 10px; padding: 10px 12px; }
  .table.table-flat td { border: none; padding: 0; display: inline-flex; }
  .table.table-flat td::before { content: none; }
}

/* ---- Topology: tap to expand (mobile only) ---- */
.topo-wrap { position: relative; }
.topo-expand {
  position: absolute; top: 8px; right: 8px; display: none;
  width: 44px; height: 44px; align-items: center; justify-content: center;
  border-radius: var(--radius-sm); border: 1px solid var(--border-bright);
  background: var(--bg-elevated); color: var(--text-dim); cursor: pointer;
}
.topo-expand svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; }
@media (max-width: 640px) { .topo-expand { display: flex; } }

.topo-fullscreen {
  position: fixed; inset: 0; z-index: 300;
  background: var(--bg); display: flex; flex-direction: column;
  padding: calc(12px + env(safe-area-inset-top)) 12px calc(12px + env(safe-area-inset-bottom));
}
.topo-fullscreen .topo-close {
  align-self: flex-end; min-height: 44px; padding: 10px 18px;
  border-radius: var(--radius-sm); border: 1px solid var(--border-bright);
  background: var(--bg-elevated); color: var(--text); font-family: var(--font-body);
  font-weight: 600; cursor: pointer;
}
.topo-fs-inner { flex: 1; display: flex; align-items: center; justify-content: center; min-height: 0; overflow: hidden; }
.topo-fs-inner svg { width: 100%; height: 100%; max-height: 100%; }
.topo-fs-hint { text-align: center; font-size: .72rem; color: var(--text-faint); margin: 8px 0 0; }

/* A network diagram is inherently landscape (640x304). Dropping it into a
   portrait phone letterboxes it -- same tiny drawing, now with grey bars, so
   "expand" would have expanded nothing. Rotating a quarter turn spends the
   screen's long axis on the diagram's long axis instead, which is the whole
   point of going full-screen. The swapped width/height keep it filling the
   box after the rotation, since transform does not affect layout size. */
@media (orientation: portrait) {
  .topo-fs-inner svg {
    width: calc(100vh - 150px);
    height: 100vw;
    /* Without this the flex parent shrinks it straight back to the portrait
       width, undoing the rotation's whole benefit. */
    flex: 0 0 auto;
    transform: rotate(90deg);
  }
}
/* Rotated far enough that the user can also just turn the phone; once they
   do, the hint has served its purpose. */
@media (orientation: landscape) { .topo-fs-hint { display: none; } }

/* ==========================================================================
   Packet Flood (tower defence)
   ========================================================================== */

.td-hud {
  display: flex; align-items: center; gap: 0; flex-wrap: wrap;
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--bg-elevated); margin-bottom: 14px; overflow: hidden;
}
.td-stat {
  display: flex; flex-direction: column; gap: 1px;
  padding: 10px 18px; border-right: 1px solid var(--border); min-width: 88px;
}
.td-stat b { font-family: var(--font-mono); font-size: 1.35rem; line-height: 1; }
.td-stat span {
  font-family: var(--font-mono); font-size: .68rem; text-transform: uppercase;
  letter-spacing: .07em; color: var(--text-faint);
}
.td-hud-status { margin-left: auto; padding: 10px 16px; }

.td-layout { display: grid; grid-template-columns: 1fr 300px; gap: 16px; align-items: start; }
.td-stage {
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--bg); padding: 10px; overflow: hidden;
}
#td-canvas {
  display: block; width: 100%; height: auto;
  border-radius: var(--radius-sm); cursor: crosshair;
  /* The canvas is a game board, not a document: a drag on it should aim, not
     scroll the page out from under the player. */
  touch-action: manipulation;
}
.td-controls { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 10px; }
.td-scroll-hint { display: none; }

/* ---- shop ---- */
.td-shop { display: flex; flex-direction: column; gap: 6px; padding: 10px; }
.td-shop-item {
  display: flex; align-items: center; gap: 10px; width: 100%; text-align: left;
  padding: 9px 11px; border-radius: var(--radius-sm);
  border: 1px solid var(--border); background: var(--bg-elevated);
  color: var(--text); cursor: pointer; font-family: inherit;
  transition: border-color .12s, background .12s, transform .12s;
}
.td-shop-item:hover { border-color: var(--accent-dim); transform: translateY(-1px); }
.td-shop-item.selected { border-color: var(--accent); background: rgba(var(--accent-rgb),.10); }
/* Unaffordable stays clickable so the player can still read what it does --
   it is dimmed as information, not disabled as a wall. */
.td-shop-item.cant-afford { opacity: .45; }
.td-shop-item .tsi-key {
  flex: 0 0 auto; width: 19px; height: 19px; border-radius: 4px;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--surface-2); color: var(--text-faint); font-size: .68rem;
}
.td-shop-item .tsi-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.td-shop-item .tsi-name { font-weight: 600; font-size: .88rem; }
.td-shop-item .tsi-desc { font-size: .74rem; color: var(--text-faint); line-height: 1.35; }
.td-shop-item .tsi-cost { flex: 0 0 auto; font-size: .78rem; color: var(--accent); }

/* ---- inspector ---- */
.td-statgrid {
  display: grid; grid-template-columns: auto 1fr; gap: 3px 12px;
  font-size: .8rem; margin-top: 4px;
}
.td-statgrid span { color: var(--text-faint); }
.td-statgrid b { text-align: right; }

.td-map { cursor: pointer; font-family: inherit; }

.td-result {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; margin-top: 6px;
}
.td-result div {
  padding: 12px 8px; border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--bg-elevated);
}
.td-result b { display: block; font-family: var(--font-mono); font-size: 1.5rem; line-height: 1; }
.td-result span {
  display: block; margin-top: 4px; font-family: var(--font-mono);
  font-size: .66rem; text-transform: uppercase; letter-spacing: .06em; color: var(--text-faint);
}

@media (max-width: 1000px) {
  .td-layout { grid-template-columns: 1fr; }
  /* Board first: on a narrow screen the shop below the board is a scroll away,
     which is right -- you look at the map, then decide what to buy. */
  .td-side { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; align-items: start; }
}

@media (max-width: 640px) {
  .td-side { grid-template-columns: 1fr; }

  /* A 20-cell board squeezed into 390px gives 17px cells -- too small to hit
     and too small to read. Hold a minimum board size and let the stage scroll
     horizontally instead: a 32px cell is a target you can actually tap, and
     panning a board is a familiar gesture. touch-action stays `manipulation`,
     which keeps panning while removing the 300ms double-tap delay, so a swipe
     scrolls and a tap places. */
  .td-stage { overflow-x: auto; -webkit-overflow-scrolling: touch; }
  #td-canvas { min-width: 640px; }
  .td-scroll-hint {
    display: block; margin: 6px 0 0; text-align: center;
    font-family: var(--font-mono); font-size: .7rem; color: var(--text-faint);
  }
  .td-stat { padding: 8px 12px; min-width: 62px; flex: 1; border-right: 1px solid var(--border); }
  .td-stat b { font-size: 1.1rem; }
  .td-hud-status { margin-left: 0; width: 100%; border-top: 1px solid var(--border); }
  .td-stage { padding: 6px; }
  .td-controls .btn { flex: 1 1 45%; }
  .td-shop-item { padding: 11px; min-height: 52px; }
  .td-shop-item .tsi-desc { display: none; }   /* the title tooltip still carries it */
  .td-result b { font-size: 1.2rem; }
}

/* ---------------------------------------------------------------- checkout */

/* Standalone page, not the app shell. One centred column: on a checkout the
   eye should travel straight down -- what you are buying, who it is for, pay --
   with nothing beside it competing for a click. */
.checkout-page { min-height: 100vh; }

.checkout-bar {
  display: flex; align-items: center; justify-content: space-between;
  padding: 16px 24px; border-bottom: 1px solid var(--border);
  background: var(--bg-elevated);
}
.checkout-exit { font-size: .86rem; color: var(--text-dim); }
.checkout-exit:hover { color: var(--accent); }

.checkout-shell { padding: 40px 20px 72px; }
.checkout-stack {
  max-width: 560px; margin: 0 auto;
  display: flex; flex-direction: column; gap: 18px;
}
.checkout-loading {
  max-width: 560px; margin: 60px auto; text-align: center;
  font-family: var(--font-mono); font-size: .85rem; color: var(--text-faint);
}

.checkout-head { text-align: center; margin-bottom: 4px; }
.checkout-head h1 { font-size: 1.7rem; margin-bottom: 6px; }
.checkout-head p { color: var(--text-dim); font-size: .92rem; margin: 0; }

.checkout-card {
  border: 1px solid var(--border); border-radius: var(--radius);
  background: linear-gradient(180deg, var(--surface), var(--bg-elevated));
  overflow: hidden;
}
.checkout-card-head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 12px 18px; border-bottom: 1px solid var(--border);
  background: var(--bg-elevated);
  font-family: var(--font-mono); font-size: .72rem; text-transform: uppercase;
  letter-spacing: .08em; color: var(--text-faint);
}
.checkout-card-body { padding: 18px; }

.checkout-line { align-items: flex-start; gap: 16px; }
.checkout-item { font-weight: 600; color: var(--text); }
.checkout-item-sub { font-size: .82rem; color: var(--text-faint); margin-top: 2px; }
.checkout-amount { font-family: var(--font-mono); font-size: 1.15rem; color: var(--text); white-space: nowrap; }

.checkout-includes { margin: 16px 0 0; padding-top: 14px; border-top: 1px solid var(--border); }
.checkout-includes li { font-size: .86rem; }

.checkout-total {
  margin-top: 16px; padding-top: 14px; border-top: 1px solid var(--border-bright);
  font-weight: 600;
}
.checkout-total-amount { font-family: var(--font-mono); font-size: 1.5rem; color: var(--accent); }
.checkout-total-amount small { font-size: .7rem; color: var(--text-faint); margin-left: 4px; }

.checkout-fineprint { margin: 12px 0 0; font-size: .78rem; line-height: 1.55; color: var(--text-faint); }

.checkout-account {
  display: flex; align-items: center; gap: 12px;
  padding-bottom: 16px; margin-bottom: 16px; border-bottom: 1px solid var(--border);
}

.checkout-fieldset { border: 0; padding: 0; margin: 0 0 16px; }
.checkout-fieldset legend {
  padding: 0; margin-bottom: 8px;
  font-family: var(--font-mono); font-size: .82rem; text-transform: uppercase;
  letter-spacing: .05em; color: var(--text-dim);
}
.checkout-known-email { display: block; margin-top: 3px; font-size: .8rem; color: var(--text-faint); word-break: break-all; }

/* The consent rows are labels wrapping the box, so the text is part of the hit
   target -- a 14px checkbox on its own is a miserable thing to tap. */
.checkout-consent {
  display: flex; gap: 10px; align-items: flex-start;
  margin: 0 0 14px; padding: 12px; cursor: pointer;
  border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--bg-elevated);
  font-size: .84rem; line-height: 1.5; color: var(--text-dim);
}
.checkout-consent:hover { border-color: var(--border-bright); }
.checkout-consent input {
  width: 17px; height: 17px; flex-shrink: 0; margin-top: 1px; accent-color: var(--accent);
}

.checkout-go { width: 100%; justify-content: center; }
.checkout-go[disabled] { opacity: .6; cursor: progress; }

.checkout-trust {
  display: flex; gap: 9px; align-items: flex-start; margin-top: 14px;
  font-size: .8rem; line-height: 1.55; color: var(--text-faint);
}
.checkout-trust .led { margin-top: 6px; }
.checkout-trust p { margin: 0; }
.checkout-trust strong { color: var(--text-dim); }

/* block, not inline-block: inside the callout it otherwise wraps into the last
   line of prose and reads as part of the sentence on a narrow screen. */
.checkout-notlive-back { display: block; width: fit-content; margin-top: 14px; }

@media (max-width: 480px) {
  .checkout-shell { padding: 26px 14px 56px; }
  .checkout-card-body { padding: 15px; }
  .checkout-head h1 { font-size: 1.45rem; }
  /* Already read on the pricing page; on a phone it is four lines between the
     price and the pay button. */
  .checkout-includes { display: none; }
}

/* ------------------------------------------------------------------- auth */

.auth-main { padding-top: 64px; padding-bottom: 72px; }
.auth-title { font-size: 1.6rem; margin-bottom: 6px; }
.auth-sub { font-size: .92rem; color: var(--text-dim); margin-bottom: 22px; line-height: 1.55; }
.auth-alt { margin: 16px 0 0; font-size: .88rem; text-align: center; }
.auth-legal { margin: 14px 0 0; font-size: .78rem; color: var(--text-faint); line-height: 1.55; text-align: center; }

/* Input with a button welded to its right edge. On a phone the button drops
   below at full width rather than squeezing the input down to nothing --
   an email field you cannot read what you typed into is worse than a wrap. */
.field-inline { display: flex; gap: 8px; align-items: stretch; }
.field-inline input { flex: 1; min-width: 0; }
.field-inline .btn { flex-shrink: 0; white-space: nowrap; }
@media (max-width: 430px) {
  .field-inline { flex-wrap: wrap; }
  .field-inline .btn { width: 100%; }
}

.field-link {
  display: inline-block; margin-top: 7px; font-size: .8rem; color: var(--text-dim);
}
.field-link:hover { color: var(--accent); }

/* Status line under a field: neutral by default, never red unless something
   actually went wrong. "A code is on its way" is not an error. */
.form-note { font-size: .8rem; line-height: 1.5; color: var(--text-faint); margin-top: 7px; min-height: 1.2em; }
.form-note.ok { color: var(--ok); }
.form-note.bad { color: var(--danger); }

/* One-time code. Wide tracking so six digits read as six digits, and a
   monospace face so 0/O and 1/l cannot be confused while transcribing. */
.code-input {
  font-family: var(--font-mono) !important;
  font-size: 1.5rem !important;
  letter-spacing: .38em;
  text-align: center;
  padding: 12px !important;
}
.code-input:disabled { color: var(--ok); border-color: rgba(36,209,126,.4); opacity: 1; }

/* --- password strength --- */
.pw-meter {
  --fill: 0%;
  display: flex; align-items: center; gap: 10px; margin-top: 10px;
}
.pw-bar {
  flex: 1; height: 5px; border-radius: 3px; background: var(--surface-3, #202b38);
  overflow: hidden; position: relative;
}
.pw-bar::after {
  content: ''; position: absolute; inset: 0 auto 0 0; width: var(--fill);
  border-radius: 3px; background: var(--text-faint);
  transition: width .22s ease, background .22s ease;
}
.pw-label {
  font-family: var(--font-mono); font-size: .7rem; text-transform: uppercase;
  letter-spacing: .06em; color: var(--text-faint); min-width: 62px; text-align: right;
}
.pw-meter[data-band="weak"] .pw-bar::after { background: var(--danger); }
.pw-meter[data-band="weak"] .pw-label { color: var(--danger); }
.pw-meter[data-band="fair"] .pw-bar::after { background: var(--warn); }
.pw-meter[data-band="fair"] .pw-label { color: var(--warn); }
.pw-meter[data-band="strong"] .pw-bar::after { background: var(--ok); }
.pw-meter[data-band="strong"] .pw-label { color: var(--ok); }

.pw-rules { list-style: none; padding: 0; margin: 10px 0 0; }
.pw-rules li {
  display: flex; align-items: center; gap: 8px; margin-bottom: 5px;
  font-size: .8rem; color: var(--text-faint); transition: color .15s;
}
.pw-rules li.met { color: var(--ok); }
.pw-rules li.optional { color: var(--text-faint); opacity: .75; }
.pw-tick { font-family: var(--font-mono); width: 12px; flex-shrink: 0; text-align: center; }

.pw-match { display: block; margin-top: 7px; font-size: .8rem; min-height: 1.2em; }
.pw-match.ok { color: var(--ok); }
.pw-match.bad { color: var(--danger); }

@media (prefers-reduced-motion: reduce) {
  .pw-bar::after { transition: none; }
}

/* Destructive section. Bordered in red and placed last so it can never be
   mistaken for one of the save-and-continue panels above it. */
.panel-danger { border-color: rgba(255,64,96,.32); }
.panel-danger .panel-head { color: var(--danger); background: rgba(255,64,96,.06); }

/* Narrow modal for a single decision — the plan comparison modal is wide
   because it holds two columns; a password prompt at that width is a lot of
   empty space around one field. */
.modal-sm { max-width: 420px; }

/* Billing cycle switcher. Two large targets rather than a toggle: a toggle
   hides one of the two prices, and the whole point is comparing them. */
.cycle-switch { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin-bottom: 18px; }
.cycle-opt {
  position: relative; display: flex; flex-direction: column; gap: 3px;
  padding: 12px 14px; text-align: left; cursor: pointer;
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--bg-elevated); color: var(--text-dim);
  font-family: inherit; transition: border-color .14s, background .14s, color .14s;
}
.cycle-opt:hover { border-color: var(--border-bright); color: var(--text); }
.cycle-opt.active {
  border-color: var(--accent); color: var(--text);
  background: rgba(var(--accent-rgb), .10);
  box-shadow: 0 0 0 1px rgba(var(--accent-rgb), .25);
}
.cycle-name {
  font-family: var(--font-mono); font-size: .68rem; text-transform: uppercase;
  letter-spacing: .07em; color: var(--text-faint);
}
.cycle-opt.active .cycle-name { color: var(--accent); }
.cycle-price { font-family: var(--font-mono); font-size: 1.1rem; font-weight: 700; color: var(--text); }
.cycle-price small { font-size: .68rem; font-weight: 400; color: var(--text-faint); margin-left: 2px; }
.cycle-save {
  font-family: var(--font-mono); font-size: .66rem; color: var(--ok);
  text-transform: uppercase; letter-spacing: .05em;
}

/* The Pro card when you already own it. Deliberately not a button: there is
   nothing to press, and a disabled-looking button reads as something broken. */
.plan-owned {
  display: flex; align-items: center; gap: 9px;
  padding: 11px 16px; border-radius: var(--radius-sm);
  border: 1px solid rgba(36,209,126,.35); background: rgba(36,209,126,.08);
  color: var(--ok); font-family: var(--font-mono); font-size: .82rem;
  text-transform: uppercase; letter-spacing: .05em;
}
/* The annual price, sitting under the headline monthly one. */
.plan-alt {
  display: block; margin-top: 6px; font-size: .8rem; font-weight: 400;
  font-family: var(--font-body); color: var(--text-faint); letter-spacing: 0;
}
.plan-alt strong { color: var(--ok); }

/* Centred confirmation toast: direct feedback for something just clicked,
   so it sits where the eye already is rather than in the corner. */
.toast-host-centre {
  right: auto; left: 50%; transform: translateX(-50%);
  align-items: center; bottom: 28px;
}
.toast.ok { border-left-color: var(--ok); }

/* ---------- profile share ---------- */
.share-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 32px; height: 32px; margin-left: 10px; vertical-align: middle;
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: transparent; color: var(--text-dim); cursor: pointer;
  transition: color .13s, border-color .13s, background .13s;
}
.share-btn svg {
  width: 15px; height: 15px; fill: none; stroke: currentColor;
  stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round;
}
.share-btn:hover { color: var(--accent); border-color: var(--accent-dim); background: rgba(var(--accent-rgb),.09); }

.share-pop {
  position: absolute; z-index: 60; margin-top: 8px; padding: 12px;
  min-width: min(320px, calc(100vw - 40px));
  border: 1px solid var(--border-bright); border-radius: var(--radius);
  background: var(--surface-2); box-shadow: var(--shadow);
}
.share-pop-label {
  font-family: var(--font-mono); font-size: .68rem; text-transform: uppercase;
  letter-spacing: .07em; color: var(--text-faint); margin-bottom: 8px;
}
.share-row { display: flex; gap: 7px; align-items: stretch; }
.share-url {
  flex: 1; min-width: 0; padding: 8px 10px; font-family: var(--font-mono); font-size: .78rem;
  background: var(--bg); border: 1px solid var(--border); border-radius: var(--radius-sm);
  color: var(--text);
}
.share-copy { flex-shrink: 0; display: inline-flex; align-items: center; gap: 6px; }
.share-copy svg {
  width: 13px; height: 13px; fill: none; stroke: currentColor;
  stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round;
}
/* The h1 is the positioning context for the popover. */
.hero-name { position: relative; }
@media (max-width: 480px) {
  .share-pop { left: 0; }
}

/* ---------------------------------------------------------------- blog */

.blog-grid { gap: 18px; }
.blog-card {
  display: flex; flex-direction: column; overflow: hidden;
  border: 1px solid var(--border); border-radius: var(--radius);
  background: linear-gradient(180deg, var(--surface), var(--bg-elevated));
  text-decoration: none; color: inherit;
  transition: transform .16s, border-color .16s, box-shadow .16s;
}
.blog-card:hover {
  transform: translateY(-3px); border-color: var(--accent-dim);
  box-shadow: var(--shadow), var(--glow-accent); text-decoration: none;
}

/* Illustrations are inline SVG built from the design tokens, not image files:
   they stay crisp at any density, recolour with the theme, cost no requests,
   and cannot break the CSP the way a remote image would. */
.blog-thumb {
  height: 150px; display: grid; place-items: center; padding: 12px;
  border-bottom: 1px solid var(--border);
  background: radial-gradient(420px 200px at 50% 0%, rgba(var(--accent-rgb),.10), transparent 70%), var(--bg);
}
.blog-thumb svg { width: 100%; height: 100%; }
.blog-thumb svg rect, .blog-thumb svg circle { fill: var(--surface-3); stroke: var(--border-bright); stroke-width: 1.5; }
.blog-thumb svg .hot { fill: rgba(var(--accent-rgb),.28); stroke: var(--accent); }
.blog-thumb svg .axis { stroke: var(--border-bright); stroke-width: 1.5; fill: none; }
.blog-thumb svg .axis.dim { stroke: var(--border); stroke-dasharray: 4 4; }
.blog-thumb svg .link { stroke: var(--border-bright); stroke-width: 1.6; fill: none; }
.blog-thumb svg .arc { stroke: var(--surface-3); stroke-width: 10; fill: none; stroke-linecap: round; }
.blog-thumb svg .arc.hot { stroke: var(--accent); }
.blog-thumb svg .needle { stroke: var(--accent-bright); stroke-width: 3; stroke-linecap: round; fill: none; }
.blog-thumb svg .fill { fill: var(--accent); stroke: none; }

.blog-body { padding: 18px; display: flex; flex-direction: column; gap: 9px; flex: 1; }
/* The card body is a column flex box, so the cross axis is horizontal and an
   inline-flex tag stretches edge to edge. Same trap as .plan > .tag. */
.blog-body > .tag { align-self: flex-start; }
.blog-body h3 { font-size: 1.08rem; line-height: 1.35; margin: 0; }
.blog-body p { font-size: .89rem; color: var(--text-dim); line-height: 1.6; margin: 0; flex: 1; }
.blog-more {
  font-family: var(--font-mono); font-size: .76rem; color: var(--accent);
  margin-top: 2px;
}

/* ---- article ---- */
.post { max-width: 720px; padding-top: 46px; padding-bottom: 80px; }
.post-back { font-size: .86rem; color: var(--text-dim); display: inline-block; margin-bottom: 22px; }
.post-back:hover { color: var(--accent); }
.post h1 { font-size: clamp(1.9rem, 1.4rem + 2vw, 2.6rem); line-height: 1.15; margin-bottom: 12px; }
.post-meta {
  display: flex; gap: 14px; flex-wrap: wrap; align-items: center;
  font-family: var(--font-mono); font-size: .76rem; color: var(--text-faint);
  margin-bottom: 30px; padding-bottom: 20px; border-bottom: 1px solid var(--border);
}
.post-lede { font-size: 1.08rem; color: var(--text-dim); line-height: 1.65; margin-bottom: 30px; }
.post h2 {
  font-size: 1.3rem; margin: 38px 0 12px; padding-top: 4px;
}
.post h3 { font-size: 1.05rem; margin: 26px 0 8px; }
.post p, .post li { color: var(--text-dim); line-height: 1.72; }
.post p { margin-bottom: 16px; }
.post ul, .post ol { padding-left: 22px; margin: 0 0 18px; }
.post li { margin-bottom: 8px; }
.post strong { color: var(--text); }
.post code {
  background: var(--bg-elevated); border: 1px solid var(--border); border-radius: 4px;
  padding: 1px 5px; font-size: .88em; color: var(--accent-bright);
}
.post pre {
  background: var(--bg); border: 1px solid var(--border); border-radius: var(--radius-sm);
  padding: 14px 16px; overflow-x: auto; margin: 0 0 20px;
}
.post pre code { background: none; border: 0; padding: 0; color: var(--text); }
.post .callout {
  border-left: 3px solid var(--accent); background: rgba(var(--accent-rgb),.06);
  padding: 14px 18px; border-radius: 0 var(--radius-sm) var(--radius-sm) 0; margin: 0 0 22px;
}
.post .callout p:last-child { margin-bottom: 0; }
.post-table { width: 100%; margin: 0 0 22px; }
.post-table th, .post-table td {
  text-align: left; padding: 9px 12px; border-bottom: 1px solid var(--border); font-size: .88rem;
}
.post-table th { font-family: var(--font-mono); font-size: .72rem; text-transform: uppercase; letter-spacing: .06em; color: var(--text-faint); }
.post-table td { color: var(--text-dim); }
.post-cta {
  margin-top: 40px; padding: 22px; border: 1px solid var(--border-bright);
  border-radius: var(--radius); background: linear-gradient(180deg, var(--surface), var(--bg-elevated));
  text-align: center;
}
.post-cta h3 { margin: 0 0 8px; }
.post-cta p { margin: 0 0 16px; font-size: .9rem; }

/* ---------- public site navigation ----------
   Three zones: identity left, pages centre, account right. The centre is
   absolutely positioned so it stays optically centred on the page regardless
   of how wide the logo or the account buttons happen to be -- centring it with
   flex means it drifts left every time the right-hand side grows.

   No `position: relative` here. The bar is already `position: sticky` further
   up, sticky establishes a containing block for absolute children just as
   relative does, and re-declaring it here silently overrode the sticky and
   dropped the nav off the top of the page on scroll. */
.nav-center {
  position: absolute; left: 50%; transform: translateX(-50%);
  display: flex; gap: 4px; align-items: center;
}
.nav-item {
  padding: 7px 12px; border-radius: var(--radius-sm);
  font-size: .88rem; color: var(--text-dim); text-decoration: none;
  transition: color .13s, background .13s;
}
.nav-item:hover { color: var(--text); background: var(--surface-2); text-decoration: none; }
.nav-item.active { color: var(--accent); background: rgba(var(--accent-rgb),.10); }

/* Below the breakpoint the centre nav cannot stay centred without colliding
   with both sides. It becomes a scrollable strip on its own row instead --
   which keeps every page reachable rather than hiding them behind a menu the
   marketing pages have no other use for. */
@media (max-width: 860px) {
  .nav { flex-wrap: wrap; row-gap: 8px; }
  .nav-center {
    position: static; transform: none; order: 3;
    width: 100%; overflow-x: auto; scrollbar-width: none;
    padding-top: 6px; border-top: 1px solid var(--border);
  }
  .nav-center::-webkit-scrollbar { display: none; }
  .nav-item { flex: 0 0 auto; }
}

.plan-cancelled {
  margin-top: 14px; padding: 12px 14px; font-size: .86rem; line-height: 1.55;
  border: 1px solid rgba(255,176,32,.32); border-radius: var(--radius-sm);
  background: rgba(255,176,32,.06); color: var(--text-dim);
}

/* ---------- difficulty ramp ----------
   Green through red across five levels. A grid of cards all reading "lvl 3" in
   the same grey has to be parsed word by word; a colour ramp is legible at a
   glance. The dot carries the hue so the pill stays readable in every theme --
   accent colour is themeable, these are not, because "level 4 is orange" must
   mean the same thing on every palette. */
.diff-pill {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 3px 9px; border-radius: 999px;
  font-family: var(--font-mono); font-size: .68rem; text-transform: uppercase;
  letter-spacing: .05em; border: 1px solid currentColor; background: rgba(255,255,255,.02);
}
.diff-dot {
  width: 7px; height: 7px; border-radius: 50%; flex-shrink: 0;
  background: currentColor; box-shadow: 0 0 7px currentColor;
}
.diff-1 { color: #24d17e; }
.diff-2 { color: #a8d840; }
.diff-3 { color: #ffb020; }
.diff-4 { color: #ff8a3d; }
.diff-5 { color: #ff4060; }

/* ---------- filter bar ---------- */
.filter-bar {
  display: flex; flex-wrap: wrap; gap: 18px; align-items: center;
  margin-bottom: 20px; padding: 12px 14px;
  border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--bg-elevated);
}
.filter-group { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.filter-label {
  font-family: var(--font-mono); font-size: .68rem; text-transform: uppercase;
  letter-spacing: .07em; color: var(--text-faint); margin-right: 2px;
}
.filter-chip {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 6px 11px; border-radius: 999px; cursor: pointer;
  border: 1px solid var(--border); background: transparent; color: var(--text-dim);
  font-family: var(--font-mono); font-size: .74rem;
  transition: border-color .13s, background .13s, color .13s;
}
.filter-chip:hover { border-color: var(--border-bright); color: var(--text); }
.filter-chip.active {
  border-color: var(--accent); color: var(--text);
  background: rgba(var(--accent-rgb), .12);
}
/* A level chip keeps its own hue when selected, so the ramp still reads. */
.filter-chip.lvl.active { border-color: currentColor; background: rgba(255,255,255,.06); }
.filter-count {
  font-size: .66rem; color: var(--text-faint);
  padding: 1px 5px; border-radius: 999px; background: var(--surface-3);
}
.filter-chip.active .filter-count { color: var(--text-dim); }
.filter-clear {
  background: none; border: 0; padding: 4px 8px; cursor: pointer;
  font-family: var(--font-mono); font-size: .72rem; color: var(--text-faint);
  text-decoration: underline; text-underline-offset: .2em;
}
.filter-clear:hover { color: var(--danger); }

@media (max-width: 640px) {
  .filter-bar { gap: 10px; padding: 10px; }
  .filter-group { width: 100%; }
}

/* The ramp colours are declared before .filter-chip, and both are single-class
   selectors, so the chip's own colour was winning and every level dot rendered
   grey. Two classes beats one — the ramp has to win here. */
.filter-chip.diff-1 { color: #24d17e; }
.filter-chip.diff-2 { color: #a8d840; }
.filter-chip.diff-3 { color: #ffb020; }
.filter-chip.diff-4 { color: #ff8a3d; }
.filter-chip.diff-5 { color: #ff4060; }
.filter-chip.lvl .filter-count { color: var(--text-faint); }

/* ---------- list controls ---------- */
.filter-bar-top { margin-bottom: 10px; }
.search-wrap { position: relative; flex: 1 1 240px; min-width: 200px; }
.search-icon {
  position: absolute; left: 11px; top: 50%; transform: translateY(-50%);
  width: 15px; height: 15px; fill: none; stroke: var(--text-faint);
  stroke-width: 1.8; stroke-linecap: round; pointer-events: none;
}
.search-input {
  width: 100%; padding: 8px 12px 8px 34px; border-radius: var(--radius-sm);
  border: 1px solid var(--border); background: var(--bg); color: var(--text);
  font-family: inherit; font-size: .86rem;
}
.search-input:focus { outline: none; border-color: var(--accent); }
.search-input::-webkit-search-cancel-button { filter: invert(.6); }

.sort-select {
  padding: 7px 10px; border-radius: var(--radius-sm); font-size: .8rem;
  border: 1px solid var(--border); background: var(--bg); color: var(--text-dim);
  font-family: var(--font-mono); cursor: pointer;
}
.sort-select:focus { outline: none; border-color: var(--accent); }

.tag-new {
  color: var(--ok); border-color: rgba(36,209,126,.4); background: rgba(36,209,126,.10);
}

/* ---------- progress ring ----------
   Twenty cards each showing "62%" is twenty numbers to read. Twenty rings at
   different fills is one glance, and the number is still inside for anyone
   who wants the exact figure. */
.ring { position: relative; display: inline-grid; place-items: center; width: 38px; height: 38px; flex-shrink: 0; }
.ring svg { width: 38px; height: 38px; transform: rotate(-90deg); }
.ring circle { fill: none; stroke-width: 3.5; }
.ring-track { stroke: var(--surface-3); }
.ring-fill { stroke: var(--accent); transition: stroke-dashoffset .45s ease; stroke-linecap: round; }
.ring-label {
  position: absolute; font-family: var(--font-mono); font-size: .62rem;
  color: var(--text-dim); font-weight: 700;
}
.ring.done .ring-fill { stroke: var(--ok); }
.ring.done .ring-label { color: var(--ok); font-size: .8rem; }
.ring.empty .ring-label { color: var(--text-faint); opacity: .6; }
.ring.partial .ring-label { color: var(--accent); }

.resume-strip {
  margin-top: 10px; padding: 6px 10px; border-radius: var(--radius-sm);
  background: rgba(var(--accent-rgb),.08); border-left: 2px solid var(--accent);
  font-family: var(--font-mono); font-size: .7rem; color: var(--accent);
}

@media (prefers-reduced-motion: reduce) { .ring-fill { transition: none; } }
@media (max-width: 640px) {
  .filter-bar-top { flex-direction: column; align-items: stretch; }
  .search-wrap { width: 100%; }
}

/* ---------- dashboard stat row ----------
   Full width under the readiness gauge rather than a cramped 2x2 beside it,
   where the labels wrapped and the numbers competed with the score for the
   same glance. */
.stat-row {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
  gap: 1px; margin: 18px 0 30px;
  background: var(--border); border: 1px solid var(--border);
  border-radius: var(--radius); overflow: hidden;
}
.stat-cell {
  display: flex; flex-direction: column; gap: 3px; align-items: center;
  padding: 16px 12px; background: var(--bg-elevated); text-align: center;
}
.stat-cell .stat-value { font-family: var(--font-mono); font-size: 1.55rem; font-weight: 700; line-height: 1; color: var(--text); }
.stat-cell .stat-label {
  font-family: var(--font-mono); font-size: .66rem; text-transform: uppercase;
  letter-spacing: .07em; color: var(--text-faint);
}
.stat-cell.accent .stat-value { color: var(--accent); }
.stat-cell.warn .stat-value { color: var(--warn); }
.stat-cell.danger .stat-value { color: var(--danger); }

/* ---------- loading skeletons ----------
   A pulsing block in the shape of the content that is coming reads as "nearly
   there"; the word "Loading…" reads as "something might be wrong". */
.skel { border-radius: var(--radius-sm); background: var(--surface-3); position: relative; overflow: hidden; }
.skel::after {
  content: ''; position: absolute; inset: 0;
  background: linear-gradient(90deg, transparent, rgba(255,255,255,.055), transparent);
  animation: skel-sweep 1.25s ease-in-out infinite;
}
.skel-line { height: 12px; margin-bottom: 9px; }
.skel-line.short { width: 45%; }
.skel-line.mid { width: 70%; }
.skel-card { padding: 18px; border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); }
.skel-row { height: 42px; margin-bottom: 8px; }
@keyframes skel-sweep { 0% { transform: translateX(-100%); } 100% { transform: translateX(100%); } }
@media (prefers-reduced-motion: reduce) { .skel::after { animation: none; } }

.danger-text { color: var(--danger); }

/* ---------- mastery ---------- */
.mastery-bar {
  display: flex; align-items: center; gap: 12px; margin-bottom: 6px;
  font-family: var(--font-mono); font-size: .78rem; color: var(--text-dim);
}
.mastery-done {
  font-size: 3rem; line-height: 1; color: var(--ok); margin-bottom: 14px;
  text-shadow: 0 0 26px rgba(36,209,126,.4);
}
