/* ==========================================================================
   Foothold

   The look comes from climbing guidebooks and survey charts, because the name
   does: a foothold is the first thing you trust your weight to. Modules are a
   route, lessons are the holds on it, and the sidebar draws that literally -
   a hairline with holds down the left, filling in as you climb. The numbering
   is not decoration; the course really is a sequence and the order matters.

   Constraints that shaped this file:
     - no webfonts, because the site must work with the network off, so all
       personality has to come from system stacks
     - no CSS framework, for the same reason
     - the editor's two layers must agree on every metric or the illusion of
       typing into highlighted code falls apart (see "Editor")
   ========================================================================== */

/* --- Tokens --------------------------------------------------------------- */

:root {
  /* Two colours carry meaning, and they never swap jobs:
       signal  (vermilion) is you  - your route, your progress, your turn
       machine (cyan)      is it   - execution, output, the trace
     A learner should be able to tell at a glance whether a mark on screen is
     about them or about the computer. */
  --bg: #101a26;
  --bg-sunk: #0b131d;
  --surface: #17232f;
  --surface-2: #1d2b39;
  --ink: #e6ecf1;
  --ink-soft: #90a3b4;
  /* Held at 4.5:1 against --surface-2, the lightest thing it ever sits on.
     Anything dimmer looked better and failed WCAG AA, and this text carries
     the module numbers and lesson counts, which people need to read. */
  --ink-faint: #7f93a8;
  --line: #263543;
  --line-strong: #35495c;

  --signal: #ff5c39;
  --signal-soft: rgba(255, 92, 57, 0.14);
  /* Vermilion is too bright to carry white text: 3.07:1, under the 4.5 a
     primary button needs. Dark ink on it reads at 6.08 and looks like what
     it is, a lit key with the legend cut out of it. */
  --on-signal: #0b131d;
  --machine: #46c8e0;
  --machine-soft: rgba(70, 200, 224, 0.13);
  --pass: #4cc38a;
  --pass-soft: rgba(76, 195, 138, 0.12);
  --fail: #f2705b;
  --fail-soft: rgba(242, 112, 91, 0.12);
  --note: #46c8e0;

  --tok-keyword: #ff8f6b;
  --tok-string: #9ed7a4;
  --tok-number: #d8c06a;
  /* Comments carry instructions here - every starter file opens with one -
     so they are held to the 4.5:1 that body text needs, measured against a
     line with the trace marker on it, which is the lightest a comment ever
     sits on. Receding further would look better and be unreadable. */
  --tok-comment: #8799ab;
  --tok-builtin: #7fc3e8;
  --tok-call: #e4ebf0;
  --tok-defname: #ffd08a;
  --tok-attr: #b8c7d4;

  --font-display: "Bahnschrift", "Avenir Next Condensed", "Roboto Condensed",
    "Archivo Narrow", "Helvetica Neue", Arial, sans-serif;
  --font-ui: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --font-read: "Iowan Old Style", "Charter", "Palatino Linotype", Cambria, Georgia, serif;
  --font-mono: ui-monospace, "SF Mono", "Cascadia Mono", "JetBrains Mono", Menlo,
    Consolas, "DejaVu Sans Mono", monospace;

  --topbar-h: 52px;
  --sidebar-w: 286px;
  --radius: 6px;
  --radius-lg: 10px;
  --shadow: 0 14px 40px rgba(0, 0, 0, 0.42);
  --code-size: 13.5px;
  --code-leading: 22px;
}

[data-theme='light'] {
  /* Graph paper: the pale ruled stock a chart recorder prints on. */
  --bg: #e8ecf0;
  --bg-sunk: #dbe2e8;
  --surface: #f7f9fb;
  --surface-2: #edf1f5;
  --ink: #101b2b;
  --ink-soft: #48596b;
  --ink-faint: #536576;
  --line: #c6d1da;
  --line-strong: #a6b6c3;

  --signal: #bc2c03;
  --signal-soft: rgba(188, 44, 3, 0.1);
  /* The light theme's deeper red carries white at 5.21:1. */
  --on-signal: #ffffff;
  --machine: #096d86;
  --machine-soft: rgba(9, 109, 134, 0.1);
  --pass: #096848;
  --pass-soft: rgba(9, 104, 72, 0.1);
  --fail: #a5301c;
  --fail-soft: rgba(165, 48, 28, 0.09);
  --note: #096d86;

  --tok-keyword: #a23601;
  --tok-string: #18673d;
  --tok-number: #7a5100;
  --tok-comment: #4a5d6d;
  --tok-builtin: #0f5f8a;
  --tok-call: #14202b;
  --tok-defname: #7a4b00;
  --tok-attr: #374b5c;

  --shadow: 0 12px 34px rgba(20, 32, 43, 0.12);
}

/* --- Base ----------------------------------------------------------------- */

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

html, body { height: 100%; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font-ui);
  font-size: 15px;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

a { color: inherit; }

h1, h2, h3, h4 { margin: 0 0 0.4em; line-height: 1.14; font-weight: 600; }

p { margin: 0 0 1em; }

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

::selection { background: var(--signal-soft); }

.skip-link {
  position: absolute;
  left: -9999px;
  top: 8px;
  z-index: 100;
  background: var(--signal);
  color: var(--on-signal);
  padding: 8px 14px;
  border-radius: var(--radius);
}
.skip-link:focus { left: 8px; }

.dim { color: var(--ink-soft); }

/* --- Buttons -------------------------------------------------------------- */

.btn {
  appearance: none;
  border: 1px solid var(--line-strong);
  background: var(--surface-2);
  color: var(--ink);
  font: inherit;
  font-size: 13.5px;
  padding: 7px 13px;
  border-radius: var(--radius);
  cursor: pointer;
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  white-space: nowrap;
  transition: background 0.12s ease, border-color 0.12s ease, transform 0.06s ease;
}
.btn:hover { border-color: var(--ink-faint); background: var(--surface); }
.btn:active { transform: translateY(1px); }
.btn:disabled { opacity: 0.45; cursor: default; transform: none; }

.btn-primary {
  background: var(--signal);
  border-color: var(--signal);
  color: var(--on-signal);
  font-weight: 600;
}
.btn-primary:hover { background: var(--signal); filter: brightness(1.08); border-color: var(--signal); }

/* Run is the filled button on this screen and it is the only one. Check used
   to be a full green outline on a green wash beside it, so the workbench
   opened with two buttons of equal shout and no clear first move. The tick
   keeps the colour; the button does not. */
.btn-check .glyph { color: var(--pass); }
.btn-check:hover { border-color: var(--pass); }

.btn-quiet { background: transparent; border-color: var(--line); color: var(--ink-soft); }
.btn-quiet:hover { color: var(--ink); border-color: var(--line-strong); background: var(--surface-2); }

.btn-danger { background: transparent; border-color: var(--fail); color: var(--fail); }

.btn-large { font-size: 15px; padding: 11px 20px; }
.btn-tiny { font-size: 11.5px; padding: 3px 9px; letter-spacing: 0.02em; }

.glyph { font-size: 11px; line-height: 1; }

.icon-btn {
  appearance: none;
  background: none;
  border: 0;
  color: var(--ink-soft);
  cursor: pointer;
  padding: 7px;
  border-radius: var(--radius);
  display: inline-flex;
  align-items: center;
  line-height: 0;
}
.icon-btn:hover { color: var(--ink); background: var(--surface-2); }

/* --- Top bar -------------------------------------------------------------- */

.topbar {
  height: var(--topbar-h);
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 0 14px;
  border-bottom: 1px solid var(--line);
  background: var(--bg-sunk);
  position: sticky;
  top: 0;
  z-index: 40;
}

.brand {
  display: flex;
  align-items: center;
  gap: 9px;
  text-decoration: none;
  color: var(--ink);
}
.brand-mark { color: var(--signal); line-height: 0; }
.brand-word {
  font-family: var(--font-display);
  font-size: 19px;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  font-weight: 600;
}

.topbar-right { margin-left: auto; display: flex; align-items: center; gap: 4px; }

.runtime-status {
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--ink-faint);
  padding: 4px 9px;
  border: 1px solid var(--line);
  border-radius: 999px;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  white-space: nowrap;
}
.runtime-status::before {
  content: '';
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: currentColor;
  flex: none;
}
.runtime-status[data-kind='ok'] { color: var(--pass); border-color: color-mix(in srgb, var(--pass) 35%, transparent); }
.runtime-status[data-kind='busy'] { color: var(--signal); border-color: color-mix(in srgb, var(--signal) 35%, transparent); }
.runtime-status[data-kind='busy']::before { animation: pulse 1.1s ease-in-out infinite; }
.runtime-status[data-kind='warn'] { color: var(--tok-number); }
.runtime-status[data-kind='error'] { color: var(--fail); }

@keyframes pulse { 50% { opacity: 0.25; } }

.bars, .bars::before, .bars::after {
  display: block;
  width: 17px;
  height: 1.6px;
  background: currentColor;
  border-radius: 2px;
}
.bars { position: relative; }
.bars::before, .bars::after { content: ''; position: absolute; left: 0; }
.bars::before { top: -5px; }
.bars::after { top: 5px; }

/* --- Shell ---------------------------------------------------------------- */

.shell {
  display: grid;
  grid-template-columns: var(--sidebar-w) minmax(0, 1fr);
  min-height: calc(100vh - var(--topbar-h));
}

.view { min-width: 0; }
.view:focus { outline: none; }

.nav-scrim { display: none; }

/* --- The route list is for people already on the route ---------------------
   On a lesson the sidebar is the map and belongs open. On the front page it
   is sixty links to lessons nobody has started, taking the first third of the
   screen before a visitor has read a word, and pushing the page it competes
   with hard against the left edge. So on the pages that introduce Foothold it
   becomes the same drawer it already is on a phone, behind the button that is
   already in the header. Nothing new to learn, and the page gets its width.  */

body[data-route='home'] .shell,
body[data-route='teachers'] .shell,
body[data-route='certificate'] .shell {
  grid-template-columns: minmax(0, 1fr);
}

body[data-route='home'] .sidebar,
body[data-route='teachers'] .sidebar,
body[data-route='certificate'] .sidebar {
  position: fixed;
  top: var(--topbar-h);
  left: 0;
  width: var(--sidebar-w);
  z-index: 50;
  transform: translateX(-100%);
  /* visibility as well as the transform. Slid off the edge, a drawer is still
     in the tab order and still hit-testable, so someone tabbing through the
     front page walks into sixty lesson links they cannot see, and a click
     lands on a target nobody can point at. visibility:hidden takes it out of
     both. The delay lets the slide finish before it disappears. */
  visibility: hidden;
  transition: transform 0.22s cubic-bezier(0.2, 0.8, 0.2, 1), visibility 0s linear 0.22s;
}

body[data-route='home'].nav-open .sidebar,
body[data-route='teachers'].nav-open .sidebar,
body[data-route='certificate'].nav-open .sidebar {
  transform: none;
  visibility: visible;
  transition: transform 0.22s cubic-bezier(0.2, 0.8, 0.2, 1), visibility 0s;
}

body[data-route='home'].nav-open .nav-scrim,
body[data-route='teachers'].nav-open .nav-scrim,
body[data-route='certificate'].nav-open .nav-scrim {
  display: block;
  position: fixed;
  inset: var(--topbar-h) 0 0;
  background: rgba(0, 0, 0, 0.45);
  z-index: 45;
}

/* --- Sidebar: the route --------------------------------------------------- */

.sidebar {
  border-right: 1px solid var(--line);
  background: var(--bg-sunk);
  position: sticky;
  top: var(--topbar-h);
  height: calc(100vh - var(--topbar-h));
  overflow-y: auto;
  overscroll-behavior: contain;
  padding-bottom: 32px;
}

.nav-search { padding: 12px 14px 8px; }
.nav-search input {
  width: 100%;
  background: var(--surface);
  border: 1px solid var(--line);
  color: var(--ink);
  border-radius: var(--radius);
  padding: 7px 10px;
  font: inherit;
  font-size: 13px;
}
.nav-search input::placeholder { color: var(--ink-faint); }

.nav-progress { padding: 4px 14px 14px; border-bottom: 1px solid var(--line); }
.nav-progress p {
  margin: 7px 0 0;
  font-family: var(--font-mono);
  font-size: 10.5px;
  color: var(--ink-faint);
  letter-spacing: 0.02em;
}

.nav-progress-bar {
  height: 3px;
  background: var(--line);
  border-radius: 999px;
  overflow: hidden;
}
.nav-progress-bar span {
  display: block;
  height: 100%;
  background: var(--signal);
  transition: width 0.4s cubic-bezier(0.2, 0.8, 0.2, 1);
}

.nav-module { border-bottom: 1px solid var(--line); }

.nav-module-head {
  width: 100%;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 11px 14px;
  background: none;
  border: 0;
  color: var(--ink);
  cursor: pointer;
  text-align: left;
  font-family: var(--font-display);
  font-size: 13.5px;
  letter-spacing: 0.055em;
  text-transform: uppercase;
}
.nav-module-head:hover { background: var(--surface); }

.nav-module-index {
  font-family: var(--font-mono);
  font-size: 10.5px;
  color: var(--ink-faint);
  letter-spacing: 0;
}
.nav-module-title { flex: 1; min-width: 0; }
.nav-module-count {
  font-family: var(--font-mono);
  font-size: 10.5px;
  color: var(--ink-faint);
  letter-spacing: 0;
}
.nav-module-count.is-done { color: var(--pass); }

.nav-lessons { display: none; list-style: none; margin: 0; padding: 2px 0 12px; position: relative; }
.nav-module.is-open .nav-lessons { display: block; }

/* The route line the holds sit on. */
.nav-lessons::before {
  content: '';
  position: absolute;
  left: 26px;
  top: 12px;
  bottom: 20px;
  width: 1.5px;
  background: var(--line-strong);
}

.nav-lesson {
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 5px 14px 5px 18px;
  text-decoration: none;
  color: var(--ink-soft);
  font-size: 13.5px;
  position: relative;
}
.nav-lesson:hover { color: var(--ink); background: var(--surface); }

.nav-tick {
  flex: none;
  width: 17px;
  height: 17px;
  border-radius: 50%;
  border: 1.5px solid var(--line-strong);
  background: var(--bg-sunk);
  display: grid;
  place-items: center;
  font-size: 9px;
  color: transparent;
  z-index: 1;
  transition: background 0.15s ease, border-color 0.15s ease, transform 0.15s ease;
}
.nav-lesson:hover .nav-tick { border-color: var(--ink-faint); }

.nav-lesson.is-done .nav-tick {
  background: var(--signal);
  border-color: var(--signal);
  color: var(--on-signal);
}
.nav-lesson.is-done { color: var(--ink); }

.nav-lesson.is-current { color: var(--ink); background: var(--surface); font-weight: 600; }
.nav-lesson.is-current .nav-tick {
  border-color: var(--signal);
  transform: scale(1.16);
  box-shadow: 0 0 0 3px var(--signal-soft);
}

.nav-lesson-title { flex: 1; min-width: 0; }
.nav-lesson-time {
  font-family: var(--font-mono);
  font-size: 10px;
  color: var(--ink-faint);
  opacity: 0;
}
.nav-lesson:hover .nav-lesson-time { opacity: 1; }

.nav-empty { padding: 20px 14px; color: var(--ink-faint); font-size: 13px; }

/* --- Home ----------------------------------------------------------------- */
/* The page opens asymmetric: words on the left, the machine working on the
   right. A centred hero with a headline and two buttons is the shape every
   product page has; this one earns its space by showing the thing.          */

.home { max-width: 1120px; margin: 0 auto; padding: 54px 26px 76px; }

/* The demo gets the larger half. The headline promises that you can watch
   your code run; the panel to the right is the only thing on the page that
   proves it, and it was rendered smaller than the sentence describing it. */
.hero {
  display: grid;
  grid-template-columns: minmax(0, 0.82fr) minmax(0, 1.18fr);
  gap: 52px;
  align-items: center;
  margin-bottom: 84px;
}

.hero h1 {
  font-family: var(--font-display);
  font-size: clamp(42px, 5.6vw, 68px);
  line-height: 0.97;
  letter-spacing: -0.018em;
  margin-bottom: 22px;
}

.hero-sub {
  font-family: var(--font-read);
  font-size: 17.5px;
  line-height: 1.6;
  color: var(--ink-soft);
  max-width: 42ch;
  margin-bottom: 14px;
}

.hero-fact {
  font-family: var(--font-mono);
  font-size: 11.5px;
  color: var(--ink-faint);
  letter-spacing: 0.01em;
  margin-bottom: 26px;
}

.hero-actions { display: flex; flex-wrap: wrap; gap: 14px; align-items: center; }

.hero-progress-inline {
  display: flex;
  align-items: center;
  gap: 10px;
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--ink-faint);
}
.hero-progress-inline .nav-progress-bar { width: 110px; }

/* --- The live trace in the hero ------------------------------------------- */

.hero-trace {
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--bg-sunk);
  overflow: hidden;
  box-shadow: var(--shadow);
}
.hero-trace.is-inert { opacity: 0.4; }

.hero-trace-head {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 9px 13px;
  border-bottom: 1px solid var(--line);
  background: var(--surface);
  font-family: var(--font-mono);
  font-size: 10.5px;
  color: var(--ink-faint);
}
.hero-trace-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--machine);
  flex: none;
  box-shadow: 0 0 0 3px var(--machine-soft);
}
.hero-trace-label { flex: 1; min-width: 0; }
.hero-trace-toggle {
  appearance: none;
  background: none;
  border: 1px solid var(--line);
  border-radius: 999px;
  color: var(--ink-faint);
  font-family: var(--font-mono);
  font-size: 9.5px;
  padding: 2px 9px;
  cursor: pointer;
}
.hero-trace-toggle:hover { color: var(--ink); border-color: var(--line-strong); }

/* A floor under the panel, so the two hero columns finish at roughly the same
   depth and the output pane has somewhere to appear rather than pushing the
   layout down when the program finally prints. */
.hero-trace-body {
  display: grid;
  grid-template-columns: minmax(0, 1.25fr) minmax(0, 1fr);
  min-height: 246px;
}

.hero-code {
  list-style: none;
  margin: 0;
  padding: 16px 0;
  font-family: var(--font-mono);
  font-size: 13.5px;
  line-height: 2;
}
.hero-code li {
  display: flex;
  gap: 12px;
  padding: 0 12px;
  border-left: 2px solid transparent;
  transition: background 0.18s ease, border-color 0.18s ease;
  white-space: pre;
}
.hero-code li.is-on {
  background: var(--machine-soft);
  border-left-color: var(--machine);
}
.hero-code-n { color: var(--ink-faint); font-size: 10.5px; width: 12px; flex: none; text-align: right; }
.hero-code-t { min-width: 0; }

.hero-watch {
  border-left: 1px solid var(--line);
  padding: 16px 15px;
  min-width: 0;
}
.hero-watch-title {
  font-family: var(--font-display);
  text-transform: uppercase;
  letter-spacing: 0.12em;
  font-size: 9.5px;
  color: var(--ink-faint);
  margin: 0 0 6px;
}
.hero-watch-title:not(:first-child) { margin-top: 14px; }

.hero-vars { width: 100%; border-collapse: collapse; font-family: var(--font-mono); font-size: 12px; }
.hero-vars td { padding: 2px 6px 2px 0; vertical-align: top; }
.hero-vars td:first-child { color: var(--ink-soft); white-space: nowrap; }
.hero-vars td:last-child { color: var(--tok-string); word-break: break-word; }
.hero-vars tr.is-new td:first-child { color: var(--pass); }
.hero-vars tr.is-changed td { background: var(--machine-soft); }
.hero-vars tr.is-changed td:first-child { color: var(--machine); }
.hero-vars tr.is-empty td { color: var(--ink-faint); font-style: italic; }

.hero-out {
  margin: 0;
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--ink);
  white-space: pre-wrap;
  /* Reserved rather than grown into. The program prints once, near the end of
     the loop, and a pane that appears at that moment shifts everything under
     it every few seconds. */
  min-height: 3.4em;
}

/* The tape: one tick per recorded step, filling as the program advances.
   Same graphic idea as the route in the sidebar, turned on its side.        */
.hero-tape {
  display: flex;
  gap: 2px;
  padding: 9px 13px;
  border-top: 1px solid var(--line);
  background: var(--surface);
}
.hero-tape-tick {
  flex: 1;
  height: 9px;
  border-radius: 1px;
  background: var(--line);
  transition: background 0.18s ease, transform 0.18s ease;
}
.hero-tape-tick.is-done { background: color-mix(in srgb, var(--machine) 55%, transparent); }
.hero-tape-tick.is-now { background: var(--machine); transform: scaleY(1.7); }

/* --- Ruled section headings ----------------------------------------------- */
/* A tick-rule instead of a hairline: the same ruler marking that runs under
   the hero tape, so sections read as measured intervals.

   The scale is a fixed short length rather than flex:1. Run across a full
   1000px column it stopped reading as a measurement and started reading as
   noise, or as a rendering fault - and it repeats on every section, so it was
   the loudest thing on a page where it carries no information. Held to a
   hand's width it is a mark in the margin again.                             */

.rule {
  font-family: var(--font-display);
  text-transform: uppercase;
  letter-spacing: 0.15em;
  font-size: 12px;
  color: var(--ink-faint);
  display: flex;
  align-items: center;
  gap: 13px;
  margin: 0 0 22px;
}
.rule::after {
  /* A measuring scale: minor ticks every 9px, a major one every fifth.
     Two layers rather than one, so it reads as a ruler and not as a
     dotted border. */
  content: '';
  flex: none;
  width: 104px;
  height: 9px;
  background-image:
    linear-gradient(to right, var(--line-strong) 1px, transparent 1px),
    linear-gradient(to right, var(--line) 1px, transparent 1px);
  background-size: 45px 9px, 9px 4px;
  background-position: bottom left, bottom left;
  background-repeat: repeat-x, repeat-x;
}

/* --- The home page grid ----------------------------------------------------
   A section is a label in a left rail with its content in the column beside
   it. Before this every section independently fell back to a heading stacked
   over a paragraph capped at 62ch, so at desktop width roughly half the page
   sat empty to the right of each block while the ruled heading above it ran
   the full width. The rail spends that gap on structure instead, and it is
   the shape the spec list below already had.                                 */

.band {
  display: grid;
  grid-template-columns: minmax(0, 190px) minmax(0, 1fr);
  gap: 0 44px;
  align-items: start;
  margin: 0 0 78px;
}

.band > .rule {
  grid-column: 1;
  flex-direction: column;
  align-items: flex-start;
  gap: 11px;
  margin: 0;
}
/* Shorter in the rail than in the open, so it cannot collide with a label that
   wraps to two lines, but long enough to still read as a scale. */
.band > .rule::after { width: 88px; }

.band-body { grid-column: 2; min-width: 0; }
.band-body > :last-child { margin-bottom: 0; }

/* The page padding below already carries the end of the document. */
.home > section:last-child, .home > .band:last-child { margin-bottom: 0; }

/* Content that wants the whole measure - the route, which is a grid of cards -
   keeps its heading above rather than beside. */
.band-wide { display: block; margin: 0 0 78px; }

.spec-list { margin: 0; display: grid; gap: 0; }
.spec-list > div {
  display: grid;
  grid-template-columns: 128px minmax(0, 1fr);
  gap: 20px;
  padding: 13px 0;
  border-bottom: 1px solid var(--line);
}
.spec-list dt {
  font-family: var(--font-mono);
  font-size: 10.5px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  /* Not --signal. Vermilion means "you" everywhere else on the site, and
     RUNTIME, NETWORK and LICENCE are facts about the machine, not about the
     learner. Colouring them was decoration that broke the one rule the
     palette has. */
  color: var(--ink-faint);
  padding-top: 3px;
}
.spec-list dd { margin: 0; color: var(--ink-soft); font-size: 14.5px; }

.module-cards {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: 12px;
}

.module-card a {
  display: block;
  height: 100%;
  padding: 16px 16px 13px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  text-decoration: none;
  transition: border-color 0.14s ease, background 0.14s ease;
}
.module-card a:hover { border-color: var(--signal); background: var(--surface-2); }

.module-card-index {
  font-family: var(--font-mono);
  font-size: 10.5px;
  color: var(--signal);
  letter-spacing: 0.06em;
}
.module-card h3 {
  font-family: var(--font-display);
  font-size: 18px;
  letter-spacing: 0.01em;
  margin: 6px 0 6px;
}
.module-card p { color: var(--ink-soft); font-size: 13px; margin-bottom: 12px; line-height: 1.5; }
.module-card-foot {
  font-family: var(--font-mono);
  font-size: 10.5px;
  color: var(--ink-faint);
  display: flex;
  align-items: center;
  gap: 7px;
}
.module-card.is-done a { border-color: color-mix(in srgb, var(--pass) 40%, var(--line)); }
.module-card .tick { color: var(--pass); }

.scratch-lead { color: var(--ink-soft); font-size: 14.5px; max-width: 62ch; }
/* Two selectors on purpose. The Terminal component adds .terminal to this same
   element, and .terminal sets height:100% and min-height:0 further down the
   file at equal specificity, so a plain .scratch-terminal height never applied
   and the prompt collapsed to about 100px - a REPL with no room to type in.
   The compound selector outranks it. */
.scratch-terminal,
.scratch-terminal.terminal {
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  overflow: hidden;
  height: 240px;
  min-height: 240px;
  background: var(--bg-sunk);
}

.keeping p { color: var(--ink-soft); max-width: 66ch; font-size: 14.5px; }
.keeping-actions { display: flex; flex-wrap: wrap; gap: 9px; margin-top: 16px; }

/* Erasing everything is not a sibling of Export and Certificate, so it does
   not sit in their row as though it were one more thing to try. */
.keeping-actions .btn-danger { margin-left: auto; }

@media (max-width: 900px) {
  .hero { grid-template-columns: 1fr; gap: 30px; align-items: start; }
  .hero-trace-body { grid-template-columns: 1fr; min-height: 0; }
  .hero-watch { border-left: 0; border-top: 1px solid var(--line); }
  .spec-list > div { grid-template-columns: 1fr; gap: 4px; }

  .band { grid-template-columns: minmax(0, 1fr); gap: 0; margin-bottom: 56px; }
  .band > .rule {
    grid-column: 1;
    flex-direction: row;
    align-items: center;
    gap: 13px;
    margin-bottom: 20px;
  }
  .band > .rule::after { width: 104px; }
  .band-body { grid-column: 1; }
  .band-wide { margin-bottom: 56px; }

  .keeping-actions .btn-danger { margin-left: 0; }
}

/* --- Lesson layout -------------------------------------------------------- */

.lesson {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  min-height: calc(100vh - var(--topbar-h));
}

.lesson-prose {
  padding: 34px 38px 70px;
  border-right: 1px solid var(--line);
  min-width: 0;
  max-width: 760px;
}

.lesson-crumb {
  font-family: var(--font-display);
  text-transform: uppercase;
  letter-spacing: 0.16em;
  font-size: 11px;
  color: var(--signal);
  margin-bottom: 10px;
}

.lesson-head h1 {
  font-family: var(--font-display);
  font-size: clamp(27px, 3.2vw, 37px);
  letter-spacing: 0;
  margin-bottom: 10px;
}

.lesson-goal {
  font-family: var(--font-read);
  font-size: 16.5px;
  color: var(--ink-soft);
  font-style: italic;
  margin-bottom: 14px;
}

.lesson-meta { display: flex; flex-wrap: wrap; gap: 7px; align-items: center; margin-bottom: 26px; }

.badge, .chip {
  font-family: var(--font-mono);
  font-size: 10.5px;
  padding: 3px 9px;
  border-radius: 999px;
  border: 1px solid var(--line);
  color: var(--ink-faint);
  letter-spacing: 0.02em;
}
.badge-done { color: var(--pass); border-color: color-mix(in srgb, var(--pass) 40%, transparent); background: var(--pass-soft); }

/* Reading copy is set in a serif: lessons are prose, and the change of voice
   from the code around them should be visible before it is read. */
.prose { font-family: var(--font-read); font-size: 16.5px; line-height: 1.68; }
.prose p { margin: 0 0 1.15em; }
.prose h2 {
  font-family: var(--font-display);
  text-transform: uppercase;
  letter-spacing: 0.1em;
  font-size: 14px;
  color: var(--signal);
  margin: 2em 0 0.8em;
  padding-top: 1em;
  border-top: 1px solid var(--line);
}
.prose h3 { font-family: var(--font-display); font-size: 19px; margin: 1.7em 0 0.5em; }
.prose ul, .prose ol { margin: 0 0 1.15em; padding-left: 1.35em; }
.prose li { margin-bottom: 0.42em; }
.prose a { color: var(--signal); text-underline-offset: 3px; }
.prose strong { font-weight: 700; color: var(--ink); }
.prose hr { border: 0; border-top: 1px solid var(--line); margin: 2em 0; }

.prose blockquote {
  margin: 1.4em 0;
  padding: 2px 0 2px 17px;
  border-left: 2px solid var(--signal);
  color: var(--ink-soft);
}
.prose blockquote p:last-child { margin-bottom: 0; }

.prose code, .check-message code {
  font-family: var(--font-mono);
  font-size: 0.86em;
  background: var(--surface-2);
  border: 1px solid var(--line);
  padding: 1px 5px;
  border-radius: 4px;
  color: var(--ink);
}

.table-wrap { overflow-x: auto; margin: 0 0 1.3em; }
.prose table { border-collapse: collapse; width: 100%; font-size: 14px; font-family: var(--font-ui); }
.prose th, .prose td { text-align: left; padding: 7px 11px; border-bottom: 1px solid var(--line); }
.prose th { font-family: var(--font-display); text-transform: uppercase; letter-spacing: 0.07em; font-size: 11.5px; color: var(--ink-faint); }

/* Examples in the prose, each with a Run button. */
.prose-code {
  margin: 0 0 1.35em;
  position: relative;
  background: var(--bg-sunk);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
}
.prose-code pre { margin: 0; padding: 13px 15px; overflow-x: auto; }
.prose-code code {
  font-family: var(--font-mono);
  font-size: var(--code-size);
  line-height: var(--code-leading);
  background: none;
  border: 0;
  padding: 0;
  white-space: pre;
}
/* Lesson one says "run the example above with the Try it button". It used to
   be opacity:0 until the block was hovered, which meant the button did not
   exist at all on a phone, a tablet, or a Chromebook in tablet mode - the
   prose pointed at a control the student could not find. It is quiet, but it
   is always there. */
.code-run {
  position: absolute;
  top: 7px;
  right: 7px;
  color: var(--ink-faint);
}
.prose-code:hover .code-run { color: var(--ink); border-color: var(--line-strong); }

/* Room for it, so a long first line does not run underneath. */
.prose-code:has(.code-run) pre { padding-right: 76px; }

/* Hints and solution */
.hints { margin-top: 34px; }
.hints h2 {
  font-family: var(--font-display);
  text-transform: uppercase;
  letter-spacing: 0.12em;
  font-size: 13px;
  color: var(--ink-faint);
  margin-bottom: 12px;
}
.hint {
  display: flex;
  gap: 12px;
  padding: 12px 15px;
  border: 1px solid var(--line);
  border-left: 2px solid var(--signal);
  border-radius: var(--radius);
  background: var(--surface);
  margin-bottom: 9px;
  font-family: var(--font-read);
  font-size: 15.5px;
}
.hint p:last-child { margin-bottom: 0; }
.hint-n {
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--signal);
  flex: none;
  padding-top: 3px;
}

.solution { margin: 30px 0; }
.solution h2 {
  font-family: var(--font-display);
  text-transform: uppercase;
  letter-spacing: 0.12em;
  font-size: 13px;
  color: var(--ink-faint);
  margin-bottom: 8px;
}
.solution p { color: var(--ink-soft); font-size: 14px; font-family: var(--font-read); }

.lesson-foot { display: flex; flex-wrap: wrap; gap: 9px; margin-top: 34px; }

.lesson-nav {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  margin-top: 30px;
  padding-top: 20px;
  border-top: 1px solid var(--line);
}

/* --- Workbench ------------------------------------------------------------ */

.workbench {
  display: flex;
  flex-direction: column;
  position: sticky;
  top: var(--topbar-h);
  height: calc(100vh - var(--topbar-h));
  background: var(--bg-sunk);
  min-width: 0;
}

.workbench-bar {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 9px 13px;
  border-bottom: 1px solid var(--line);
  flex: none;
}

.workbench-status {
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--ink-faint);
  margin-left: auto;
  white-space: nowrap;
}
.workbench-status.is-ok { color: var(--pass); }
.workbench-status.is-error { color: var(--fail); }
.workbench-status.is-busy { color: var(--signal); }

.editor-wrap { flex: 1 1 52%; min-height: 150px; display: flex; }

/* --- Editor --------------------------------------------------------------- */
/* The highlighted <pre> and the <textarea> must agree on every metric below,
   or the caret drifts away from the glyphs. Change one, change both.        */

.editor {
  /* Two rows: the code, then the keyboard hint spanning under it. This was a
     wrapping flex row, which put the hint on its own line correctly but then
     let align-content hand the leftover height to that line instead of to the
     code - so the editor ended at the last typed character and a slab of dead
     grey filled the rest of the pane. The grid gives the space to the row that
     wants it. Metrics inside .editor-surface are untouched. */
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  grid-template-rows: minmax(0, 1fr) auto;
  width: 100%;
  min-width: 0;
  background: var(--bg);
  font-family: var(--font-mono);
  font-size: var(--code-size);
  line-height: var(--code-leading);
  tab-size: 4;
}

.editor-gutter {
  flex: none;
  padding: 12px 0 12px;
  width: 46px;
  text-align: right;
  overflow: hidden;
  color: var(--ink-faint);
  background: var(--bg-sunk);
  border-right: 1px solid var(--line);
  user-select: none;
}
.editor-gutter span { display: block; padding-right: 11px; font-size: 11.5px; line-height: var(--code-leading); }

.editor-surface { position: relative; flex: 1; min-width: 0; }

.editor-highlight,
.editor-input {
  margin: 0;
  padding: 12px 15px;
  border: 0;
  font-family: inherit;
  font-size: inherit;
  line-height: inherit;
  letter-spacing: normal;
  tab-size: inherit;
  white-space: pre;
  word-wrap: normal;
  overflow-wrap: normal;
}

.editor-highlight {
  position: absolute;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
  color: var(--ink);
}
.editor-highlight code { font: inherit; background: none; border: 0; padding: 0; }

.editor-input {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  resize: none;
  background: transparent;
  color: transparent;
  caret-color: var(--signal);
  overflow: auto;
  outline: none;
}
.editor-input::selection { background: color-mix(in srgb, var(--signal) 30%, transparent); }

.tok-keyword { color: var(--tok-keyword); }
.tok-string { color: var(--tok-string); }
.tok-number { color: var(--tok-number); }
.tok-comment { color: var(--tok-comment); font-style: italic; }
.tok-builtin { color: var(--tok-builtin); }
.tok-call { color: var(--tok-call); }
.tok-defname { color: var(--tok-defname); }
.tok-attr { color: var(--tok-attr); }
.tok-error { color: var(--fail); font-weight: 600; }
.tok-trace { color: var(--ink-faint); }
.tok-caret { color: var(--signal); }

/* --- Panes ---------------------------------------------------------------- */

.panes {
  flex: 1 1 48%;
  min-height: 160px;
  display: flex;
  flex-direction: column;
  border-top: 1px solid var(--line);
  min-width: 0;
}

.pane-tabs { display: flex; gap: 0; border-bottom: 1px solid var(--line); flex: none; }

.pane-tab {
  appearance: none;
  background: none;
  border: 0;
  border-bottom: 2px solid transparent;
  color: var(--ink-faint);
  font-family: var(--font-display);
  text-transform: uppercase;
  letter-spacing: 0.1em;
  font-size: 11.5px;
  padding: 9px 15px;
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 7px;
}
.pane-tab:hover { color: var(--ink); }
.pane-tab.is-active { color: var(--ink); border-bottom-color: var(--signal); }

.tab-badge { font-family: var(--font-mono); font-size: 10px; letter-spacing: 0; }
.tab-badge.is-ok { color: var(--pass); }
.tab-badge.is-partial { color: var(--signal); }

.pane { display: none; flex: 1; min-height: 0; overflow: auto; }
.pane.is-active { display: block; }
.pane[data-pane='terminal'].is-active { display: flex; overflow: hidden; }
.pane[data-pane='terminal'] > div { flex: 1; min-height: 0; }

.console, .checks { padding: 13px 15px; }

.console-empty { color: var(--ink-faint); font-family: var(--font-mono); font-size: 12px; margin: 0; }

.console-out, .console-err, .check-message {
  font-family: var(--font-mono);
  font-size: var(--code-size);
  line-height: var(--code-leading);
  margin: 0 0 10px;
  white-space: pre-wrap;
  word-break: break-word;
}
.console-out { color: var(--ink); }
.console-err {
  color: var(--fail);
  background: var(--fail-soft);
  border-left: 2px solid var(--fail);
  padding: 9px 12px;
  border-radius: 0 var(--radius) var(--radius) 0;
}

.console-ask {
  display: flex;
  align-items: center;
  gap: 9px;
  margin-top: 4px;
  padding: 9px 11px;
  border: 1px solid var(--signal);
  border-radius: var(--radius);
  background: var(--signal-soft);
}
.console-ask label { display: flex; align-items: center; gap: 9px; flex: 1; min-width: 0; }
.console-ask-prompt { font-family: var(--font-mono); font-size: var(--code-size); color: var(--signal); white-space: nowrap; }
.console-ask-input {
  flex: 1;
  min-width: 0;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: 4px;
  color: var(--ink);
  font-family: var(--font-mono);
  font-size: var(--code-size);
  padding: 5px 9px;
}

/* Checks */
.check-list { list-style: none; margin: 0 0 14px; padding: 0; }
.check {
  display: flex;
  gap: 11px;
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  margin-bottom: 7px;
  background: var(--surface);
}
.check.is-pass { border-color: color-mix(in srgb, var(--pass) 35%, var(--line)); background: var(--pass-soft); }
.check.is-fail { border-color: color-mix(in srgb, var(--fail) 35%, var(--line)); background: var(--fail-soft); }
.check-mark { flex: none; font-size: 13px; line-height: 1.5; }
.check.is-pass .check-mark { color: var(--pass); }
.check.is-fail .check-mark { color: var(--fail); }
.check-name { margin: 0; font-size: 14px; }
.check-message { margin: 7px 0 0; color: var(--ink-soft); font-size: 12.5px; }

.check-error h3, .check-pass h3 {
  font-family: var(--font-display);
  text-transform: uppercase;
  letter-spacing: 0.1em;
  font-size: 13px;
  margin-bottom: 9px;
}
.check-error h3 { color: var(--fail); }

.check-pass {
  border: 1px solid var(--pass);
  background: var(--pass-soft);
  border-radius: var(--radius);
  padding: 15px;
}
.check-pass h3 { color: var(--pass); }
.check-pass p { color: var(--ink-soft); font-size: 14px; }

.check-output summary {
  cursor: pointer;
  font-family: var(--font-mono);
  font-size: 11.5px;
  color: var(--ink-faint);
  margin-bottom: 8px;
}


/* The band behind the line the tracer is currently sitting on. It lives
   behind the highlighted text and in front of nothing, so the code stays
   readable through it. */
.editor-marker {
  position: absolute;
  left: 0;
  right: 0;
  background: var(--machine-soft);
  border-left: 2px solid var(--machine);
  pointer-events: none;
  z-index: 0;
  transition: top 0.09s linear;
}

.editor-gutter span.is-current {
  color: var(--machine);
  font-weight: 700;
}

/* --- Trace: the step-by-step recorder ------------------------------------- */

.trace { display: flex; flex-direction: column; height: 100%; min-height: 0; }

.trace-idle { padding: 26px 20px; max-width: 46ch; }
.trace-idle-lead {
  font-family: var(--font-display);
  text-transform: uppercase;
  letter-spacing: 0.1em;
  font-size: 13px;
  color: var(--machine);
  margin-bottom: 9px;
}
.trace-idle-sub { color: var(--ink-soft); font-size: 14px; margin-bottom: 16px; }

.trace-bar {
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 8px 12px;
  border-bottom: 1px solid var(--line);
  flex: none;
}
.trace-bar .icon-btn { padding: 5px 7px; font-size: 11px; }

.trace-slider {
  flex: 1;
  min-width: 60px;
  margin: 0 8px;
  accent-color: var(--machine);
  height: 4px;
  cursor: pointer;
}

.trace-count {
  font-family: var(--font-mono);
  font-size: 10.5px;
  color: var(--ink-faint);
  white-space: nowrap;
  min-width: 62px;
  text-align: right;
}

.trace-warn {
  margin: 0;
  padding: 8px 12px;
  font-size: 12px;
  color: var(--ink-soft);
  background: var(--machine-soft);
  border-bottom: 1px solid var(--line);
}

.trace-what {
  margin: 0;
  padding: 10px 14px;
  font-size: 13.5px;
  border-bottom: 1px solid var(--line);
  flex: none;
}
.trace-what code {
  font-family: var(--font-mono);
  font-size: 12px;
  background: var(--surface-2);
  padding: 1px 5px;
  border-radius: 4px;
}
.trace-what.is-exception { color: var(--fail); background: var(--fail-soft); }
.trace-what.is-return { color: var(--pass); }
.trace-what.is-call { color: var(--note); }

.trace-body {
  flex: 1;
  min-height: 0;
  display: grid;
  grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr);
}
.trace-vars { overflow: auto; padding: 12px 14px; min-width: 0; }
.trace-side { overflow: auto; padding: 12px 14px; border-left: 1px solid var(--line); min-width: 0; }

.trace-body h4 {
  font-family: var(--font-display);
  text-transform: uppercase;
  letter-spacing: 0.11em;
  font-size: 10.5px;
  color: var(--ink-faint);
  margin: 0 0 9px;
}
.trace-side h4:not(:first-child) { margin-top: 16px; }

.trace-table { width: 100%; border-collapse: collapse; font-family: var(--font-mono); font-size: 12px; }
.trace-table td { padding: 4px 7px 4px 0; vertical-align: top; border-bottom: 1px solid var(--line); }
.trace-name { color: var(--ink); white-space: nowrap; }
.trace-type { color: var(--ink-faint); font-size: 10.5px; white-space: nowrap; }
.trace-value { color: var(--tok-string); word-break: break-word; }
.trace-size {
  color: var(--ink-faint);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 0 5px;
  font-size: 9.5px;
}

/* A variable that just appeared, or just changed, is the only thing on the
   screen worth looking at. */
.trace-table tr.is-new .trace-name { color: var(--pass); }
.trace-table tr.is-new .trace-name::before { content: '+ '; }
.trace-table tr.is-changed { background: var(--machine-soft); }
.trace-table tr.is-changed .trace-name { color: var(--machine); }
.trace-was { display: block; color: var(--ink-faint); font-size: 10.5px; text-decoration: line-through; }

.trace-output {
  margin: 0;
  font-family: var(--font-mono);
  font-size: 12px;
  line-height: 1.6;
  white-space: pre-wrap;
  word-break: break-word;
  color: var(--ink);
}
.trace-output.is-empty { color: var(--ink-faint); font-style: italic; }

.trace-stack { list-style: none; margin: 0; padding: 0; font-family: var(--font-mono); font-size: 11.5px; }
.trace-stack li {
  padding: 2px 0 2px calc(var(--depth) * 12px);
  color: var(--ink-soft);
  position: relative;
}
.trace-stack li:last-child { color: var(--machine); }

.trace-empty { color: var(--ink-faint); font-size: 12px; font-style: italic; margin: 0; }

.check-why {
  border: 1px dashed var(--line-strong);
  border-radius: var(--radius);
  padding: 13px 15px;
  margin-bottom: 12px;
}
.check-why p { color: var(--ink-soft); font-size: 13.5px; margin-bottom: 10px; }

.pane[data-pane='trace'].is-active { display: flex; overflow: hidden; }
.pane[data-pane='trace'] > div { flex: 1; min-height: 0; }

@media (max-width: 720px) {
  .trace-body { grid-template-columns: 1fr; }
  .trace-side { border-left: 0; border-top: 1px solid var(--line); }
}

/* --- Terminal ------------------------------------------------------------- */

.terminal { display: flex; flex-direction: column; height: 100%; min-height: 0; background: var(--bg); }

.terminal-output {
  flex: 1;
  overflow-y: auto;
  padding: 12px 15px 4px;
  font-family: var(--font-mono);
  font-size: var(--code-size);
  line-height: var(--code-leading);
  min-height: 0;
}
.terminal-output:focus { outline: none; }

.terminal-line { white-space: pre-wrap; word-break: break-word; }
.terminal-banner { color: var(--ink-faint); font-style: italic; }
.terminal-error { color: var(--fail); }
.terminal-echo { display: flex; gap: 9px; }
.terminal-echo .terminal-code { flex: 1; min-width: 0; }

.terminal-prompt { color: var(--signal); user-select: none; flex: none; }

.terminal-inputline {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 9px 15px;
  border-top: 1px solid var(--line);
  background: var(--bg-sunk);
  flex: none;
}
.terminal-entry {
  flex: 1;
  min-width: 0;
  background: none;
  border: 0;
  outline: none;
  color: var(--ink);
  font-family: var(--font-mono);
  font-size: var(--code-size);
}

/* --- Certificate ---------------------------------------------------------- */

.certificate-page { max-width: 800px; margin: 0 auto; padding: 40px 26px 80px; }

.notice {
  border: 1px solid var(--signal);
  background: var(--signal-soft);
  border-radius: var(--radius);
  padding: 12px 15px;
  margin-bottom: 22px;
  font-size: 14px;
}
.notice p { margin: 0; }

.cert-name-field {
  display: block;
  font-family: var(--font-display);
  text-transform: uppercase;
  letter-spacing: 0.12em;
  font-size: 11.5px;
  color: var(--ink-faint);
  margin-bottom: 22px;
}
.cert-name-field input {
  display: block;
  margin-top: 7px;
  width: 100%;
  max-width: 360px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 9px 12px;
  color: var(--ink);
  font-family: var(--font-ui);
  font-size: 15px;
  letter-spacing: 0;
  text-transform: none;
}

.certificate {
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-lg);
  background: var(--surface);
  padding: 46px 40px;
  text-align: center;
  position: relative;
}
.certificate::after {
  content: '';
  position: absolute;
  inset: 9px;
  border: 1px solid var(--line);
  border-radius: 5px;
  pointer-events: none;
}

.cert-mark {
  font-family: var(--font-display);
  text-transform: uppercase;
  letter-spacing: 0.4em;
  font-size: 15px;
  color: var(--signal);
  margin-bottom: 5px;
}
.cert-kind {
  font-family: var(--font-display);
  text-transform: uppercase;
  letter-spacing: 0.19em;
  font-size: 11px;
  color: var(--ink-faint);
  margin-bottom: 32px;
}
.cert-lead { font-family: var(--font-read); font-style: italic; color: var(--ink-soft); margin-bottom: 6px; }
.cert-name {
  font-family: var(--font-read);
  font-size: clamp(28px, 5vw, 42px);
  margin-bottom: 18px;
  line-height: 1.2;
}
.cert-course { font-family: var(--font-display); font-size: 20px; letter-spacing: 0.02em; margin-bottom: 26px; }

.cert-modules { display: flex; flex-wrap: wrap; gap: 7px; justify-content: center; margin-bottom: 32px; }
.cert-module {
  font-family: var(--font-mono);
  font-size: 10.5px;
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 4px 10px;
  color: var(--ink-faint);
}
.cert-module.is-done { color: var(--pass); border-color: color-mix(in srgb, var(--pass) 40%, transparent); }

.cert-date { font-family: var(--font-mono); font-size: 12px; color: var(--ink-soft); margin-bottom: 14px; }
.cert-fine { font-size: 11.5px; color: var(--ink-faint); max-width: 54ch; margin: 0 auto; line-height: 1.6; }

.cert-actions { display: flex; gap: 10px; margin-top: 24px; flex-wrap: wrap; }

/* --- Toast, errors -------------------------------------------------------- */

.toast {
  position: fixed;
  left: 50%;
  bottom: 26px;
  transform: translate(-50%, 16px);
  background: var(--surface-2);
  border: 1px solid var(--line-strong);
  border-left: 3px solid var(--pass);
  color: var(--ink);
  padding: 11px 17px;
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  font-size: 14px;
  opacity: 0;
  transition: opacity 0.22s ease, transform 0.22s ease;
  z-index: 80;
}
.toast.is-in { opacity: 1; transform: translate(-50%, 0); }
.toast-error { border-left-color: var(--fail); }

.error-page { max-width: 620px; margin: 0 auto; padding: 80px 26px; text-align: center; }
.error-page h1 { font-family: var(--font-display); font-size: 30px; margin-bottom: 14px; }
.error-page p { color: var(--ink-soft); }

.hero-fact-link {
  color: var(--ink-faint);
  text-decoration: underline;
  text-underline-offset: 3px;
  white-space: nowrap;
}
.hero-fact-link:hover { color: var(--signal); }

.lesson-report {
  margin-top: 26px;
  font-size: 13px;
  text-align: center;
}
.lesson-report a { color: var(--ink-faint); text-decoration: underline; text-underline-offset: 3px; }
.lesson-report a:hover { color: var(--signal); }

/* --- Site footer -----------------------------------------------------------
   The page used to end on a row of buttons. This is on the same rail as every
   section above it, so it reads as the last band rather than as furniture
   bolted to the bottom.                                                      */

.sitefoot {
  border-top: 1px solid var(--line);
  background: var(--bg-sunk);
}
/* A lesson has no bottom: the workbench is sticky and full height. */
body[data-route='lesson'] .sitefoot { display: none; }

.sitefoot-in {
  max-width: 1120px;
  margin: 0 auto;
  padding: 34px 26px 44px;
  display: grid;
  grid-template-columns: minmax(0, 190px) minmax(0, 1fr);
  gap: 0 44px;
  align-items: start;
}

.sitefoot-mark {
  margin: 0;
  font-family: var(--font-display);
  font-size: 15px;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

.sitefoot-body { min-width: 0; }

.sitefoot-line {
  margin: 0 0 16px;
  color: var(--ink-faint);
  font-size: 13.5px;
  max-width: 60ch;
}

.sitefoot-links {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 22px;
  font-size: 13.5px;
}
.sitefoot-links a {
  color: var(--ink-soft);
  text-decoration: none;
  text-underline-offset: 3px;
}
.sitefoot-links a:hover { color: var(--ink); text-decoration: underline; }

@media (max-width: 900px) {
  .sitefoot-in {
    grid-template-columns: minmax(0, 1fr);
    gap: 16px;
    padding: 28px 20px 36px;
  }
}

/* Present to a screen reader, absent to everyone else. Not display:none,
   which would take it out of the accessibility tree along with the layout. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* The way out of the editor. A textarea that eats Tab is a keyboard trap, so
   the escape hatch has to be visible rather than folklore. */

.editor-keys {
  grid-column: 1 / -1;
  padding: 6px 12px 8px;
  font-size: 11.5px;
  color: var(--ink-faint);
  border-top: 1px solid var(--line);
  background: var(--bg-sunk);
}
.editor-keys kbd {
  font-family: var(--font-mono);
  font-size: 10.5px;
  padding: 1px 4px;
  border: 1px solid var(--line-strong);
  border-bottom-width: 2px;
  border-radius: 3px;
  color: var(--ink-soft);
  background: var(--surface);
}
.editor.is-releasing .editor-keys {
  color: var(--machine);
  border-top-color: var(--machine);
}
.editor.is-releasing .editor-keys::after { content: ' Tab now moves on.'; }

/* --- Offering to report a break -------------------------------------------
   Bottom corner, out of the way of the editor and the output, and gone the
   moment it is dismissed. It is an offer, not an error dialog. */

.reporter {
  position: fixed;
  right: 16px;
  bottom: 16px;
  z-index: 200;
  max-width: 330px;
  padding: 14px 16px 12px;
  border: 1px solid var(--fail);
  border-radius: var(--radius);
  background: var(--surface);
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
}
.reporter-what { font-size: 13px; line-height: 1.5; color: var(--ink); }
.reporter-actions { display: flex; gap: 8px; margin-top: 11px; align-items: center; }
.reporter-fine { margin-top: 9px; font-size: 11px; color: var(--ink-faint); }

@media (max-width: 560px) {
  .reporter { right: 10px; bottom: 10px; left: 10px; max-width: none; }
}

/* --- When Python cannot start ---------------------------------------------
   Every exercise needs the interpreter, so this is not a warning to tuck in a
   corner. It sits under the header, across the full width, and stays until
   Python starts or the learner retries. */

.runtime-alert {
  border-bottom: 1px solid var(--fail);
  background: var(--fail-soft);
}
.runtime-alert[hidden] { display: none; }

.runtime-alert-body {
  max-width: 760px;
  margin: 0 auto;
  padding: 18px 26px 20px;
}
.runtime-alert h2 {
  font-family: var(--font-display);
  font-size: 19px;
  margin-bottom: 6px;
  color: var(--ink);
}
.runtime-alert p { color: var(--ink-soft); font-size: 14px; line-height: 1.55; }
.runtime-alert-actions { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 14px; }
.runtime-alert details { margin-top: 12px; }
.runtime-alert summary {
  cursor: pointer;
  font-size: 12px;
  color: var(--ink-faint);
  font-family: var(--font-mono);
}
.runtime-alert pre {
  margin-top: 8px;
  padding: 10px 12px;
  overflow-x: auto;
  font-family: var(--font-mono);
  font-size: 11.5px;
  color: var(--ink-faint);
  background: var(--bg-sunk);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  white-space: pre-wrap;
}

/* --- Responsive ----------------------------------------------------------- */

@media (max-width: 1180px) {
  .lesson { grid-template-columns: 1fr; }
  .lesson-prose { border-right: 0; border-bottom: 1px solid var(--line); max-width: none; padding: 28px 24px 44px; }
  .workbench { position: static; height: auto; min-height: 86vh; }
}

@media (max-width: 900px) {
  :root { --sidebar-w: 280px; }

  .shell { grid-template-columns: 1fr; }

  .sidebar {
    position: fixed;
    top: var(--topbar-h);
    left: 0;
    width: var(--sidebar-w);
    z-index: 50;
    transform: translateX(-100%);
    /* See the note on the route-scoped drawer above: off the edge is not the
       same as gone, and a closed drawer must not be reachable by Tab. */
    visibility: hidden;
    transition: transform 0.22s cubic-bezier(0.2, 0.8, 0.2, 1), visibility 0s linear 0.22s;
  }
  body.nav-open .sidebar {
    transform: none;
    visibility: visible;
    transition: transform 0.22s cubic-bezier(0.2, 0.8, 0.2, 1), visibility 0s;
  }

  body.nav-open .nav-scrim {
    display: block;
    position: fixed;
    inset: var(--topbar-h) 0 0;
    background: rgba(0, 0, 0, 0.45);
    z-index: 45;
  }

  .home { padding: 32px 20px 70px; }
  .runtime-status { display: none; }
}

@media (min-width: 901px) {
  .nav-only { display: none; }

  /* Except where the sidebar is a drawer at every width, in which case the
     button that opens it has to be there too. */
  body[data-route='home'] .nav-only,
  body[data-route='teachers'] .nav-only,
  body[data-route='certificate'] .nav-only { display: inline-flex; }
}

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

/* --- Print: the certificate is the only thing worth printing -------------- */

@media print {
  .topbar, .sidebar, .cert-actions, .cert-name-field, .notice, .nav-scrim,
  .sitefoot { display: none !important; }
  body, .certificate { background: #fff; color: #000; }
  .certificate { border-color: #999; box-shadow: none; }
  .cert-mark { color: #000; }
  .cert-module { color: #444; }
  .shell { display: block; }
  .certificate-page { padding: 0; }
}

/* --- The page for teachers, clubs, and libraries ---------------------------
   Reuses the home page's width, rules and spec list. The one override that
   matters: .spec-list normally labels a short mono key in a 128px column, and
   here the terms are whole questions a programme director actually asks, so
   they get a wider column and ordinary sentence case. */

/* The statement on the left, the answer to it on the right. Set as one stacked
   column it left the whole right half of the page empty above the fold, which
   is the first thing a head of department sees. */
.teachers-head {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 0 52px;
  align-items: start;
  padding: 8px 0 48px;
}
.teachers-eyebrow { grid-column: 1 / -1; }
.teachers-head h1 { grid-column: 1; grid-row: 2 / span 2; }
.teachers-head .hero-sub { grid-column: 2; grid-row: 2; }
.teachers-head .hero-actions { grid-column: 2; grid-row: 3; }

.teachers-eyebrow {
  font-family: var(--font-mono);
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--signal);
  margin: 0 0 16px;
}

.teachers-head h1 {
  font-family: var(--font-display);
  font-size: clamp(34px, 5.4vw, 54px);
  line-height: 1.04;
  letter-spacing: -0.02em;
  margin: 0 0 20px;
}

.teachers-head .hero-sub { max-width: 60ch; margin-bottom: 26px; }

.teachers-lead {
  color: var(--ink-soft);
  font-size: 14.5px;
  line-height: 1.65;
  max-width: 68ch;
  margin: 0 0 22px;
}

.teachers .spec-list > div { grid-template-columns: minmax(0, 260px) minmax(0, 1fr); }

.teachers .spec-list dt {
  font-family: inherit;
  font-size: 14.5px;
  text-transform: none;
  letter-spacing: 0;
  color: var(--ink);
  padding-top: 0;
}

.teachers .spec-list dd { line-height: 1.6; }

.teachers-plans, .teachers-limits, .teachers-take, .teachers-ask { margin: 66px 0 0; }

.teachers-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 14.5px;
}

.teachers-table th, .teachers-table td {
  text-align: left;
  padding: 12px 16px 12px 0;
  border-bottom: 1px solid var(--line);
  font-weight: 400;
}

.teachers-table thead th {
  font-family: var(--font-mono);
  font-size: 10.5px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--ink-faint);
  border-bottom-color: var(--line-strong);
}

.teachers-table tbody th { color: var(--ink); }
.teachers-table tbody td { color: var(--ink-soft); }

.teachers-limit-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 2px;
}

.teachers-limit-list li {
  border-left: 2px solid var(--line-strong);
  padding: 10px 0 10px 18px;
  color: var(--ink-soft);
  font-size: 14.5px;
  line-height: 1.6;
  max-width: 74ch;
}

.teachers-limit-list strong { color: var(--ink); font-weight: 600; }

.teachers-take-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(232px, 1fr));
  gap: 14px;
}

.teachers-card {
  display: block;
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 18px 20px 20px;
  background: var(--surface);
  text-decoration: none;
  color: inherit;
  transition: border-color 120ms ease, background 120ms ease;
}

.teachers-card:hover { border-color: var(--signal); background: var(--surface-2); }

.teachers-card h3 {
  font-family: var(--font-display);
  font-size: 16px;
  margin: 0 0 8px;
  color: var(--ink);
}

.teachers-card p { margin: 0; color: var(--ink-soft); font-size: 13.5px; line-height: 1.55; }

@media (max-width: 900px) {
  .teachers-head { grid-template-columns: minmax(0, 1fr); gap: 0; }
  .teachers-head h1,
  .teachers-head .hero-sub,
  .teachers-head .hero-actions { grid-column: 1; grid-row: auto; }
}

@media (max-width: 640px) {
  .teachers .spec-list > div { grid-template-columns: minmax(0, 1fr); gap: 6px; }
  .teachers-table th, .teachers-table td { padding-right: 10px; }
}
