/* ============================================================
   STEMLinePK — Oscilloscope HUD design system
   CH1 = phosphor green (primary)   CH2 = amber (secondary)
   ============================================================ */

/* --- Typefaces -------------------------------------------------
   Served from this site rather than fonts.googleapis.com. Two reasons:
   Google no longer receives every visitor's IP address just for a font,
   and an @import made the browser download and parse this stylesheet
   before it could even discover the font request. Only the latin subset
   and the weights actually used below are shipped; the browser still
   fetches only the faces a given page renders.

   Chakra Petch and IBM Plex are both under the SIL Open Font License
   1.1 — see assets/fonts/LICENSE.txt.
   --------------------------------------------------------------- */
@font-face {
  font-family: 'Chakra Petch';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('../fonts/chakra-petch-500.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Chakra Petch';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('../fonts/chakra-petch-600.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Chakra Petch';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('../fonts/chakra-petch-700.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('../fonts/ibm-plex-mono-400.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('../fonts/ibm-plex-mono-500.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('../fonts/ibm-plex-mono-600.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'IBM Plex Sans';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('../fonts/ibm-plex-sans-400.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'IBM Plex Sans';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('../fonts/ibm-plex-sans-500.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'IBM Plex Sans';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('../fonts/ibm-plex-sans-600.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
:root {
  /* --- instrument palette --- */
  --void:        #05090a;
  --void-2:      #070e10;
  --panel:       #0a1214;
  --panel-2:     #0d191b;
  --grid:        #12201e;
  --grid-hot:    #1b3330;

  --ch1:         #4dffb0;   /* phosphor green */
  --ch1-hot:     #b9ffdf;   /* trace core / highlight */
  --ch1-dim:     #2a8f66;
  --ch1-glow:    rgba(77, 255, 176, .35);

  --ch2:         #ffb300;   /* amber channel */
  --ch2-dim:     #9c6f0a;
  --ch2-glow:    rgba(255, 179, 0, .28);

  --ch3:         #38bdf8;   /* third trace, used sparingly in diagrams */

  --ink:         #d6e6e2;
  --ink-2:       #93aca7;
  --ink-3:       #5d7570;
  --danger:      #ff5f56;

  /* tints and scrims — redefined wholesale by the light theme below */
  --tint-1:      rgba(77, 255, 176, .05);
  --tint-2:      rgba(77, 255, 176, .07);
  --tint-3:      rgba(77, 255, 176, .09);
  --tint-4:      rgba(77, 255, 176, .13);
  --tint-a:      rgba(255, 179, 0, .06);
  --ch1-glow-2:  rgba(77, 255, 176, .15);
  --scan:        rgba(0, 0, 0, .16);
  --scrim-0:     rgba(5, 9, 10, 0);
  --scrim-2:     rgba(5, 9, 10, .2);
  --scrim-3:     rgba(5, 9, 10, .35);
  --scrim-5:     rgba(5, 9, 10, .55);
  --scrim-8:     rgba(5, 9, 10, .82);
  --scrim-9:     rgba(5, 9, 10, .94);
  --shadow:      0 18px 50px rgba(0, 0, 0, .5);

  /* --- type --- */
  --display: 'Chakra Petch', 'Segoe UI', system-ui, sans-serif;
  --body:    'IBM Plex Sans', 'Segoe UI', system-ui, sans-serif;
  --mono:    'IBM Plex Mono', ui-monospace, 'Cascadia Mono', Consolas, monospace;

  /* --- scale --- */
  --step--1: clamp(.75rem, .72rem + .12vw, .82rem);
  --step-0:  clamp(.95rem, .92rem + .16vw, 1.05rem);
  --step-1:  clamp(1.15rem, 1.08rem + .35vw, 1.4rem);
  --step-2:  clamp(1.5rem, 1.35rem + .75vw, 2.1rem);
  --step-3:  clamp(1.6rem, 1.15rem + 2.2vw, 3.4rem);
  --step-4:  clamp(1.95rem, 1.2rem + 3.6vw, 5.2rem);

  --rail: 56px;
  --maxw: 1220px;
  --ease: cubic-bezier(.22, .61, .36, 1);
}


/* ============================================================
   LIGHT THEME — same instrument, daylight readout.
   Only tokens change; not one rule below is duplicated.
   ============================================================ */
:root[data-theme="light"] {
  --void:        #f7faf8;
  --void-2:      #eef4f1;
  --panel:       #ffffff;
  --panel-2:     #f3f8f5;
  --grid:        #dfeae5;
  --grid-hot:    #c2d8cf;

  --ch1:         #0b7f52;   /* phosphor green, darkened for white paper */
  --ch1-hot:     #055636;
  --ch1-dim:     #57ad8a;
  --ch1-glow:    rgba(11, 127, 82, .22);
  --ch1-glow-2:  rgba(11, 127, 82, .10);

  --ch2:         #a35c00;   /* amber channel, likewise */
  --ch2-dim:     #d7a866;
  --ch2-glow:    rgba(163, 92, 0, .20);

  --ch3:         #0369a1;

  --ink:         #10201c;
  --ink-2:       #46615a;
  --ink-3:       #7b918b;
  --danger:      #c0342b;

  --tint-1:      rgba(11, 127, 82, .045);
  --tint-2:      rgba(11, 127, 82, .07);
  --tint-3:      rgba(11, 127, 82, .10);
  --tint-4:      rgba(11, 127, 82, .15);
  --tint-a:      rgba(163, 92, 0, .07);

  --scan:        rgba(11, 127, 82, .05);
  --scrim-0:     rgba(247, 250, 248, 0);
  --scrim-2:     rgba(247, 250, 248, .25);
  --scrim-3:     rgba(247, 250, 248, .45);
  --scrim-5:     rgba(247, 250, 248, .7);
  --scrim-9:     rgba(247, 250, 248, .95);
  --scrim-8:     rgba(247, 250, 248, .88);
  --shadow:      0 14px 38px rgba(16, 32, 28, .10);
}

/* The glow that reads as phosphor on black reads as smudge on white. */
:root[data-theme="light"] .hero h1 { text-shadow: none; }
:root[data-theme="light"] .scan { opacity: .5; }
:root[data-theme="light"] body { background: var(--void); }

/* ============================================================
   THEME TOGGLE ICON
   ============================================================ */
/* Two cells in a bordered pill: sun on the left, moon on the right, the active
   one filled. It shows both options at once, so it reads as a theme switch
   without a label — which the lone icon did not. Sized to line up with the
   other 32px rail controls. */
.theme-seg { display: inline-flex; border: 1px solid var(--grid-hot); border-radius: 5px; overflow: hidden; height: 32px; }
.seg-cell {
  width: 30px; display: grid; place-items: center; cursor: pointer;
  background: var(--panel); color: var(--ink-3); border: 0;
  transition: background .18s var(--ease), color .18s var(--ease);
}
.seg-cell + .seg-cell { border-left: 1px solid var(--grid-hot); }
.seg-cell:hover { color: var(--ink); }
.seg-cell[aria-pressed="true"] { background: var(--ch1); color: var(--void); }
/* --void is the page ground: near-black in dark mode, off-white in light — so
   the lit icon always contrasts against the --ch1 green fill. */

/* ============================================================
   OFFLINE PLAYER FALLBACK (page opened from disk)
   ============================================================ */
.player-offline {
  position: relative; display: block; width: 100%; height: 100%;
  background: var(--void-2); overflow: hidden;
}
.player-offline img { width: 100%; height: 100%; object-fit: cover; opacity: .5; }
.player-offline .po-play {
  position: absolute; inset: 0; display: grid; place-items: center;
  font-size: 2.6rem; color: var(--ch1); text-shadow: 0 0 24px var(--ch1-glow);
}
.player-offline .po-note {
  position: absolute; left: 0; right: 0; bottom: 0; display: block;
  padding: .8rem 1rem; background: var(--scrim-9);
  border-top: 1px solid var(--grid-hot); font-size: .82rem; line-height: 1.45;
}
.player-offline .po-note strong { display: block; color: var(--ch2); font-weight: 600; }
.player-offline .po-note em { display: block; color: var(--ink-2); font-style: normal; }

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

html {
  /* No overflow guard here at all. Setting it on either <html> or <body>
     can make that element the scroll container, and a sticky header inside
     a scroll container stops tracking the screen. The pages are checked for
     sideways overflow at build time instead, which is the real fix. */
  scroll-behavior: smooth; -webkit-text-size-adjust: 100%;
  /* Jumping to #quiz used to put the heading directly under the sticky
     header. This keeps the target clear of it. */
  scroll-padding-top: calc(var(--rail) + env(safe-area-inset-top, 0px) + 12px);
}

body {
  /* 100vh on a phone means the viewport with the browser bars hidden, so a
     full-height block runs underneath them. dvh is the height actually
     visible right now; vh stays as the fallback for older browsers. */
  min-height: 100vh;
  min-height: 100dvh;
  background: var(--void);
  color: var(--ink);
  font-family: var(--body);
  font-size: var(--step-0);
  line-height: 1.65;
  /* Nothing here may set overflow: it would make body a scroll container and
     break the sticky header. The guard lives on <html> instead. */
  -webkit-font-smoothing: antialiased;
}

img, svg, canvas { display: block; max-width: 100%; }
button, input, select, textarea { font: inherit; color: inherit; }
a { color: var(--ch1); text-decoration: none; }

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

::selection { background: var(--ch1); color: var(--void); }

/* scrollbar */
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: var(--void-2); }
::-webkit-scrollbar-thumb { background: var(--grid-hot); border: 2px solid var(--void-2); }
::-webkit-scrollbar-thumb:hover { background: var(--ch1-dim); }

/* ============================================================
   AMBIENT — graticule + scanline
   ============================================================ */
.graticule {
  position: fixed; inset: 0; z-index: 0; pointer-events: none;
  background-image:
    linear-gradient(var(--grid) 1px, transparent 1px),
    linear-gradient(90deg, var(--grid) 1px, transparent 1px);
  background-size: 48px 48px;
  mask-image: radial-gradient(ellipse 90% 70% at 50% 30%, #000 20%, transparent 100%);
  opacity: .55;
}
.vignette {
  position: fixed; inset: 0; z-index: 0; pointer-events: none;
  background: radial-gradient(ellipse 100% 80% at 50% 0%, var(--tint-1), transparent 60%);
}
.scanline {
  position: fixed; inset: 0; z-index: 900; pointer-events: none;
  background: repeating-linear-gradient(180deg, var(--scan) 0 1px, transparent 1px 3px);
  opacity: .5; mix-blend-mode: multiply;
}

/* ============================================================
   RAIL (top nav)
   ============================================================ */
.rail {
  position: sticky; top: 0; z-index: 100;
  /* A FIXED height meant anything taller than 56px — a phone with larger
     system text, a control that grew a little — overflowed the bar and was
     cut in half. min-height lets the bar grow to fit whatever is in it.
     The safe-area inset is kept so a notch is still cleared; it resolves to
     zero everywhere else. */
  min-height: calc(var(--rail) + env(safe-area-inset-top, 0px));
  padding-top: env(safe-area-inset-top, 0px);
  padding-bottom: .3rem;
  display: flex; align-items: center; gap: 1.25rem;
  padding-left: max(clamp(1rem, 3vw, 2rem), env(safe-area-inset-left, 0px));
  padding-right: max(clamp(1rem, 3vw, 2rem), env(safe-area-inset-right, 0px));
  background: var(--scrim-8);
  backdrop-filter: blur(14px);
  border-bottom: 1px solid var(--grid-hot);
}
/* The 82%-opaque header only reads as solid because of the blur behind it.
   Plenty of mobile browsers do not support backdrop-filter, and there the
   page scrolls visibly through the header — which looks exactly like the
   header being covered over. Those browsers get an opaque bar instead. */
@supports not (backdrop-filter: blur(1px)) {
  .rail { background: var(--void); }
  .rail nav { background: var(--void); }
}
.rail::after {
  content: ''; position: absolute; left: 0; right: 0; bottom: -1px; height: 1px;
  background: linear-gradient(90deg, transparent, var(--ch1-glow) 30%, var(--ch1-glow) 70%, transparent);
}

.mark { display: flex; align-items: center; gap: .6rem; flex-shrink: 0; }
.mark canvas { width: 42px; height: 22px; }
.mark b {
  font-family: var(--display); font-weight: 700; font-size: 1.05rem;
  letter-spacing: .06em; color: var(--ink); white-space: nowrap;
}
.mark b i { color: var(--ch1); font-style: normal; text-shadow: 0 0 12px var(--ch1-glow); }
.mark small {
  font-family: var(--mono); font-size: .58rem; color: var(--ch2);
  border: 1px solid var(--ch2-dim); padding: .05rem .3rem; letter-spacing: .1em;
}

.rail nav { display: flex; gap: .25rem; margin-left: auto; align-items: center; }
.rail nav a {
  font-family: var(--mono); font-size: .74rem; letter-spacing: .12em; text-transform: uppercase;
  color: var(--ink-2); padding: .5rem .75rem; position: relative;
  transition: color .18s var(--ease);
}
.rail nav a::after {
  content: ''; position: absolute; left: .75rem; right: .75rem; bottom: .3rem; height: 1px;
  background: var(--ch1); transform: scaleX(0); transform-origin: left;
  transition: transform .28s var(--ease);
}
.rail nav a:hover, .rail nav a[aria-current] { color: var(--ch1-hot); }
.rail nav a:hover::after, .rail nav a[aria-current]::after { transform: scaleX(1); }
.rail nav a[aria-current] { color: var(--ch1); }

.rail-tools { display: flex; align-items: center; gap: .5rem; flex-shrink: 0; }
/* Nothing in the bar may be squashed to half its size. */
.icon-btn, .burger { flex: 0 0 auto; }

.lvl {
  display: flex; align-items: center; gap: .5rem;
  font-family: var(--mono); font-size: .68rem; color: var(--ink-2);
  border: 1px solid var(--grid-hot); padding: .3rem .55rem; background: var(--panel);
}
.lvl b { color: var(--ch1); }
.lvl .bar { width: 46px; height: 4px; background: var(--grid-hot); position: relative; overflow: hidden; }
.lvl .bar i { position: absolute; inset: 0 auto 0 0; background: var(--ch1); box-shadow: 0 0 8px var(--ch1-glow); transition: width .6s var(--ease); }

.icon-btn {
  width: 32px; height: 32px; display: grid; place-items: center;
  background: var(--panel); border: 1px solid var(--grid-hot); color: var(--ink-2);
  cursor: pointer; transition: all .18s var(--ease); font-family: var(--mono); font-size: .8rem;
}
.icon-btn:hover { border-color: var(--ch1-dim); color: var(--ch1); }
.icon-btn[aria-pressed="true"] { color: var(--ch1); border-color: var(--ch1-dim); background: var(--tint-2); }

/* speaker toggle: waves when on, strike-through when off */
.snd .wv   { opacity: 0; transition: opacity .18s var(--ease); }
.snd .mute { opacity: 1; transition: opacity .18s var(--ease); }
.snd[aria-pressed="true"] .wv   { opacity: 1; }
.snd[aria-pressed="true"] .mute { opacity: 0; }

.burger { display: none; }

/* ============================================================
   PHONE AND TABLET HEADER
   Something on these devices is eating roughly the top 15-20px of the
   header. Four separate causes have been ruled out by testing on the
   device itself, so rather than keep guessing this is built to survive
   it: a taller bar, and the controls aligned to the BOTTOM of that bar
   instead of its middle. Whatever is lost is then empty space, not the
   buttons.
   ============================================================ */
@media (max-width: 860px) {
  :root { --rail: 78px; }
  .rail {
    align-items: flex-end;
    padding-bottom: .7rem;
    padding-top: calc(env(safe-area-inset-top, 0px) + 1.4rem);
  }
  .rail nav {
    position: fixed;
    inset: calc(var(--rail) + env(safe-area-inset-top, 0px)) 0 auto 0;
    flex-direction: column; align-items: stretch; gap: 0;
    background: var(--scrim-9); border-bottom: 1px solid var(--grid-hot);
    padding: .5rem; backdrop-filter: blur(20px);
    /* THE HEADER BUG.
       -120% is 1.2x the menu's OWN height. The menu is positioned at
       top: var(--rail), so its hidden bottom edge ends up at
       rail - 0.2 x height. With a 3-item menu about 157px tall and a 78px
       rail that is 78 - 31 = +47px — i.e. the last menu item sits 47px
       DOWN THE SCREEN, painted at 94% opacity on top of the header.
       That is what has been covering the logo and the buttons: not
       clipping, an opaque panel parked over them. It never moved on
       scroll because it is position: fixed.

       Moving it up by its own height PLUS the offset it was given puts
       its bottom edge exactly at 0 whatever the menu's height. */
    transition: transform .3s var(--ease);
  }
  /* Open and closed are written with equal specificity, closed first, so the
     open state simply wins by coming later. Relying on one selector
     out-weighing the other is how this ends up silently stuck shut. */
  .rail nav:not([data-open="true"]) {
    transform: translateY(calc(-100% - var(--rail) - env(safe-area-inset-top, 0px) - 4px));
  }
  .rail nav[data-open="true"] { transform: translateY(0); }
  .rail nav a { padding: .8rem 1rem; font-size: .8rem; }
  .burger { display: grid; margin-left: auto; }
  .lvl .bar { display: none; }
}

/* ============================================================
   LAYOUT
   ============================================================ */
.wrap { max-width: var(--maxw); margin: 0 auto; padding: 0 clamp(1rem, 3vw, 2rem); position: relative; z-index: 1; }
main { position: relative; z-index: 1; }
.section { padding: clamp(3rem, 7vw, 5.5rem) 0; }
.section-tight { padding: clamp(2rem, 4vw, 3rem) 0; }

.eyebrow {
  font-family: var(--mono); font-size: .7rem; letter-spacing: .22em; text-transform: uppercase;
  color: var(--ch2); display: flex; align-items: center; gap: .6rem; margin-bottom: 1rem;
}
.eyebrow::before { content: ''; width: 22px; height: 1px; background: var(--ch2); }

h1, h2, h3, h4 { font-family: var(--display); font-weight: 600; line-height: 1.12; letter-spacing: -.01em; }

/* Bold body text is 600, not the browser default of 700. That was already
   the rule inside a lesson (.prose strong) but not anywhere else, so the
   same <strong> rendered differently depending on the page. Settling on
   600 makes it consistent and means the 700 weight of the body face never
   has to be downloaded. Rules that set their own weight still win. */
b, strong { font-weight: 600; }
h1 { font-size: var(--step-4); }
h2 { font-size: var(--step-3); }
h3 { font-size: var(--step-2); }
h4 { font-size: var(--step-1); }

.lede { font-size: var(--step-1); color: var(--ink-2); max-width: 62ch; line-height: 1.55; }
.muted { color: var(--ink-2); }
.dim { color: var(--ink-3); }
.mono { font-family: var(--mono); }

/* divider — the trace, reused as a rule */
.trace-rule { width: 100%; height: 26px; display: block; opacity: .85; }

/* ============================================================
   HERO
   ============================================================ */
.hero { position: relative; min-height: min(88vh, 780px); display: flex; align-items: center; overflow: hidden; }
.hero-scope { position: absolute; inset: 0; width: 100%; height: 100%; z-index: 0; }
/* Scrim: keeps the trace visible as atmosphere without running through the copy. */
.hero::after {
  content: ''; position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background: linear-gradient(100deg, var(--void) 10%, var(--scrim-9) 34%, var(--scrim-3) 58%, var(--scrim-0) 80%);
}
@media (max-width: 900px) {
  .hero::after { background: linear-gradient(180deg, var(--scrim-9) 40%, var(--scrim-5) 75%, var(--scrim-2) 100%); }
}
.hero .wrap { position: relative; z-index: 2; width: 100%; }
.hero-grid { display: grid; grid-template-columns: minmax(0, 1.15fr) minmax(0, .85fr); gap: clamp(2rem, 5vw, 4rem); align-items: center; }
@media (max-width: 900px) { .hero-grid { grid-template-columns: 1fr; } }

.hero h1 { margin: .4rem 0 1.2rem; }
.hero h1 em {
  font-style: normal; color: var(--ch1);
  text-shadow: 0 0 30px var(--ch1-glow), 0 0 60px var(--ch1-glow-2);
}
.hero-cta { display: flex; flex-wrap: wrap; gap: .8rem; margin-top: 2rem; }

.readout {
  border: 1px solid var(--grid-hot); background: linear-gradient(160deg, var(--panel), var(--void-2));
  padding: 1.25rem; font-family: var(--mono); font-size: .78rem;
}
.readout-row { display: flex; justify-content: space-between; gap: 1rem; padding: .5rem 0; border-bottom: 1px dashed var(--grid-hot); }
.readout-row:last-child { border-bottom: 0; }
.readout-row span { color: var(--ink-3); letter-spacing: .08em; text-transform: uppercase; font-size: .68rem; }
.readout-row b { color: var(--ch1); font-weight: 500; }
.readout-row b.amber { color: var(--ch2); }

/* ============================================================
   BUTTONS
   ============================================================ */
.btn {
  --c: var(--ch1);
  position: relative; display: inline-flex; align-items: center; gap: .55rem;
  font-family: var(--mono); font-size: .78rem; letter-spacing: .14em; text-transform: uppercase;
  padding: .85rem 1.5rem; background: transparent; color: var(--c);
  border: 1px solid var(--c); cursor: pointer; overflow: hidden;
  transition: color .22s var(--ease), box-shadow .22s var(--ease);
}
.btn::before {
  content: ''; position: absolute; inset: 0; background: var(--c);
  transform: translateX(-101%); transition: transform .32s var(--ease); z-index: -1;
}
.btn > * { position: relative; z-index: 1; }
.btn:hover { color: var(--void); box-shadow: 0 0 26px -4px var(--c); }
.btn:hover::before { transform: translateX(0); }
.btn:disabled { opacity: .4; cursor: not-allowed; }
.btn:disabled:hover { color: var(--c); box-shadow: none; }
.btn:disabled:hover::before { transform: translateX(-101%); }

.btn-solid { background: var(--c); color: var(--void); font-weight: 600; }
.btn-solid:hover { box-shadow: 0 0 30px -4px var(--c); color: var(--void); }
.btn-ghost { --c: var(--ink-2); border-color: var(--grid-hot); }
.btn-ghost:hover { --c: var(--ch1); }
.btn-amber { --c: var(--ch2); }
.btn-danger { --c: var(--danger); }
.btn-sm { padding: .55rem 1rem; font-size: .7rem; }

/* ============================================================
   PANEL / CARD — corner brackets
   ============================================================ */
.panel {
  position: relative; background: var(--panel); border: 1px solid var(--grid-hot);
  padding: clamp(1.1rem, 2.5vw, 1.75rem);
}
.panel::before, .panel::after {
  content: ''; position: absolute; width: 12px; height: 12px; pointer-events: none;
  border-color: var(--ch1-dim); transition: border-color .25s var(--ease);
}
.panel::before { top: -1px; left: -1px; border-top: 2px solid; border-left: 2px solid; }
.panel::after  { bottom: -1px; right: -1px; border-bottom: 2px solid; border-right: 2px solid; }

.card {
  position: relative; display: flex; flex-direction: column;
  background: linear-gradient(165deg, var(--panel-2), var(--void-2));
  border: 1px solid var(--grid-hot); padding: 1.35rem;
  transition: border-color .25s var(--ease), transform .25s var(--ease), box-shadow .25s var(--ease);
  will-change: transform;
}
.card::before, .card::after {
  content: ''; position: absolute; width: 14px; height: 14px; pointer-events: none;
  border-color: var(--ch1-dim); opacity: 0; transition: opacity .25s var(--ease);
}
.card::before { top: -1px; left: -1px; border-top: 2px solid; border-left: 2px solid; }
.card::after  { bottom: -1px; right: -1px; border-bottom: 2px solid; border-right: 2px solid; }
.card:hover, .card:focus-within {
  border-color: var(--ch1-dim); transform: translateY(-3px);
  box-shadow: 0 14px 40px -22px var(--ch1-glow);
}
.card:hover::before, .card:hover::after,
.card:focus-within::before, .card:focus-within::after { opacity: 1; }

.card h3, .card h4 { margin-bottom: .5rem; }
.card p { color: var(--ink-2); font-size: .92rem; }
.card-foot { margin-top: auto; padding-top: 1.1rem; display: flex; align-items: center; justify-content: space-between; gap: 1rem; }

.grid { display: grid; gap: 1.1rem; }
.g2 { grid-template-columns: repeat(auto-fit, minmax(290px, 1fr)); }
.g3 { grid-template-columns: repeat(auto-fit, minmax(255px, 1fr)); }
.g4 { grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); }

/* ============================================================
   TAGS / CHIPS / BADGES
   ============================================================ */
.chip {
  display: inline-flex; align-items: center; gap: .35rem;
  font-family: var(--mono); font-size: .64rem; letter-spacing: .12em; text-transform: uppercase;
  padding: .25rem .55rem; border: 1px solid var(--grid-hot); color: var(--ink-2); white-space: nowrap;
}
.chip-ch1 { color: var(--ch1); border-color: var(--ch1-dim); background: var(--tint-2); }
.chip-ch2 { color: var(--ch2); border-color: var(--ch2-dim); background: var(--tint-a); }
.chip-done { color: var(--void); background: var(--ch1); border-color: var(--ch1); font-weight: 600; }

.chips { display: flex; flex-wrap: wrap; gap: .4rem; }

/* ============================================================
   COURSE / VIDEO
   ============================================================ */
.course-hero { display: grid; grid-template-columns: minmax(0,1fr) 340px; gap: 2rem; align-items: start; }
@media (max-width: 940px) { .course-hero { grid-template-columns: 1fr; } }

.player-shell { position: relative; border: 1px solid var(--grid-hot); background: #000; padding: 8px; }
.player-shell::before, .player-shell::after {
  content: ''; position: absolute; width: 16px; height: 16px; border-color: var(--ch1);
}
.player-shell::before { top: -1px; left: -1px; border-top: 2px solid; border-left: 2px solid; }
.player-shell::after  { bottom: -1px; right: -1px; border-bottom: 2px solid; border-right: 2px solid; }
.player-frame { position: relative; aspect-ratio: 16 / 9; background: var(--void-2); overflow: hidden; }
.player-frame iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
.player-empty { position: absolute; inset: 0; display: grid; place-content: center; gap: .6rem; text-align: center; font-family: var(--mono); font-size: .78rem; color: var(--ink-3); }

.playlist { border: 1px solid var(--grid-hot); background: var(--panel); max-height: 520px; overflow-y: auto; }
.playlist-head {
  position: sticky; top: 0; background: var(--panel-2); border-bottom: 1px solid var(--grid-hot);
  padding: .8rem 1rem; font-family: var(--mono); font-size: .7rem; letter-spacing: .14em;
  text-transform: uppercase; color: var(--ink-2); display: flex; justify-content: space-between; z-index: 2;
}
.pl-item {
  display: grid; grid-template-columns: 34px 1fr auto; gap: .75rem; align-items: center;
  width: 100%; text-align: left; padding: .8rem 1rem; background: transparent;
  border: 0; border-bottom: 1px solid var(--grid); cursor: pointer;
  transition: background .18s var(--ease);
}
.pl-item:hover { background: var(--tint-1); }
.pl-item[aria-current="true"] { background: var(--tint-3); }
.pl-item[aria-current="true"] .pl-idx { color: var(--void); background: var(--ch1); border-color: var(--ch1); }
.pl-idx {
  width: 28px; height: 28px; display: grid; place-items: center;
  font-family: var(--mono); font-size: .72rem; color: var(--ink-3);
  border: 1px solid var(--grid-hot);
}
.pl-title { font-size: .88rem; color: var(--ink); line-height: 1.35; }
.pl-item[aria-current="true"] .pl-title { color: var(--ch1-hot); }
.pl-meta { font-family: var(--mono); font-size: .68rem; color: var(--ink-3); }

/* ============================================================
   LESSON CONTENT
   ============================================================ */
.lesson { display: grid; grid-template-columns: 220px minmax(0, 1fr); gap: clamp(1.5rem, 4vw, 3rem); align-items: start; }
/* minmax(0, 1fr), not 1fr. A bare 1fr track refuses to shrink below the
   min-content width of its children, so one long unbroken formula or code
   span pushes the whole page sideways on a narrow phone. */
@media (max-width: 900px) { .lesson { grid-template-columns: minmax(0, 1fr); } .lesson-toc { position: static !important; } }

.lesson-toc { position: sticky; top: calc(var(--rail) + 1.5rem); }
.lesson-toc ol { list-style: none; counter-reset: t; }
.lesson-toc a {
  display: block; padding: .45rem 0 .45rem .95rem; font-size: .82rem; color: var(--ink-3);
  border-left: 2px solid var(--grid-hot); transition: all .2s var(--ease);
}
.lesson-toc a:hover, .lesson-toc a.on { color: var(--ch1); border-left-color: var(--ch1); }

.prose > * + * { margin-top: 1.05rem; }
.prose p { color: var(--ink-2); }
.prose strong { color: var(--ink); font-weight: 600; }
.prose em { color: var(--ch1); font-style: normal; }
.prose ul, .prose ol { padding-left: 1.3rem; color: var(--ink-2); }
.prose li + li { margin-top: .4rem; }
.prose li::marker { color: var(--ch1-dim); }
.prose code {
  font-family: var(--mono); font-size: .88em; color: var(--ch1-hot);
  background: var(--tint-3); padding: .1rem .35rem; border: 1px solid var(--tint-4);
}
.prose h3 { margin-top: 2.5rem; }

.block { margin: 2.75rem 0; }
.block > * + * { margin-top: 1.15rem; }
.block-head { display: flex; align-items: baseline; gap: .75rem; margin-bottom: .9rem; }
.block-head + * { margin-top: 0; }
.block-head .n {
  font-family: var(--mono); font-size: .7rem; color: var(--ch2); letter-spacing: .1em;
  border: 1px solid var(--ch2-dim); padding: .1rem .4rem;
}

.formula {
  font-family: var(--mono); font-size: clamp(1rem, 2.4vw, 1.35rem); color: var(--ch1-hot);
  text-align: center; padding: 1.5rem 1rem; background: var(--void-2);
  border: 1px solid var(--grid-hot); border-left: 3px solid var(--ch1);
  overflow-x: auto; letter-spacing: .02em;
}
/* A formula block often carries two or three related results, written as
   one string with newlines between them and runs of spaces standing in for
   columns. HTML collapses both, so on a phone the whole block ran together
   into one centred, arbitrarily wrapped line.

   Newlines become real lines; each space-separated item becomes an inline
   block that never breaks internally. So a formula and its annotation stay
   side by side when there is room and drop to the next line when there is
   not — instead of splitting mid-symbol. */
.formula .fx { display: block; }
.formula .fx + .fx { margin-top: .55rem; }
.formula .fxi { display: inline-block; white-space: nowrap; margin: .12rem .5rem; }

.formula small { display: block; margin-top: .7rem; font-size: .72rem; color: var(--ink-3); letter-spacing: .08em; font-family: var(--mono); }

.callout {
  border: 1px solid var(--ch1-dim); border-left: 3px solid var(--ch1);
  background: var(--tint-1); padding: 1.1rem 1.25rem;
}
.callout h4 { font-family: var(--mono); font-size: .74rem; letter-spacing: .16em; text-transform: uppercase; color: var(--ch1); margin-bottom: .5rem; }
.callout p { color: var(--ink-2); font-size: .93rem; }
.callout-warn { border-color: var(--ch2-dim); border-left-color: var(--ch2); background: var(--tint-a); }
.callout-warn h4 { color: var(--ch2); }

/* viz stage */
.stage { border: 1px solid var(--grid-hot); background: var(--void-2); }
.stage-bar {
  display: flex; align-items: center; justify-content: space-between; gap: 1rem; flex-wrap: wrap;
  padding: .55rem .85rem; background: var(--panel-2); border-bottom: 1px solid var(--grid-hot);
  font-family: var(--mono); font-size: .68rem; letter-spacing: .12em; text-transform: uppercase; color: var(--ink-3);
}
.stage-bar .live { display: flex; align-items: center; gap: .4rem; color: var(--ch1); }
.stage-bar .live::before {
  content: ''; width: 6px; height: 6px; border-radius: 50%; background: var(--ch1);
  box-shadow: 0 0 8px var(--ch1); animation: pulse 1.8s infinite;
}
@keyframes pulse { 0%,100% { opacity: 1 } 50% { opacity: .25 } }
.stage canvas { width: 100%; height: auto; display: block; }
.stage-ctl {
  display: flex; flex-wrap: wrap; gap: 1rem 1.5rem; align-items: center;
  padding: .85rem; border-top: 1px solid var(--grid-hot); background: var(--panel);
}
.ctl { display: flex; align-items: center; gap: .6rem; font-family: var(--mono); font-size: .7rem; color: var(--ink-2); }
.ctl label { letter-spacing: .1em; text-transform: uppercase; font-size: .65rem; color: var(--ink-3); white-space: nowrap; }
.ctl output { color: var(--ch1); min-width: 3.4em; }

input[type="range"] {
  -webkit-appearance: none; appearance: none; width: 130px; height: 3px;
  background: var(--grid-hot); cursor: pointer; outline-offset: 6px;
}
input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none; width: 13px; height: 13px; background: var(--ch1);
  box-shadow: 0 0 10px var(--ch1-glow); cursor: pointer; border-radius: 0; transform: rotate(45deg);
}
input[type="range"]::-moz-range-thumb {
  width: 12px; height: 12px; background: var(--ch1); border: 0; border-radius: 0; cursor: pointer;
}

/* Four or more options make a segmented control wider than a phone screen.
   Both the control and its row wrap rather than pushing the whole page into
   a horizontal scroll — measured at 390px on the sorting diagram, which
   overflowed by 23px. */
.seg { display: inline-flex; flex-wrap: wrap; max-width: 100%; border: 1px solid var(--grid-hot); }
.ctl { flex-wrap: wrap; }
.seg button {
  background: transparent; border: 0; border-right: 1px solid var(--grid-hot);
  padding: .35rem .7rem; font-family: var(--mono); font-size: .68rem; color: var(--ink-3);
  cursor: pointer; letter-spacing: .08em; text-transform: uppercase; transition: all .18s var(--ease);
}
.seg button:last-child { border-right: 0; }
.seg button:hover { color: var(--ch1); }
.seg button[aria-pressed="true"] { background: var(--ch1); color: var(--void); font-weight: 600; }

/* data table */
.tbl { width: 100%; border-collapse: collapse; font-family: var(--mono); font-size: .8rem; }
.tbl th, .tbl td { padding: .6rem .8rem; text-align: left; border-bottom: 1px solid var(--grid); }
.tbl th { color: var(--ch2); font-size: .66rem; letter-spacing: .14em; text-transform: uppercase; font-weight: 500; border-bottom-color: var(--grid-hot); }
.tbl td { color: var(--ink-2); }
.tbl tr:hover td { background: var(--tint-1); color: var(--ink); }
.tbl-wrap { overflow-x: auto; border: 1px solid var(--grid-hot); background: var(--panel); }

/* ============================================================
   QUIZ
   ============================================================ */
.quiz { border: 1px solid var(--ch1-dim); background: linear-gradient(170deg, var(--panel-2), var(--void-2)); }
.quiz-head {
  padding: 1rem 1.25rem; border-bottom: 1px solid var(--grid-hot);
  display: flex; align-items: center; justify-content: space-between; gap: 1rem; flex-wrap: wrap;
}
.quiz-head h3 { font-size: var(--step-1); }
.quiz-progress { font-family: var(--mono); font-size: .7rem; color: var(--ink-3); letter-spacing: .1em; }
.quiz-body { padding: 1.5rem 1.25rem; }
.quiz-q { font-family: var(--display); font-size: var(--step-1); font-weight: 500; margin-bottom: 1.25rem; line-height: 1.35; }

.opt {
  display: grid; grid-template-columns: 30px 1fr; gap: .8rem; align-items: start;
  width: 100%; text-align: left; padding: .85rem 1rem; margin-bottom: .6rem;
  background: var(--void-2); border: 1px solid var(--grid-hot); color: var(--ink-2);
  cursor: pointer; transition: all .18s var(--ease); font-size: .93rem; line-height: 1.5;
}
.opt:hover:not(:disabled) { border-color: var(--ch1-dim); color: var(--ink); background: var(--tint-1); }
.opt .k {
  font-family: var(--mono); font-size: .72rem; color: var(--ink-3);
  border: 1px solid var(--grid-hot); width: 24px; height: 24px; display: grid; place-items: center;
}
.opt:disabled { cursor: default; }
.opt.correct { border-color: var(--ch1); background: var(--tint-4); color: var(--ch1-hot); }
.opt.correct .k { background: var(--ch1); color: var(--void); border-color: var(--ch1); }
.opt.wrong { border-color: var(--danger); background: rgba(255,95,86,.08); color: #ffb3ae; }
.opt.wrong .k { background: var(--danger); color: var(--void); border-color: var(--danger); }

.why {
  margin-top: .9rem; padding: .9rem 1.1rem; border-left: 2px solid var(--ch2);
  background: var(--tint-a); font-size: .89rem; color: var(--ink-2);
}
.why b { color: var(--ch2); font-family: var(--mono); font-size: .7rem; letter-spacing: .14em; text-transform: uppercase; display: block; margin-bottom: .3rem; }

.quiz-foot { padding: 1rem 1.25rem; border-top: 1px solid var(--grid-hot); display: flex; justify-content: space-between; align-items: center; gap: 1rem; }

.result { text-align: center; padding: 2rem 1.25rem; }
.result .score { font-family: var(--display); font-size: clamp(3rem, 9vw, 5rem); line-height: 1; color: var(--ch1); text-shadow: 0 0 40px var(--ch1-glow); }
.result .score small { font-size: .3em; color: var(--ink-3); text-shadow: none; }
.result canvas { margin: 1.5rem auto; }

/* ============================================================
   ADMIN
   ============================================================ */
.gate { min-height: calc(100vh - var(--rail)); min-height: calc(100dvh - var(--rail)); display: grid; place-items: center; padding: 2rem; }
.gate-box { width: min(420px, 100%); }
.gate-box .term { font-family: var(--mono); font-size: .82rem; color: var(--ch1); line-height: 1.9; }
.gate-box .term span { color: var(--ink-3); }

.field { display: block; margin-bottom: 1rem; }
.field > span {
  display: block; font-family: var(--mono); font-size: .66rem; letter-spacing: .14em;
  text-transform: uppercase; color: var(--ink-3); margin-bottom: .4rem;
}
.field > span i { color: var(--ch2); font-style: normal; }
input[type="text"], input[type="password"], input[type="url"], input[type="number"], select, textarea {
  width: 100%; background: var(--void-2); border: 1px solid var(--grid-hot);
  padding: .65rem .8rem; font-family: var(--mono); font-size: .85rem; color: var(--ink);
  transition: border-color .18s var(--ease), box-shadow .18s var(--ease);
}
input:focus, select:focus, textarea:focus {
  outline: none; border-color: var(--ch1-dim); box-shadow: 0 0 0 1px var(--ch1-dim), 0 0 18px -8px var(--ch1);
}
textarea { resize: vertical; min-height: 90px; line-height: 1.6; }
input::placeholder, textarea::placeholder { color: var(--ink-3); opacity: .7; }

/* A display rule on an element beats the browser's default [hidden]
   styling, so hiding a grid or flex container by setting .hidden did
   nothing at all — the studio's Courses panel stayed on screen when the
   Lesson videos tab was selected. This makes the attribute win. */
[hidden] { display: none !important; }

.admin { display: grid; grid-template-columns: 300px minmax(0, 1fr); gap: 1.25rem; align-items: start; }
@media (max-width: 940px) { .admin { grid-template-columns: 1fr; } }

.list-item {
  display: flex; align-items: center; gap: .7rem; width: 100%; text-align: left;
  padding: .7rem .85rem; background: var(--void-2); border: 1px solid var(--grid-hot);
  margin-bottom: .5rem; cursor: pointer; transition: all .18s var(--ease); color: var(--ink-2);
}
.list-item:hover { border-color: var(--ch1-dim); color: var(--ink); }
.list-item[aria-current="true"] { border-color: var(--ch1); background: var(--tint-3); color: var(--ch1-hot); }
.list-item .grab { font-family: var(--mono); color: var(--ink-3); font-size: .7rem; cursor: grab; }
.list-item .nm { flex: 1; font-size: .87rem; line-height: 1.3; }
.list-item.drag-over { border-color: var(--ch2); }

.row { display: flex; gap: .75rem; flex-wrap: wrap; align-items: flex-end; }
.row > * { flex: 1 1 180px; }
.row > .shrink { flex: 0 0 auto; }

.vid-row {
  display: grid; grid-template-columns: 28px 88px minmax(0,1fr) auto; gap: .75rem; align-items: center;
  padding: .6rem; border: 1px solid var(--grid-hot); background: var(--void-2); margin-bottom: .5rem;
}
@media (max-width: 700px) { .vid-row { grid-template-columns: 28px 1fr; } .vid-row .thumb { display: none; } }
.vid-row .thumb { width: 88px; aspect-ratio: 16/9; object-fit: cover; border: 1px solid var(--grid-hot); background: var(--void); }

.toast {
  position: fixed; bottom: 1.5rem; left: 50%; transform: translate(-50%, 200%);
  background: var(--panel); border: 1px solid var(--ch1); color: var(--ch1-hot);
  padding: .8rem 1.4rem; font-family: var(--mono); font-size: .78rem; letter-spacing: .06em;
  z-index: 999; box-shadow: 0 0 40px -10px var(--ch1-glow); transition: transform .35s var(--ease);
  max-width: 90vw; text-align: center;
}
.toast[data-show="true"] { transform: translate(-50%, 0); }
.toast[data-tone="warn"] { border-color: var(--ch2); color: var(--ch2); box-shadow: 0 0 40px -10px var(--ch2-glow); }

/* ============================================================
   MISC
   ============================================================ */
.empty {
  text-align: center; padding: clamp(2.5rem, 8vw, 5rem) 1rem;
  border: 1px dashed var(--grid-hot); background: var(--void-2);
}
.empty h3 { color: var(--ink-2); margin-bottom: .6rem; }
.empty p { color: var(--ink-3); max-width: 46ch; margin: 0 auto 1.5rem; font-size: .92rem; }

.bar-track { height: 4px; background: var(--grid-hot); overflow: hidden; }
.bar-track i { display: block; height: 100%; background: var(--ch1); box-shadow: 0 0 10px var(--ch1-glow); transition: width .7s var(--ease); }

.crumbs { font-family: var(--mono); font-size: .72rem; color: var(--ink-3); letter-spacing: .08em; margin-bottom: 1rem; }
.crumbs a { color: var(--ink-2); }
.crumbs a:hover { color: var(--ch1); }
.crumbs span { color: var(--grid-hot); margin: 0 .5rem; }

footer {
  border-top: 1px solid var(--grid-hot); margin-top: 4rem; padding: 2.5rem 0;
  font-family: var(--mono); font-size: .74rem; color: var(--ink-3);
}
footer .wrap { display: flex; flex-wrap: wrap; gap: 1rem; justify-content: space-between; align-items: center; }
footer a { color: var(--ink-2); }
footer a:hover { color: var(--ch1); }

/* boot */
#boot {
  position: fixed; inset: 0; z-index: 9999; background: var(--void);
  display: grid; place-content: center; gap: 1rem; padding: 2rem;
  font-family: var(--mono); font-size: .8rem; color: var(--ch1);
  transition: opacity .5s var(--ease), visibility .5s;
}
#boot[data-done="true"] { opacity: 0; visibility: hidden; }
#boot .line { opacity: 0; animation: bootline .3s var(--ease) forwards; }
#boot .line span { color: var(--ink-3); }
@keyframes bootline { to { opacity: 1 } }

/* reveal on scroll */
/* Content is visible by default. It is only hidden once JavaScript has
   armed it, which it does immediately before wiring up the observer that
   will bring it back. If the script never runs, fails, or is blocked, the
   reader still sees everything — a fade-in is not worth the risk of a
   blank page, and this one has hidden content more than once. */
.rise { opacity: 1; transform: none; }
.rise.armed { opacity: 0; transform: translateY(18px); transition: opacity .6s var(--ease), transform .6s var(--ease); }
.rise.armed.in { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .001ms !important; animation-iteration-count: 1 !important; transition-duration: .001ms !important; }
  html { scroll-behavior: auto; }
  .rise, .rise.armed { opacity: 1; transform: none; }
  #boot { display: none; }
}

/* ============================================================
   SYLLABUS — chapter list
   ============================================================ */
.chap { margin: 0 0 2.2rem; border: 1px solid var(--grid-hot); background: var(--panel); }
.chap-head {
  display: flex; align-items: center; gap: .9rem;
  padding: .85rem 1.2rem; border-bottom: 1px solid var(--grid-hot); background: var(--panel-2);
}
.chap-n {
  font-family: var(--mono); font-size: .72rem; letter-spacing: .1em;
  color: var(--ch2); border: 1px solid var(--ch2-dim); padding: .12rem .42rem; flex: none;
}
.chap-head h2, .chap-head h4 { font-size: 1.02rem; margin: 0; }

.chap-rows { display: flex; flex-direction: column; }

.chap-row {
  display: grid; grid-template-columns: 26px 1fr auto; align-items: center; gap: .8rem;
  padding: .8rem 1.2rem; border-top: 1px solid var(--grid);
  color: var(--ink); transition: background .16s var(--ease), color .16s var(--ease);
}
.chap-rows > :first-child { border-top: none; }
a.chap-row:hover { background: var(--tint-1); }
a.chap-row:hover .chap-title { color: var(--ch1); }

.chap-dot { font-family: var(--mono); color: var(--ch1); text-align: center; }
.chap-title { display: block; font-weight: 500; line-height: 1.35; }
.chap-meta { display: block; font-family: var(--mono); font-size: .68rem; color: var(--ink-3); margin-top: .22rem; }
.chap-go { color: var(--ch1); opacity: 0; transition: opacity .16s var(--ease); }
a.chap-row:hover .chap-go { opacity: 1; }

.chap-row.is-done .chap-dot { color: var(--ch1-hot); }
.chap-row.is-soon { color: var(--ink-3); cursor: default; }
.chap-row.is-soon .chap-dot { color: var(--ink-3); }
.chap-row.is-soon .chap-title { font-weight: 400; }

@media (max-width: 560px) {
  .chap-row { grid-template-columns: 20px 1fr; padding: .7rem .9rem; }
  .chap-go { display: none; }
  .chap-head { padding: .75rem .9rem; }
}

/* ============================================================
   TEXTBOOK BLOCKS — definition, symbol table, worked example,
   key points. These carry the notes-style structure that plain
   paragraphs cannot.
   ============================================================ */

/* --- definition --- */
.define {
  border-left: 3px solid var(--ch1);
  background: var(--tint-1);
  padding: .95rem 1.15rem .95rem 1.1rem;
  margin: 1.4rem 0;
}
.define-tag {
  display: block; font-family: var(--mono); font-size: .62rem;
  letter-spacing: .18em; text-transform: uppercase;
  color: var(--ch1); margin-bottom: .4rem;
}
.define p { margin: 0; }
.define b { color: var(--ink); font-weight: 600; }

/* --- symbols under a formula --- */
.where {
  margin: -.4rem 0 1.4rem;
  padding: .8rem 1.1rem;
  border: 1px solid var(--grid-hot); border-top: none;
  background: var(--panel-2);
  font-size: .88rem;
}
.where > div { display: grid; grid-template-columns: 4.4rem 1fr; gap: .5rem 1rem; padding: .2rem 0; }
.where dt { font-family: var(--mono); color: var(--ch2); font-size: .92rem; }
.where dd { margin: 0; color: var(--ink-2); }
.where dd i { display: block; font-family: var(--mono); font-style: normal; font-size: .72rem; color: var(--ink-3); margin-top: .1rem; }

/* --- worked example --- */
.worked {
  border: 1px solid var(--grid-hot);
  background: var(--panel);
  margin: 1.7rem 0;
}
.worked-head {
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
  padding: .6rem 1.1rem;
  border-bottom: 1px solid var(--grid-hot);
  background: var(--panel-2);
}
.worked-tag {
  font-family: var(--mono); font-size: .62rem; letter-spacing: .18em;
  text-transform: uppercase; color: var(--ch2);
}
.worked-marks { font-family: var(--mono); font-size: .68rem; color: var(--ink-3); }
.worked-q { margin: 0; padding: 1rem 1.1rem .2rem; font-weight: 500; }

.worked-steps { margin: 0; padding: .8rem 1.1rem 1rem 2.6rem; }
.worked-steps li { margin: 0 0 .75rem; padding-left: .25rem; }
.worked-steps li::marker { font-family: var(--mono); color: var(--ch1); font-size: .85rem; }
.step-do { display: block; }
.step-why {
  display: block; font-size: .82rem; color: var(--ink-3);
  border-left: 2px solid var(--grid-hot); padding-left: .7rem; margin-top: .3rem;
}
.worked-ans {
  margin: 0; padding: .75rem 1.1rem;
  border-top: 1px solid var(--grid-hot);
  background: var(--tint-1);
  font-family: var(--mono); font-size: .92rem; color: var(--ch1-hot);
}

/* --- key points --- */
.keypoints {
  border: 1px solid var(--ch1-dim);
  background: var(--tint-1);
  padding: 1rem 1.2rem;
  margin: 1.7rem 0;
}
.keypoints h4 {
  font-family: var(--mono); font-size: .64rem; letter-spacing: .18em;
  text-transform: uppercase; color: var(--ch1); margin-bottom: .6rem;
}
.keypoints ol { margin: 0; padding-left: 1.35rem; }
.keypoints li { margin-bottom: .45rem; font-size: .94rem; }
.keypoints li:last-child { margin-bottom: 0; }
.keypoints li::marker { font-family: var(--mono); color: var(--ch2); font-size: .82rem; }

@media (max-width: 560px) {
  .where > div { grid-template-columns: 3.4rem 1fr; }
  .worked-steps { padding-left: 2.1rem; }
}

/* ============================================================
   PER-SUBJECT PROGRESS ROWS on the Learn cards
   ============================================================ */
.subj-rows { display: flex; flex-direction: column; gap: .38rem; }
.subj-row {
  display: grid; grid-template-columns: 1fr 52px 40px;
  align-items: center; gap: .6rem;
  font-size: .82rem;
}
.subj-name { color: var(--ink-2); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.subj-bar { height: 3px; background: var(--grid-hot); position: relative; overflow: hidden; }
.subj-bar i { position: absolute; inset: 0 auto 0 0; background: var(--ch1); }
.subj-n { font-family: var(--mono); font-size: .7rem; color: var(--ch1); text-align: right; }
.subj-row.is-empty .subj-name,
.subj-row.is-empty .subj-n { color: var(--ink-3); }
.subj-row.is-empty .subj-bar i { background: var(--grid-hot); }

/* ============================================================
   FLASHCARD DECK — part of the lesson, sized like the quiz
   ============================================================ */
.deck {
  margin-top: 3.5rem;
  border: 1px solid var(--grid-hot);
  background: var(--panel);
}
.deck-head {
  display: flex; align-items: flex-start; justify-content: space-between; gap: 1rem;
  padding: 1.1rem 1.3rem;
  border-bottom: 1px solid var(--grid-hot);
  background: var(--panel-2);
}
.deck-head h3 { font-size: 1.1rem; margin: 0; }
.deck-progress {
  font-family: var(--mono); font-size: .7rem; letter-spacing: .1em;
  color: var(--ch2); white-space: nowrap; padding-top: .3rem;
}

.deck-face { padding: 1.3rem; display: flex; flex-direction: column; gap: 1rem; }

.deck-side { border: 1px solid var(--grid-hot); padding: 1.1rem 1.2rem; background: var(--void-2); }
.deck-front { border-left: 3px solid var(--ch1); }
.deck-back  { border-left: 3px solid var(--ch2); background: var(--tint-1); }
.deck-tag {
  display: block; font-family: var(--mono); font-size: .6rem;
  letter-spacing: .18em; text-transform: uppercase;
  color: var(--ink-3); margin-bottom: .5rem;
}
.deck-front .deck-tag { color: var(--ch1); }
.deck-back .deck-tag { color: var(--ch2); }
.deck-text { font-size: 1.02rem; line-height: 1.6; }
.deck-text code { font-size: .95em; }

.deck-foot {
  display: flex; gap: .7rem; flex-wrap: wrap;
  padding: 0 1.3rem 1.3rem;
}

.deck-done { text-align: center; padding: 1.5rem 1rem; }
.deck-done-n {
  font-family: var(--display); font-size: 2.6rem; line-height: 1;
  color: var(--ch1); margin-bottom: .6rem;
}
.deck-done p { color: var(--ink-2); max-width: 46ch; margin: 0 auto; font-size: .94rem; }

@media (max-width: 560px) {
  .deck-head { flex-direction: column; gap: .4rem; padding: .9rem 1rem; }
  .deck-face, .deck-foot { padding-left: 1rem; padding-right: 1rem; }
  .deck-foot .btn { flex: 1 1 auto; justify-content: center; }
}

/* ============================================================
   LIST INDENTS inside the textbook blocks

   `.prose ul, .prose ol` sets padding-left: 1.3rem and outranks a plain
   `.worked-steps` rule, so the numbers were being drawn outside the panel
   they belong to. These selectors match that specificity and give the
   markers room — enough for two digits in the mono face.
   ============================================================ */
.prose .worked-steps { padding-left: 2.9rem; }
.prose .keypoints ol { padding-left: 1.9rem; }
.prose .worked-steps li,
.prose .keypoints li { padding-left: .15rem; }
.prose .worked-steps li + li { margin-top: 0; }

/* The markers are the one place a number is meant to read as a readout. */
.prose .worked-steps li::marker,
.prose .keypoints li::marker { font-family: var(--mono); }
.prose .worked-steps li::marker { color: var(--ch1); font-size: .85rem; }
.prose .keypoints li::marker { color: var(--ch2); font-size: .82rem; }

@media (max-width: 560px) {
  .prose .worked-steps { padding-left: 2.2rem; }
  .prose .keypoints ol { padding-left: 1.6rem; }
}

/* ============================================================
   HEADING LEVELS on the generated pages

   The pre-rendered pages use a correct document outline — one h1
   for the page, h2 for each section — because that is what a
   crawler reads. The visual scale is set here so that semantics
   and appearance can differ without either being compromised.
   ============================================================ */
.prose .block-head h2 { font-size: var(--step-2); margin-top: 0; }
.prose .block + .block { margin-top: 2.75rem; }
.card h2, .card h3 { line-height: 1.25; }
.viz-caption {
  font-size: .86rem; margin-top: .8rem;
  padding-left: .9rem; border-left: 2px solid var(--grid-hot);
}

/* ============================================================
   EXAM QUESTIONS — practice under paper conditions

   Marks are shown where they are awarded, because that is where
   they are lost. The scheme sits in a <details> so the answer is
   in the page source for a crawler but out of sight until the
   student has had a go.
   ============================================================ */
.exam { margin-top: 3.5rem; }
.exam-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 1rem; margin-bottom: .6rem; }
.exam-head h2 { font-size: var(--step-2); margin: 0; }
.exam-lede { color: var(--ink-2); max-width: 64ch; margin-bottom: 1.6rem; font-size: .94rem; }

.exq { border: 1px solid var(--grid-hot); background: var(--panel); margin-bottom: 1.2rem; }
.exq-head {
  display: flex; align-items: center; gap: .7rem;
  padding: .6rem 1.1rem; border-bottom: 1px solid var(--grid-hot); background: var(--panel-2);
}
.exq-n {
  font-family: var(--mono); font-size: .68rem; letter-spacing: .12em;
  color: var(--ch1); border: 1px solid var(--ch1-dim); padding: .1rem .4rem;
}
.exq-src { font-family: var(--mono); font-size: .66rem; color: var(--ink-3); }
.exq-marks { font-family: var(--mono); font-size: .68rem; color: var(--ch2); margin-left: auto; }

.exq-q { padding: 1rem 1.1rem; line-height: 1.6; }
.exq-q + .exq-parts { margin-top: -.4rem; }
/* The exam section is a sibling of .prose, not a child, so these rules must
   not be scoped to it — that is exactly how the indent broke the first time. */
.exq-parts { padding: 0 1.1rem 0 2.9rem; margin: 0; }
.exq-parts li { margin-bottom: .45rem; }
.exq-parts li::marker { font-family: var(--mono); color: var(--ch2); font-size: .82rem; }

.exq-ms { border-top: 1px solid var(--grid-hot); }
.exq-ms summary {
  padding: .65rem 1.1rem; cursor: pointer; list-style: none;
  font-family: var(--mono); font-size: .68rem; letter-spacing: .14em;
  text-transform: uppercase; color: var(--ink-2);
  transition: color .16s var(--ease), background .16s var(--ease);
}
.exq-ms summary::-webkit-details-marker { display: none; }
.exq-ms summary::before { content: '▸ '; color: var(--ch1); }
.exq-ms[open] summary::before { content: '▾ '; }
.exq-ms summary:hover { color: var(--ch1); background: var(--tint-1); }

.ms { padding: .3rem 1.1rem .4rem 2.9rem; margin: 0; }
.ms li { margin-bottom: .7rem; position: relative; }
.ms li::marker { font-family: var(--mono); color: var(--ink-3); font-size: .8rem; }
.ms-do { display: block; }
.ms-note {
  display: block; font-size: .82rem; color: var(--ink-3);
  border-left: 2px solid var(--grid-hot); padding-left: .7rem; margin-top: .25rem;
}
.ms-mark { font-family: var(--mono); font-size: .72rem; color: var(--ch1); margin-left: .4rem; }

.exq-ans {
  margin: 0; padding: .7rem 1.1rem;
  border-top: 1px solid var(--grid-hot); background: var(--tint-1);
  font-family: var(--mono); font-size: .9rem; color: var(--ch1-hot);
}
.exq-tip {
  margin: 0; padding: .7rem 1.1rem;
  border-top: 1px solid var(--grid-hot);
  font-size: .88rem; color: var(--ink-2); background: var(--tint-a);
}
.exq-tip b { color: var(--ch2); }

@media (max-width: 560px) {
  .exq-q, .exq-ms summary, .exq-ans, .exq-tip { padding-left: .9rem; padding-right: .9rem; }
  .ms { padding-left: 2.2rem; }
  .exq-parts { padding-left: 2.2rem; }
}

/* ============================================================
   QUESTION GROUPS

   Punjab papers are built from short questions and numericals,
   and those are the words students search for, so each kind gets
   its own heading rather than being lumped together.
   ============================================================ */
.exam-group { margin-bottom: 2.4rem; }
.exam-group-head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 1rem; border-bottom: 1px solid var(--grid-hot);
  padding-bottom: .5rem; margin-bottom: .5rem;
}
.exam-group-head h3 { font-size: var(--step-1); margin: 0; }
.exam-group-head .mono { font-size: .68rem; white-space: nowrap; }
.exam-group-note { color: var(--ink-3); font-size: .85rem; margin-bottom: 1.1rem; max-width: 66ch; }

.exq-given {
  margin: 0; padding: .55rem 1.1rem;
  border-top: 1px solid var(--grid); background: var(--panel-2);
  font-family: var(--mono); font-size: .84rem; color: var(--ink-2);
}
.exq-given b { color: var(--ch2); font-family: var(--body); }

/* The number badge is colour-coded by kind so the eye can skim for one type. */
.exq-short .exq-n     { color: var(--ch1); border-color: var(--ch1-dim); }
.exq-numerical .exq-n { color: var(--ch2); border-color: var(--ch2-dim); }
.exq-long .exq-n      { color: var(--ch1-hot); border-color: var(--ch1); }

/* ============================================================
   PAPER SCHEME — what the exam is actually made of
   ============================================================ */
.paper { margin: 0 0 2.6rem; }
.paper-head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 1rem; border-bottom: 1px solid var(--ch2-dim);
  padding-bottom: .5rem; margin-bottom: 1rem;
}
.paper-head h2 { font-size: var(--step-1); margin: 0; color: var(--ch2); }
.paper-head .mono { font-size: .7rem; white-space: nowrap; }
.paper .tbl td:nth-child(3) { font-family: var(--mono); color: var(--ch1); }

/* ============================================================
   SMALL PHONES

   Measured, not guessed: at 360px the rail was 380px wide, so
   every page scrolled sideways by 30px. The logo canvas and the
   generous gaps are what did not fit, and neither is load-bearing
   at this size.
   ============================================================ */
@media (max-width: 430px) {
  .rail { gap: .5rem; padding: 0 .7rem; }
  .mark { gap: .4rem; }
  .mark canvas { display: none; }        /* the scope trace is decoration */
  .mark b { font-size: .98rem; }
  .rail-tools { gap: .35rem; }
  .lvl { padding: .2rem .4rem; font-size: .62rem; }
  .icon-btn { width: 30px; height: 30px; }

  .crumbs { font-size: .68rem; }
  .chips { gap: .35rem; }
}

/* ============================================================
   TOUCH TARGETS

   On a touch screen a link needs to be big enough to hit. Chips
   and inline links were 20-26px tall, which is a miss waiting to
   happen; this lifts anything tappable to 40px without changing
   how it looks on a mouse-driven screen.
   ============================================================ */
@media (pointer: coarse) {
  a.chip, .chips a {
    min-height: 40px; display: inline-flex; align-items: center;
  }
  .lesson-toc a { padding: .5rem 0 .5rem .9rem; }
  .exq-ms summary { padding-top: .9rem; padding-bottom: .9rem; }
  .pl-item { min-height: 52px; }
  .opt { min-height: 52px; }
}

/* ============================================================
   NARROW SCREENS — nothing may force the page wider

   Long formulas, code spans and worked-example steps are the
   usual culprits: they have no break opportunity, so they set a
   min-content width that a grid track then refuses to go below.
   ============================================================ */
.prose, .prose > *, .block, .define, .worked, .keypoints, .exam, .exq { min-width: 0; }
.prose code, .step-do code, .ms-do code, .exq-q code, .deck-text code { overflow-wrap: anywhere; }
.formula { max-width: 100%; }
.where dd { overflow-wrap: anywhere; }

@media (max-width: 430px) {
  .formula { font-size: .92rem; padding: 1.1rem .8rem; letter-spacing: 0; }
  .where > div { grid-template-columns: 2.8rem 1fr; }
  .worked-head, .exq-head { flex-wrap: wrap; row-gap: .3rem; }
  .deck-foot .btn { flex: 1 1 100%; }
}

/* ---------- answer workspace ---------- */
/* A place to write the answer before the mark scheme is opened. The check
   is a self-marking aid, so the styling deliberately avoids anything that
   looks like a grade being handed down — no ticks, crosses or red. */
.workspace { margin: .9rem 0 .2rem; }
.work-box {
  width: 100%; min-height: 7rem; padding: .8rem .9rem;
  background: var(--void-2); color: var(--ink);
  border: 1px solid var(--grid-hot); border-left: 3px solid var(--ch3);
  font-family: var(--mono); font-size: .88rem; line-height: 1.6;
  resize: vertical;
}
.work-box:focus { outline: none; border-left-color: var(--ch1); }
.work-actions { display: flex; gap: .6rem; margin-top: .6rem; flex-wrap: wrap; }

.work-feedback { margin-top: 1rem; border-top: 1px solid var(--grid-hot); padding-top: .9rem; }
.work-feedback[hidden] { display: none; }
.work-score { display: flex; align-items: baseline; gap: .6rem; flex-wrap: wrap; margin-bottom: .9rem; }
.work-score strong { font-family: var(--mono); font-size: 1.35rem; color: var(--ch1); }
.work-score-note { font-size: .74rem; color: var(--ink-3); letter-spacing: .04em; }

.work-part { margin-bottom: 1rem; }
.work-part h5 {
  font-family: var(--mono); font-size: .7rem; letter-spacing: .14em;
  text-transform: uppercase; color: var(--ch2); margin-bottom: .45rem;
}
.work-part p { font-size: .88rem; color: var(--ink-2); }
.work-part ul { list-style: none; padding: 0; margin: .3rem 0 0; }
.work-part li {
  display: flex; gap: .6rem; align-items: baseline; flex-wrap: wrap;
  padding: .45rem 0 .45rem .8rem; border-left: 2px solid var(--grid-hot);
  font-size: .89rem; color: var(--ink-2); margin-bottom: .25rem;
}
.work-part li span { flex: 1 1 12rem; }
.work-part li b { font-family: var(--mono); font-size: .78rem; color: var(--ink-3); }
.work-good li { border-left-color: var(--ch1); }
.work-miss li { border-left-color: var(--ch2-dim, var(--grid-hot)); }
.work-miss li.claimed { border-left-color: var(--ch1); opacity: .85; }
.work-good h5 { color: var(--ch1); }

.work-claim {
  background: transparent; border: 1px solid var(--grid-hot); color: var(--ink-3);
  font-family: var(--mono); font-size: .68rem; letter-spacing: .06em;
  padding: .25rem .55rem; cursor: pointer; white-space: nowrap;
}
.work-claim:hover { color: var(--ch1); border-color: var(--ch1); }
.work-hint, .work-next, .work-empty {
  font-size: .8rem; color: var(--ink-3); margin-bottom: .5rem; line-height: 1.6;
}
.work-next { margin-top: .8rem; font-style: italic; }

@media (max-width: 430px) {
  .work-part li { flex-direction: column; align-items: flex-start; gap: .3rem; }
  .work-actions .btn { flex: 1 1 100%; }
}

/* ---------- lesson video ---------- */
/* Rendered only when a video has been attached in the studio, so there is
   deliberately no empty state to style. The thumbnail stands in until the
   student clicks, which keeps YouTube off the page for everyone who does
   not watch — and keeps the lesson quick on a phone. */
.lesson-video:empty { display: none; }
.lv { border: 1px solid var(--grid-hot); background: var(--panel); margin-bottom: 2rem; }
.lv-head {
  display: flex; justify-content: space-between; align-items: center; gap: 1rem;
  padding: .7rem .9rem; border-bottom: 1px solid var(--grid-hot); flex-wrap: wrap;
}
.lv-tag {
  font-family: var(--mono); font-size: .64rem; letter-spacing: .18em;
  text-transform: uppercase; color: var(--ch1);
}
.lv-yt { font-family: var(--mono); font-size: .68rem; color: var(--ink-3); text-decoration: none; }
.lv-yt:hover { color: var(--ch1); }
.lv-frame { position: relative; aspect-ratio: 16 / 9; background: var(--void-2); }
.lv-frame iframe, .lv-thumb {
  position: absolute; inset: 0; width: 100%; height: 100%; border: 0;
  object-fit: cover; display: block;
}
.lv-thumb { cursor: pointer; }
.lv-play {
  position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
  width: 62px; height: 62px; border-radius: 50%; cursor: pointer;
  background: rgba(5, 9, 10, .78); border: 1.5px solid var(--ch1); color: var(--ch1);
  font-size: 1.3rem; line-height: 1; display: grid; place-items: center;
  transition: all .18s var(--ease);
}
.lv-play:hover { background: var(--ch1); color: var(--void); }

/* The workspace opens on request. Six questions each showing a textarea by
   default pushed the mark schemes far down the page and made the lesson look
   like a form to fill in rather than something to read. */
.work-open {
  background: transparent; border: 1px dashed var(--grid-hot); color: var(--ink-3);
  font-family: var(--mono); font-size: .72rem; letter-spacing: .1em;
  padding: .55rem .9rem; cursor: pointer; width: 100%; text-align: left;
  transition: all .18s var(--ease);
}
.work-open:hover { color: var(--ch1); border-color: var(--ch1); border-style: solid; }
.work-pane { margin-top: .8rem; }

/* ---------- diagnostic switches (see core.js) ---------- */
/* backdrop-filter puts the header on its own GPU layer. On some mobile
   GPUs that layer composites at the wrong offset, which slices the element
   visually while every DOM measurement still reports it correctly — which
   is exactly the symptom we are chasing. */
html[data-fx="off"] .rail { backdrop-filter: none; background: var(--void); }
html[data-fx="off"] .rail nav { backdrop-filter: none; background: var(--void); }
html[data-stick="off"] .rail { position: static; }

/* Notes download under the player — rendered only when a link exists.
   Deliberately the loudest thing on the page after the video itself: it is
   what most people arrive looking for, so it gets the amber channel, a solid
   left edge and a full-size button rather than the small ghost button it
   used to be. */
/* scroll-margin keeps the block clear of the fixed rail when the Notes chip
   scrolls to it, so the ad above the card is not hidden under the nav. */
.notes-link { margin-top: 1rem; scroll-margin-top: 90px; }
.notes-link[hidden] { display: none !important; }

.notes-card {
  display: flex; align-items: center; justify-content: space-between;
  gap: 1rem 1.25rem; flex-wrap: wrap;
  padding: 1rem 1.15rem;
  border: 1px solid var(--ch2-dim);
  border-left: 3px solid var(--ch2);
  border-radius: 4px;
  background: linear-gradient(90deg, rgba(255, 179, 0, .10), transparent 70%);
}
.notes-copy { min-width: 0; }
.notes-tag {
  font-family: var(--mono); font-size: .6rem; letter-spacing: .2em;
  text-transform: uppercase; color: var(--ch2); margin-bottom: .35rem;
}
.notes-head {
  font-family: var(--display); font-weight: 600; font-size: 1.02rem;
  color: var(--ink); line-height: 1.2;
}
.notes-sub { font-size: .84rem; color: var(--ink-3); margin-top: .2rem; }
.notes-go { flex-shrink: 0; }

/* On a narrow screen the button drops below the text and goes full width,
   so it stays an obvious tap target rather than a stub in the corner. */
@media (max-width: 540px) {
  .notes-card { flex-direction: column; align-items: stretch; gap: .9rem; }
  .notes-go { width: 100%; justify-content: center; }
}

/* Arriving via ?notes=1 pulses the card once. Someone who followed a link
   labelled "notes" should be left in no doubt which box is the notes. */
.notes-link[data-flash="true"] .notes-card { animation: notesFlash 1.3s ease-out 2; }
@keyframes notesFlash {
  0%, 100% { box-shadow: 0 0 0 0 rgba(255, 179, 0, 0); }
  35%      { box-shadow: 0 0 0 4px rgba(255, 179, 0, .28); }
}
@media (prefers-reduced-motion: reduce) {
  .notes-link[data-flash="true"] .notes-card { animation: none; border-color: var(--ch2); }
}

/* The ad slot beside a download, and the short wait before the link appears.
   Both are inert until a provider is configured in assets/js/ads.js. */
.ad-slot { margin: 1rem 0; }
.ad-slot[hidden] { display: none !important; }
.ad-label {
  font-family: var(--mono); font-size: .58rem; letter-spacing: .18em;
  text-transform: uppercase; color: var(--ink-3); margin-bottom: .4rem;
}
.ad-box {
  min-height: 90px; display: grid; place-content: center;
  border: 1px dashed var(--grid-hot); border-radius: 4px;
  color: var(--ink-3); font-family: var(--mono); font-size: .7rem;
}
.notes-wait { font-size: .84rem; color: var(--ink-3); }
.notes-wait b { color: var(--ch2); font-family: var(--mono); }

/* The compact notes chip beside the title. Amber like the card it points at,
   and a link rather than a label, so it reads as something to press. */
.chip-notes {
  color: var(--void); background: var(--ch2); border-color: var(--ch2);
  font-weight: 600; text-decoration: none;
}
.chip-notes:hover { filter: brightness(1.12); }
.chip-notes[hidden] { display: none !important; }


/* Prev / next at the foot of a lesson. Reaching the end of a topic and having
   to go back to the syllabus to find the following one is the sort of friction
   that stops a revision session, so the next lesson is one tap away. */
.lesson-nav[hidden] { display: none !important; }
.lesson-nav { margin: 2.5rem 0 1rem; border-top: 1px solid var(--grid-hot); padding-top: 1.2rem; }
.ln-where {
  font-family: var(--mono); font-size: .62rem; letter-spacing: .16em;
  text-transform: uppercase; color: var(--ink-3); margin-bottom: .8rem;
}
.ln-row { display: grid; grid-template-columns: 1fr auto 1fr; gap: .7rem; align-items: stretch; }
.lesson-nav a {
  display: flex; flex-direction: column; justify-content: center; gap: .2rem;
  padding: .75rem .9rem; border: 1px solid var(--grid-hot); border-radius: 4px;
  text-decoration: none; transition: border-color .15s var(--ease), background .15s var(--ease);
}
.lesson-nav a:hover { border-color: var(--ch1); background: var(--tint-1); }
.ln-next { text-align: right; }
.ln-dir { font-family: var(--mono); font-size: .62rem; letter-spacing: .14em; color: var(--ch1); }
.ln-name { color: var(--ink); font-size: .9rem; line-height: 1.25; }
.ln-up { justify-content: center; align-items: center; color: var(--ink-3); font-size: .8rem; white-space: nowrap; }
.ln-gap { display: block; }

/* On a phone the three-column row would leave two cramped columns and a
   stranded middle, so they stack and the middle link drops to the end. */
@media (max-width: 620px) {
  .ln-row { grid-template-columns: 1fr; }
  .ln-next { text-align: left; }
  .ln-up { order: 3; }
  .ln-gap { display: none; }
}

/* A diagram control that cannot do anything in the current mode — the magnet
   separation when only one magnet is shown. Dimmed and disabled rather than
   left looking live, since a slider that moves and changes nothing is what
   made the old magnet diagram feel broken. */
.ctl.is-off { opacity: .4; }
.ctl.is-off input[type="range"] { cursor: not-allowed; }


/* Ad placements. Each unit has a fixed pixel size, so every slot is centred and
   capped at the container width — a 728x90 leaderboard must never be the thing
   that makes a phone scroll sideways. */
#adTop { margin: 1.4rem 0 .4rem; }
#adTop .ad-slot, #adSlot .ad-slot, .ad-mid .ad-slot, .ad-end .ad-slot { display: flex; flex-direction: column; align-items: center; }
#adTop iframe, #adSlot iframe, .ad-slot iframe { max-width: 100%; }
#adSide { margin-top: 1.4rem; }

/* The in-reading slot needs more air around it than the 1.05rem the prose gives
   its children, so that it reads as an interruption of the page rather than as
   part of the section above it. The rule under it does the same job as the rule
   above a heading: it says the teaching stopped here. */
.ad-mid { margin: 2.6rem 0; padding-top: 2.2rem; border-top: 1px solid var(--grid-hot); }
.ad-end { margin: 3rem 0 0; }

/* An ad that never filled would otherwise leave an empty column that still
   takes its margins and rule, opening a gap mid-reading for nothing. The
   collapse is driven from script rather than :has(), because :has() is absent
   from the older Android browsers a good share of these readers are on — and
   there it would fail silently, leaving exactly the gap it exists to remove. */
.ad-mid[hidden], .ad-end[hidden] { display: none !important; }

/* Navigation pages. These are card grids with no reading column, so the slot
   is centred on the full page width and the wide unit fits. */
.ad-nav { margin: 1.6rem 0 2.4rem; }
.ad-nav-end { margin: 2.8rem 0 0; }
.ad-nav .ad-slot, .ad-nav-end .ad-slot { display: flex; flex-direction: column; align-items: center; }
.ad-nav[hidden], .ad-nav-end[hidden] { display: none !important; }

/* The sidebar unit is 160x300 and only appears on screens wide enough to have a
   sidebar at all; below that the aside collapses above the content, where a tall
   narrow ad would just be in the way. */
@media (max-width: 1023px) { #adSide { display: none; } }

/* ---------------- search ---------------- */
/* A veil rather than a dropdown: on a phone the header has room for an icon and
   nothing else, and a results list that has to share the screen with the page
   underneath ends up too short to be worth reading. */
.search-veil {
  position: fixed; inset: 0; z-index: 1200;
  background: rgba(0, 0, 0, .62); backdrop-filter: blur(3px);
  display: flex; justify-content: center; align-items: flex-start;
  padding: clamp(1rem, 8vh, 6rem) 1rem 1rem;
}
.search-veil[hidden] { display: none !important; }
.search-panel {
  width: min(680px, 100%); max-height: 78vh; display: flex; flex-direction: column;
  background: var(--bg-1, #0a0f0e); border: 1px solid var(--grid-hot);
  border-radius: 6px; box-shadow: 0 24px 70px rgba(0, 0, 0, .6); overflow: hidden;
}
.search-head { display: flex; align-items: center; gap: .7rem; padding: .85rem 1rem; border-bottom: 1px solid var(--grid-hot); }
.search-head svg { color: var(--ch1); flex: none; }
.search-input {
  flex: 1; min-width: 0; background: none; border: 0; outline: none;
  color: var(--ink); font: inherit; font-size: 1.02rem; padding: .15rem 0;
}
.search-input::placeholder { color: var(--ink-3); }
.search-input::-webkit-search-cancel-button { filter: grayscale(1) opacity(.5); }
.search-esc {
  flex: none; background: none; border: 1px solid var(--grid-hot); border-radius: 3px;
  color: var(--ink-3); font-family: var(--mono); font-size: .62rem; letter-spacing: .1em;
  padding: .2rem .4rem; cursor: pointer;
}
.search-esc:hover { color: var(--ink); border-color: var(--ch1); }
.search-status { padding: .5rem 1rem; font-size: .66rem; letter-spacing: .1em; color: var(--ink-3); text-transform: uppercase; }
.search-list { overflow-y: auto; padding: 0 .4rem .5rem; }
.sr {
  display: block; padding: .6rem .7rem; border-radius: 4px;
  border-left: 2px solid transparent; text-decoration: none; color: inherit;
}
.sr[aria-selected="true"] { background: var(--grid); border-left-color: var(--ch1); }
.sr-main { display: block; color: var(--ink); font-size: .95rem; line-height: 1.35; }
.sr-sub { display: block; color: var(--ink-3); font-size: .78rem; margin-top: .12rem; }
.sr-where {
  display: inline-block; margin-top: .3rem; font-family: var(--mono); font-size: .6rem;
  letter-spacing: .1em; text-transform: uppercase; color: var(--ch1);
  border: 1px solid var(--grid-hot); border-radius: 2px; padding: .1rem .35rem;
}
@media (max-width: 560px) {
  .search-veil { padding: 0; }
  .search-panel { max-height: 100vh; height: 100%; border: 0; border-radius: 0; width: 100%; }
}

/* The course name was an h2 and is now an h1, for the sake of a page that had no
   h1 at all. It should look exactly as it did, so it keeps h2 sizing —
   --step-3, which is what h2 uses. --step-2 is the h3 size and would have made
   the heading smaller than the one it replaced. */
h1.course-title { font-size: var(--step-3); }
