/* ================================================================
   CODESAGO — BRIGHT WORKSHOP
   Implements DESIGN.md. Every value here is a token; components use
   tokens, never raw values.

   The product withholds code on purpose. A roomy, warm surface makes
   that read as teaching. An austere one makes it read as punishment.
   ================================================================ */

*,
*::before,
*::after { box-sizing: border-box; margin: 0; padding: 0; }

:root {
  /* ── NEUTRALS — tinted toward indigo, never pure grey ───────── */
  --canvas:  #f5f7fd;
  --surface: #ffffff;
  --raised:  #fafbfe;
  --sunken:  #eef1fa;

  --ink:   #0d1231;
  --ink-2: #3e4667;
  --ink-3: #5b6488;

  --line:        #e4e8f4;
  --line-strong: #ccd3e8;

  /* ── BRAND — indigo means "you may act on this" ─────────────── */
  --brand:       #4530d9;
  --brand-hover: #3722b0;
  --brand-wash:  #eeebff;
  --brand-line:  #cdc5ff;
  --brand-on:    #ffffff;

  /* ── ACCENT — earned progress only, never a CTA ─────────────── */
  --accent:      #ff6a3d;
  --accent-wash: #fff0ea;
  --accent-line: #ffd0bd;

  /* ── SIGNAL — describes the student's code ──────────────────── */
  --sig-held:  #a15c00;
  --sig-fault: #cf2338;
  --sig-clear: #0f7b4f;
  --sig-held-wash:  #fff6e6;
  --sig-fault-wash: #ffedef;
  --sig-clear-wash: #e6f6ee;
  --sig-held-line:  #f2d9a8;
  --sig-fault-line: #f8c6cd;
  --sig-clear-line: #b3ddc8;

  /* ── TAGS — pale category chips. Labels, not decoration. ────── */
  --tag-indigo-bg: #ecebff; --tag-indigo-fg: #3d2fb8;
  --tag-green-bg:  #e4f6ec; --tag-green-fg:  #10693f;
  --tag-amber-bg:  #fff3dc; --tag-amber-fg:  #8a5300;
  --tag-rose-bg:   #ffe9ee; --tag-rose-fg:   #b41d40;
  --tag-teal-bg:   #e0f4f6; --tag-teal-fg:   #0d6470;

  /* ── HEAT — the practice calendar. Five steps of --accent, because a
     filled day is earned progress and nothing else on it is. ───── */
  --heat-0: #eaeefa;
  --heat-1: #ffdccf;
  --heat-2: #ffb598;
  --heat-3: #ff8b5f;
  --heat-4: #ef5a24;

  --focus-ring: #4530d9;
  --focus-halo: #ffffff;
  --scrim: rgba(9, 12, 30, .55);

  /* ── DEPTH — layered, ink-tinted, never pure black ──────────── */
  --shadow-sm: 0 1px 2px rgba(13, 18, 49, .05), 0 1px 3px rgba(13, 18, 49, .04);
  --shadow-md: 0 4px 8px rgba(13, 18, 49, .06), 0 12px 24px rgba(13, 18, 49, .08);
  --shadow-lg: 0 8px 16px rgba(13, 18, 49, .08), 0 28px 56px rgba(13, 18, 49, .16);
  --shadow-brand: 0 1px 2px rgba(69, 48, 217, .24), 0 6px 16px rgba(69, 48, 217, .28);

  /* ── TYPE — three faces, three jobs ─────────────────────────── */
  --display: 'Bricolage Grotesque', 'Inter', -apple-system, sans-serif;
  --font:    'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  --mono:    'JetBrains Mono', 'Cascadia Code', ui-monospace, monospace;

  --t-xs:   12px;
  --t-sm:   13px;
  --t-base: 15px;
  --t-md:   17px;
  --t-lg:   21px;
  --t-xl:   27px;
  --t-2xl:  36px;
  --t-3xl:  52px;

  /* ── SPACE — 4px base. The fix for "cramped". ───────────────── */
  --s-1: 4px;   --s-2: 8px;   --s-3: 12px;  --s-4: 16px;
  --s-5: 20px;  --s-6: 24px;  --s-7: 32px;  --s-8: 40px;
  --s-9: 48px;  --s-10: 64px; --s-11: 80px; --s-12: 96px;

  --r-sm: 8px;
  --r: 12px;
  --r-lg: 18px;
  --r-pill: 999px;

  --nav-h: 64px;
  --sb-h: 32px;
  --sidebar-w: 280px;
  /* The collapsed sidebar: a rail of the mode icons (.icon-btn plus --s-2 each side). */
  --rail-w: 56px;

  --ease: cubic-bezier(.2, .7, .3, 1);
  --fast: 130ms var(--ease);
  --med:  220ms var(--ease);

  /* ── ALIASES ─────────────────────────────────────────────────
     ~170 inline styles and three scripts name these. They are how
     this system reaches markup this file does not own. Do not remove. */
  --bg: var(--canvas);   --bg2: var(--surface); --bg3: var(--sunken);
  --stock: var(--canvas); --stock-2: var(--surface); --stock-3: var(--sunken);
  --border: var(--line); --border2: var(--line-strong);
  --rule: var(--line);   --hair: var(--line);
  --text: var(--ink); --text-main: var(--ink);
  --text2: var(--ink-2); --text3: var(--ink-3);
  --primary: var(--brand); --primary-dim: var(--brand-wash);
  --primary-glow: rgba(69, 48, 217, .22);
  --accent-color: var(--accent);
  --font-mono: var(--mono);
  --sage: var(--sig-clear);   --sage-dim: var(--sig-clear-wash);
  --sergeant: var(--sig-fault); --sergeant-dim: var(--sig-fault-wash);
  --buddy: var(--sig-held);   --buddy-dim: var(--sig-held-wash);
  --amber: var(--sig-held);   --amber-dim: var(--sig-held-wash);
  --gold: var(--accent);
  --violet: var(--brand);     --violet-dim: var(--brand-wash);
  --sage-glow: transparent; --sergeant-glow: transparent; --buddy-glow: transparent;
  --radius: var(--r); --radius-sm: var(--r-sm); --radius-lg: var(--r-lg);
  --radius-pill: var(--r-pill);
  --transition: var(--fast);
}

/* ── DARK — a real second design, not an inversion ─────────────
   Palette: Deep Azure #021227 (ground), Pale Azure #f4f5f6 and Soft
   Indigo #c1c8f4 (text), Rich Violet #9264e7 (brand, lifted a step so
   brand text on raised cards passes AA 4.5:1), Soft Azure #51c2f0
   (earned progress). Signal inks stay warm so a fault reads as a fault. */
[data-theme="dark"] {
  --canvas:  #021227;
  --surface: #0a1d37;
  --raised:  #0e2440;
  --sunken:  #010c1b;

  --ink:   #f4f5f6;
  --ink-2: #c1c8f4;
  --ink-3: #8891b6;

  --line:        #152e4e;
  --line-strong: #203e65;

  --brand:       #9f78ed;
  --brand-hover: #b393f2;
  --brand-wash:  rgba(146, 100, 231, .16);
  --brand-line:  rgba(146, 100, 231, .42);
  --brand-on:    #021227;

  --accent:      #51c2f0;
  --accent-wash: rgba(81, 194, 240, .14);
  --accent-line: rgba(81, 194, 240, .38);

  --sig-held:  #f5b455;
  --sig-fault: #ff7d8f;
  --sig-clear: #4ed597;
  --sig-held-wash:  rgba(245, 180, 85, .13);
  --sig-fault-wash: rgba(255, 125, 143, .13);
  --sig-clear-wash: rgba(78, 213, 151, .13);
  --sig-held-line:  rgba(245, 180, 85, .34);
  --sig-fault-line: rgba(255, 125, 143, .34);
  --sig-clear-line: rgba(78, 213, 151, .34);

  --tag-indigo-bg: rgba(72,93,238,.22);   --tag-indigo-fg: #c1c8f4;
  --tag-green-bg:  rgba(78,213,151,.14);  --tag-green-fg:  #6ee0aa;
  --tag-amber-bg:  rgba(245,180,85,.14);  --tag-amber-fg:  #f7c477;
  --tag-rose-bg:   rgba(255,125,143,.14); --tag-rose-fg:   #ff9aa8;
  --tag-teal-bg:   rgba(56,189,205,.14);  --tag-teal-fg:   #6fd4e0;

  --heat-0: #0d2038;
  --heat-1: #1a4160;
  --heat-2: #286285;
  --heat-3: #398ab1;
  --heat-4: #51c2f0;

  --focus-ring: #9f78ed;
  --focus-halo: #021227;
  --scrim: rgba(9, 12, 30, .55);

  --shadow-sm: 0 1px 2px rgba(0, 0, 0, .5);
  --shadow-md: 0 4px 8px rgba(0, 0, 0, .5), 0 12px 24px rgba(0, 0, 0, .45);
  --shadow-lg: 0 8px 16px rgba(0, 0, 0, .5), 0 28px 56px rgba(0, 0, 0, .6);
  --shadow-brand: 0 1px 2px rgba(0, 0, 0, .5), 0 6px 18px rgba(146, 100, 231, .3);

  --primary-glow: rgba(146, 100, 231, .28);
}

/* ── BASE ─────────────────────────────────────────────────────── */
html, body { height: 100%; overflow: hidden; }

body {
  font-family: var(--font);
  font-size: var(--t-base);
  line-height: 1.6;
  color: var(--ink-2);
  background: var(--canvas);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

/* The display face appears at --t-xl and above and nowhere else.
   That restraint is what keeps it from becoming noise. */
h1, h2 {
  font-family: var(--display);
  font-weight: 800;
  letter-spacing: -.025em;
  line-height: 1.12;
  color: var(--ink);
}
h3, h4 {
  font-family: var(--font);
  font-weight: 700;
  letter-spacing: -.012em;
  line-height: 1.3;
  color: var(--ink);
}
h1 { font-size: var(--t-2xl); }
h2 { font-size: var(--t-xl); }
h3 { font-size: var(--t-lg); }
h4 { font-size: var(--t-md); }
strong, b { color: var(--ink); font-weight: 600; }
p { max-width: 72ch; }

code, pre, .code-area, .exec-output, .diff-code, .log-id,
.result-time, .line-numbers, .exec-prompt, .frust-val, .nav-frust-val,
.inline-code, .breadcrumb, .statusbar {
  font-family: var(--mono);
  font-variant-numeric: tabular-nums;
}
.inline-code {
  font-size: .9em;
  background: var(--brand-wash);
  border: 1px solid var(--brand-line);
  border-radius: var(--r-sm);
  padding: 2px 6px;
  color: var(--brand);
}

a { color: var(--brand); text-underline-offset: 3px; text-decoration-thickness: 1px; }
a:hover { color: var(--brand-hover); }

/* ── BROWSER SURFACES ─────────────────────────────────────────── */
::selection { background: var(--brand-wash); color: var(--brand); }
input, textarea { caret-color: var(--brand); }

* { scrollbar-width: thin; scrollbar-color: var(--line-strong) transparent; }
::-webkit-scrollbar { width: 12px; height: 12px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb {
  background: var(--line-strong);
  border: 3px solid transparent;
  background-clip: content-box;
  border-radius: var(--r-pill);
}
::-webkit-scrollbar-thumb:hover { background-color: var(--ink-3); }

/* Two rings so focus survives on white, on indigo, and on a wash. */
:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
  box-shadow: 0 0 0 2px var(--focus-halo);
  border-radius: var(--r-sm);
}

input[type="range"] {
  -webkit-appearance: none; appearance: none;
  height: 22px; background: transparent; cursor: pointer;
  accent-color: var(--brand);
}
input[type="range"]::-webkit-slider-runnable-track {
  height: 6px; background: var(--sunken);
  border: 1px solid var(--line); border-radius: var(--r-pill);
}
input[type="range"]::-moz-range-track {
  height: 6px; background: var(--sunken);
  border: 1px solid var(--line); border-radius: var(--r-pill);
}
input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none;
  width: 18px; height: 18px; margin-top: -7px;
  background: var(--brand); border: 3px solid var(--surface);
  border-radius: var(--r-pill); box-shadow: var(--shadow-sm);
  transition: transform var(--fast);
}
input[type="range"]::-webkit-slider-thumb:hover { transform: scale(1.12); }
input[type="range"]::-moz-range-thumb {
  width: 18px; height: 18px; background: var(--brand);
  border: 3px solid var(--surface); border-radius: var(--r-pill);
}

/* ── TOP NAV ──────────────────────────────────────────────────── */
.topnav {
  position: fixed; top: 0; left: 0; right: 0; z-index: 40;
  height: var(--nav-h);
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--s-4);
  background: var(--surface);
  border-bottom: 1px solid var(--line);
  padding: 0 var(--s-6);
}
.topnav-left, .topnav-right { display: flex; align-items: center; gap: var(--s-4); }
.nav-divider { width: 1px; height: 24px; background: var(--line); }

.logo { display: flex; align-items: center; gap: var(--s-3); text-decoration: none; }
.logo-mark {
  width: 34px; height: 34px; flex: none;
  border-radius: 10px;
  box-shadow: var(--shadow-brand);
}
.logo-text {
  font-family: var(--display); font-weight: 800;
  font-size: var(--t-md); color: var(--ink);
  letter-spacing: -.02em;
}

.workspace { display: flex; height: 100vh; padding-top: var(--nav-h); position: relative; }
.main-area { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.main-scroll { flex: 1; overflow-y: auto; overflow-x: hidden; }
.workspace-inner {
  /* Fills the main area: the editor is the workspace, so it takes the room,
     and collapsing the sidebar hands its width to the editor. min-height lets
     the editor stretch down to the status bar (see EDITOR). */
  min-height: 100%;
  padding: var(--s-5) var(--s-6) var(--s-6);
  display: flex; flex-direction: column; gap: var(--s-5);
}

/* ── SIDEBAR ──────────────────────────────────────────────────
   The modes on top, then the history of the mode on screen, like a chat
   app's conversation list.
   .collapsed folds it to a rail of the mode icons. */
.sidebar {
  width: var(--sidebar-w); flex-shrink: 0;
  border-right: 1px solid var(--line);
  background: var(--surface);
  display: flex; flex-direction: column;
  overflow: hidden;
  transition: width var(--med);
}
.sidebar-top { display: flex; padding: var(--s-2) var(--s-2) var(--s-1); }
.side-nav { display: flex; flex-direction: column; gap: 2px; padding: 0 var(--s-2); }
.nav-tab {
  display: flex; align-items: center; gap: var(--s-3);
  width: 100%; min-height: 40px; padding: 0 var(--s-3);
  font: inherit; font-size: var(--t-sm); font-weight: 600; text-align: left;
  color: var(--ink-2); background: transparent;
  border: 0; border-radius: var(--r-sm);
  cursor: pointer; white-space: nowrap;
  transition: background var(--fast), color var(--fast);
}
.nav-tab:hover { background: var(--sunken); color: var(--ink); }
.nav-tab.active { background: var(--brand-wash); color: var(--brand); }
.nav-tab .tab-icon { width: 18px; height: 18px; flex: none; }
.nav-label { overflow: hidden; text-overflow: ellipsis; }
/* An h2 for screen readers, set small: the display face is for --t-xl and up. */
.sidebar-label {
  font: 600 var(--t-xs)/1.4 var(--font); letter-spacing: 0; color: var(--ink-3);
  padding: var(--s-5) var(--s-5) var(--s-2);
}
.sidebar-footer {
  margin-top: auto; padding: var(--s-4) var(--s-5);
  border-top: 1px solid var(--line); background: var(--raised);
}
.sidebar.collapsed { width: var(--rail-w); }
.sidebar.collapsed .nav-label,
.sidebar.collapsed .sidebar-label,
.sidebar.collapsed .history-list,
.sidebar.collapsed .sidebar-footer { display: none; }
.sidebar.collapsed .nav-tab { justify-content: center; padding: 0; }

.statusbar {
  height: var(--sb-h); flex-shrink: 0;
  display: flex; align-items: center; justify-content: space-between;
  padding: 0 var(--s-6); border-top: 1px solid var(--line);
  background: var(--surface);
  font-size: var(--t-xs); color: var(--ink-3);
}
.sb-left, .sb-right { display: flex; align-items: center; gap: var(--s-3); }
.sb-sep { color: var(--line-strong); }
.sb-lang, .sb-mentor { color: var(--ink-2); font-weight: 500; }

.breadcrumb {
  display: flex; align-items: center; gap: var(--s-2);
  font-size: var(--t-sm); color: var(--ink-3); min-width: 0;
}
.bc-file { color: var(--ink-2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bc-sep { color: var(--ink-3); }
.acc-icon { width: 18px; height: 18px; flex-shrink: 0; transition: transform var(--fast); }
.accordion-card.collapsed .acc-icon { transform: rotate(-90deg); }

/* ── PANELS ───────────────────────────────────────────────────
   Roomy white surfaces on a tinted canvas. 24px padding is the
   floor — that is the fix for "cramped". */
.section, .hf-card, .result-panel, .execution-panel, .diff-panel,
.editor-panel, .exec-panel, .accordion-card,
.bug-card, .challenge-card, .perf-card, .diff-section, .highfive-banner {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-sm);
  padding: var(--s-6);
  margin: 0;
  overflow: hidden;
}
.hf-card-header, .exec-header, .diff-header-row, .result-header, .loading-header {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--s-4); padding-bottom: var(--s-4); margin-bottom: var(--s-5);
  border-bottom: 1px solid var(--line);
}
.result-header-top, .result-header-info { display: flex; align-items: center; gap: var(--s-3); }
.hf-card-title, .exec-title, .diff-title {
  font-size: var(--t-md); font-weight: 700; color: var(--ink);
  letter-spacing: -.012em;
}
.hf-card-body, .exec-body, .result-body { padding: 0; }
.hf-card-text, .res-block-text, .mentor-sub, .empty-sub, .footer-note {
  color: var(--ink-2); max-width: 72ch;
}
.footer-note { font-size: var(--t-sm); color: var(--ink-3); }

.hf-list, .tiplist, .checklist { list-style: none; }
.hf-list li, .tiplist li, .checklist li {
  padding: var(--s-3) 0 var(--s-3) var(--s-6);
  position: relative; border-bottom: 1px solid var(--line);
}
.hf-list li::before, .tiplist li::before, .checklist li::before {
  content: ""; position: absolute; left: var(--s-2); top: 1.1em;
  width: 7px; height: 7px; border-radius: var(--r-pill); background: var(--brand);
}
.hf-list li:last-child, .tiplist li:last-child, .checklist li:last-child { border-bottom: 0; }

.section-label {
  display: flex; align-items: center; gap: var(--s-3);
  font-size: var(--t-md); font-weight: 700; color: var(--ink);
  letter-spacing: -.012em; margin-bottom: var(--s-5);
}
.section-label::before {
  content: ""; width: 4px; height: 20px; flex: none;
  background: var(--brand); border-radius: var(--r-pill);
}
.mt12 { margin-top: var(--s-3); }

/* ── BUTTONS ──────────────────────────────────────────────────
   Every interactive surface lifts on hover. Static hover is why the
   earlier version read as unfinished. */
.btn, .run-btn, .analyze-btn, .tool-btn, .exec-close-btn,
.exec-kill-btn, .explain-error-btn, .diff-toggle,
.profile-theme-btn, .close-exec-btn, .submit-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--s-2);
  min-height: 40px; padding: 0 var(--s-5);
  font-family: var(--font); font-weight: 600; font-size: var(--t-base);
  letter-spacing: -.008em;
  color: var(--ink); background: var(--surface);
  border: 1px solid var(--line-strong); border-radius: var(--r);
  cursor: pointer; white-space: nowrap;
  box-shadow: var(--shadow-sm);
  transition: background var(--fast), color var(--fast), border-color var(--fast),
              box-shadow var(--fast), transform var(--fast);
}
.btn:hover, .analyze-btn:hover, .tool-btn:hover, .diff-toggle:hover,
.profile-theme-btn:hover,
.explain-error-btn:hover, .exec-close-btn:hover {
  background: var(--brand-wash); color: var(--brand);
  border-color: var(--brand-line);
  box-shadow: var(--shadow-md); transform: translateY(-1px);
}
.btn:active, .run-btn:active, .analyze-btn:active { transform: translateY(0); }

.btn-primary, .run-btn, .submit-btn {
  min-height: 44px;
  background: var(--brand); color: var(--brand-on);
  border-color: var(--brand); box-shadow: var(--shadow-brand);
}
.btn-primary:hover, .run-btn:hover, .submit-btn:hover {
  background: var(--brand-hover); border-color: var(--brand-hover);
  color: var(--brand-on); transform: translateY(-1px);
  box-shadow: 0 2px 4px rgba(69, 48, 217, .28), 0 10px 24px rgba(69, 48, 217, .34);
}
.btn:disabled, .run-btn:disabled, .analyze-btn:disabled, .btn[disabled],
.submit-btn:disabled {
  color: var(--ink-3); border-color: var(--line);
  background: var(--sunken); box-shadow: none;
  cursor: not-allowed; transform: none;
}
.btn-sm { min-height: 32px; padding: 0 var(--s-4); font-size: var(--t-sm); }

.icon-btn, .btn-icon-wrap {
  display: inline-grid; place-items: center;
  width: 40px; height: 40px; padding: 0; min-height: 0;
  background: transparent; border: 1px solid transparent;
  border-radius: var(--r-sm); box-shadow: none;
  color: var(--ink-2); cursor: pointer;
  transition: background var(--fast), color var(--fast);
}
.icon-btn:hover { background: var(--brand-wash); color: var(--brand); transform: none; box-shadow: none; }
.exec-kill-btn {
  border-color: var(--sig-fault-line); color: var(--sig-fault);
  background: var(--sig-fault-wash); box-shadow: none;
}
.exec-kill-btn:hover {
  background: var(--sig-fault); color: var(--surface);
  border-color: var(--sig-fault); transform: translateY(-1px);
}
.analyze-btn-idle { opacity: 1; }

/* A link styled as a button is a button to the eye. */
a.btn, a.btn:hover { text-decoration: none; }
a.btn { color: var(--ink); }
a.btn:hover { color: var(--brand); }
a.btn-primary, a.btn-primary:hover { color: var(--brand-on); }

/* ── CHIPS AND TAGS ───────────────────────────────────────────
   Pale category pills. A chip's colour maps to a real category,
   consistently — they are labels, not decoration. */
.chip, .tag {
  display: inline-flex; align-items: center; gap: var(--s-2);
  font-size: var(--t-xs); font-weight: 600; letter-spacing: .01em;
  padding: 5px var(--s-3);
  border: 1px solid var(--line); border-radius: var(--r-pill);
  color: var(--ink-2); background: var(--sunken);
  white-space: nowrap;
}
.tag-indigo { background: var(--tag-indigo-bg); color: var(--tag-indigo-fg); border-color: transparent; }
.tag-green  { background: var(--tag-green-bg);  color: var(--tag-green-fg);  border-color: transparent; }
.tag-amber  { background: var(--tag-amber-bg);  color: var(--tag-amber-fg);  border-color: transparent; }
.tag-rose   { background: var(--tag-rose-bg);   color: var(--tag-rose-fg);   border-color: transparent; }
.tag-teal   { background: var(--tag-teal-bg);   color: var(--tag-teal-fg);   border-color: transparent; }

.chip-green {
  border-color: var(--sig-clear-line); color: var(--sig-clear);
  background: var(--sig-clear-wash);
}
.error-pill {
  display: inline-flex; align-items: center; gap: var(--s-2);
  font-size: var(--t-xs); font-weight: 600; padding: 5px var(--s-3);
  border: 1px solid var(--sig-fault-line); border-radius: var(--r-pill);
  color: var(--sig-fault); background: var(--sig-fault-wash);
}
.ai-badge {
  font-size: var(--t-xs); font-weight: 700; letter-spacing: .01em;
  color: var(--tag-indigo-fg); background: var(--tag-indigo-bg);
  border-radius: var(--r-pill); padding: 3px var(--s-3);
}
.fallback-badge {
  font-size: var(--t-xs); font-weight: 700;
  color: var(--tag-amber-fg); background: var(--tag-amber-bg);
  border-radius: var(--r-pill); padding: 3px var(--s-3);
}

/* ── INPUTS ───────────────────────────────────────────────────── */
.lang-select, .stdin-area, .stdin-inline, .exec-stdin-input,
.stderr-input, .inline-terminal-input, input[type="text"],
input[type="email"], input[type="password"], input[type="number"],
textarea:not(.code-area) {
  font-family: var(--font); font-size: var(--t-base);
  min-height: 40px; padding: var(--s-2) var(--s-4);
  color: var(--ink); background: var(--surface);
  border: 1px solid var(--line-strong); border-radius: var(--r-sm);
  transition: border-color var(--fast), box-shadow var(--fast);
}
.stdin-area, .stdin-inline, .exec-stdin-input, .stderr-input,
.inline-terminal-input { font-family: var(--mono); font-size: var(--t-sm); }
.lang-select { font-weight: 500; cursor: pointer; padding-right: var(--s-7); }
.lang-select:hover { border-color: var(--brand-line); }
input:focus, textarea:focus, select:focus {
  border-color: var(--brand);
  box-shadow: 0 0 0 3px var(--brand-wash);
}
::placeholder { color: var(--ink-3); opacity: 1; }

/* Drawn icons only. Emoji are never icons — DESIGN.md §13. */
button svg, .icon-btn svg, .tool-btn svg, .btn svg {
  width: 18px; height: 18px; flex: none; stroke-width: 1.75;
}
.chip svg, .tag svg { width: 14px; height: 14px; stroke-width: 2; }

/* ── EDITOR ───────────────────────────────────────────────────── */
#mainEditorPanel { padding: 0; }
.editor-tabbar {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--s-4); flex-wrap: wrap;
  border-bottom: 1px solid var(--line); background: var(--raised);
  padding: var(--s-3) var(--s-5);
}
.editor-tabs { display: flex; gap: var(--s-1); }
.editor-tab {
  font-size: var(--t-sm); font-weight: 600; padding: var(--s-2) var(--s-4);
  color: var(--ink-3); background: transparent;
  border: 0; border-radius: var(--r-sm); cursor: pointer;
  transition: color var(--fast), background var(--fast);
}
.editor-tab:hover { color: var(--ink-2); background: var(--sunken); }
.editor-tab.active-tab {
  color: var(--brand); background: var(--brand-wash);
  box-shadow: inset 0 0 0 1px var(--brand-line);
}
.tab-dot {
  width: 7px; height: 7px; background: var(--accent);
  border-radius: var(--r-pill); display: inline-block; margin-left: var(--s-2);
}
/* The editor fills the page down to the status bar, so it is a workspace
   rather than a box that grows a line every time the student presses Enter.
   It had no height at all once: the textarea fell back to its two-row default
   and the panel resized on every keystroke, which is what "the compiler keeps
   changing size" was. The terminal, when open, takes its room from here.
   Basis 0, not auto: the gutter holds a row per line of code, and an auto
   basis let a long file stretch the page instead of scrolling the editor. */
#rightWorkspaceWrapper { flex: 1 0 auto; }
#mainEditorPanel { flex: 1 0 auto; display: flex; flex-direction: column; }
.editor-body {
  display: flex; background: var(--surface);
  flex: 1 1 0; min-height: 340px;
}
.line-numbers {
  padding: var(--s-4) var(--s-3); text-align: right; color: var(--ink-3);
  font-size: var(--t-sm); line-height: 1.75; user-select: none;
  border-right: 1px solid var(--line); background: var(--sunken);
  overflow: hidden; flex-shrink: 0;
}
.code-area {
  flex: 1; padding: var(--s-4) var(--s-5); border: 0; resize: none;
  font-family: var(--mono); font-size: var(--t-sm); line-height: 1.75;
  color: var(--ink); background: transparent; tab-size: 4;
  overflow-y: auto;
}
.code-area:focus { box-shadow: none; }
.editor-actions, .file-actions, .exec-actions, .result-actions {
  display: flex; align-items: center; gap: var(--s-3); flex-wrap: wrap;
}
.editor-panel > .editor-actions, .editor-panel > .result-actions {
  padding: var(--s-4) var(--s-5); border-top: 1px solid var(--line);
  background: var(--raised);
}

/* ── EXECUTION ────────────────────────────────────────────────── */
.exec-output {
  font-size: var(--t-sm); line-height: 1.7; white-space: pre-wrap;
  padding: var(--s-4) var(--s-5); background: var(--sunken);
  border: 1px solid var(--line); border-radius: var(--r);
  max-height: 340px; overflow: auto; color: var(--ink);
}
.exec-prompt { color: var(--sig-clear); font-weight: 500; }
.exec-stdin-row, .stdin-row, .stderr-row, .typing-row {
  display: flex; align-items: center; gap: var(--s-3);
  padding: var(--s-3) 0; flex-wrap: wrap;
}
.editor-panel > .stderr-row {
  padding: var(--s-4) var(--s-5); border-top: 1px solid var(--line);
}
.stderr-label, .frust-label, .meter-label, .diff-col-label {
  font-size: var(--t-sm); font-weight: 600; color: var(--ink-3);
  display: inline-flex; align-items: center; gap: var(--s-2); flex: none;
}
.stderr-label svg { width: 16px; height: 16px; stroke-width: 2; color: var(--sig-held); }

/* ── THE LADDER — the signature ───────────────────────────────
   Three gates the student cannot skip. Cleared fills indigo, current
   wears a ring, locked stays hollow behind a dashed connector. The
   gate state is the product, so it is drawn, not described. */
.res-block, .hint-block, .tip-block, .analogy-block {
  display: grid;
  grid-template-columns: 36px 1fr;
  gap: var(--s-2) var(--s-4);
  align-items: start;
  padding: var(--s-5) 0;
  position: relative;
}
.res-block + .res-block, .hint-block + .hint-block { border-top: 1px solid var(--line); }

.res-block-label, .hint-label, .tip-label {
  grid-column: 2;
  font-size: var(--t-md); font-weight: 700; color: var(--ink);
  letter-spacing: -.012em;
  display: flex; align-items: center; gap: var(--s-3); flex-wrap: wrap;
}
.res-block > *:not(.res-block-label),
.hint-block > *:not(.hint-label),
.tip-block > *:not(.tip-label) { grid-column: 2; }

/* The gate disc. */
.res-block::before, .hint-block::before, .tip-block::before {
  grid-column: 1; grid-row: 1 / span 2;
  content: counter(rung); counter-increment: rung;
  width: 36px; height: 36px; display: grid; place-items: center;
  border-radius: var(--r-pill);
  background: var(--surface); color: var(--ink-3);
  border: 2px solid var(--line-strong);
  font-family: var(--display); font-size: var(--t-base); font-weight: 800;
  transition: background var(--med), color var(--med), border-color var(--med),
              box-shadow var(--med);
}
/* The connector between gates. */
.res-block::after, .hint-block::after {
  content: ""; position: absolute; left: 17px; top: 56px; bottom: -4px;
  width: 2px; border-radius: 2px;
  background: repeating-linear-gradient(
    to bottom, var(--line-strong) 0 4px, transparent 4px 9px);
}
.res-block:last-child::after, .hint-block:last-child::after { display: none; }

.result-body, #resultContent, .hint-ladder { counter-reset: rung; }

/* Locked — hollow, muted, dashed connector in. */
.hint-block[data-state="held"] .hint-label,
.res-block[data-state="held"] .res-block-label { color: var(--ink-3); }
.hint-block[data-state="held"]::before,
.res-block[data-state="held"]::before {
  background: var(--sunken); color: var(--ink-3); border-color: var(--line-strong);
}
.hint-block[data-state="held"] .hint-label::after {
  content: "Locked";
  font-family: var(--font); font-size: var(--t-xs); font-weight: 600;
  letter-spacing: .01em; text-transform: none;
  color: var(--sig-held); background: var(--sig-held-wash);
  border: 1px solid var(--sig-held-line); border-radius: var(--r-pill);
  padding: 3px var(--s-3);
}

/* Cleared — filled indigo, solid connector out. */
.hint-block[data-state="released"] .hint-label { color: var(--ink); }
.hint-block[data-state="released"]::before,
.res-block[data-state="released"]::before {
  background: var(--brand); color: var(--brand-on); border-color: var(--brand);
  box-shadow: var(--shadow-brand);
}
.hint-block[data-state="released"]::after,
.res-block[data-state="released"]::after { background: var(--brand); }

/* Current — where you are. A ring and a halo, no fill. */
.hint-block[data-state="current"]::before,
.res-block[data-state="current"]::before {
  background: var(--surface); color: var(--brand); border-color: var(--brand);
  box-shadow: 0 0 0 5px var(--brand-wash);
}

/* Step two names the line without touching it. */
.hint-block[data-step="2"] .res-block-text { position: relative; padding-left: var(--s-5); }
.hint-block[data-step="2"] .res-block-text::before {
  content: ""; position: absolute; left: 0; top: .72em;
  width: 12px; height: 2px; border-radius: 2px; background: var(--brand);
}

/* ── DIFF ─────────────────────────────────────────────────────── */
.diff-cols {
  display: grid; grid-template-columns: 1fr 1fr; gap: 0;
  border: 1px solid var(--line); border-radius: var(--r); overflow: hidden;
}
.diff-col { padding: var(--s-4) var(--s-5); }
.diff-col + .diff-col { border-left: 1px solid var(--line); }
.diff-col-label { display: flex; margin-bottom: var(--s-3); }
.diff-before { background: var(--sig-fault-wash); }
.diff-before .diff-col-label { color: var(--sig-fault); }
.diff-after { background: var(--sig-clear-wash); }
.diff-after .diff-col-label { color: var(--sig-clear); }
.diff-code { font-size: var(--t-sm); line-height: 1.7; white-space: pre-wrap; color: var(--ink); }

/* ── HISTORY ──────────────────────────────────────────────────── */
.history-list {
  display: flex; flex-direction: column; gap: 2px;
  padding: 0 var(--s-2) var(--s-3); flex: 1 1 auto; min-height: 0;
}
/* One line per entry, like a chat app's conversation list. */
.history-card {
  display: flex; align-items: center; gap: var(--s-3); flex: none;
  width: 100%; min-height: 36px; padding: 0 var(--s-3);
  font: inherit; font-size: var(--t-sm); text-align: left;
  color: var(--ink-2); background: transparent;
  border: 0; border-radius: var(--r-sm); cursor: pointer;
  transition: background var(--fast), color var(--fast);
}
.history-card:hover { background: var(--sunken); color: var(--ink); }
.history-card.is-active { background: var(--brand-wash); color: var(--brand); }
.hc-title { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.log-id, .result-time { font-size: var(--t-xs); color: var(--ink-3); }

/* Empty states name the next action — they never shrug. */
.history-empty { padding: var(--s-8) var(--s-5); text-align: center; }
.empty-icon {
  width: 48px; height: 48px; margin: 0 auto var(--s-4);
  display: grid; place-items: center; border-radius: var(--r-pill);
  background: var(--brand-wash); color: var(--brand);
}
.empty-icon svg { width: 22px; height: 22px; stroke-width: 1.75; }
/* Signal inks, not brand: these describe a state of the student's data, and
   indigo is reserved for things that can be acted on. */
.empty-icon-fault { background: var(--sig-fault-wash); color: var(--sig-fault); }
.empty-icon-held  { background: var(--sig-held-wash);  color: var(--sig-held); }
.empty-action { margin-top: var(--s-4); }
.empty-title { font-size: var(--t-base); font-weight: 700; color: var(--ink); margin-bottom: var(--s-1); }
.empty-sub { font-size: var(--t-sm); color: var(--ink-3); margin: 0 auto; }

/* ── MENTOR CARDS ─────────────────────────────────────────────── */
.mentor-row { display: flex; align-items: stretch; gap: var(--s-3); flex-wrap: wrap; }
.mentor-card {
  flex: 1 1 180px; min-width: 0; text-align: left;
  padding: var(--s-4) var(--s-5); cursor: pointer;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r); font: inherit;
  transition: border-color var(--fast), background var(--fast),
              box-shadow var(--fast), transform var(--fast);
}
.mentor-card:hover {
  border-color: var(--brand-line); box-shadow: var(--shadow-md);
  transform: translateY(-2px);
}
.mentor-emoji { display: none; } /* icons are drawn, never glyphs */
.mentor-name { font-size: var(--t-base); font-weight: 700; color: var(--ink); }
.mentor-sub { font-size: var(--t-sm); color: var(--ink-3); margin-top: 2px; }

/* Selection is an action state, so it reads in the brand. The mentor's
   own colour stays on their replies, where it means tone. */
.mentor-card.active-sage, .mentor-card.active-sergeant,
.mentor-card.active-buddy, .mentor-card.active-card {
  border-color: var(--brand); background: var(--brand-wash);
  box-shadow: inset 0 0 0 1px var(--brand), var(--shadow-sm);
}
.mentor-card.active-sage .mentor-name,
.mentor-card.active-sergeant .mentor-name,
.mentor-card.active-buddy .mentor-name { color: var(--brand); }
.sage-color { color: var(--sig-clear); }
.sergeant-color { color: var(--sig-fault); }
.buddy-color { color: var(--sig-held); }
.sage-label { font-size: var(--t-xs); font-weight: 600; }

.result-avatar {
  width: 40px; height: 40px; display: grid; place-items: center;
  border-radius: var(--r-pill); background: var(--sunken);
  border: 1px solid var(--line); flex: none; color: var(--ink-2);
}
.result-sage .result-avatar {
  background: var(--sig-clear-wash); border-color: var(--sig-clear-line); color: var(--sig-clear);
}
.result-sergeant .result-avatar {
  background: var(--sig-fault-wash); border-color: var(--sig-fault-line); color: var(--sig-fault);
}
.result-buddy .result-avatar {
  background: var(--sig-held-wash); border-color: var(--sig-held-line); color: var(--sig-held);
}
.result-mentor-name { font-size: var(--t-md); font-weight: 700; color: var(--ink); }

/* ── METERS AND STATUS ────────────────────────────────────────── */
.meter-row { display: flex; align-items: center; gap: var(--s-3); padding: var(--s-2) 0; font-size: var(--t-sm); }
.meter-track {
  flex: 1; height: 8px; background: var(--sunken);
  border: 1px solid var(--line); border-radius: var(--r-pill); overflow: hidden;
}
/* The fill scales rather than resizing: animating width relayouts the
   track every frame, a transform does not. */
.meter-fill {
  height: 100%; width: 100%; background: var(--brand);
  border-radius: var(--r-pill);
  transform: scaleX(0); transform-origin: left center;
  transition: transform 600ms var(--ease);
}
.status-dot-wrap, .agent-status-wrap { display: flex; align-items: center; gap: var(--s-2); }
.status-dot, .agent-dot, .live-dot, .dot {
  width: 8px; height: 8px; background: var(--ink-3);
  border-radius: var(--r-pill); flex-shrink: 0;
}
.live-dot, .agent-dot { background: var(--sig-clear); }
.status-dot.ai-online, .agent-dot.ai-online { background: var(--sig-clear); }
.status-dot.ai-offline, .agent-dot.ai-offline { background: var(--sig-fault); }
.agent-label.ai-online, .status-text.ai-online { color: var(--sig-clear); font-weight: 500; }
.agent-label.ai-offline, .status-text.ai-offline { color: var(--sig-fault); font-weight: 500; }
.dots { display: inline-flex; gap: var(--s-1); }
.status-text, .agent-label { font-size: var(--t-sm); color: var(--ink-3); }

.frustration-control, .nav-frustration { display: flex; align-items: center; gap: var(--s-3); }
.frust-val, .nav-frust-val {
  font-family: var(--mono); font-size: var(--t-sm); font-weight: 600;
  color: var(--brand); background: var(--brand-wash);
  border-radius: var(--r-sm); padding: 2px var(--s-2);
  min-width: 28px; text-align: center;
}

/* ── SKELETONS ────────────────────────────────────────────────── */
.skel, .skel-avatar, .skel-lines { background: var(--sunken); border-radius: var(--r-sm); }
.skel { height: 14px; margin-bottom: var(--s-2); }
.skel-avatar { width: 40px; height: 40px; border-radius: var(--r-pill); }
.skel-lines > * + * { margin-top: var(--s-2); }
.skel.short { width: 62%; }
.skel.xshort { width: 34%; }

/* ── NAV MENU AND AVATAR ──────────────────────────────────────── */
.avatar-btn {
  width: 38px; height: 38px; flex: none;
  display: grid; place-items: center;
  background: var(--brand); color: var(--brand-on);
  border: 0; border-radius: var(--r-pill);
  font: inherit; font-weight: 700; font-size: var(--t-sm);
  cursor: pointer; box-shadow: var(--shadow-brand);
  transition: transform var(--fast), box-shadow var(--fast);
}
.avatar-btn:hover { transform: translateY(-1px) scale(1.04); }

/* app.js animates opacity and transform inline, so the resting state
   and the transition live here and the script only flips the values. */
.nav-menu {
  position: absolute; top: calc(100% + 12px); right: 0;
  width: 236px; z-index: 1000;
  display: flex; flex-direction: column; gap: 2px; padding: var(--s-2);
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r); box-shadow: var(--shadow-lg);
  opacity: 0; transform: translateY(-8px); pointer-events: none;
  transition: opacity var(--med), transform var(--med);
}
.nav-menu-label {
  padding: var(--s-2) var(--s-3) var(--s-3); margin-bottom: var(--s-1);
  border-bottom: 1px solid var(--line);
  font-size: var(--t-xs); font-weight: 700; color: var(--ink-3);
  letter-spacing: .05em; text-transform: uppercase;
}
.nav-menu-item {
  width: 100%; text-align: left; padding: var(--s-3);
  border: 0; border-radius: var(--r-sm); background: transparent;
  color: var(--ink-2); font: inherit; font-size: var(--t-base);
  cursor: pointer; transition: background var(--fast), color var(--fast);
}
.nav-menu-item:hover { background: var(--brand-wash); color: var(--brand); }
.nav-menu.is-open { opacity: 1; transform: translateY(0); pointer-events: auto; }

/* ── PROFILE MENU ──
   What the avatar opens. The dashboard is one entry here, so the full page
   only appears when it is asked for. Reuses .nav-menu. */
.profile-menu-wrap { position: relative; }
#profileMenu { width: 264px; }
.pm-head {
  display: flex; align-items: center; gap: var(--s-3);
  width: 100%; text-align: left;
  padding: var(--s-3); margin-bottom: var(--s-2);
  font: inherit; color: inherit;
  background: transparent; border: 0; border-bottom: 1px solid var(--line);
  border-radius: var(--r-sm) var(--r-sm) 0 0; cursor: pointer;
  transition: background var(--fast);
}
.pm-head:hover { background: var(--brand-wash); }
.pm-head:hover .pm-name, .pm-head:hover .pm-head-go { color: var(--brand); }
.pm-head-text { display: flex; flex-direction: column; min-width: 0; flex: 1; }
.pm-head-go { width: 16px; height: 16px; flex: none; color: var(--ink-3); }
.pm-name { font-size: var(--t-sm); font-weight: 700; color: var(--ink); line-height: 1.3; }
.pm-email {
  font-size: var(--t-xs); color: var(--ink-3); line-height: 1.4;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.pm-item {
  display: flex; align-items: center; gap: var(--s-3);
  font-size: var(--t-sm); font-weight: 500;
}
.pm-item svg { width: 17px; height: 17px; flex: none; }
.pm-danger { color: var(--sig-fault); }
.pm-danger:hover { background: var(--sig-fault-wash); color: var(--sig-fault); }
.pm-group { padding: var(--s-3) var(--s-3) var(--s-2); margin-top: var(--s-2); border-top: 1px solid var(--line); }
.pm-group-label {
  font-size: var(--t-xs); font-weight: 700; text-transform: uppercase;
  letter-spacing: .08em; color: var(--ink-3); margin-bottom: var(--s-2);
}
.pm-seg { display: flex; gap: var(--s-2); }
.pm-seg .profile-theme-btn { flex: 1; min-height: 34px; padding: 0 var(--s-2); font-size: var(--t-xs); }
.pm-seg .profile-theme-btn svg { width: 15px; height: 15px; }
.pm-seg .profile-theme-btn.active-theme {
  background: var(--brand-wash); border-color: var(--brand-line); color: var(--brand);
}

/* ── MODAL ────────────────────────────────────────────────────── */
.modal-scrim {
  position: fixed; inset: 0; z-index: 10000;
  display: flex; align-items: center; justify-content: center; padding: var(--s-6);
  background: var(--scrim);
  backdrop-filter: blur(3px);
}
.modal-card {
  width: 100%; max-width: 460px; padding: var(--s-7);
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r-lg); box-shadow: var(--shadow-lg);
}
.modal-card h2 { font-size: var(--t-lg); margin-bottom: var(--s-3); }
.modal-card p { color: var(--ink-2); margin-bottom: var(--s-6); }
.modal-actions { display: flex; gap: var(--s-3); justify-content: flex-end; flex-wrap: wrap; }

/* ── SLIDE PANEL AND PROFILE ──────────────────────────────────── */
.slide-panel {
  position: fixed; top: 0; right: 0; height: 100vh; width: min(440px, 100vw);
  background: var(--surface); border-left: 1px solid var(--line);
  box-shadow: var(--shadow-lg);
  display: flex; flex-direction: column; z-index: 9999;
  transform: translateX(100%);
  transition: transform 280ms var(--ease);
}
.slide-panel.active { transform: translateX(0); }
.slide-panel-header {
  display: flex; align-items: center; justify-content: space-between;
  padding: var(--s-5) var(--s-6); border-bottom: 1px solid var(--line);
  font-size: var(--t-md); font-weight: 700; color: var(--ink);
}
.slide-panel-body { flex: 1; overflow-y: auto; padding: var(--s-6); }
#btnOpenProfile { cursor: pointer; }
/* The dashboard's tab is the avatar, so it takes an open state like one. */
#btnOpenProfile.is-active { box-shadow: var(--shadow-brand), 0 0 0 3px var(--brand-wash); }
#resultContent { min-width: 0; }


#mouse-glow, .mouse-glitter { display: none !important; }

/* ── UTILITIES ────────────────────────────────────────────────
   app.js writes these class names into innerHTML. There is
   no Tailwind here and no build step, so every class the scripts emit
   must be defined or it renders as nothing. Mapped to project tokens. */
.hidden { display: none !important; }
.custom-scroll { overflow-y: auto; }
.relative { position: relative; }
.overflow-hidden { overflow: hidden; }
.w-full { width: 100%; }
.flex { display: flex; }
.flex-col { flex-direction: column; }
.items-center { align-items: center; }
.items-end { align-items: flex-end; }
.justify-between { justify-content: space-between; }
.justify-center { justify-content: center; }
.flex-shrink-0 { flex-shrink: 0; }
.min-w-0 { min-width: 0; }
.truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.text-center { text-align: center; }
.font-medium { font-weight: 500; }
.font-mono { font-family: var(--mono); font-variant-numeric: tabular-nums; }
.space-fill { flex: 1; }

.gap-1\.5 { gap: 6px; }
.gap-2 { gap: var(--s-2); }
.mb-0 { margin-bottom: 0; }
.mb-2 { margin-bottom: var(--s-2); }
.mb-3 { margin-bottom: var(--s-3); }
.mb-4 { margin-bottom: var(--s-4); }
.mt-1 { margin-top: var(--s-1); }
.mt-2 { margin-top: var(--s-2); }
.ml-2 { margin-left: var(--s-2); }
.px-5 { padding-left: var(--s-5); padding-right: var(--s-5); }
.py-6 { padding-top: var(--s-6); padding-bottom: var(--s-6); }
.h-3\.5 { height: 14px; }
.w-3\.5 { width: 14px; }
.opacity-25 { opacity: .25; }
.opacity-75 { opacity: .75; }

.text-\[10px\] { font-size: var(--t-xs); }
.text-\[11px\] { font-size: var(--t-xs); }
.text-xs { font-size: var(--t-sm); }
.text-sm { font-size: var(--t-base); }
.text-3xl {
  font-family: var(--display); font-size: var(--t-2xl);
  font-weight: 800; letter-spacing: -.025em; color: var(--ink);
}
.text-slate-100 { color: var(--ink); }
.text-slate-400 { color: var(--ink-3); }
.text-slate-500 { color: var(--ink-3); }
.text-slate-600 { color: var(--ink-2); }
.text-rose-400 { color: var(--sig-fault); }

/* ── MOTION ───────────────────────────────────────────────────
   The ladder advancing is the one orchestrated moment. Everything
   else is micro-interaction on hover and focus. */
@keyframes gate-clear {
  0%   { opacity: 0; transform: translateY(8px); }
  60%  { opacity: 1; }
  100% { opacity: 1; transform: translateY(0); }
}
.hint-block[data-state="released"], .res-block.slide-up {
  animation: gate-clear 320ms var(--ease) both;
}
@keyframes spin { to { transform: rotate(360deg); } }
.spin, .animate-spin { animation: spin 900ms linear infinite; transform-origin: center; }
@keyframes btn-shimmer { 0%, 100% { opacity: 1; } 50% { opacity: .6; } }
.btn-shimmer-anim { animation: btn-shimmer 1.2s ease-in-out infinite; }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
    scroll-behavior: auto !important;
  }
  .btn:hover, .mentor-card:hover, .history-card:hover,
  .btn-primary:hover, .avatar-btn:hover { transform: none !important; }
}

/* ── RESPONSIVE — designed, not shrunk ────────────────────────── */
@media (max-width: 1279px) {
  :root { --sidebar-w: 248px; }
}

@media (max-width: 767px) {
  /* The sidebar becomes a strip above the page: the toggle and the modes
     in one sideways-scrolling row, the history under it. Collapsed hides the history. */
  .workspace { flex-direction: column; }
  .main-area { min-height: 0; }
  .sidebar, .sidebar.collapsed {
    width: 100%; display: grid; grid-template-columns: auto minmax(0, 1fr);
    border-right: 0; border-bottom: 1px solid var(--line);
  }
  .sidebar > .sidebar-label, .sidebar > .history-list { grid-column: 1 / -1; }
  .sidebar-footer { display: none; }
  .side-nav { flex-direction: row; overflow-x: auto; scrollbar-width: none; padding: var(--s-2) var(--s-2) var(--s-2) 0; }
  .side-nav::-webkit-scrollbar { display: none; }
  .nav-tab, .sidebar.collapsed .nav-tab { width: auto; flex: none; padding: 0 var(--s-3); }
  .sidebar.collapsed .nav-label { display: inline; }
  .sidebar-label { padding-top: var(--s-2); }
  .history-list { max-height: 30vh; }

  /* On a phone the editor goes first. A student opened this to paste a
     traceback, not to pick a mentor. */
  .workspace-inner { padding: var(--s-4) var(--s-4) var(--s-7); gap: var(--s-4); }
  .editor-body { min-height: 260px; }
  #rightWorkspaceWrapper { order: -1; }

  .section, .hf-card, .result-panel, .execution-panel, .diff-panel,
  .editor-panel, .exec-panel, .accordion-card, .bug-card,
  .challenge-card, .perf-card, .diff-section, .highfive-banner {
    padding: var(--s-5);
  }
  .diff-cols { grid-template-columns: 1fr; }
  .diff-col + .diff-col { border-left: 0; border-top: 1px solid var(--line); }
  .editor-tabbar, .editor-panel > .editor-actions,
  .editor-panel > .stderr-row { padding: var(--s-3) var(--s-4); }
  .mentor-card { flex-basis: 100%; }

  /* The nav is rebuilt for the phone, not squeezed. The server and AI
     wording collapses to its two dots, which is the whole signal. */
  .topnav { padding: 0 var(--s-4); gap: var(--s-3); flex-wrap: nowrap; }
  .topnav-left { flex: 1; min-width: 0; }
  .topnav-left, .topnav-right { gap: var(--s-3); }
  .logo-text { font-size: var(--t-base); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .status-text, .agent-label, #navUsername, .nav-divider { display: none; }
  .status-dot-wrap, .agent-status-wrap { gap: 0; }
}

@media (max-width: 480px) {
  /* The filename loses to the mark: the file is named again in the
     editor tab two rows down; the product is named nowhere else. */
  .breadcrumb { display: none; }
  .res-block, .hint-block, .tip-block, .analogy-block {
    grid-template-columns: 30px 1fr; gap: var(--s-2) var(--s-3);
  }
  .res-block::before, .hint-block::before, .tip-block::before {
    width: 30px; height: 30px; font-size: var(--t-sm);
  }
  .res-block::after, .hint-block::after { left: 14px; top: 48px; }
  .btn, .run-btn, .analyze-btn, .tool-btn { padding: 0 var(--s-4); }
}


/* ── CODE ANALYSIS REPORT ─────────────────────────────────────────
   The mentor's answer, in the panel. Three parts, in the order a
   student needs them: the count, the one mistake to fix first (the
   ladder), then everything else the file is carrying.

   Every value here is a token. The report is a light surface first;
   the dark block below is a second design, not an inversion.
   ────────────────────────────────────────────────────────────── */

.slide-panel-body > * + * { margin-top: var(--s-6); }

/* ── Header ─────────────────────────────────────────────────── */
.report-head {
  background: var(--raised);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  padding: var(--s-6);
  box-shadow: var(--shadow-sm);
}
.report-title {
  font-family: var(--display);
  font-size: var(--t-xl);
  font-weight: 600;
  line-height: 1.15;
  color: var(--ink);
  margin: 0;
}
.report-sub {
  margin: var(--s-2) 0 0;
  font-size: var(--t-sm);
  color: var(--ink-3);
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--s-2);
}
.report-file {
  font-family: var(--mono);
  color: var(--ink-2);
  background: var(--sunken);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  padding: 2px var(--s-2);
  /* A 60-character filename must not push the count off the row. */
  max-width: 100%;
  overflow-wrap: anywhere;
}

/* One even row, never a 3+1 orphan: there are at most four tiles (total plus
   three categories) and the panel is fixed-width, so equal flex beats an
   auto-fit grid that wraps the last one onto its own line. */
.report-tiles {
  display: flex;
  gap: var(--s-3);
  margin-top: var(--s-5);
}
.report-tile { flex: 1 1 0; min-width: 0; }
.report-tile {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r);
  padding: var(--s-4);
  display: flex;
  flex-direction: column;
  gap: var(--s-1);
}
.tile-label {
  font-size: var(--t-xs);
  font-weight: 600;
  color: var(--ink-3);
}
.tile-num {
  font-family: var(--display);
  font-size: var(--t-xl);
  font-weight: 700;
  line-height: 1;
}
/* Hue carries category, not severity - these are the label hues from
   the tag set, used consistently wherever a category is named. */
.tile-total    { color: var(--sig-fault); }
.tile-syntax   { color: var(--tag-amber-fg); }
.tile-logic    { color: var(--tag-teal-fg); }
.tile-spelling { color: var(--tag-rose-fg); }

/* ── Section headings ───────────────────────────────────────── */
.report-section-title {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-3);
  font-size: var(--t-md);
  font-weight: 700;
  color: var(--ink);
  margin: 0 0 var(--s-4);
}
.group-count {
  flex: none;
  font-size: var(--t-xs);
  font-weight: 700;
  border-radius: var(--r-pill);
  padding: 3px var(--s-3);
}
.group-count-syntax   { color: var(--tag-amber-fg); background: var(--tag-amber-bg); }
.group-count-logic    { color: var(--tag-teal-fg);  background: var(--tag-teal-bg); }
.group-count-spelling { color: var(--tag-rose-fg);  background: var(--tag-rose-bg); }

/* ── Findings ───────────────────────────────────────────────── */
.report-group {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  padding: var(--s-6);
  box-shadow: var(--shadow-sm);
}
.finding {
  border-left: 3px solid var(--line-strong);
  border-radius: 0 var(--r) var(--r) 0;
  background: var(--raised);
  padding: var(--s-4) var(--s-5);
  transition: transform var(--fast), box-shadow var(--fast);
}
.finding + .finding { margin-top: var(--s-3); }
.finding:hover {
  transform: translateX(2px);
  box-shadow: var(--shadow-sm);
}
.finding-syntax   { border-left-color: var(--tag-amber-fg); }
.finding-logic    { border-left-color: var(--tag-teal-fg); }
.finding-spelling { border-left-color: var(--tag-rose-fg); }

.finding-title {
  margin: 0;
  font-size: var(--t-base);
  font-weight: 700;
  color: var(--ink);
  line-height: 1.4;
}
.finding-lead {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--s-2);
  margin: var(--s-3) 0 0;
  padding: var(--s-2) var(--s-3);
  border-radius: var(--r-sm);
  background: var(--sunken);
  font-size: var(--t-sm);
  line-height: 1.55;
  color: var(--ink-2);
}
.finding-lead-label {
  flex: none;
  font-size: var(--t-xs);
  font-weight: 700;
  color: var(--ink-3);
}
.finding-lead-label::after { content: ' \2192'; }
.finding-lead-value { overflow-wrap: anywhere; }
.finding-lead-value.is-code {
  font-family: var(--mono);
  color: var(--ink);
}
.finding-syntax .finding-lead   { background: var(--tag-amber-bg); }
.finding-logic .finding-lead    { background: var(--tag-teal-bg); }
.finding-spelling .finding-lead { background: var(--tag-rose-bg); }
.finding-syntax .finding-lead-label   { color: var(--tag-amber-fg); }
.finding-logic .finding-lead-label    { color: var(--tag-teal-fg); }
.finding-spelling .finding-lead-label { color: var(--tag-rose-fg); }

.finding-detail {
  margin: var(--s-3) 0 0;
  font-size: var(--t-base);
  line-height: 1.65;
  color: var(--ink-2);
}

/* ── Empty report — names the next action, never a blank panel ── */
.report-empty {
  background: var(--surface);
  border: 1px dashed var(--line-strong);
  border-radius: var(--r-lg);
  padding: var(--s-6);
}
.report-empty p {
  margin: 0;
  font-size: var(--t-base);
  line-height: 1.65;
  color: var(--ink-2);
}
.report-empty-clear {
  border-style: solid;
  border-color: var(--sig-clear-line);
  background: var(--sig-clear-wash);
}
.report-empty-clear .report-section-title { color: var(--sig-clear); }

.report-concept {
  margin: var(--s-4) 0 0;
  font-size: var(--t-sm);
  color: var(--ink-3);
}

@media (prefers-reduced-motion: reduce) {
  .finding:hover { transform: none; }
}

/* ── PROBLEM SPLIT VIEW ───────────────────────────────────────────
   Opening a problem used to set flex-direction: row inline on
   .workspace-inner, which is a column containing seven panels - the
   nav tabs included - so every one of them was laid out side by side
   and collapsed. Nothing cleared it afterwards either, which is what
   crushed the course reader to a one-character column.

   The layout lives here instead, and it only ever moves the two
   panels that belong in a split: the description and the editor.
   Hidden siblings are display:none, so grid never allots them a cell.
   ────────────────────────────────────────────────────────────── */
.workspace-inner.split-view {
  display: grid;
  grid-template-columns: minmax(300px, 2fr) 3fr;
  align-items: start;
}
/* Placed explicitly rather than left to auto-flow: the container also holds
   the nav, several hidden panels and a trailing spacer, and auto-flow gave
   the nav a cell and pushed the editor onto its own row. */
.workspace-inner.split-view > * { grid-column: 1 / -1; }
.workspace-inner.split-view > #problemDescriptionPane {
  grid-column: 1;
  grid-row: 2;
  position: sticky;
  top: var(--s-5);
  /* .editor-panel is a block with overflow:hidden, so the description was
     clipped at the panel edge and nothing scrolled - the tail of every
     problem was unreachable. Flex column instead: the header keeps its
     height, #probDescContent takes the rest and scrolls itself. */
  display: flex;
  flex-direction: column;
  max-height: calc(100vh - 140px);
  overflow: hidden;
}
/* A flex child defaults to min-height:auto, which refuses to shrink below its
   content - the scroll would never engage without this. */
.workspace-inner.split-view > #problemDescriptionPane > #probDescContent {
  flex: 1 1 auto;
  min-height: 0;
}
.workspace-inner.split-view > #problemDescriptionPane > :first-child { flex-shrink: 0; }
.workspace-inner.split-view > #rightWorkspaceWrapper {
  grid-column: 2;
  grid-row: 2;
}

/* One column below the laptop breakpoint: a 300px description beside a
   300px editor helps nobody. The description leads, the editor follows. */
@media (max-width: 1023px) {
  .workspace-inner.split-view {
    grid-template-columns: 1fr;
  }
  .workspace-inner.split-view > #problemDescriptionPane,
  .workspace-inner.split-view > #rightWorkspaceWrapper {
    grid-column: 1 / -1;
    grid-row: auto;
  }
  .workspace-inner.split-view > #problemDescriptionPane {
    position: static;
    max-height: 46vh;
  }
}

/* ── COURSE READER ───────────────────────────────────────────────
   A flex child's min-width is auto, so the lesson pane refused to
   shrink politely and then collapsed instead. The contents-of-the-
   course rail keeps its width; the reader keeps a floor. */
#courseToc { flex: 0 0 250px; min-width: 0; }
#courseLessonContent { flex: 1 1 auto; min-width: 0; }

@media (max-width: 767px) {
  #courseArenaPanel > div:last-child { flex-direction: column; }
  #courseToc {
    flex: 0 0 auto;
    width: 100%;
    max-height: 34vh;
    border-right: 0;
    border-bottom: 1px solid var(--line);
  }
}

/* ── FLOATING PROBLEM-CHAT BUTTON ────────────────────────────────
   Was a 50x50 square of --primary with no icon inside and no radius,
   parked bottom-left: the stray purple block. It is a real control
   now, and switchWorkbenchView hides it when the problem view closes. */
#btnToggleProblemChat {
  /* Fixed to the viewport, clear of both the left rail and the status bar.
     At bottom-left it sat on top of the sidebar's own footer text. */
  position: fixed;
  bottom: calc(var(--s-6) + 28px);
  right: var(--s-6);
  width: 52px;
  height: 52px;
  border-radius: var(--r-pill);
  background: var(--brand);
  color: var(--brand-on);
  border: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  z-index: 40;
  box-shadow: var(--shadow-brand), var(--shadow-md);
  transition: transform var(--fast), background var(--fast);
}
#btnToggleProblemChat:hover {
  background: var(--brand-hover);
  transform: translateY(-2px);
}
#btnToggleProblemChat:active { transform: translateY(0); }
#btnToggleProblemChat svg {
  width: 24px;
  height: 24px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.75;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* ── DIALOGS ─────────────────────────────────────────────────────
   Replacing window.alert / confirm / prompt. Reuses .modal-scrim and
   .modal-card; only the form parts are new. */
.dlg-label {
  display: block;
  font-size: var(--t-sm);
  font-weight: 600;
  color: var(--ink-2);
  margin-bottom: var(--s-2);
}
.dlg-input {
  width: 100%;
  min-height: 40px;
  padding: 0 var(--s-3);
  font-size: var(--t-base);
  font-family: inherit;
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-sm);
  margin-bottom: var(--s-3);
}
.dlg-input:hover { border-color: var(--brand-line); }

/* ── FEEDBACK FORM ───────────────────────────────────────────────
   A native <dialog> wearing .modal-card. The form carries the padding so
   a click that lands on the <dialog> itself is always the backdrop. */
.fb-dialog {
  width: min(560px, calc(100vw - var(--s-8)));
  max-height: calc(100dvh - var(--s-8));
  padding: 0; margin: auto; color: var(--ink);
  overflow-y: auto; overscroll-behavior: contain;
}
.fb-dialog::backdrop { background: var(--scrim); backdrop-filter: blur(3px); }
.fb-form { padding: var(--s-7); }
.fb-q { border: 0; margin: 0 0 var(--s-5); padding: 0; min-width: 0; }
.fb-q legend { padding: 0; }
.fb-follow { margin-top: var(--s-3); font-weight: 500; }
.fb-text { padding: var(--s-2) var(--s-3); line-height: 1.5; resize: vertical; margin-bottom: 0; }

/* Ten half-star labels per row. Each shows one half of a 28px star:
   the right half shifts its star left by its own width. Filled = --brand,
   because a star is a control the student clicks. */
.fb-stars { display: flex; align-items: center; flex-wrap: wrap; }
.fb-half { width: 14px; height: 28px; overflow: hidden; cursor: pointer; color: var(--ink-3); }
.fb-half svg { width: 28px; height: 28px; display: block; fill: none; stroke: currentColor; stroke-width: 1.6; stroke-linejoin: round; transition: color var(--fast) var(--ease); }
.fb-r svg { margin-left: -14px; }
.fb-r { margin-right: var(--s-1); }
.fb-half.on { color: var(--brand); }
.fb-half.on svg { fill: currentColor; }
.fb-stars input:focus-visible + .fb-half { outline: 2px solid var(--focus-ring); outline-offset: 2px; border-radius: 2px; }
.fb-value { margin-left: var(--s-3); font-size: var(--t-sm); font-weight: 600; color: var(--ink-2); }

.fb-top svg { width: 16px; height: 16px; }
@media (max-width: 1023px) {
  .fb-top { padding: 0 var(--s-2); }
  .fb-top-label { display: none; }
}

/* ── ALL FEEDBACK (admins) ── */
.fba-head { display: flex; align-items: flex-end; justify-content: space-between; gap: var(--s-4); flex-wrap: wrap; padding-bottom: var(--s-5); border-bottom: 1px solid var(--line); }
.fba-head h1 { font-size: var(--t-2xl); color: var(--ink); }
.fba-actions { display: flex; gap: var(--s-2); }
.fba-actions svg { width: 16px; height: 16px; }
.fba-list { display: flex; flex-direction: column; gap: var(--s-4); }
.fba-item-head { display: flex; justify-content: space-between; align-items: flex-start; gap: var(--s-3); flex-wrap: wrap; }
.fba-who { font-weight: 700; color: var(--ink); }
.fba-ratings { display: flex; gap: var(--s-2); flex-wrap: wrap; margin-top: var(--s-4); }
.fba-rating { padding: var(--s-1) var(--s-3); border-radius: var(--r-pill); background: var(--sunken); font-size: var(--t-sm); color: var(--ink-2); }
.fba-rating b { color: var(--ink); }
.fba-answers { margin-top: var(--s-4); }
.fba-answers dt { font-size: var(--t-xs); font-weight: 700; text-transform: uppercase; letter-spacing: .06em; color: var(--ink-3); margin-top: var(--s-4); }
.fba-answers dd { margin: var(--s-1) 0 0; color: var(--ink); white-space: pre-wrap; overflow-wrap: anywhere; }
.dlg-error {
  margin: calc(var(--s-2) * -1) 0 var(--s-3);
  font-size: var(--t-sm);
  color: var(--sig-fault);
}
.btn-danger {
  color: var(--brand-on);
  background: var(--sig-fault);
  border: 1px solid var(--sig-fault);
}
.btn-danger:hover { filter: brightness(.93); }

/* ── TOASTS ──────────────────────────────────────────────────────
   For things that only need acknowledging. Bottom-centre so it never
   collides with the chat button in the bottom-left corner. */
#toastHost {
  position: fixed;
  bottom: var(--s-8);
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  flex-direction: column;
  gap: var(--s-2);
  z-index: 200;
  pointer-events: none;
}
.toast {
  padding: var(--s-3) var(--s-5);
  max-width: min(92vw, 460px);
  font-size: var(--t-sm);
  line-height: 1.5;
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--line-strong);
  border-radius: var(--r);
  box-shadow: var(--shadow-md);
  opacity: 1;
  transition: opacity var(--fast), transform var(--fast);
}
.toast-out { opacity: 0; transform: translateY(6px); }
.toast-clear { border-color: var(--sig-clear-line); background: var(--sig-clear-wash); }
.toast-fault { border-color: var(--sig-fault-line); background: var(--sig-fault-wash); }

/* ── API ERROR IN THE RESULT PANEL ───────────────────────────── */
.api-error {
  border-color: var(--sig-fault-line);
  background: var(--sig-fault-wash);
}
.api-error .report-section-title { color: var(--sig-fault); }

@media (prefers-reduced-motion: reduce) {
  #btnToggleProblemChat:hover { transform: none; }
  .toast-out { transform: none; }
}

/* ── PROBLEM HEADER META ─────────────────────────────────────────
   The topics row (#probTopicsDisplay) did not exist in the markup at
   all, so openProblem threw setting it and the description, the chat
   reset and the editor clear that followed never ran - the problem
   opened blank. Chips reuse the .tag set rather than inline styles. */
.prob-difficulty {
  padding: 3px var(--s-3);
  font-size: var(--t-xs);
  font-weight: 700;
  border-radius: var(--r-pill);
  color: var(--sig-clear);
  background: var(--sig-clear-wash);
  border: 1px solid var(--sig-clear-line);
}
.prob-topics { display: flex; flex-wrap: wrap; gap: var(--s-2); }

/* ── REPORT, second pass ─────────────────────────────────────────
   Matches the Figma "Code analysis report" (frame 10) now that the
   ladder is out of the panel: numbered cards inside each group, the
   evidence strip set in mono, and the fix at the end behind a button.

   Numbering is back because the ladder no longer owns it here. If the
   ladder returns to this panel, DESIGN.md's rule applies again and
   these numbers come out.

   The palette is tokens, not the hexes this block first shipped with.
   Those were stock Tailwind (#f59e0b, #64748b) plus a hardcoded dark
   chip (#27272a on #ffffff) that sat outside any [data-theme] scope, so
   the report rendered its dark treatment in light mode too. */

.finding-title {
  display: flex;
  align-items: baseline;
  gap: var(--s-3);
  margin-bottom: var(--s-2);
}
.finding-num {
  flex: none;
  width: 22px;
  height: 22px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  align-self: flex-start;
  font-size: var(--t-xs);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  border-radius: 50%;
  color: var(--ink);
  background: var(--raised);
  border: 1px solid var(--line-strong);
}

/* The 3px edge is the categorical marker: held for a logic mistake, fault for
   a syntax one. Kept deliberately - it is what separates the two groups at a
   glance. Signal inks, so it means the same thing here as everywhere else. */
.finding-logic {
  border-left: 3px solid var(--sig-held);
}
.finding-logic .finding-num {
  color: var(--ink);
  background: var(--sig-held-wash);
  border-color: var(--sig-held-line);
}
.finding-syntax {
  border-left: 3px solid var(--sig-fault);
}
.finding-syntax .finding-num {
  color: var(--ink);
  background: var(--sunken);
  border-color: var(--line);
}

/* The evidence/headline callout strip matching screenshot design */
.finding-lead {
  display: block;
  padding: 10px 14px;
  margin: var(--s-2) 0 var(--s-3);
  border-radius: 6px;
  font-size: 13.5px;
  line-height: 1.5;
}
.finding-logic .finding-lead {
  background: var(--sig-held-wash);
  border: 1px solid var(--sig-held-line);
  color: var(--ink);
}
.finding-logic .finding-lead-label {
  color: var(--sig-held);
  font-weight: 700;
}
.finding-logic .finding-lead-value {
  color: var(--ink);
  font-weight: 500;
}
.finding-syntax .finding-lead {
  background: var(--sunken);
  border: 1px solid var(--line);
  color: var(--ink);
  font-family: var(--mono);
}
.finding-syntax .finding-lead-label {
  color: var(--ink-3);
  font-weight: 600;
  font-family: var(--sans);
}
.finding-syntax .finding-lead-value,
.finding-lead-value.is-code {
  font-family: var(--mono);
  font-size: 13px;
  color: var(--ink);
}

/* ── THE FIX ─────────────────────────────────────────────────────
   Last card in the report. The corrected line stays behind a click:
   the student reads what is wrong before the answer is on screen,
   which is the one piece of the ladder worth keeping here. */
.fix-card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  padding: var(--s-6);
  box-shadow: var(--shadow-sm);
}
.fix-card-head {
  display: flex;
  align-items: center;
  gap: var(--s-3);
}
.fix-card-head .report-section-title { margin-bottom: 0; }
.fix-line-tag {
  font-family: var(--mono);
  font-size: var(--t-xs);
  color: var(--brand);
  background: var(--brand-wash);
  border: 1px solid var(--brand-line);
  border-radius: var(--r-sm);
  padding: 2px var(--s-2);
}
.fix-line-tag:empty { display: none; }

#btnRevealFix { margin-top: var(--s-4); }

.fix-body { margin-top: var(--s-4); }
.fix-code {
  margin: 0;
  font-family: var(--mono);
  font-size: var(--t-sm);
  line-height: 1.7;
  color: var(--ink);
  background: var(--sig-clear-wash);
  border: 1px solid var(--sig-clear-line);
  border-radius: var(--r);
  padding: var(--s-4);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
.fix-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-2);
  margin-top: var(--s-3);
}
.chat-code-block {
  margin: 8px 0;
  padding: 8px;
  overflow-x: auto;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--bg);
  font: 12px/1.5 var(--font-mono);
}

.chat-inline-code {
  padding: 1px 4px;
  border: 1px solid var(--border);
  border-radius: 3px;
  background: var(--bg);
  font: 12px/1.4 var(--font-mono);
}

/* ── PROBLEM STUDIO ──────────────────────────────────────────────
   A problem opens as a brief (left) and the editor with a Checks panel
   (right). Tokens only. Pass/fail colours appear on example results
   alone: they describe the student's code, never the chrome. */
.studio-brief { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-lg); }
.studio-head {
  padding: var(--s-5) var(--s-6) var(--s-4);
  background: var(--raised);
  border-bottom: 1px solid var(--line);
  display: flex; flex-direction: column; gap: var(--s-3);
}
.studio-head-row { display: flex; align-items: center; gap: var(--s-3); }
.studio-num {
  font: 600 var(--t-xs)/1 var(--mono);
  color: var(--tag-indigo-fg); background: var(--tag-indigo-bg);
  padding: var(--s-1) var(--s-2); border-radius: var(--r-sm);
}
.studio-num:empty { display: none; }
.studio-title { flex: 1; min-width: 0; font: 700 var(--t-lg)/1.25 var(--font); color: var(--ink); }
.studio-meta { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-2); }
.studio-body { padding: var(--s-6); color: var(--ink); font-size: var(--t-base); line-height: 1.6; overflow-y: auto; }
.studio-prose p + p { margin-top: var(--s-3); }
.studio-prose { margin-bottom: var(--s-6); }
.studio-example {
  margin-top: var(--s-4); padding: var(--s-4) var(--s-5);
  background: var(--sunken); border-radius: var(--r);
}
.studio-example-title, .studio-subhead {
  font: 700 var(--t-sm)/1.3 var(--font); color: var(--ink-2);
  text-transform: uppercase; letter-spacing: .06em; margin-bottom: var(--s-2);
}
.studio-subhead { margin-top: var(--s-6); display: flex; align-items: center; gap: var(--s-2); }
.studio-io { display: grid; grid-template-columns: max-content 1fr; gap: var(--s-1) var(--s-4); font-size: var(--t-sm); }
.studio-io dt { color: var(--ink-3); font-weight: 600; }
.studio-io dd { min-width: 0; overflow-wrap: anywhere; }
.studio-io code, .studio-error, .studio-printed pre { font-family: var(--mono); font-size: var(--t-sm); color: var(--ink); }
.studio-constraints { padding-left: var(--s-5); font-size: var(--t-sm); color: var(--ink-2); }
.studio-constraints li + li { margin-top: var(--s-1); }

.studio-checks { margin-top: var(--s-4); border-radius: var(--r-lg); border: 1px solid var(--line); background: var(--surface); }
.studio-checks-bar {
  display: flex; align-items: center; gap: var(--s-3);
  padding: var(--s-3) var(--s-4); border-bottom: 1px solid var(--line); background: var(--raised);
}
.studio-seg { display: inline-flex; padding: 3px; gap: 2px; background: var(--sunken); border-radius: var(--r-pill); }
.studio-seg button {
  border: 0; background: transparent; color: var(--ink-3); cursor: pointer;
  font: 600 var(--t-sm)/1 var(--font); padding: var(--s-2) var(--s-4); border-radius: var(--r-pill);
  transition: background var(--fast) var(--ease), color var(--fast) var(--ease);
}
.studio-seg button[aria-selected="true"] { background: var(--surface); color: var(--ink); box-shadow: var(--shadow-sm); }
.studio-summary { flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-size: var(--t-sm); font-weight: 600; color: var(--ink-3); }
.studio-summary[data-state="pass"] { color: var(--sig-clear); }
.studio-summary[data-state="fail"] { color: var(--sig-fault); }
.studio-checks-body { padding: var(--s-4); max-height: 340px; overflow-y: auto; display: flex; flex-direction: column; gap: var(--s-3); }
.studio-case { padding: var(--s-3) var(--s-4); border-radius: var(--r); background: var(--sunken); border: 1px solid transparent; }
.studio-case.is-pass { background: var(--sig-clear-wash); border-color: var(--sig-clear-line); }
.studio-case.is-fail { background: var(--sig-fault-wash); border-color: var(--sig-fault-line); }
.studio-case-name { display: flex; align-items: center; gap: var(--s-2); font-weight: 700; font-size: var(--t-sm); color: var(--ink); margin-bottom: var(--s-2); }
.studio-verdict { font-size: var(--t-xs); text-transform: uppercase; letter-spacing: .06em; }
.is-pass .studio-verdict { color: var(--sig-clear); }
.is-fail .studio-verdict { color: var(--sig-fault); }
.studio-error { white-space: pre-wrap; margin: 0; color: var(--sig-fault); }
.studio-note { font-size: var(--t-sm); color: var(--ink-3); }
.studio-printed summary { cursor: pointer; font-size: var(--t-sm); color: var(--ink-2); }
.studio-printed pre { white-space: pre-wrap; margin-top: var(--s-2); padding: var(--s-3); background: var(--sunken); border-radius: var(--r-sm); }
.studio-review { padding-top: var(--s-2); border-top: 1px solid var(--line); font-size: var(--t-sm); color: var(--ink); }
.studio-review .studio-subhead { margin-top: var(--s-2); }
.studio-advisory { font-weight: 500; text-transform: none; letter-spacing: 0; color: var(--ink-3); }
.studio-complexity { display: flex; gap: var(--s-5); margin-bottom: var(--s-2); color: var(--ink-2); }
.studio-complexity strong { font-family: var(--mono); color: var(--ink); }

@media (prefers-reduced-motion: reduce) {
  .studio-seg button { transition: none; }
}

/* ── PROBLEM LIST ────────────────────────────────────────────────
   A compact table over the whole catalogue (searched on the server),
   50 rows a page. Difficulty is a category, so it takes a tag colour. */
.plist { display: flex; flex-direction: column; border: 1px solid var(--line); border-radius: var(--r-lg); overflow: hidden; background: var(--surface); }
.plist-head, .plist-row {
  display: grid; grid-template-columns: 64px minmax(0, 1fr) 110px minmax(0, .9fr);
  align-items: center; gap: var(--s-4); padding: var(--s-3) var(--s-5);
}
.plist-head {
  background: var(--raised); border-bottom: 1px solid var(--line);
  font: 600 var(--t-xs)/1.2 var(--font); text-transform: uppercase; letter-spacing: .06em; color: var(--ink-3);
}
.plist-rows { max-height: 62vh; overflow-y: auto; }
.plist-row {
  width: 100%; border: 0; border-bottom: 1px solid var(--line); background: transparent;
  color: var(--ink); text-align: left; cursor: pointer; font: 500 var(--t-sm)/1.4 var(--font);
  transition: background var(--fast) var(--ease);
}
.plist-row:nth-child(even) { background: var(--raised); }
.plist-row:hover { background: var(--sunken); }
.plist-row[aria-busy="true"] { opacity: .6; cursor: progress; }
.plist-num { font-family: var(--mono); color: var(--ink-3); }
.plist-title { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.plist-topics { color: var(--ink-3); font-size: var(--t-xs); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.plist-foot {
  display: flex; align-items: center; justify-content: space-between; gap: var(--s-3);
  padding: var(--s-3) var(--s-5); background: var(--raised); font-size: var(--t-sm); color: var(--ink-3);
}
.plist-state {
  padding: var(--s-9) var(--s-6); text-align: center; color: var(--ink-3);
  display: flex; flex-direction: column; align-items: center; gap: var(--s-3);
}
@media (max-width: 720px) {
  .plist-head, .plist-row { grid-template-columns: 48px minmax(0, 1fr) 90px; }
  .plist-head span:last-child, .plist-topics { display: none; }
}

/* ── PROBLEM FOCUS MODE ──────────────────────────────────────────
   .workspace.studio-focus is set while a problem is open. The panes fill the
   viewport down to the status bar (--studio-h is measured in app.js) and the
   divider sets --studio-left. Tools a problem does not need are hidden. */
.studio-focus .file-actions,
.studio-focus #modelSelect,
.studio-focus #runBtn,
.studio-focus .stderr-row,
.studio-focus #executionPanel { display: none; }
.studio-head .icon-btn:disabled { opacity: .35; cursor: default; }
.studio-hint { font-size: var(--t-xs); color: var(--ink-3); white-space: nowrap; }
.studio-hint kbd {
  font: 500 var(--t-xs)/1 var(--mono); padding: 2px var(--s-1);
  border: 1px solid var(--line); border-radius: var(--r-sm); background: var(--surface); color: var(--ink-2);
}
.studio-splitter { position: relative; cursor: col-resize; touch-action: none; }
.studio-splitter::after {
  content: ''; position: absolute; inset: 0 3px; border-radius: var(--r-pill);
  background: var(--line); transition: background var(--fast) var(--ease);
}
.studio-splitter:hover::after, .studio-splitter:focus-visible::after, body.is-resizing .studio-splitter::after { background: var(--brand); }
body.is-resizing { cursor: col-resize; user-select: none; }

@media (min-width: 1024px) {
  .studio-focus .workspace-inner { width: 100%; max-width: none; padding: var(--s-4) var(--s-5) 0; gap: var(--s-4); }
  .studio-focus .workspace-inner.split-view {
    grid-template-columns: minmax(280px, var(--studio-left, 42%)) 8px minmax(380px, 1fr);
    column-gap: var(--s-1);
  }
  .studio-focus .workspace-inner.split-view > #problemDescriptionPane {
    position: static; height: var(--studio-h, 70vh); max-height: none;
  }
  .studio-focus .workspace-inner.split-view > #studioSplitter { grid-column: 2; grid-row: 2; height: var(--studio-h, 70vh); }
  .studio-focus .workspace-inner.split-view > #rightWorkspaceWrapper {
    grid-column: 3; grid-row: 2; height: var(--studio-h, 70vh); min-height: 0;
    display: flex; flex-direction: column; gap: 0;
  }
  .studio-focus #mainEditorPanel { flex: 1 1 auto; min-height: 220px; display: flex; flex-direction: column; }
  .studio-focus #mainEditorPanel .editor-body { flex: 1 1 auto; height: auto; min-height: 0; }
  .studio-focus #problemChecks { flex: 0 0 var(--checks-h, 45%); min-height: 140px; margin-top: 0; display: flex; flex-direction: column; }
  .studio-focus #problemChecks .studio-checks-body { flex: 1 1 auto; max-height: none; min-height: 0; }
}
@media (max-width: 1023px) {
  .studio-splitter { display: none !important; }
  .studio-hint { display: none; }
}
@media (prefers-reduced-motion: reduce) {
  .plist-row, .studio-splitter::after { transition: none; }
}

/* Focus-mode tidy-up: the spacer that clears the status bar is not needed when
   the panes already stop above it, and the filename tab repeats the language. */
.studio-focus .editor-tabs { display: none; }
/* Difficulty filter: the same segmented control as the Checks tabs. */
.studio-seg button[aria-pressed="true"] { background: var(--surface); color: var(--ink); box-shadow: var(--shadow-sm); }

/* Output panel header: a title and a pass count, so the outcome shows at a glance. */
.studio-checks-title {
  display: inline-flex; align-items: center; gap: var(--s-2);
  font: 600 var(--t-sm)/1 var(--font); color: var(--ink);
}
.studio-checks-title svg { width: 16px; height: 16px; color: var(--ink-3); }
.studio-tab-count {
  font: 600 var(--t-xs)/1 var(--font); padding: 3px var(--s-2); border-radius: var(--r-pill);
  background: var(--sunken); color: var(--ink-2);
}
.studio-tab-count:empty { display: none; }
.studio-tab-count[data-state="pass"] { background: var(--sig-clear-wash); color: var(--sig-clear); }
.studio-tab-count[data-state="fail"] { background: var(--sig-fault-wash); color: var(--sig-fault); }


/* ── LEVEL CARD and tier marks ───────────────────────────────────
   The tier card above the problem list. */
.level-card {
  margin: 0 0 var(--s-4); padding: var(--s-4) var(--s-5); border-radius: var(--r-lg);
  background: var(--raised); border: 1px solid var(--line); color: var(--ink); font-size: var(--t-sm);
  display: flex; flex-direction: column; gap: var(--s-3); align-items: flex-start;
}
.level-card-head { display: flex; align-items: center; gap: var(--s-3); }
.level-card-meta { color: var(--ink-3); font-size: var(--t-xs); }

.plist-num { display: inline-flex; align-items: center; gap: var(--s-2); }
.plist-status { width: 16px; display: inline-flex; color: var(--accent); }
.plist-status svg { width: 16px; height: 16px; }
.plist-row.is-locked { color: var(--ink-3); cursor: not-allowed; }
.plist-row.is-locked .plist-title { font-weight: 500; }
.plist-title svg { width: 14px; height: 14px; vertical-align: -2px; margin-right: var(--s-1); }
.studio-solve { font-size: var(--t-sm); color: var(--ink); padding: var(--s-3) var(--s-4); border-radius: var(--r); background: var(--raised); border: 1px solid var(--line); }

.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}

/* ── FULL-SCREEN PROBLEM VIEW ────────────────────────────────────
   body.studio-focus while a problem is open: the mode tabs, sidebar toggle and
   close button go; the top bar offers only Back to the list and Home, and the
   panes move up into the space the tabs used. */
.studio-top-actions { display: none; align-items: center; gap: var(--s-2); }
.studio-top-actions .btn { display: inline-flex; align-items: center; gap: var(--s-1); }
.studio-top-actions svg { width: 16px; height: 16px; }
.studio-focus .studio-top-actions { display: flex; }
.studio-focus .topnav .breadcrumb,
.studio-focus .sidebar,
.studio-focus #btnExitProblem { display: none; }
@media (min-width: 1024px) {
  .studio-focus .workspace-inner.split-view > #problemDescriptionPane,
  .studio-focus .workspace-inner.split-view > #studioSplitter,
  .studio-focus .workspace-inner.split-view > #rightWorkspaceWrapper { grid-row: 1; }
}

/* ── CHECKS PANEL, second pass ───────────────────────────────────
   No inherited .editor-panel padding (the tab bar sits flush), a divider
   above it to trade height with the editor, and one example at a time
   behind Case chips: each argument in its own labelled box. */
#problemChecks.editor-panel { padding: 0; }
.studio-checks-body { padding: var(--s-4) var(--s-5); gap: var(--s-4); }
/* No gap: the editor and the output read as one card, split by a 1px line.
   The grab area (::before) is wider than the line, so it is still easy to drag. */
.checks-splitter { flex: 0 0 1px; position: relative; z-index: 1; cursor: row-resize; touch-action: none; background: var(--line); }
.checks-splitter::before { content: ''; position: absolute; left: 0; right: 0; top: -6px; bottom: -6px; }
.checks-splitter::after {
  content: ''; position: absolute; left: 50%; top: 50%; width: 48px; height: 4px;
  transform: translate(-50%, -50%); border-radius: var(--r-pill);
  background: var(--line); transition: background var(--fast) var(--ease);
}
.studio-focus #mainEditorPanel { border-bottom: 0; border-bottom-left-radius: 0; border-bottom-right-radius: 0; }
.studio-focus #problemChecks { border-top: 0; border-top-left-radius: 0; border-top-right-radius: 0; }
.checks-splitter:hover::after, .checks-splitter:focus-visible::after, body.is-resizing-rows .checks-splitter::after { background: var(--brand); }
body.is-resizing-rows { cursor: row-resize; user-select: none; }
@media (max-width: 1023px) { .checks-splitter { display: none !important; } }

.case-chips { display: flex; flex-wrap: wrap; gap: var(--s-2); }
.case-chip {
  display: inline-flex; align-items: center; gap: var(--s-2);
  border: 1px solid var(--line); background: var(--surface); color: var(--ink-2);
  font: 600 var(--t-sm)/1 var(--font); padding: var(--s-2) var(--s-3); border-radius: var(--r-sm); cursor: pointer;
  transition: background var(--fast) var(--ease), color var(--fast) var(--ease), border-color var(--fast) var(--ease);
}
.case-chip:hover { background: var(--sunken); color: var(--ink); }
.case-chip[aria-pressed="true"] { background: var(--sunken); color: var(--ink); border-color: var(--ink-3); }
.case-dot { width: 8px; height: 8px; border-radius: var(--r-pill); background: var(--line); }
.case-dot[data-state="pass"] { background: var(--sig-clear); }
.case-dot[data-state="fail"] { background: var(--sig-fault); }
.case-detail { display: flex; flex-direction: column; gap: var(--s-3); }
.case-field { display: flex; flex-direction: column; gap: var(--s-1); }
.case-label { font: 600 var(--t-xs)/1.2 var(--font); color: var(--ink-3); }
.case-value {
  display: block; font: 400 var(--t-sm)/1.5 var(--mono); color: var(--ink);
  padding: var(--s-2) var(--s-3); background: var(--sunken); border-radius: var(--r-sm);
  white-space: pre-wrap; overflow-wrap: anywhere;
}
.case-value.is-pass { box-shadow: inset 3px 0 0 var(--sig-clear); }
.case-value.is-fail { box-shadow: inset 3px 0 0 var(--sig-fault); }
.case-error {
  margin: 0; font: 400 var(--t-sm)/1.5 var(--mono); padding: var(--s-2) var(--s-3);
  border-radius: var(--r-sm); background: var(--sig-fault-wash); color: var(--sig-fault); white-space: pre-wrap;
}
.case-why { font-size: var(--t-sm); color: var(--ink-2); }
.result-head { font: 700 var(--t-md)/1.3 var(--font); }
.result-head.is-pass { color: var(--sig-clear); }
.result-head.is-fail { color: var(--sig-fault); }
@media (prefers-reduced-motion: reduce) {
  .case-chip, .checks-splitter::after { transition: none; }
}

/* The column's gap lives here, not inline, so the full-screen problem view can
   drop it (its divider already spaces the editor and the checks). */
#rightWorkspaceWrapper { gap: var(--s-4); }

/* ── EXAMPLE VERDICTS in the statement ───────────────────────────
   After a run the example cards on the left carry the result: a tally
   above them ("2 / 3 example test cases passed") and a Passed / Failed
   mark on each card. Signal inks: this is the student's code's outcome. */
.studio-tally {
  margin-top: var(--s-5); padding: var(--s-2) var(--s-4); border-radius: var(--r);
  font-size: var(--t-sm); font-weight: 600;
}
.studio-tally strong { font-size: var(--t-md); }
.studio-tally[data-state="pass"] { background: var(--sig-clear-wash); color: var(--sig-clear); }
.studio-tally[data-state="fail"] { background: var(--sig-fault-wash); color: var(--sig-fault); }
.studio-example-title { display: flex; align-items: center; gap: var(--s-2); }
.studio-example-mark { margin-left: auto; padding: 3px var(--s-2); border-radius: var(--r-pill); font-size: var(--t-xs); }
.studio-example-mark:empty { display: none; }
.studio-example[data-state="pass"] { box-shadow: inset 3px 0 0 var(--sig-clear); }
.studio-example[data-state="fail"] { box-shadow: inset 3px 0 0 var(--sig-fault); }
.studio-example[data-state="pass"] .studio-example-mark { background: var(--sig-clear-wash); color: var(--sig-clear); }
.studio-example[data-state="fail"] .studio-example-mark { background: var(--sig-fault-wash); color: var(--sig-fault); }
.studio-example[data-state="skip"] .studio-example-mark { background: var(--surface); color: var(--ink-3); }

/* ── LEVEL ASSESSMENT ────────────────────────────────────────────
   Reuses .modal-scrim / .modal-card. A bar for where the student is, the
   code of the question in --mono, and options with a letter. The level is
   earned: --accent. */
.quiz-card { max-width: 640px; max-height: calc(100vh - var(--s-12)); overflow-y: auto; }
.quiz-head { display: flex; align-items: center; justify-content: space-between; gap: var(--s-3); margin-bottom: var(--s-5); }
.modal-card h2.quiz-title { margin: 0; }
.quiz-meta { display: flex; align-items: baseline; justify-content: space-between; gap: var(--s-3); font-size: var(--t-sm); }
.quiz-count { color: var(--ink-3); }
.quiz-bar { height: 6px; margin: var(--s-2) 0 var(--s-5); border-radius: var(--r-pill); background: var(--sunken); overflow: hidden; }
.quiz-bar span { display: block; height: 100%; width: 0; background: var(--brand); transition: width var(--med) var(--ease); }
.modal-card p.quiz-question { margin: 0 0 var(--s-4); font: 600 var(--t-md)/1.45 var(--font); color: var(--ink); max-width: none; }
.quiz-code {
  margin: 0 0 var(--s-4); padding: var(--s-3) var(--s-4); border-radius: var(--r-sm);
  background: var(--sunken); border: 1px solid var(--line);
  font: 400 var(--t-sm)/1.6 var(--mono); color: var(--ink); white-space: pre; overflow-x: auto;
}
.quiz-options { display: flex; flex-direction: column; gap: var(--s-2); }
.quiz-option {
  display: flex; align-items: flex-start; gap: var(--s-3); width: 100%; text-align: left;
  padding: var(--s-3) var(--s-4); border-radius: var(--r); cursor: pointer;
  background: var(--surface); border: 1px solid var(--line); color: var(--ink);
  font: 500 var(--t-sm)/1.5 var(--font);
  transition: border-color var(--fast) var(--ease), background var(--fast) var(--ease);
}
.quiz-option:hover { border-color: var(--brand); background: var(--raised); }
.quiz-letter {
  flex: 0 0 auto; width: 24px; height: 24px; display: inline-grid; place-items: center;
  border-radius: var(--r-pill); background: var(--sunken); color: var(--ink-2); font: 600 var(--t-xs)/1 var(--font);
}
.quiz-option-text { min-width: 0; overflow-wrap: anywhere; padding-top: 2px; }
.quiz-option.is-code .quiz-option-text { font-family: var(--mono); white-space: pre-wrap; }
.modal-card p.quiz-note { margin: var(--s-4) 0 0; font-size: var(--t-sm); color: var(--ink-2); }
.quiz-result { text-align: center; }
.modal-card p.quiz-result-label { margin: 0; font-size: var(--t-sm); color: var(--ink-3); max-width: none; }
.modal-card p.quiz-level { margin: var(--s-1) 0 var(--s-5); font: 800 var(--t-2xl)/1.1 var(--display); color: var(--accent); max-width: none; }
.quiz-result .btn { margin-top: var(--s-5); }
@media (prefers-reduced-motion: reduce) {
  .quiz-bar span, .quiz-option { transition: none; }
}

/* ── PROFILE DASHBOARD ───────────────────────────────────────────
   The avatar opens this as a full-width view, not an overlay: a
   400px drawer cannot hold a calendar and four charts without
   turning into a list of numbers, which is what it was.

   Colour discipline, and the reason the page reads as one thing:
     --accent  every earned quantity — solves, the calendar, the
               tier ladder. Never a control.
     --brand   the things you may click, and nothing else.
     signal    only where the subject is the student's code: the
               fault breakdown, a passed assessment section.
   Neutrals carry the rest. */
.pf { display: flex; flex-direction: column; gap: var(--s-5); }
#pfBody { display: flex; flex-direction: column; gap: var(--s-5); }

.pf-card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-sm);
  padding: var(--s-6);
}
.pf-head { margin-bottom: var(--s-5); }
.pf-title {
  font-family: var(--font); font-size: var(--t-md); font-weight: 700;
  color: var(--ink); letter-spacing: -.012em; line-height: 1.3;
}
.pf-sub { font-size: var(--t-sm); color: var(--ink-3); margin-top: var(--s-1); max-width: 56ch; }
.pf-subhead {
  font-family: var(--font); font-size: var(--t-xs); font-weight: 700;
  text-transform: uppercase; letter-spacing: .08em; color: var(--ink-3);
  margin: var(--s-6) 0 var(--s-4);
  padding-top: var(--s-5); border-top: 1px solid var(--line);
}
.pf-meta { font-size: var(--t-xs); color: var(--ink-3); }

.pf-error {
  display: flex; align-items: center; gap: var(--s-4);
  padding: var(--s-4) var(--s-5);
  border: 1px solid var(--sig-fault-line); border-radius: var(--r);
  background: var(--sig-fault-wash); color: var(--sig-fault);
}
.pf-error svg { width: 20px; height: 20px; flex: none; }
.pf-error p { flex: 1; font-size: var(--t-sm); font-weight: 600; }

/* ── WHO ──
   Avatar, name, address, level. Three lines and no more: tier, join date
   and goal sit on the cards that are about them. */
.pf-hero {
  display: flex; align-items: center; gap: var(--s-5); flex-wrap: wrap;
  padding-bottom: var(--s-5); border-bottom: 1px solid var(--line);
}
.pf-avatar {
  width: 88px; height: 88px; flex: none;
  display: grid; place-items: center; border-radius: var(--r-pill);
  background: var(--brand); color: var(--brand-on);
  font-family: var(--display); font-size: var(--t-2xl); font-weight: 800;
  line-height: 1; box-shadow: var(--shadow-brand);
}
.pf-who-text { min-width: 0; }
#pfName { font-size: var(--t-2xl); color: var(--ink); }
.pf-email { font-size: var(--t-sm); color: var(--ink-3); margin-top: 2px; }
.pf-level { font-size: var(--t-sm); color: var(--ink-3); margin-top: var(--s-2); }
.pf-level span { font-weight: 600; color: var(--ink-2); }
.pf-chiprow { display: flex; align-items: center; gap: var(--s-2); flex-wrap: wrap; margin-top: var(--s-3); }
.pf-aim { font-size: var(--t-sm); color: var(--ink-2); margin-top: var(--s-3); }
.pf-aim strong { color: var(--ink); font-weight: 600; }
.pf-aim:empty { display: none; }

/* ── THE FOUR NUMBERS ──
   A single card split by rules rather than four floating boxes: they
   are one reading of one student, so they sit on one surface. */
.pf-figures {
  display: grid; grid-template-columns: repeat(4, 1fr);
  background: var(--surface);
  border: 1px solid var(--line); border-radius: var(--r-lg);
  box-shadow: var(--shadow-sm); overflow: hidden;
}
.pf-figure { padding: var(--s-5) var(--s-6); border-left: 1px solid var(--line); }
.pf-figure:first-child { border-left: 0; }
.pf-figure dt {
  font-size: var(--t-xs); font-weight: 700; text-transform: uppercase;
  letter-spacing: .08em; color: var(--ink-3);
}
.pf-figure dd {
  font-family: var(--mono); font-size: var(--t-2xl); font-weight: 700;
  color: var(--ink); line-height: 1.15; margin-top: var(--s-2);
  font-variant-numeric: tabular-nums;
}
.pf-figure dd .pf-unit { font-size: var(--t-lg); color: var(--ink-3); margin-left: 1px; }
/* Earned, so it takes the accent — but only once it is above zero.
   A bright 0 reads as a score you lost, not a number you have not made yet. */
.pf-figure.is-earned dd { color: var(--accent); }
.pf-fig-sub { font-size: var(--t-xs); color: var(--ink-3); margin-top: var(--s-1); }

/* ── PRACTICE CALENDAR ── */
.pf-heat-scroll { overflow-x: auto; padding-bottom: var(--s-2); }
.pf-heat {
  display: grid; grid-auto-flow: column;
  grid-template-rows: auto repeat(7, 13px);
  grid-auto-columns: 13px; gap: 3px;
  width: max-content;
}
.pf-heat .pf-month {
  grid-row: 1; font-size: 10px; font-weight: 600; color: var(--ink-3);
  white-space: nowrap; line-height: 1; padding-bottom: var(--s-1);
}
.pf-cell {
  display: block; width: 13px; height: 13px;
  border-radius: 3px; background: var(--heat-0);
}
.pf-cell.l1 { background: var(--heat-1); }
.pf-cell.l2 { background: var(--heat-2); }
.pf-cell.l3 { background: var(--heat-3); }
.pf-cell.l4 { background: var(--heat-4); }
/* Today, so you can find the end of the run at a glance. */
.pf-cell.is-today { box-shadow: 0 0 0 1.5px var(--ink-3); }
.pf-heat-foot {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--s-4); flex-wrap: wrap; margin-top: var(--s-4);
}
.pf-legend { display: flex; align-items: center; gap: var(--s-1); }
.pf-legend .pf-meta { margin: 0 var(--s-1); }

/* ── ROWS ── */
.pf-row { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s-5); align-items: start; }
.pf-row-2-1 { grid-template-columns: 1.4fr 1fr; }

/* ── TIER LADDER ──
   Three stops on one line. The rule between them fills to show how far
   into the current tier the student is. */
.pf-ladder { display: flex; list-style: none; }
.pf-ladder li { flex: 1; position: relative; padding-top: var(--s-6); text-align: center; }
.pf-ladder li::before {
  content: ""; position: absolute; top: 5px; left: 50%; right: -50%;
  height: 3px; background: var(--sunken); border-radius: var(--r-pill);
}
.pf-ladder li:last-child::before { display: none; }
/* The filled part of the run behind the dot. */
.pf-ladder li::after {
  content: ""; position: absolute; top: 5px; left: 50%;
  width: var(--fill, 0%); max-width: 100%;
  height: 3px; background: var(--accent); border-radius: var(--r-pill);
  transition: width 600ms var(--ease);
}
.pf-ladder li:last-child::after { display: none; }
.pf-dot {
  position: absolute; top: 0; left: 50%; transform: translateX(-50%);
  width: 13px; height: 13px; border-radius: var(--r-pill);
  background: var(--surface); border: 3px solid var(--line-strong); z-index: 1;
}
.pf-ladder li.done .pf-dot { background: var(--accent); border-color: var(--accent); }
.pf-ladder li.now .pf-dot {
  background: var(--surface); border-color: var(--accent);
  box-shadow: 0 0 0 4px var(--accent-wash);
}
.pf-ladder-name { font-size: var(--t-sm); font-weight: 600; color: var(--ink-3); }
.pf-ladder li.now .pf-ladder-name, .pf-ladder li.done .pf-ladder-name { color: var(--ink); }

.pf-gate { margin-top: var(--s-6); }
.pf-gate-line {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: var(--s-3); margin-bottom: var(--s-2);
}
.pf-gate-line span:first-child { font-size: var(--t-sm); color: var(--ink-2); }
.pf-gate-line span:last-child {
  font-family: var(--mono); font-size: var(--t-xs); font-weight: 700; color: var(--ink);
}
.pf-gate .meter-fill { background: var(--accent); }
.pf-gate-note { font-size: var(--t-sm); color: var(--ink-3); margin-top: var(--s-3); }

/* ── ASSESSMENT SECTIONS ── */
.pf-bars { display: flex; flex-direction: column; gap: var(--s-3); }
.pf-bar { display: grid; grid-template-columns: 1fr auto; gap: var(--s-2) var(--s-4); align-items: center; }
.pf-bar-label { font-size: var(--t-sm); color: var(--ink-2); }
.pf-bar-val {
  font-family: var(--mono); font-size: var(--t-xs); font-weight: 700;
  color: var(--ink); font-variant-numeric: tabular-nums;
}
.pf-bar-track {
  grid-column: 1 / -1; height: 8px; border-radius: var(--r-pill);
  background: var(--sunken); overflow: hidden;
}
.pf-bar-fill {
  height: 100%; width: 100%; border-radius: var(--r-pill);
  background: var(--ink-3);
  transform: scaleX(0); transform-origin: left center;
  transition: transform 600ms var(--ease);
}
/* A passed section is a verdict on work, so it takes a signal ink. */
.pf-bar.passed .pf-bar-fill { background: var(--sig-clear); }
.pf-bar.passed .pf-bar-val { color: var(--sig-clear); }
.pf-bar.fault .pf-bar-fill { background: var(--sig-fault); }
.pf-bar-note { font-size: var(--t-sm); color: var(--ink-3); margin-top: var(--s-4); }

/* ── HOURS ──
   24 columns, one per hour, so the shape of a day is the shape of the
   chart. Bars are baseline-aligned in a fixed box. */
.pf-clock, .pf-clock-axis { display: grid; grid-template-columns: repeat(24, 1fr); gap: 2px; }
.pf-clock { align-items: end; height: 92px; }
.pf-clock-axis {
  padding-top: var(--s-2); margin-top: var(--s-2);
  border-top: 1px solid var(--line);
  font-family: var(--mono); font-size: 10px; color: var(--ink-3);
}
/* Four ticks on the same 24 columns as the bars, each left-aligned at the
   hour it marks, so they hold at any width. */
.pf-hour {
  border-radius: 2px 2px 0 0; background: var(--accent);
  min-height: 2px; transform-origin: bottom;
  animation: pf-grow 500ms var(--ease) both;
}
.pf-hour.empty { background: var(--sunken); animation: none; }
@keyframes pf-grow { from { transform: scaleY(0); } to { transform: scaleY(1); } }

.pf-facts { list-style: none; margin-top: var(--s-5); display: flex; flex-direction: column; gap: var(--s-3); }
.pf-facts li {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: var(--s-4); font-size: var(--t-sm); color: var(--ink-2);
}
.pf-facts b {
  font-family: var(--mono); font-weight: 700; color: var(--ink);
  font-variant-numeric: tabular-nums; text-align: right;
}

/* ── RECENT ACTIVITY ── */
.pf-feed { display: flex; flex-direction: column; }
.pf-item {
  display: grid; grid-template-columns: 26px 1fr auto;
  align-items: center; gap: var(--s-3);
  width: 100%; padding: var(--s-3) var(--s-2);
  font: inherit; text-align: left; color: var(--ink-2);
  background: transparent; border: 0; border-top: 1px solid var(--line);
  border-radius: var(--r-sm); cursor: pointer;
  transition: background var(--fast), color var(--fast);
}
.pf-item:first-child { border-top: 0; }
.pf-item:hover { background: var(--sunken); color: var(--ink); }
.pf-item-icon { display: grid; place-items: center; }
.pf-item-icon svg { width: 18px; height: 18px; }
.pf-item.solve .pf-item-icon { color: var(--sig-clear); }
.pf-item.fix .pf-item-icon { color: var(--ink-3); }
.pf-item-text {
  min-width: 0; font-size: var(--t-sm);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.pf-item-text b { font-weight: 600; color: var(--ink); }
.pf-item-when { font-size: var(--t-xs); color: var(--ink-3); white-space: nowrap; }

/* ── FAULTS AND LANGUAGES ── */
.pf-split { display: flex; height: 10px; border-radius: var(--r-pill); overflow: hidden; background: var(--sunken); }
.pf-split span { height: 100%; flex: none; }
.pf-keys { display: flex; flex-wrap: wrap; gap: var(--s-2) var(--s-5); margin-top: var(--s-4); }
.pf-key { display: flex; align-items: center; gap: var(--s-2); font-size: var(--t-sm); color: var(--ink-2); }
.pf-key i { width: 9px; height: 9px; border-radius: 2px; flex: none; }
.pf-key b { font-family: var(--mono); font-weight: 700; color: var(--ink); font-variant-numeric: tabular-nums; }

/* ── EMPTY STATES ──
   Every panel has one. A card with nothing in it and no words is how a
   broken query hides. */
.pf-blank { padding: var(--s-6) 0; text-align: center; }
.pf-blank p { font-size: var(--t-sm); color: var(--ink-3); max-width: 44ch; margin: 0 auto; }
.pf-blank p + .btn { margin-top: var(--s-4); }
.pf-empty { text-align: center; padding: var(--s-9) var(--s-6); }
.pf-empty .empty-icon { margin: 0 auto var(--s-4); }
.pf-empty .empty-sub { margin: var(--s-2) auto var(--s-5); }

/* ── EDIT PROFILE ──
   Two columns of fields on one card. The label sits above its control so a
   long question does not squeeze the input, and the whole thing collapses to
   one column before the fields get narrow enough to be awkward. */
.pf-edit { display: block; }

.pf-fields { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s-5); }
.pf-field { display: flex; flex-direction: column; gap: var(--s-2); min-width: 0; border: 0; padding: 0; }
.pf-wide { grid-column: 1 / -1; }
.pf-field > label, .pf-field > legend {
  font-size: var(--t-sm); font-weight: 600; color: var(--ink-2); padding: 0;
}
.pf-field input, .pf-field select { width: 100%; }
.pf-opt { font-weight: 500; color: var(--ink-3); }
.pf-hint { font-size: var(--t-xs); color: var(--ink-3); }

.pf-checks { display: flex; flex-wrap: wrap; gap: var(--s-2); }
.pf-check {
  display: inline-flex; align-items: center; gap: var(--s-2);
  padding: var(--s-2) var(--s-4); min-height: 40px;
  font-size: var(--t-sm); color: var(--ink-2);
  background: var(--surface); border: 1px solid var(--line-strong);
  border-radius: var(--r-pill); cursor: pointer;
  transition: border-color var(--fast), background var(--fast), color var(--fast);
}
.pf-check:hover { border-color: var(--brand-line); }
.pf-check input { width: auto; accent-color: var(--brand); cursor: pointer; }
.pf-check:has(input:checked) {
  background: var(--brand-wash); border-color: var(--brand-line); color: var(--brand);
}

.pf-form-error {
  margin-top: var(--s-5); padding: var(--s-3) var(--s-4);
  font-size: var(--t-sm); font-weight: 600;
  color: var(--sig-fault); background: var(--sig-fault-wash);
  border: 1px solid var(--sig-fault-line); border-radius: var(--r-sm);
}
.pf-form-actions {
  display: flex; gap: var(--s-3); flex-wrap: wrap;
  margin-top: var(--s-6); padding-top: var(--s-5); border-top: 1px solid var(--line);
}

@media (max-width: 760px) {
  .pf-fields { grid-template-columns: 1fr; }
}

/* ── FOOT ── */
.pf-foot { text-align: center; padding-bottom: var(--s-4); }

@media (max-width: 1080px) {
  .pf-row, .pf-row-2-1 { grid-template-columns: 1fr; }
  .pf-figures { grid-template-columns: 1fr 1fr; }
  .pf-figure:nth-child(3) { border-left: 0; }
  .pf-figure:nth-child(n+3) { border-top: 1px solid var(--line); }
}
@media (max-width: 640px) {
  .pf-hero { align-items: flex-start; }
  .pf-figures { grid-template-columns: 1fr; }
  .pf-figure { border-left: 0; border-top: 1px solid var(--line); }
  .pf-figure:first-child { border-top: 0; }
}
