/* ─────────────────────────────────────────────────────────────────────────
   PLATFORM — a learning tool people would trust with their evenings.

   What stays from earlier passes, because it is substance rather than costume:
   rustc's diagnostic structure, the caret that points at the failing span, the
   Rust-notation chapter marks, no emoji, no confetti.

   What goes: the terminal cosplay. Inverted ncurses title bars, a vim status
   line, `^R` keybinding shorthand and `rm -rf` jokes are in-jokes, and an
   in-joke is the opposite of credible. Plain language, real hierarchy,
   comfortable density, restrained colour.
   ───────────────────────────────────────────────────────────────────────── */

:root {
  /* Neutrals do the work; the accent is rationed. */
  --bg:        #0f1216;
  --inset:     #14181d;
  --panel:     #171b21;
  --panel-hi:  #1e242c;
  --edge:      #2b323c;
  --edge-soft: #232830;
  --fg:        #e7eaf0;
  --muted:     #9099a6;
  --rust:      #e2683a;
  --rust-deep: #b8481f;
  --ember:     #f0a07a;
  --on-rust:   #10130f;

  --red:   #e5484d;
  --gold:  #d9a01e;
  --green: #37a06a;
  --info:  #4c8dfa;

  --shadow-sm: 0 1px 2px rgb(0 0 0 / .28);
  --shadow-lg: 0 12px 32px rgb(0 0 0 / .45);
  --r: 6px;
  --r-lg: 10px;

  color-scheme: dark;
}

:root[data-theme="light"] {
  --bg:        #ffffff;
  --inset:     #fbfcfd;
  --panel:     #f6f7f9;
  --panel-hi:  #eceef2;
  --edge:      #dfe3e9;
  --edge-soft: #eaedf1;
  --fg:        #171a1f;
  --muted:     #5d6673;
  --rust:      #c1481c;
  --rust-deep: #9c3712;
  --ember:     #8a4526;
  --on-rust:   #ffffff;

  --red:   #c62a2f;
  --gold:  #8a6200;
  --green: #17794c;
  --info:  #1a56c4;

  --shadow-sm: 0 1px 2px rgb(16 24 40 / .06);
  --shadow-lg: 0 16px 40px rgb(16 24 40 / .14);
  color-scheme: light;
}

/* ── surfaces have a slight radius and real depth, not hard offsets ─────── */
.btn, .chip, .hud-dock, .problem-wrap, .trophy, input { border-radius: var(--r) }
dialog { border-radius: var(--r-lg) }
.btn { box-shadow: var(--shadow-sm) }
dialog { box-shadow: var(--shadow-lg); border: 1px solid var(--edge) }

/* ── one hairline between regions; the panels carry the separation ─────── */
header, aside, .taskbar, .runbar, nav details, #out, .grip {
  border-color: var(--edge);
  border-width: 1px;
}
header {
  border-bottom: 1px solid var(--edge);
  background: var(--panel);
  padding: .6rem 1rem;
  display: flex;
  align-items: center;
}
.header-search {
  display: inline-flex;
  align-items: center;
  gap: 0.75rem;
  background: var(--inset);
  border: 1px solid var(--edge);
  border-radius: var(--r);
  color: var(--muted);
  font-family: var(--font-ui);
  font-size: 0.78rem;
  font-weight: 500;
  padding: 0.3rem 0.8rem;
  width: min(18rem, 24vw);
  cursor: pointer;
  box-shadow: none;
  transition: border-color 0.12s ease, background 0.12s ease, color 0.12s ease;
}
.header-search:hover {
  border-color: var(--muted);
  background: var(--panel-hi);
  color: var(--fg);
}
.header-search kbd {
  font-family: var(--font-ui);
  font-size: 0.68rem;
  background: var(--panel);
  border: 1px solid var(--edge);
  border-radius: 4px;
  padding: 0.05rem 0.35rem;
  color: var(--muted);
  margin-left: auto;
}
aside { border-right: 1px solid var(--edge); background: var(--panel) }
.taskbar { background: var(--bg); border-bottom: 1px solid var(--edge); padding: .7rem 1rem; display: flex; align-items: center; }
.runbar { background: var(--panel); border-top: 1px solid var(--edge); padding: .7rem 1rem }
main::before { display: none }        /* the diagnostic spine was cosplay too */
.taskbar, .runbar, #out, .grip, .problem { padding-left: 1rem }

/* ── type: hierarchy from weight and size, not from shouting ───────────── */
body { font: 14px/1.55 var(--font-ui); letter-spacing: 0 }
.brand {
  font-family: var(--font-ui);
  font-size: .95rem;
  font-weight: 700;
  letter-spacing: -.015em;
  text-transform: none;
  background: none;
  color: var(--fg);
  border: 0;
  padding: 0;
}
.brand em { color: var(--rust); font-style: normal }
.taskbar .who {
  background: none;
  color: var(--fg);
  font-family: var(--font-ui);
  font-size: 1rem;
  font-weight: 600;
  letter-spacing: -.01em;
  padding: 0;
}
.taskbar .file, .taskbar .place, .taskbar .mode { font-size: .8rem; color: var(--muted) }
.taskbar .mode { text-transform: none; letter-spacing: 0 }
dialog h2 {
  font-family: var(--font-ui);
  font-size: .95rem;
  font-weight: 600;
  text-transform: none;
  letter-spacing: -.01em;
  color: var(--fg);
}
dialog .top { background: var(--panel); color: var(--fg); border-bottom: 1px solid var(--edge); padding: .8rem 1rem }
dialog .body { padding: 1rem }

/* ── controls: 3-tier hierarchy ────────────────────────────────────────── */
/*  Tier 1 – .btn           Ghost outline (most buttons)
    Tier 2 – .btn.primary   Loud CTA (Run, Next exercise)
    Tier 3 – runbar :not(.primary)  Minimal / toolbar (Raw, Split)        */

.btn {
  background: transparent;
  border: 1px solid var(--edge);
  color: var(--fg);
  font-family: var(--font-ui);
  font-size: .82rem;
  font-weight: 500;
  text-transform: none;
  letter-spacing: 0;
  padding: .35rem .7rem;
  box-shadow: none;
  transition: background .12s ease, border-color .12s ease;
}
.btn:hover:not(:disabled) { background: var(--panel-hi); border-color: var(--muted); color: var(--fg) }
.btn:active:not(:disabled) { transform: none; background: var(--edge) }
.btn:focus-visible { outline: 2px solid var(--info); outline-offset: 2px }
.btn[aria-pressed="true"] { background: var(--rust); border-color: var(--rust); color: var(--on-rust) }

/* The primary CTA button (standardized outline style) */
.btn.primary {
  background: transparent;
  border: 1.5px solid var(--rust);
  color: var(--rust);
  font-weight: 600;
  font-size: .84rem;
  padding: .42rem 1.1rem;
  box-shadow: none;
}
.btn.primary:hover:not(:disabled) {
  background: var(--rust);
  color: var(--on-rust);
  border-color: var(--rust);
}
.btn.primary:active:not(:disabled) {
  background: var(--rust-deep);
  border-color: var(--rust-deep);
  color: var(--on-rust);
}

/* Runbar secondary buttons: minimal toolbar style */
.runbar .btn:not(.primary) {
  border-color: transparent;
  background: transparent;
  color: var(--muted);
  font-size: .8rem;
  box-shadow: none;
}
.runbar .btn:not(.primary):hover:not(:disabled) {
  color: var(--fg);
  background: var(--panel-hi);
  border-color: var(--edge);
}
.runbar .btn:not(.primary)[aria-pressed="true"] {
  color: var(--rust);
  background: color-mix(in srgb, var(--rust) 12%, transparent);
  border-color: var(--edge);
}

/* ── the terminal furniture goes ───────────────────────────────────────── */
.statusline { display: none }
nav summary::before, nav li button::before, .problem::before, .clean::before { content: none }
nav li button.solved::before { content: "✓"; color: var(--green); margin-right: .4rem }
nav li button:not(.solved)::before { content: ""; margin-right: .4rem }
nav li button[aria-current="true"] {
  background: color-mix(in srgb, var(--rust) 12%, transparent);
  color: var(--fg);
  border-left: 2px solid var(--rust);
}
nav summary { padding: .55rem .9rem; font-size: .84rem }
nav summary:hover, nav details[open] > summary { background: var(--panel-hi) }

/* ── chapter marks stay: notation is typography, not costume ────────────── */
nav .badge, .taskbar .badge {
  font-family: var(--font-code);
  font-size: .74rem;
  font-weight: 600;
  color: var(--muted);
  min-width: 3.4ch;
  text-align: center;
}
nav .cleared .badge, nav .cleared .score { color: var(--green) }

/* ── progress: quiet facts, legible at a glance ────────────────────────── */
.xp {
  font-family: var(--font-ui);
  background: none;
  border: 0;
  gap: .55rem;
  font-size: .82rem;
  color: var(--muted);
}
.xp .build-verb { display: none }
.xp .track { width: 96px; height: 6px; border: 0; background: var(--edge); padding: 0; border-radius: 99px }
.xp .fill { background: var(--rust); border-radius: 99px; transition: width .4s ease }
.xp small { color: var(--muted) }
.xp .lv, .xp #level { color: var(--fg); font-weight: 600 }
.xp .milestone { color: var(--muted); border-left: 1px solid var(--edge); padding-left: .6rem }
.chip { background: none; border: 1px solid var(--edge); font-family: var(--font-ui); font-size: .8rem }

/* HUD Dock styling: matching thin borders and rounded corners */
.hud-dock {
  border-radius: var(--r) !important;
  border-width: 1px !important;
  border-color: var(--edge) !important;
  box-shadow: var(--shadow-sm) !important;
  overflow: hidden;
}
.hud-dock > *:not([hidden]) {
  border-left-width: 1px !important;
  border-left-color: var(--edge-soft) !important;
}
.dock-btn:hover:not(:disabled) {
  background: var(--panel-hi) !important;
  color: var(--rust) !important;
}
.dock-btn:active:not(:disabled),
.dock-btn[aria-pressed="true"]:not(:disabled) {
  background: color-mix(in srgb, var(--rust) 10%, transparent) !important;
  color: var(--rust) !important;
}
.dock-btn:disabled {
  opacity: 0.45 !important;
  cursor: not-allowed !important;
  pointer-events: none !important;
}

/* ── results keep rustc's structure, in a readable card ────────────────── */
#out {
  background: var(--bg);
  transition: height 0.15s cubic-bezier(0.2, 0.8, 0.2, 1), width 0.15s cubic-bezier(0.2, 0.8, 0.2, 1);
}
body.dragging #out {
  transition: none !important;
}
.problem {
  border-left: 3px solid var(--red);
  border-bottom: 1px solid var(--edge-soft);
  padding: .7rem 1rem;
  font-family: var(--font-ui);
  font-size: .86rem;
}
.problem.warning { border-left-color: var(--gold) }
.problem.test { border-left-color: var(--info) }
.problem .what { font-weight: 600 }
.problem .code {
  font-family: var(--font-code);
  font-size: .74rem;
  font-weight: 600;
  padding: .1rem .35rem;
  border-radius: 4px;
  background: color-mix(in srgb, var(--red) 14%, transparent);
  border: 0;
  color: var(--red);
}
.problem.warning .code { background: color-mix(in srgb, var(--gold) 14%, transparent); color: var(--gold) }
.problem.test .code { background: color-mix(in srgb, var(--info) 14%, transparent); color: var(--info) }
.problem .where { text-transform: none; letter-spacing: 0; font-size: .78rem }
.problem .label, .problem .help { font-family: var(--font-code); font-size: .78rem }
.clean { border-left: 3px solid var(--green); padding: .8rem 1rem; font-weight: 600 }

/* ── rewards: acknowledged, not celebrated ─────────────────────────────── */
#milestonedlg { background: var(--bg); color: var(--fg); border: 1px solid var(--edge); width: min(30rem, 92vw) }
#milestonedlg .top { background: var(--panel); color: var(--fg); justify-content: flex-start }
#milestonedlg .top h2 { font-family: var(--font-ui); font-size: .95rem; letter-spacing: -.01em }
#milestonedlg .body { background: var(--bg); text-align: left; padding: 1.2rem }
#milestonedlg .mile-icon {
  font-family: var(--font-code);
  font-size: 1.6rem;
  font-weight: 600;
  color: var(--rust);
  margin: 0 0 .5rem;
}
#milestonedlg .mile-head {
  font-family: var(--font-ui);
  font-size: 1.15rem;
  font-weight: 600;
  letter-spacing: -.015em;
  color: var(--fg);
  line-height: 1.35;
}
#milestonedlg .mile-sub { font-family: var(--font-ui); color: var(--muted); font-size: .88rem; opacity: 1 }
#milestonedlg .progress-facts { background: var(--panel); border: 1px solid var(--edge); border-radius: var(--r); color: var(--fg) }
#milestonedlg .progress-facts dt { color: var(--muted); opacity: 1; text-transform: none; letter-spacing: 0; font-weight: 400 }
#milestonedlg .progress-facts dd, #milestonedlg .progress-facts b { color: var(--fg) }
#milestonedlg .primary {
  background: var(--rust); color: var(--on-rust); border: 0;
  font-family: var(--font-ui); font-size: .86rem; font-weight: 600; line-height: 1.6; padding: .45rem 1rem;
}
#milestonedlg::backdrop, dialog::backdrop { background: rgb(8 10 14 / .6) }

.toast {
  background: var(--panel);
  border: 1px solid var(--edge);
  border-left: 3px solid var(--rust);
  border-radius: var(--r);
  box-shadow: var(--shadow-lg);
  color: var(--fg);
  font-family: var(--font-ui);
}
.toast b { color: var(--fg); font-family: var(--font-ui); font-size: .88rem; font-weight: 600; line-height: 1.4 }
.toast em { color: var(--muted) }
.toast .icon { font-family: var(--font-code); font-size: .78rem; color: var(--rust); font-weight: 600 }

.trophy { background: var(--panel); border: 1px solid var(--edge); border-radius: var(--r) }
.trophy.won { background: var(--panel); border-color: var(--rust) }
.trophy .icon { font-family: var(--font-code); font-size: .72rem; color: var(--muted); font-weight: 600 }
.trophy.won .icon { color: var(--rust) }
.trophy.won b { color: var(--fg); font-family: var(--font-ui); font-size: .84rem; line-height: 1.4 }
.trophy b { font-family: var(--font-ui); font-size: .84rem }

/* ── the caret survives every redesign: it is the one exact thing ──────── */
#mark { border-bottom: 2px solid var(--rust); background: color-mix(in srgb, var(--rust) 12%, transparent) }

.bracket-match {
  background: color-mix(in srgb, var(--info) 22%, transparent);
  border-bottom: 1.5px solid var(--info);
  border-radius: 2px;
}

.ferris { color: var(--rust) }
:focus-visible { outline: 2px solid var(--info); outline-offset: 2px }

/* ─────────────────────────────────────────────────────────────────────────
   LIGHT MODE, PROPERLY

   The first pass reused the dark structure with inverted greys, which left
   three problems: the editor surface and the page were both effectively
   white so the code had no ground of its own; the syntax palette was still
   the one tuned for a dark background; and the borders were so light that
   the structure disappeared.

   Every colour below was measured against its actual background. Nothing
   sits under 4.5:1, so the code stays readable on a bright screen for an
   hour, which is the real test for a learning tool.
   ───────────────────────────────────────────────────────────────────────── */

:root[data-theme="light"] {
  /* Warm Alabaster theme: soft sand background, reduced glare, excellent contrast */
  --bg:        #faf9f5;   /* softest warm off-white content ground */
  --inset:     #f4f2eb;   /* warm paper background for the code editor */
  --panel:     #ebe8df;   /* chrome: header, rail, run bar */
  --panel-hi:  #e0dbcf;   /* hover elements */
  --edge:      #ccc6b8;   /* soft visible structure borders */
  --edge-soft: #dfd9cd;
  --fg:        #2d3035;   /* soft charcoal main text */
  --muted:     #5e626a;   /* secondary text */
  --gutter:    #9ba0a9;   /* line numbers */

  --rust:      #b84218;   /* themed primary color */
  --rust-deep: #95330e;
  --ember:     #8a4526;
  --on-rust:   #ffffff;

  --red:   #c1272c;
  --gold:  #875f00;
  --green: #157448;
  --info:  #1852bd;

  /* Eye-friendly Syntax Palette on Warm Paper */
  --t-kw:  #a73c16;       /* keyword */
  --t-str: #1c6b45;       /* string literal */
  --t-typ: #2458aa;       /* type */
  --t-com: #78818f;       /* comment */
  --t-num: #805000;       /* number literal */
  --t-fnc: #5c4033;       /* function */
  --t-att: #7a3cb5;       /* attribute */
  --t-mac: #855b00;       /* macro */

  --shadow-sm: 0 1px 2px rgb(28 32 38 / .06);
  --shadow-lg: 0 16px 40px rgb(28 32 38 / .12);
}

/* The token names the base sheet uses for two of these differ; keep both in step. */
:root[data-theme="light"] .t-kw  { color: var(--t-kw) }
:root[data-theme="light"] .t-mac { color: var(--t-mac) }
:root[data-theme="light"] .t-str { color: var(--t-str) }
:root[data-theme="light"] .t-typ { color: var(--t-typ) }

/* Tints need more weight on white than they do on black to read at all. */
:root[data-theme="light"] #mark {
  background: color-mix(in srgb, var(--rust) 16%, transparent);
}
:root[data-theme="light"] .problem .code { background: color-mix(in srgb, var(--red) 12%, transparent) }
:root[data-theme="light"] .problem.warning .code { background: color-mix(in srgb, var(--gold) 16%, transparent) }
:root[data-theme="light"] .problem.test .code { background: color-mix(in srgb, var(--info) 12%, transparent) }
:root[data-theme="light"] nav li button[aria-current="true"] {
  background: color-mix(in srgb, var(--rust) 9%, transparent);
}

/* A white-on-white editor needs its own edge to exist. */
:root[data-theme="light"] .editor { background: var(--inset); border-top: 1px solid var(--edge-soft) }
:root[data-theme="light"] .gutter { border-right: 1px solid var(--edge-soft) }
:root[data-theme="light"] #out { background: var(--panel) !important; color: var(--fg) !important; border-top: 1px solid var(--edge) !important; }
:root[data-theme="light"] dialog::backdrop { background: rgb(26 29 35 / .35) }
:root[data-theme="light"] ::selection { background: color-mix(in srgb, var(--rust) 24%, transparent); color: inherit }

/* ─────────────────────────────────────────────────────────────────────────
   ZOOM AND DENSITY

   The layout is a full-height grid with `overflow: hidden`, which is right
   at normal zoom and dangerous past it: once the fixed chrome is taller than
   the viewport, the editor gets whatever is left, which can be nothing, and
   there is no scrollbar to rescue you.

   So: the chrome gets smaller as vertical room runs out, the editor keeps a
   floor it will not go below, and lengths are set in rem so they track the
   reader's font size rather than ignoring it.
   ───────────────────────────────────────────────────────────────────────── */

/* Lengths in rem: browser zoom scales px too, but a reader who has simply set
   a larger default font size gets nothing from px. The rail is an overlay, so
   its width is its own business — body stays a rows-only grid. */
aside { width: min(20rem, 82vw) }
.xp .track { width: 6rem }
.grip::after { width: 2.4rem }
.grip:hover::after, .grip:focus-visible::after { width: 3.6rem }

/* main holds six rows: task bar, hint panel, editor, run bar, grip, results.
   Normally the editor takes the slack and the results size to their content.
   Expanded, the roles swap. Neither state names an absolute height, so no row
   can ever be squeezed to nothing and spill its contents over its neighbour. */
main {
  grid-template-rows: auto auto minmax(2rem, 1fr) auto auto auto;
  height: 100%;
  min-height: 0;
  overflow: hidden;
}

.taskbar   { grid-row: 1 }
#hintsteps { grid-row: 2 }
.editor    { grid-row: 3 }
.runbar    { grid-row: 4 }
.grip      { grid-row: 5 }
#out       { grid-row: 6 }

/* Placed explicitly, never auto-placed: the tab strip, the grip and the results
   all hide themselves, and with auto-placement every one of them disappearing
   shifted the rows below it up by one — which is how the task bar ended up
   holding the space meant for the editor. An empty row collapses to zero, so
   fixed positions cost nothing and cannot drift. */

@media (max-height: 460px) {
  .taskbar .file, .taskbar .mode { display: none }
  main { grid-template-rows: auto auto minmax(3.5rem, 1fr) auto auto auto }
  #out { max-height: 26vh }
}

/* Narrow — including a wide screen zoomed a long way in, since zoom shrinks
   the CSS viewport. */
@media (max-width: 860px) {
  aside { width: min(20rem, 88vw) }
}

/* Last resort: if the chrome genuinely cannot fit, let the page scroll rather
   than clipping the editor out of existence. */
@media (max-height: 380px), (max-width: 380px) {
  body { overflow: auto; height: auto; min-height: 100% }
  main { grid-template-rows: auto auto minmax(14rem, auto) auto auto auto }
}


/* ─────────────────────────────────────────────────────────────────────────
   THE EXERCISE DRAWER

   Overlaying is right — the list is a place you visit, not a permanent tax on
   the width. But an overlay with nothing behind it reads as a rendering fault:
   the code stays at full contrast and competes with the list on top of it.
   A scrim makes the same layout read as deliberate, and gives you somewhere
   obvious to click to dismiss.
   ───────────────────────────────────────────────────────────────────────── */

aside {
  border-right: 1px solid var(--edge);
  box-shadow: var(--shadow-lg);
  z-index: 15;
}

/* ─────────────────────────────────────────────────────────────────────────
   PINNED SIDEBAR

   Obsidian's sidebar is pinned, resizable and remembered — it does not float
   over what you are reading. For a course you work through in order, that is
   plainly better than a drawer: the list is context, not an interruption.

   It stays a drawer on a narrow screen, where there is no width to give away.
   ───────────────────────────────────────────────────────────────────────── */

@media (min-width: 900px) {
  body aside {
    position: static;
    width: auto;
    /* height: auto let the column grow to fit 94 exercises and push the page
       past the viewport, which `overflow: hidden` then made unscrollable.
       It takes its grid row and scrolls its own contents instead. */
    height: 100%;
    min-height: 0;
    overflow: hidden;
    display: grid;
    grid-template-rows: auto minmax(0, 1fr);
    box-shadow: none;
    border-right: 1px solid var(--edge);
  }
  body aside nav { min-height: 0; overflow-y: auto }
  /* While the divider is held, the results track loses its minimum so the panel
     follows the pointer right down to nothing instead of sticking at 16rem and
     then disappearing all at once. */
  body.split.dragging:not(.out-collapsed) main {
    grid-template-columns: minmax(0, 1fr) 7px var(--out-w, 34%) !important;
  }
  body.split main {
    grid-template-columns: minmax(0, 1fr) 7px minmax(16rem, var(--out-w, 34%));
    grid-template-rows: auto auto minmax(6rem, 1fr) auto;
  }
  body.split .taskbar    { grid-column: 1 / -1; grid-row: 1 }
  body.split #hintsteps  { grid-column: 1 / -1; grid-row: 2 }
  body.split .editor     { grid-column: 1;      grid-row: 3 }
  body.split #out        { grid-column: 3;      grid-row: 3 / -1 }
  body.split .runbar     { grid-column: 1;      grid-row: 4 }

  /* The divider turns with the layout: drag it sideways to size the results. */
  body.split .grip {
    display: block;
    padding: 0;
    grid-column: 2;
    grid-row: 3 / -1;
    width: 100%;
    height: auto;
    cursor: ew-resize;
    z-index: 10;
    background: none;
    border: 0;
  }
  body.split .grip::after { display: none }
  body.split #out {
    max-height: none;
    height: auto;
    border-top: 0;
    padding-left: 0 !important;
  }
}

/* Very wide: the code column stops growing and the results take the surplus. */
@media (min-width: 1700px) {
  body.split main { grid-template-columns: minmax(0, 1fr) 7px minmax(20rem, var(--out-w, 42%)) }
}

/* Below this there is no honest way to split, so the control goes away. */
@media (max-width: 1099px) { #r-split { display: none } }


/* ── Sidebar Toggle header button ────────────────────────────────────────── */
.sidebar-toggle {
  margin-right: 0.6rem;
  font-size: 1rem !important;
  color: var(--muted) !important;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  background: transparent;
  border: 0;
  padding: 0;
  transition: color 0.12s ease;
}
.sidebar-toggle:hover {
  color: var(--fg) !important;
}

/* ── Sidebar search input wrap ────────────────────────────────────────────── */
.sidebar-search-wrap {
  padding: 0.65rem 0.8rem;
  border-bottom: 1px solid var(--edge-soft);
  background: var(--panel);
}
#sidebar-search {
  width: 100%;
  box-sizing: border-box;
  background: var(--inset);
  border: 1px solid var(--edge);
  border-radius: var(--r);
  color: var(--fg);
  font-family: var(--font-ui);
  font-size: 0.8rem;
  padding: 0.38rem 0.6rem;
  cursor: pointer;
  outline: none;
  transition: border-color 0.12s ease, background 0.12s ease;
}
#sidebar-search:hover {
  border-color: var(--muted);
  background: var(--panel-hi);
}
#sidebar-search::placeholder {
  color: var(--muted);
  opacity: 0.9;
}

/* ── Gamified Flame Streak Badge ──────────────────────────────────────────── */
.streak-badge {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  background: color-mix(in srgb, var(--gold) 12%, transparent);
  border: 1px solid var(--gold);
  border-radius: var(--r);
  color: var(--gold);
  font-weight: 600;
  font-size: 0.78rem;
  padding: 0.2rem 0.5rem;
  animation: streakPulse 2.2s infinite ease-in-out;
}
@keyframes streakPulse {
  0% { box-shadow: 0 0 0 rgba(217, 160, 30, 0); }
  50% { box-shadow: 0 0 8px rgba(217, 160, 30, 0.25); }
  100% { box-shadow: 0 0 0 rgba(217, 160, 30, 0); }
}


.palette-empty { padding: .8rem .65rem; color: var(--muted); font-size: .84rem }

/* ── Premium Floating Glassmorphic Hint Box ──────────────────────────────── */
.hint-box {
  position: absolute !important;
  bottom: 1.25rem !important;
  right: 1.5rem !important;
  width: min(25rem, 85vw) !important;
  z-index: 100 !important;
  border-radius: var(--r) !important;
  border: 1px solid var(--edge) !important;
  box-shadow: var(--shadow-lg) !important;
  background: color-mix(in srgb, var(--bg) 92%, transparent) !important;
  backdrop-filter: blur(12px) !important;
  -webkit-backdrop-filter: blur(12px) !important;
  animation: hintSlideUp 0.22s cubic-bezier(0.2, 0.8, 0.2, 1) !important;
  margin-top: 0 !important;
}
@keyframes hintSlideUp {
  0% { opacity: 0; transform: translateY(8px); }
  100% { opacity: 1; transform: translateY(0); }
}
.hint-box-header {
  display: flex !important;
  align-items: center !important;
  justify-content: space-between !important;
  background: transparent !important;
  border-bottom: 1px solid var(--edge-soft) !important;
  padding: 0.5rem 0.8rem !important;
}
.hint-box-header .title {
  display: inline-flex !important;
  align-items: center !important;
  gap: 0.35rem !important;
  font-family: var(--font-ui) !important;
  font-size: 0.76rem !important;
  font-weight: 700 !important;
  color: var(--rust) !important;
}
.hint-box-header .title::before {
  content: "💡" !important;
  font-size: 0.84rem !important;
}
.hint-box-body {
  padding: 0.7rem 0.9rem !important;
}
.hint-step {
  margin: 0 !important;
  padding: 0 !important;
  font-size: 0.82rem !important;
  line-height: 1.5 !important;
  color: var(--fg) !important;
}
.hint-box-footer {
  background: transparent !important;
  border-top: 1px solid var(--edge-soft) !important;
  padding: 0.45rem 0.8rem !important;
  font-size: 0.68rem !important;
  color: var(--muted) !important;
}
.hint-box-footer kbd {
  background: var(--panel) !important;
  border: 1px solid var(--edge) !important;
  box-shadow: 1px 1px 0 var(--shadow) !important;
  padding: 0.05rem 0.25rem !important;
  font-size: 0.65rem !important;
}


/* ── while a handle is held ────────────────────────────────────────────────
   Nothing animates the property being dragged, nothing selects text under the
   pointer, and the resize cursor stays put even when the pointer runs ahead of
   the handle. This is most of what makes a drag feel direct. */
body.dragging, body.dragging * {
  user-select: none;
  transition: none !important;
  animation: none !important;
}
body.dragging { cursor: ew-resize }
body.dragging:has(.grip:active) { cursor: ns-resize }
body.dragging.split:has(.grip:active) { cursor: ew-resize }
body.dragging aside, body.dragging #out { will-change: width, height }
/* ─────────────────────────────────────────────────────────────────────────
   THE TWO COLUMNS

   `.workspace` is a single-column grid in the base sheet, and the pinned rule
   below sets the sidebar to `position: static`. Without a column definition
   here, that makes it a full-width row sitting above the editor. It needs both
   halves of the pair or neither.
   ───────────────────────────────────────────────────────────────────────── */
@media (min-width: 900px) {
  .workspace { grid-template-columns: var(--rail-w, 264px) minmax(0, 1fr) }
}


/* Only the textarea scrolls. The highlight layer sits underneath it and is
   scrolled to match from JS, so giving it `overflow: auto` too just drew a
   second set of scrollbars behind the first. */
#paint { overflow: hidden }


/* Collapsed in split view: the column goes to nothing. Without this the
   minmax() floor of 16rem kept a slice of the panel on screen no matter what,
   so the results could be hidden in the stacked layout but not beside the code. */
@media (min-width: 1100px) {
  /* Collapsed means gone: the divider track and its grab zone go too, or a
     7px strip of chrome is left behind where the panel used to be. */
  body.split.out-collapsed main { grid-template-columns: minmax(0, 1fr) 0 0 }
  body.split.out-collapsed #out { display: none }
  /* The handle stays: hiding it mid-drag cut off the pointer events that end the
     drag, and left nothing to pull the panel back out with. It keeps a grabbable
     8px, pulled back over the editor's edge so no strip of chrome shows. */
  body.split.out-collapsed .grip {
    display: block;
    width: 8px;
    margin-left: -8px;
    background: none;
  }
  body.split.out-collapsed .grip::before { left: auto; right: 0; width: 1px }
}

/* ─────────────────────────────────────────────────────────────────────────
   PROBLEM ROWS ARE ALL THE SAME SHAPE

   A problem with an error code needs an Explain button, and a button cannot
   nest inside the button that is the row — so it gets a wrapper. That wrapper
   was styled as a card: border, shadow, margin. The result was two kinds of
   row at two different widths, one of them indented inside a box.

   The wrapper is now invisible. Every row spans the full width, carries its
   own severity stripe, and Explain sits inline beneath the message.
   ───────────────────────────────────────────────────────────────────────── */
.problem-wrap {
  display: block;
  border: 0;
  box-shadow: none;
  margin: 0;
  padding: 0;
  background: none;
  border-radius: 0;
  border-left: 3px solid var(--red);
}
.problem-wrap:has(.problem.warning) { border-left-color: var(--gold) }
.problem-wrap:has(.problem.test)    { border-left-color: var(--info) }
.problem-wrap > .problem { border-left: 0 }
.problem-wrap { border-bottom: 1px solid var(--edge-soft) }
.problem-wrap > .problem { border-bottom: 0 }

.explain {
  display: inline-block;
  margin: 0 0 .7rem 1rem;
  padding: .15rem .5rem;
  font-family: var(--font-code);
  font-size: .72rem;
  font-weight: 500;
  color: var(--muted);
  background: none;
  border: 1px solid var(--edge);
  border-radius: var(--r);
  box-shadow: none;
}
.explain:hover { color: var(--fg); border-color: var(--muted); background: var(--panel-hi) }

/* ─────────────────────────────────────────────────────────────────────────
   PANE HEADER — the LeetCode arrangement

   Controls that act on a panel live in that panel's own header, not in a
   global toolbar. Problems and Raw become tabs over the thing they switch;
   Split and collapse sit at the far end. It also gives the results pane a
   visible way to be dismissed, so Ctrl+J is a shortcut rather than the only route.
   ───────────────────────────────────────────────────────────────────────── */
.pane-head {
  position: sticky;
  top: 0;
  z-index: 5;
  display: flex;
  align-items: stretch;
  gap: .1rem;
  padding: 0 .4rem;
  background: var(--panel);
  border-bottom: 1px solid var(--edge);
}
.pane-tab, .pane-btn {
  border: 0;
  background: none;
  color: var(--muted);
  font: inherit;
  font-size: .78rem;
  padding: .4rem .6rem;
  border-bottom: 2px solid transparent;
  cursor: pointer;
}
.pane-tab:hover, .pane-btn:hover { color: var(--fg) }
.pane-tab[aria-selected="true"] { color: var(--fg); border-bottom-color: var(--rust) }
.pane-btn[aria-pressed="true"] { color: var(--rust) }
.pane-head .spacer { flex: 1 }
#outcollapse { font-size: 1rem; line-height: 1; padding: .35rem .5rem }

/* A splitter you can actually grab: one hairline to look at, a wide zone to hit.
   This is the detail that makes LeetCode's panes feel precise. */
.grip {
  background: transparent;
  border: 0;
  height: 9px;
  margin: -4px 0;
  position: relative;
  z-index: 6;
}
.grip::before {
  content: "";
  position: absolute;
  left: 0; right: 0; top: 4px;
  height: 1px;
  background: var(--edge);
}
.grip:hover::before, .grip:focus-visible::before { background: var(--rust); height: 2px }
body.split .grip { height: auto; width: 9px; margin: 0 -4px }
body.split .grip::before { left: 4px; right: auto; top: 0; bottom: 0; width: 1px; height: auto }
body.split .grip:hover::before, body.split .grip:focus-visible::before { width: 2px; background: var(--rust) }

/* ─────────────────────────────────────────────────────────────────────────
   RESULTS PANE STATES

   The pane used to keep showing the previous run's problems while you edited,
   with line numbers pointing at code that had moved. It now says what it is
   showing: running, or stale, or current.
   ───────────────────────────────────────────────────────────────────────── */
.pane-note {
  margin: 0;
  padding: .35rem .9rem;
  font-size: .76rem;
  color: var(--muted);
  background: var(--panel);
  border-bottom: 1px solid var(--edge-soft);
}
body.out-running #out .problem,
body.out-running #out .clean,
body.out-stale   #out .problem,
body.out-stale   #out .clean { opacity: .45 }
body.out-running .pane-note { color: var(--rust) }

.pane-tab b { color: var(--muted); font-weight: 600; margin-left: .35rem }
.pane-tab[aria-selected="true"] b { color: var(--rust) }

/* the row F8 is currently on */
.problem.current { background: color-mix(in srgb, var(--rust) 10%, transparent) }

/* an assertion reads as a comparison */
.assert {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: .1rem .6rem;
  margin-top: .35rem;
  font-family: var(--font-code);
  font-size: .78rem;
}
.assert b { color: var(--muted); font-weight: 500 }
.assert code { background: none; border: 0; padding: 0; color: var(--ember) }

/* Explain opens under its row instead of over the code */
.explain-body {
  padding: .2rem 1rem .8rem;
  font-size: .82rem;
  color: var(--muted);
  max-width: 60rem;
}
.explain-body[hidden] { display: none }
.explain-body .block { margin: .5rem 0 }

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


/* ─────────────────────────────────────────────────────────────────────────
   RESULTS: LESS FURNITURE

   The severity stripe ran down the left of every row, so a list of problems
   read as one long red rule bordering the whole pane — and it cost an indent
   the content did not need. Severity is already carried by the error-code
   chip, which is coloured. One signal is enough.
   ───────────────────────────────────────────────────────────────────────── */
.problem, .problem-wrap { border-left: 0 }
.problem { padding: .6rem .9rem }
#out { padding-left: 0 }
.explain { margin-left: .9rem }
.pane-head, .pane-note { padding-left: .9rem }

/* Only one line between the code and the results. There were three: the
   divider itself, the pane's own border, and the editor's edge. */
body.split #out { border-left: 0 }
body.split .editor { border-right: 0 }

/* Collapsed in the stacked layout, the same way as in split. setOutHeight has a
   40px floor, so a height alone could never express "hidden". */
body.out-collapsed:not(.split) #out { display: none }


/* ─────────────────────────────────────────────────────────────────────────
   THE VERTICAL DIVIDER SITS ON THE PANE

   The grip is deliberately wider than the line it draws, so it can be grabbed
   easily — but that meant the line was painted in the middle of the grab zone,
   7px clear of the panel, and the Problems/Raw header looked detached from it.

   The line now belongs to the results panel's own edge, so the header meets it
   exactly. The grip stays transparent and oversized, and hovering it still
   lights the line, because it is the panel's previous sibling.
   ───────────────────────────────────────────────────────────────────────── */
@media (min-width: 1100px) {
  body.split .grip::before { display: none }
  body.split #out { border-left: 1px solid var(--edge) }
  body.split .grip:hover ~ #out,
  body.split .grip:focus-visible ~ #out { border-left-color: var(--rust) }
}

/* ─────────────────────────────────────────────────────────────────────────
   PHONES AND SMALL TABLETS — one pane at a time

   A three-pane workspace cannot shrink to 380px; below 768px it becomes one
   pane you switch between, with a tab bar at the bottom and a horizontal
   swipe as the shortcut. That is what every mobile coding site settles on,
   and what rustworkbook.com does too — their stylesheet is mobile-first with
   only 640/768 breakpoints.
   ───────────────────────────────────────────────────────────────────────── */
.mobile-tabs { display: none }

@media (max-width: 767px) {
  /* Split is a desktop idea. On a phone the panes are switched, not tiled, so
     the split placement is neutralised rather than left half-applied. */
  body.split main { grid-template-columns: minmax(0, 1fr) !important }
  body.split .taskbar, body.split .editor, body.split .runbar,
  body.split #out, body.split .grip {
    grid-column: 1 !important;
    grid-row: auto !important;
  }
  body.split #out { border-left: 0 }

  /* the status bar stays one line; it was wrapping to 76px */
  header { flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none }
  header::-webkit-scrollbar { display: none }
  .hud-dock { gap: .15rem }
  .keys { display: none }
  .lvl { display: none }

  body {
    grid-template-rows: auto minmax(0, 1fr);
    padding-bottom: 3.25rem;                 /* room for the tab bar */
  }

  /* one pane fills the screen; the others step aside */
  .workspace { grid-template-columns: minmax(0, 1fr) }
  body aside {
    position: static !important;   /* beats the drawer rule, which is scoped narrower */
    inset: auto;
    width: auto;
    height: auto;
    min-height: 0;
    display: grid;
    grid-template-rows: auto minmax(0, 1fr);
    border-right: 0;
    box-shadow: none;
  }
  body[data-pane="list"] main,
  body[data-pane="code"] aside,
  body[data-pane="results"] aside { display: none }
  body[data-pane="results"] .editor,
  body[data-pane="results"] .taskbar { display: none }
  body[data-pane="code"] #out,
  body[data-pane="code"] .grip { display: none }
  /* the chosen pane takes the whole grid, whatever row it normally occupies */
  /* The desktop layout pins each child to a fixed row. When only two of them
     are on screen those pins leave empty rows above, which is why the results
     sat at the bottom under a screenful of nothing. */
  body[data-pane="results"] main { grid-template-rows: minmax(0, 1fr) auto }
  body[data-pane="results"] #out {
    /* `display: block` here undid the flex column, so raw output sized itself to
       its content and parked its scrollbar mid-screen with dead space beneath. */
    display: flex;
    flex-direction: column;
    grid-row: 1 !important;
    max-height: none;
    height: auto !important;
    min-height: 0;
  }
  body[data-pane="results"] .runbar { grid-row: 2 !important }
  body[data-pane="code"] main { grid-template-rows: auto minmax(0, 1fr) auto }
  body[data-pane="code"] .taskbar { grid-row: 1 !important }
  body[data-pane="code"] .editor  { grid-row: 2 !important }
  body[data-pane="code"] .runbar  { grid-row: 3 !important }
  body[data-pane="list"] aside, body[data-pane="list"] nav { min-height: 0 }

  /* nothing is dragged on a touch screen */
  .grip, #r-split, .rail-resize { display: none }

  /* the one action stays put and is thumb-sized */
  .runbar { position: sticky; bottom: 0; padding: .5rem .75rem; gap: .5rem }
  .runbar .primary { flex: 1; min-height: 2.75rem; font-size: .95rem }
  .btn, .pane-tab, .pane-btn { min-height: 2.25rem }

  /* chrome that costs more than it gives on a small screen */
  .taskbar { padding: .4rem .75rem; gap: .4rem }
  .taskbar .file, .place { display: none }
  header { padding: .4rem .6rem; gap: .5rem }
  .brand { font-size: .85rem }
  .editor { min-height: 0 }
  .gutter { padding-left: .5rem; padding-right: .35rem }

  .mobile-tabs {
    position: fixed;
    inset: auto 0 0 0;
    z-index: 30;
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    background: var(--panel);
    border-top: 1px solid var(--edge);
    padding-bottom: env(safe-area-inset-bottom, 0);
  }
  .mobile-tabs button {
    border: 0;
    background: none;
    color: var(--muted);
    font: inherit;
    font-size: .82rem;
    padding: .8rem .4rem;
    border-top: 2px solid transparent;
    margin-top: -1px;
  }
  .mobile-tabs button[aria-pressed="true"] { color: var(--fg); border-top-color: var(--rust) }
}

/* Tablets keep two panes but never three. */
@media (min-width: 768px) and (max-width: 1099px) {
  .workspace { grid-template-columns: var(--rail-w, 240px) minmax(0, 1fr) }
  body aside {
    position: static;
    width: auto;
    height: 100%;
    min-height: 0;
    overflow: hidden;
    display: grid;
    grid-template-rows: auto minmax(0, 1fr);
    box-shadow: none;
  }
  body aside nav { min-height: 0; overflow-y: auto }
  .runbar .primary { min-height: 2.4rem }
}

/* Raw output is wide, and letting the pane scroll sideways dragged the header
   (Problems · Raw · Copy · Split · ×) off with it. The long lines scroll inside
   their own box instead, so the controls stay where you left them. */
#out { overflow-x: hidden }
#out .raw { overflow-x: auto; max-width: 100% }
.pane-head { position: sticky; top: 0; left: 0 }

/* The raw block was a box inside the pane: its own scrollbars, and dead space
   below it. It fills the pane instead, so every available line is used and the
   long lines scroll within the full height rather than a short window. */
#out { display: flex; flex-direction: column }
#out > .pane-head, #out > .pane-note { flex: 0 0 auto }
#out .raw {
  flex: 1 1 auto;
  min-height: 0;
  margin: 0;
  overflow: auto;
}

/* ── hint: one at a time, with a way between them ──────────────────────── */
.hint-box-header { display: flex; align-items: center; gap: .5rem }
.hint-box-header .title { font-weight: 600; font-size: .8rem; color: var(--gold) }
.hint-nav {
  display: flex; justify-content: space-between; gap: .5rem;
  padding: .1rem .6rem .55rem;
}
.hint-nav button, .hint-box-close {
  border: 0;
  background: none;
  color: var(--muted);
  font: inherit;
  font-size: .95rem;
  line-height: 1;
  padding: .15rem .45rem;
  cursor: pointer;
  border-radius: var(--r);
}
.hint-nav button { font-size: .78rem; font-weight: 600; padding: .2rem .55rem }
.hint-nav button:last-child { margin-left: auto }
.hint-nav button:hover:not(:disabled), .hint-box-close:hover { background: var(--panel-hi); color: var(--fg) }
.hint-nav button:disabled { opacity: .35; cursor: default }
.hint-box-close { margin-left: .1rem }
.hint-box-close { margin-left: auto }

/* ── the shortcut table in Settings ────────────────────────────────────── */
.keys h3 { font-size: .78rem; text-transform: uppercase; letter-spacing: .06em;
  color: var(--muted); margin: 0 0 .6rem }
.keys dl { display: grid; grid-template-columns: max-content 1fr;
  gap: .45rem 1rem; margin: 0 0 1.4rem; align-items: baseline }
.keys dt { display: flex; gap: .25rem; flex-wrap: wrap }
.keys dd { margin: 0; color: var(--muted); font-size: .88rem }
.keys kbd { font: 600 .72rem/1.5 var(--mono); background: var(--panel-hi);
  border: 1px solid var(--line); border-bottom-width: 2px; border-radius: 4px;
  padding: .05rem .35rem; color: var(--fg) }

/* ── the solution on a phone: a sheet, not a shrunken desktop dialog ────── */
@media (max-width: 767px) {
  #solutiondlg {
    width: 100%;
    max-width: none;
    height: 100dvh;
    max-height: none;
    margin: 0;
    border: 0;
    border-radius: 0;
  }
  #solutiondlg[open] { display: flex; flex-direction: column }
  #solutiondlg .top {
    flex: 0 0 auto;
    flex-wrap: wrap;
    gap: .5rem;
    padding: .7rem .9rem;
  }
  #solutiondlg .top .spacer { display: none }
  #solutiondlg h2 { flex: 1 0 100%; min-width: 0 }
  /* The full path wrapped to two lines and squeezed the buttons into three. */
  #solutiondlg h2 .path { display: none }
  #solutiondlg .top .btn { flex: 1; min-height: 2.6rem; white-space: nowrap }
  /* One scroller, and it fills the sheet: the code block used to end halfway
     down with a screenful of nothing under it. */
  #solutiondlg .body {
    flex: 1 1 auto;
    min-height: 0;
    max-height: none;          /* dialog .body caps at 66vh — not for a sheet */
    display: flex;
    padding: .6rem;
  }
  #solutionbody {
    flex: 1 1 auto;
    min-height: 0;
    margin: 0;
    overflow: auto;
    font-size: .82rem;
    line-height: 1.55;
  }
}

/* The stacked results pane carried a 45vh ceiling from the base stylesheet, so
   the divider stopped halfway up the screen. The height is already bounded in
   script by the bars plus a line of code — this cap only got in the way. */
#out { max-height: none }
@media (max-height: 460px) { #out { max-height: 26vh } }

/* ── the wordmark: a crab, then the name ───────────────────────────────── */
.brand, .brand a { display: inline-flex; align-items: center; gap: .38rem }
.brand .crab { width: 1.35rem; height: 1.35rem; flex: 0 0 auto }
/* Point at the mark and the crab spins once, the way a coin flips: a full turn
   about its own vertical axis, not a tip to one side. The perspective is what
   makes it read as a flip rather than a horizontal squash, and it belongs on
   the parent because that is the eye's distance from the whole mark.
   One turn and it stops, because 360 degrees lands exactly where it started. */
.brand { perspective: 320px }
.brand .crab { transform-style: preserve-3d }
.brand:hover .crab { animation: crab-flip .75s ease-in-out }
@keyframes crab-flip {
  from { transform: rotateY(0) }
  to   { transform: rotateY(360deg) }
}
@media (prefers-reduced-motion: reduce) {
  .brand:hover .crab { animation: none }
}
/* In the workspace the mark is a link home, and it should not look like one
   until you are on it. */
.brand a { color: inherit; text-decoration: none }
.brand a:hover em { color: var(--fg) }

/* ═══ workspace mockup: the header group, the rail header, the code fade ═══ */

/* One bordered group instead of a text chip plus three loose buttons. Three
   equal cells, one icon language: 1.7 stroke on currentColor, like the crab. */
.hud-dock {
  display: flex;
  align-items: stretch;
  /* the base .dock-btn is height:100%, so the group sets the height for all three */
  height: 2.25rem;
  border: 1px solid var(--edge);
  border-radius: 8px;
  overflow: hidden;
}
.hud-dock .dock-btn {
  border: 0;
  border-left: 1px solid var(--edge-soft);
  border-radius: 0;
  background: none;
  color: var(--muted);
  min-width: 2.4rem;
  padding: 0 .45rem !important;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.hud-dock .dock-btn:first-child { border-left: 0 }
.hud-dock .dock-btn:hover:not(:disabled) { background: var(--panel-hi); color: var(--fg) }
.dock-btn .ico { width: 1.05rem; height: 1.05rem; display: block }

/* The theme control shows what it is: a disc half filled, rays on the light
   half. No second glyph to swap in, so nothing can fall out of sync. */
#theme .rays { opacity: 0 }
:root[data-theme="light"] #theme .half { d: path("M12 5a7 7 0 0 1 0 14Z") }
:root[data-theme="light"] #theme .rays { opacity: .55 }

/* The level reads left to right: Lv 4 — how far into it — the numbers, then a
   hairline, then the three controls. */
.lvl {
  display: inline-flex;
  align-items: center;
  gap: .45rem;
  margin-right: .7rem;
  padding-right: .8rem;
  border-right: 1px solid var(--edge);
  color: var(--muted);
  font-size: .78rem;
}
.lvl .lv-label { letter-spacing: .02em }
.lvl b { font: 700 .82rem var(--font-code); color: var(--fg) }
.xp-track {
  width: 7.5rem;
  height: 7px;
  border-radius: 99px;
  background: var(--edge);
  overflow: hidden;
}
.xp-track i { display: block; height: 100%; border-radius: 99px; background: var(--rust) }
.xp-num { font-family: var(--font-code); font-size: .76rem }

/* How far through the course you are, above the list it describes. */
.rail-head {
  flex: 0 0 auto;
  padding: .55rem .8rem;
  border-bottom: 1px solid var(--edge-soft);
}
.rail-nums { display: flex; align-items: baseline; gap: .4rem }
.rail-nums b { font-size: .86rem; font-weight: 600; color: var(--fg) }
.rail-nums span { font-size: .78rem; color: var(--muted) }
.rail-nums .of { font-family: var(--font-code); font-size: .76rem }
.rail-bar {
  margin-top: .4rem;
  height: 6px;
  border-radius: 99px;
  background: var(--edge);
  overflow: hidden;
}
.rail-bar i { display: block; height: 100%; border-radius: 99px; background: var(--rust) }
aside nav { min-height: 0 }

/* A problem reads title, code, place. */
.problem .head { display: flex; align-items: baseline; gap: .5rem; flex-wrap: wrap }
.problem .what { font-weight: 600; font-size: .86rem; order: 0 }
.problem .code { order: 1 }
.problem .where { order: 2; font-family: var(--font-code); font-size: .78rem; color: var(--muted) }

/* The code runs under the run bar rather than stopping at a hard edge. */
.editor .stack { position: relative }
.editor .stack::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 34px;
  background: linear-gradient(180deg, transparent, var(--inset));
  pointer-events: none;
}

/* Running needs an account; editing does not. The pane is set in mono, so this
   panel says plainly that it is prose, not compiler output. */
#out .gate {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: .55rem;
  padding: 1.1rem 1rem;
  font-family: var(--font-ui);
  border-left: 0;
}
#out .gate b { font-size: 1rem; font-weight: 600; color: var(--fg); letter-spacing: -.01em }
#out .gate p { margin: 0; font-size: .86rem; line-height: 1.55; color: var(--muted); max-width: 46ch }
#out .gate .gate-row { display: flex; align-items: center; gap: .8rem; margin-top: .35rem }
#out .gate .btn { text-decoration: none }
/* one button, one quieter way in beside it */
#out .gate .gate-alt {
  font-size: .82rem;
  color: var(--muted);
  text-decoration: none;
  border-bottom: 1px solid var(--edge);
}
#out .gate .gate-alt:hover { color: var(--fg); border-bottom-color: var(--rust) }

/* ── profile ──────────────────────────────────────────────────────────── */
.profile .rule { height: 1px; background: var(--edge-soft); margin: 1rem 0 }
.who-row { display: flex; align-items: center; gap: .8rem }
.who-row .avatar {
  width: 44px;
  height: 44px;
  flex: 0 0 auto;
  border-radius: var(--r);
  border: 1px solid color-mix(in srgb, var(--rust) 50%, transparent);
  background: color-mix(in srgb, var(--rust) 14%, transparent);
  display: flex;
  align-items: center;
  justify-content: center;
}
.who-row .avatar svg { width: 26px; height: 26px; display: block }
.who-name { display: flex; flex-direction: column; min-width: 0 }
.who-name b { font-size: 1rem; font-weight: 600; letter-spacing: -.01em; color: var(--fg) }
.who-name small { font: .78rem var(--font-code); color: var(--muted); margin-top: .15rem }

.p-course { display: flex; align-items: baseline; gap: .5rem }
.p-course b { font-size: .95rem; font-weight: 600; color: var(--fg) }
.p-course #p-pct { font: .78rem var(--font-code); color: var(--muted) }
.p-bar { margin-top: .45rem; height: 6px; border-radius: 99px; background: var(--edge); overflow: hidden }
.p-bar i { display: block; height: 100%; border-radius: 99px; background: var(--rust) }
.p-facts { margin: .7rem 0 0; font: .8rem/1.6 var(--font-code); color: var(--muted) }

.p-actions { display: flex; gap: .5rem }
.p-actions .danger-quiet { color: var(--muted) }
.p-actions .danger-quiet:hover { color: var(--red); border-color: var(--red) }


/* the crab that stands for you, and the name it is drawn from */
.who-row .avatar { overflow: hidden }
.who-row .avatar svg, #profilebtn .crabatar { display: block }
#profilebtn { padding: 0 .45rem !important }
.name-line { display: flex; align-items: center; gap: .5rem }
.name-line b { font-size: 1rem; font-weight: 600; letter-spacing: -.01em; color: var(--fg) }
.name-line input {
  background: var(--inset);
  border: 1px solid var(--edge);
  border-radius: var(--r);
  color: var(--fg);
  font: 600 .95rem var(--font-ui);
  padding: .2rem .45rem;
  width: 11rem;
}
.btn.tiny { padding: .15rem .5rem; font-size: .74rem }

/* profile: the account row, the facts grid, every trophy */
.who-row .who-name { flex: 0 1 auto }
.p-err { color: var(--red) !important; margin-top: .15rem }
.p-country { display: flex; flex-direction: column; gap: .25rem; align-items: flex-end }
.p-country span { font-size: .74rem; color: var(--muted) }
.p-country select {
  background: var(--inset);
  border: 1px solid var(--edge);
  border-radius: var(--r);
  color: var(--fg);
  font: .82rem var(--font-ui);
  padding: .28rem .45rem;
  max-width: 12rem;
}
.p-country select:disabled { opacity: .5 }
.p-kicker {
  font-size: .78rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .06em;
  color: var(--muted);
  margin-bottom: .55rem;
}
.p-kicker span { font-family: var(--font-code); text-transform: none; letter-spacing: 0 }
.p-grid {
  display: grid;
  grid-template-columns: max-content 1fr max-content 1fr;
  gap: .5rem 1rem;
  align-items: baseline;
  margin: 1rem 0 0;
}
.p-grid dt { font-size: .82rem; color: var(--muted) }
.p-grid dd { margin: 0; font: 600 .84rem var(--font-code); color: var(--fg) }
.p-trophies { display: grid; grid-template-columns: repeat(3, 1fr); gap: .5rem; align-items: start }
.p-trophy {
  border: 1px solid var(--edge);
  border-radius: var(--r);
  background: var(--panel);
  padding: .5rem .6rem;
  display: flex;
  flex-direction: column;
  gap: .15rem;
}
.p-trophy > * { flex: 0 0 auto }
.p-trophy .icon { font: 600 .72rem var(--font-code); color: var(--muted) }
.p-trophy b { font: 600 .84rem/1.4 var(--font-ui); color: var(--fg) }
.p-trophy small { font: .72rem/1.4 var(--font-ui); color: var(--muted) }
.p-trophy.won { border-color: var(--rust) }
.p-trophy.won .icon { color: var(--rust) }
.p-trophy.locked { opacity: .45 }
.p-trophy.locked b { color: var(--muted) }

@media (max-width: 767px) {
  .who-row { flex-wrap: wrap }
  .p-country { align-items: flex-start; width: 100% }
  .p-country select { max-width: none; width: 100% }
  .p-grid { grid-template-columns: max-content 1fr }
  .p-trophies { grid-template-columns: repeat(2, 1fr) }
}
