/* bellaziraptor.org — study site
   Shared stylesheet. Palette continues the original placeholder page. */

:root {
  --bg: #f7f6f3;
  --panel: #ffffff;
  --panel-2: #fbfaf8;
  --ink: #1b1b1f;
  --muted: #6b6b76;
  --line: #e3e1dc;
  --line-soft: #eeece7;
  --accent: #c2571f;
  --accent-soft: #fbeee5;
  --grid: #d6d3cc;
  --ok: #1f6f4a;
  --ok-soft: #e9f4ee;
  --warn: #8a5a00;
  --warn-soft: #fbf2df;
  --info: #1f5590;
  --info-soft: #eaf1f9;
  --vec-cross: #7b3fb8;   /* vector lab: A x B, a fifth hue the palette lacks */
  --code-bg: #f2f0ec;
  --shadow: 0 1px 2px rgba(0,0,0,.04), 0 10px 28px rgba(0,0,0,.055);
  /* launcher panel */
  --grid-line: rgba(28, 32, 48, .055);
  --grid-major: rgba(28, 32, 48, .10);
  --seam-blue: rgba(20, 64, 140, .13);
  --panel-glow: rgba(255, 255, 255, .85);
  --launcher-edge: rgba(255, 255, 255, .55);
  --tile-bg: #ffffff;
  --tile-shadow: 0 1px 2px rgba(20,24,40,.05), 0 6px 18px rgba(20,24,40,.075);
  --tile-shadow-hover: 0 14px 34px rgba(16,58,136,.26), 0 4px 10px rgba(16,58,136,.16);
  --tile-blue-a: #1a55a8;
  --tile-blue-b: #2a7ae0;
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #14141a;
    --panel: #1c1c24;
    --panel-2: #21212b;
    --ink: #f0eee9;
    --muted: #9a9aa6;
    --line: #2c2c38;
    --line-soft: #262631;
    --accent: #ff8a4c;
    --accent-soft: #2a2019;
    --grid: #3a3a48;
    --ok: #62c495;
    --ok-soft: #1a2620;
    --warn: #e0ac4d;
    --warn-soft: #272115;
    --info: #7fb2e8;
    --info-soft: #182230;
    --vec-cross: #c9a3f5;
    --code-bg: #23232d;
    --shadow: 0 1px 2px rgba(0,0,0,.2), 0 10px 28px rgba(0,0,0,.28);
    /* launcher panel — tile sits a step lighter than --bg here too */
    --grid-line: rgba(150, 170, 220, .06);
    --grid-major: rgba(150, 170, 220, .11);
    --seam-blue: rgba(70, 140, 240, .14);
    /* a white glow would blow out on a dark ground — lift with blue instead */
    --panel-glow: rgba(90, 130, 210, .085);
    --launcher-edge: rgba(150, 185, 255, .16);
    --tile-bg: #20202a;
    --tile-shadow: 0 1px 2px rgba(0,0,0,.4), 0 6px 20px rgba(0,0,0,.38);
    --tile-shadow-hover: 0 14px 36px rgba(0,0,0,.55), 0 4px 12px rgba(10,40,100,.5);
    --tile-blue-a: #17509f;
    --tile-blue-b: #2f80e8;
  }
}

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font: 16px/1.65 ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  -webkit-text-size-adjust: 100%;
}

/* ================= HOME: hero panel ================= */

/* scroll progress, 0 at the top and 1 once the hero is parked.
   Set by the inline script on index.html; the 0 default is the
   no-JavaScript resting state, which renders correctly on its own. */
:root { --pe: 0; }

.hero {
  position: sticky;
  top: 0;
  z-index: 1;
  height: 58vh;
  min-height: 380px;
  overflow: hidden;
  display: flex;
  align-items: center;
  /* Much darker than before: near-black at the top, deep navy at the bottom
     instead of royal blue, so the nebulae and stars are the light sources
     rather than the background competing with them. */
  background:
    linear-gradient(168deg, #00030a 0%, #01071a 22%, #020d24 44%, #031430 64%, #041c3c 84%, #05264c 100%);
}

/* ---- nebula field: three layers drifting at different rates ----
   Each animates only transform and opacity, so it is rasterised once and then
   composited — the same discipline as the star layers. Deliberately no
   mix-blend-mode: it looks similar at these alphas over a near-black base and
   avoids forcing a blend group on an animating layer. */
.nebula-field {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  opacity: calc(1 - var(--pe) * .6);
}
.neb {
  position: absolute;
  inset: -22%;
  will-change: transform, opacity;
}
/* deep structural clouds — the body of the nebula */
.neb.n1 {
  background:
    radial-gradient(58% 42% at 18% 24%, rgba(26, 78, 178, .40), rgba(26, 78, 178, 0) 72%),
    radial-gradient(44% 38% at 82% 70%, rgba(16, 48, 126, .38), rgba(16, 48, 126, 0) 74%),
    radial-gradient(38% 52% at 52% 94%, rgba(10, 36, 100, .32), rgba(10, 36, 100, 0) 76%);
  animation: neb1 38s ease-in-out infinite alternate;
}
/* brighter cyan wisps, counter-drifting to open up the depth */
.neb.n2 {
  background:
    radial-gradient(30% 24% at 68% 18%, rgba(48, 158, 216, .26), rgba(48, 158, 216, 0) 70%),
    radial-gradient(24% 30% at 30% 60%, rgba(32, 122, 192, .22), rgba(32, 122, 192, 0) 72%),
    radial-gradient(18% 15% at 88% 46%, rgba(96, 204, 236, .17), rgba(96, 204, 236, 0) 68%);
  animation: neb2 27s ease-in-out infinite alternate;
}
/* a whisper of the site accent, so the hero ties to the rest of the palette
   instead of being an isolated blue box */
.neb.n3 {
  background:
    radial-gradient(28% 21% at 22% 80%, rgba(158, 70, 30, .16), rgba(158, 70, 30, 0) 72%),
    radial-gradient(16% 14% at 76% 90%, rgba(126, 56, 24, .13), rgba(126, 56, 24, 0) 70%);
  animation: neb3 47s ease-in-out infinite alternate;
}

/* cinematic vignette — darkens the corners and the bottom edge */
.hero::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  background:
    radial-gradient(128% 96% at 50% 34%, rgba(1, 4, 14, 0) 40%, rgba(1, 4, 14, .66) 100%),
    linear-gradient(to bottom, rgba(1, 4, 14, 0) 58%, rgba(1, 4, 14, .55) 100%);
}

/* the three star layers ride in a wrapper so the whole field can fade
   on scroll without fighting the twinkle animation on the near layer */
.starfield {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  opacity: calc(1 - var(--pe) * .85);
}
/* Different periods and opposite drift directions, so the layers separate and
   recombine rather than moving as one slab. */
@keyframes neb1 {
  from { transform: translate3d(0, 0, 0) scale(1);          opacity: .82; }
  to   { transform: translate3d(3%, -2%, 0) scale(1.13);    opacity: 1; }
}
@keyframes neb2 {
  from { transform: translate3d(0, 0, 0) scale(1.07);       opacity: .62; }
  to   { transform: translate3d(-4.5%, 2.5%, 0) scale(1);   opacity: 1; }
}
@keyframes neb3 {
  from { transform: translate3d(0, 0, 0) scale(1);          opacity: .45; }
  to   { transform: translate3d(2%, 3.5%, 0) scale(1.16);   opacity: .85; }
}

/* three parallax star layers — far/mid/near */
/* Each layer is extended one tile past the panel on the two edges it travels
   away from (left and bottom), so translating by exactly one tile period never
   exposes an edge and the loop stays seamless.

   These animate TRANSFORM, not background-position. background-position cannot
   be composited, so the old version repainted three full-viewport layers of
   radial gradients every frame and made the whole tab janky. Transform is
   rasterised once and then moved by the compositor. Do not "simplify" this
   back to background-position. */
.stars {
  position: absolute;
  top: 0;
  right: 0;
  pointer-events: none;
  background-repeat: repeat;
  will-change: transform;
}
/* far — small, dim, plain points */
.stars.s1 {
  opacity: .6;
  left: -320px;
  bottom: -320px;
  background-size: 300px 300px;
  background-image:
    radial-gradient(1.4px 1.4px at 25px 42px,   rgba(198,222,255,.92), rgba(255,255,255,0)),
    radial-gradient(1.4px 1.4px at 88px 174px,  rgba(255,255,255,.95), rgba(255,255,255,0)),
    radial-gradient(1.4px 1.4px at 143px 66px,  rgba(169,201,255,.85), rgba(255,255,255,0)),
    radial-gradient(1.4px 1.4px at 205px 231px, rgba(255,255,255,.9),  rgba(255,255,255,0)),
    radial-gradient(1.4px 1.4px at 268px 108px, rgba(207,226,255,.85), rgba(255,255,255,0)),
    radial-gradient(1.4px 1.4px at 33px 259px,  rgba(255,255,255,.9),  rgba(255,255,255,0)),
    radial-gradient(1.4px 1.4px at 178px 289px, rgba(182,210,255,.85), rgba(255,255,255,0)),
    radial-gradient(1.4px 1.4px at 249px 18px,  rgba(255,255,255,.9),  rgba(255,255,255,0)),
    radial-gradient(1.4px 1.4px at 116px 122px, rgba(216,232,255,.85), rgba(255,255,255,0)),
    radial-gradient(1.4px 1.4px at 62px 205px,  rgba(255,255,255,.9),  rgba(255,255,255,0));
  animation: drift1 150s linear infinite;
}
/* mid — bright core with a soft halo */
.stars.s2 {
  opacity: .95;
  left: -280px;
  bottom: -280px;
  background-size: 260px 260px;
  background-image:
    radial-gradient(2.6px 2.6px at 40px 60px,   #ffffff 0%, #ffffff 26%, rgba(140,196,255,.40) 55%, rgba(255,255,255,0) 100%),
    radial-gradient(2.6px 2.6px at 150px 30px,  #ffffff 0%, #ffffff 26%, rgba(140,196,255,.40) 55%, rgba(255,255,255,0) 100%),
    radial-gradient(2.2px 2.2px at 96px 190px,  #ffffff 0%, #ffffff 26%, rgba(160,206,255,.36) 55%, rgba(255,255,255,0) 100%),
    radial-gradient(2.6px 2.6px at 220px 140px, #ffffff 0%, #ffffff 26%, rgba(140,196,255,.40) 55%, rgba(255,255,255,0) 100%),
    radial-gradient(2.2px 2.2px at 15px 165px,  #ffffff 0%, #ffffff 26%, rgba(160,206,255,.36) 55%, rgba(255,255,255,0) 100%),
    radial-gradient(2.6px 2.6px at 190px 232px, #ffffff 0%, #ffffff 26%, rgba(140,196,255,.40) 55%, rgba(255,255,255,0) 100%),
    radial-gradient(2.2px 2.2px at 128px 96px,  #ffffff 0%, #ffffff 26%, rgba(160,206,255,.36) 55%, rgba(255,255,255,0) 100%);
  animation: drift2 95s linear infinite;
}
/* near — the cinematic ones: big core, wide two-stage glow */
.stars.s3 {
  left: -440px;
  bottom: -440px;
  background-size: 420px 420px;
  background-image:
    radial-gradient(4.4px 4.4px at 70px 90px,   #ffffff 0%, #ffffff 24%, rgba(168,214,255,.58) 46%, rgba(92,162,255,.20) 72%, rgba(255,255,255,0) 100%),
    radial-gradient(3.8px 3.8px at 300px 60px,  #ffffff 0%, #ffffff 24%, rgba(190,224,255,.52) 46%, rgba(92,162,255,.16) 72%, rgba(255,255,255,0) 100%),
    radial-gradient(4.6px 4.6px at 190px 330px, #ffffff 0%, #ffffff 24%, rgba(168,214,255,.60) 46%, rgba(92,162,255,.22) 72%, rgba(255,255,255,0) 100%),
    radial-gradient(3.8px 3.8px at 380px 250px, #ffffff 0%, #ffffff 24%, rgba(190,224,255,.52) 46%, rgba(92,162,255,.16) 72%, rgba(255,255,255,0) 100%),
    radial-gradient(4.2px 4.2px at 40px 380px,  #ffffff 0%, #ffffff 24%, rgba(168,214,255,.56) 46%, rgba(92,162,255,.18) 72%, rgba(255,255,255,0) 100%),
    radial-gradient(3.6px 3.6px at 250px 190px, #ffffff 0%, #ffffff 24%, rgba(190,224,255,.50) 46%, rgba(92,162,255,.16) 72%, rgba(255,255,255,0) 100%),
    radial-gradient(4.0px 4.0px at 132px 236px, #ffffff 0%, #ffffff 24%, rgba(168,214,255,.54) 46%, rgba(92,162,255,.18) 72%, rgba(255,255,255,0) 100%);
  animation: drift3 58s linear infinite, twinkle 6s ease-in-out infinite alternate;
}
/* Exactly one tile period per cycle, so the pattern realigns and the loop is
   seamless. Each distance matches that layer's own background-size. */
@keyframes drift1 {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(300px, -300px, 0); }
}
@keyframes drift2 {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(260px, -260px, 0); }
}
@keyframes drift3 {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(420px, -420px, 0); }
}
@keyframes twinkle {
  from { opacity: .65; }
  to   { opacity: 1; }
}

/* sits toward the middle of the panel, and lifts + fades as the page scrolls */
.hero-inner {
  position: relative;
  z-index: 3;
  width: 100%;
  max-width: none;
  margin: 0;
  padding: 34px clamp(20px, 5vw, 64px) 34px clamp(22px, 11vw, 196px);
  transform-origin: left center;
  transform:
    translateY(calc(var(--pe) * -56px))
    scale(calc(1 - var(--pe) * .07));
  opacity: calc(1 - var(--pe) * 1.55);
  will-change: transform, opacity;
}
.site-title {
  font-family: Arial, Helvetica, "Helvetica Neue", sans-serif;
  font-weight: 700;
  color: #ffffff;
  font-size: clamp(2.4rem, 8.5vw, 6.6rem);
  line-height: 1;
  letter-spacing: -.025em;
  margin: 0 0 20px;
  text-shadow: 0 3px 40px rgba(0, 8, 30, .55);
}

/* the animation is decoration; honour a reduced-motion preference */
@media (prefers-reduced-motion: reduce) {
  .neb, .stars { animation: none !important; }
  .hero-inner, .launcher-inner { transform: none !important; opacity: 1 !important; }
  .starfield, .nebula-field { opacity: 1 !important; }
  /* keep the colour cross-fade, drop the movement */
  .tile:hover, a.tile:focus-visible { transform: none !important; }
}

/* ================= HOME: launcher panel ================= */

/* rides up over the sticky hero on a rounded lip, and scales up as it comes */
.launcher {
  position: relative;
  z-index: 3;
  min-height: 72vh;
  display: flex;
  align-items: center;
  background: var(--bg);
  border-radius: 26px 26px 0 0;
  margin-top: -30px;
  /* outer shadow lifts the panel off the hero; the inset hairline gives the
     rounded lip a lit top edge as it rises out of the dark */
  box-shadow:
    0 -20px 60px rgba(1, 6, 22, .45),
    inset 0 1px 0 var(--launcher-edge);
  padding: 52px 22px 84px;
}
/* Engineering-paper background: a fine grid with heavier major lines every
   fourth cell, a soft glow lifting the card row, and a blue bleed down from the
   top edge so the seam with the dark hero above reads as deliberate.

   Layer order matters — earlier entries in background-image paint on top, so the
   grid sits over the colour washes rather than under them. Masked at the bottom
   so the whole thing dissolves before the footer. */
.launcher::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  border-radius: inherit;
  background-image:
    /* minor grid */
    linear-gradient(to right,  var(--grid-line) 1px, transparent 1px),
    linear-gradient(to bottom, var(--grid-line) 1px, transparent 1px),
    /* major grid, every 4th cell */
    linear-gradient(to right,  var(--grid-major) 1.5px, transparent 1.5px),
    linear-gradient(to bottom, var(--grid-major) 1.5px, transparent 1.5px),
    /* glow behind the card row */
    radial-gradient(62% 46% at 50% 32%, var(--panel-glow), transparent 72%),
    /* blue bleeding down from the hero */
    linear-gradient(to bottom, var(--seam-blue), transparent 30%);
  background-size:
    44px 44px, 44px 44px,
    176px 176px, 176px 176px,
    auto, auto;
  -webkit-mask-image: linear-gradient(to bottom, #000 0%, #000 68%, transparent 100%);
  mask-image: linear-gradient(to bottom, #000 0%, #000 68%, transparent 100%);
}
.launcher-inner {
  position: relative;
  z-index: 1;
  width: 100%;
  max-width: 1240px;
  margin: 0 auto;
  transform-origin: 50% 0;
  transform:
    translateY(calc((1 - var(--pe)) * 26px))
    scale(calc(.94 + var(--pe) * .06));
  will-change: transform;
}
.launcher h2 {
  margin: 0 0 22px;
  font-size: .92rem;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--muted);
  border: 0;
  padding: 0;
}
.tiles {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 20px;
}
.tile {
  position: relative;
  isolation: isolate;
  display: flex;
  flex-direction: column;
  min-height: 214px;
  padding: 22px;
  border: 1px solid var(--line);
  border-radius: 14px;
  background: var(--tile-bg);
  color: inherit;
  text-decoration: none;
  box-shadow: var(--tile-shadow);
  transition:
    transform .34s cubic-bezier(.4, 0, .2, 1),
    box-shadow .34s cubic-bezier(.4, 0, .2, 1),
    border-color .34s cubic-bezier(.4, 0, .2, 1),
    opacity .34s cubic-bezier(.4, 0, .2, 1);
}
/* The blue wash lives on its own layer rather than being a background swap,
   because background-image does not interpolate — a gradient cannot fade.
   z-index:-1 paints it above the tile's own background but below the text. */
.tile::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  border-radius: inherit;
  background: linear-gradient(150deg, var(--tile-blue-a) 0%, var(--tile-blue-b) 100%);
  opacity: 0;
  transition: opacity .34s cubic-bezier(.4, 0, .2, 1);
}
.tile .tile-icon { font-size: 2.15rem; line-height: 1; margin-bottom: auto; }
.tile h3 {
  font-family: Arial, Helvetica, "Helvetica Neue", sans-serif;
  font-weight: 700;
  font-size: 1.2rem;
  margin: 14px 0 5px;
  letter-spacing: -.01em;
  transition: color .34s cubic-bezier(.4, 0, .2, 1);
}
.tile p {
  margin: 0;
  font-size: .9rem;
  color: var(--muted);
  line-height: 1.45;
  transition: color .34s cubic-bezier(.4, 0, .2, 1);
}

/* Hover — deliberately on .tile, not a.tile, so the placeholder tiles
   respond identically even though they carry no href. */
.tile:hover,
a.tile:focus-visible {
  transform: translateY(-7px) scale(1.035);
  border-color: var(--tile-blue-b);
  box-shadow: var(--tile-shadow-hover);
}
.tile:hover::before,
a.tile:focus-visible::before { opacity: 1; }
.tile:hover h3,
a.tile:focus-visible h3 { color: #ffffff; }
.tile:hover p,
a.tile:focus-visible p { color: rgba(255, 255, 255, .87); }
.tile:hover .soon-badge {
  color: rgba(255, 255, 255, .85);
  border-color: rgba(255, 255, 255, .4);
}

.tile.soon-tile { opacity: .58; }
.tile.soon-tile:hover { opacity: 1; }
.tile .soon-badge {
  align-self: flex-start;
  font-size: .66rem;
  font-weight: 700;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--muted);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 3px 9px;
  margin-top: 10px;
  transition: color .34s cubic-bezier(.4, 0, .2, 1), border-color .34s cubic-bezier(.4, 0, .2, 1);
}

/* ================= HOME: cinematic intro ================= */

/* These run on the elements INSIDE .hero-inner and .launcher-inner, never on
   those two themselves — they already carry --pe-driven transforms, and an
   animation on the same property would win while running and then snap back
   when it ended.

   fill-mode is `backwards`, not `forwards`: backwards holds the from-state
   during the delay (so nothing flashes before its turn) but releases the
   element afterwards, leaving hover transforms free to work. `forwards` would
   pin the final keyframe and permanently block the card hover. */

@keyframes intro-rise {
  from { opacity: 0; transform: translateY(26px); }
  to   { opacity: 1; transform: translateY(0); }
}
@keyframes intro-card {
  from { opacity: 0; transform: translateY(30px) scale(.965); }
  to   { opacity: 1; transform: translateY(0) scale(1); }
}
/* placeholders rest at .58, so their intro must land there or they pop */
@keyframes intro-card-soon {
  from { opacity: 0;   transform: translateY(30px) scale(.965); }
  to   { opacity: .58; transform: translateY(0) scale(1); }
}
@keyframes veil-out {
  from { opacity: 1; }
  to   { opacity: 0; }
}

/* fade up from black over the whole hero */
.hero-veil {
  position: absolute;
  inset: 0;
  z-index: 4;
  pointer-events: none;
  background: #01040e;
  opacity: 0;                 /* resting state == end state, so no fill-mode */
  animation: veil-out 1s ease-out;
}

/* Retimed after the tagline was removed — the old schedule left a dead beat
   at .38s where it used to appear. */
.site-title  { animation: intro-rise 1s   cubic-bezier(.16, .84, .44, 1) .15s backwards; }
.launcher h2 { animation: intro-rise .85s cubic-bezier(.16, .84, .44, 1) .42s backwards; }

.tile { animation: intro-card .85s cubic-bezier(.16, .84, .44, 1) backwards; }
.tile.soon-tile { animation-name: intro-card-soon; }

.tiles > *:nth-child(1) { animation-delay: .54s; }
.tiles > *:nth-child(2) { animation-delay: .62s; }
.tiles > *:nth-child(3) { animation-delay: .70s; }
.tiles > *:nth-child(4) { animation-delay: .78s; }
.tiles > *:nth-child(5) { animation-delay: .86s; }

@media (prefers-reduced-motion: reduce) {
  .hero-veil, .site-title, .launcher h2, .tile {
    animation: none !important;
  }
}

@media (max-width: 980px) { .tiles { grid-template-columns: repeat(3, 1fr); } }
@media (max-width: 660px) { .tiles { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 520px) { .tiles { grid-template-columns: 1fr; } }

/* ================= Course browser (two-pane) ================= */

/* Narrow picker hard left, wide unit list in the centre, a rule between them. */
.course-browser {
  position: relative;
  display: grid;
  grid-template-columns: 252px 1fr;
  gap: 0;
  margin-top: 20px;
  align-items: start;
}
/* radios drive the panes — no JavaScript needed */
.course-browser > input {
  position: absolute;
  width: 1px; height: 1px;
  opacity: 0;
  margin: 0;
}
.course-list {
  display: flex;
  flex-direction: column;
  gap: 12px;
  align-self: start;
  position: sticky;
  top: 68px;
  border-right: 1px solid var(--line);
  padding-right: 26px;
}
.courses-head {
  font-size: 1.3rem;
  line-height: 1.2;
  letter-spacing: -.02em;
  margin: 0;
}
.courses-sub {
  font-size: .82rem;
  color: var(--muted);
  margin: -6px 0 2px;
  line-height: 1.5;
}
.course-list label {
  position: relative;
  isolation: isolate;
  display: block;
  padding: 13px 15px;
  border: 1px solid var(--line);
  border-radius: 11px;
  background: var(--tile-bg);
  box-shadow: var(--tile-shadow);
  cursor: pointer;
  user-select: none;
  font-weight: 700;
  font-size: .95rem;
  transition:
    transform .34s cubic-bezier(.4, 0, .2, 1),
    box-shadow .34s cubic-bezier(.4, 0, .2, 1),
    border-color .34s cubic-bezier(.4, 0, .2, 1),
    color .34s cubic-bezier(.4, 0, .2, 1);
}
/* same cross-fading blue layer as the launcher tiles */
.course-list label::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  border-radius: inherit;
  background: linear-gradient(150deg, var(--tile-blue-a) 0%, var(--tile-blue-b) 100%);
  opacity: 0;
  transition: opacity .34s cubic-bezier(.4, 0, .2, 1);
}
.course-list label small {
  display: block;
  font-weight: 400;
  font-size: .78rem;
  color: var(--muted);
  margin-top: 4px;
  transition: color .34s cubic-bezier(.4, 0, .2, 1);
}
.course-list label:hover {
  transform: translateY(-6px) scale(1.03);
  border-color: var(--tile-blue-b);
  box-shadow: var(--tile-shadow-hover);
  color: #ffffff;
}
.course-list label:hover::before { opacity: 1; }
.course-list label:hover small { color: rgba(255, 255, 255, .87); }

.course-panes { min-width: 0; padding-left: 34px; }

.pane { display: none; }
.pane > h2:first-child { margin-top: 0; }
/* unit rows want the extra width; running prose does not — cap the measure so
   the intro paragraph stays readable on a wide monitor */
.pane > p { max-width: 84ch; }

#c1:checked ~ .course-panes .p1,
#c2:checked ~ .course-panes .p2,
#c3:checked ~ .course-panes .p3 { display: block; }

/* Selected card wears the warm accent — light-red outline and fill. It keeps
   the standard drop shadow from .course-list label rather than overriding it,
   so it sits at the same elevation as the unselected one.

   :not(:hover) matters — these are ID selectors and would otherwise outrank
   .course-list label:hover, leaving the selected card unable to lift. */
#c1:checked ~ .course-list label[for="c1"]:not(:hover),
#c2:checked ~ .course-list label[for="c2"]:not(:hover),
#c3:checked ~ .course-list label[for="c3"]:not(:hover) {
  border-color: var(--accent);
  background: var(--accent-soft);
  color: var(--accent);
}
#c1:focus-visible ~ .course-list label[for="c1"],
#c2:focus-visible ~ .course-list label[for="c2"],
#c3:focus-visible ~ .course-list label[for="c3"] {
  outline: 2px solid var(--tile-blue-b);
  outline-offset: 2px;
}

/* ---- the centre pane is tinted by which course is showing ----
   Physics gets a cool blue wash with a scattered-dot field, echoing the
   starfield on the landing page. Calculus gets a warm amber wash over graph
   paper, echoing the launcher grid. Both are low-alpha layers over --panel,
   so --ink keeps its contrast in light and dark alike. */
.pane {
  background-color: var(--panel);
  border: 1px solid var(--line);
  border-radius: 14px;
  padding: 24px 28px 26px;
  box-shadow: var(--tile-shadow);
}
.pane.p1 {
  background-image:
    radial-gradient(circle at 10% 6%, rgba(42, 112, 224, .17), rgba(42, 112, 224, 0) 44%),
    radial-gradient(circle at 92% 94%, rgba(18, 68, 168, .15), rgba(18, 68, 168, 0) 46%),
    radial-gradient(rgba(62, 132, 236, .16) 1px, transparent 1px);
  background-size: auto, auto, 26px 26px;
}
.pane.p2 {
  background-image:
    radial-gradient(circle at 92% 6%, rgba(206, 118, 32, .17), rgba(206, 118, 32, 0) 46%),
    radial-gradient(circle at 8% 94%, rgba(176, 88, 20, .13), rgba(176, 88, 20, 0) 46%),
    linear-gradient(to right,  rgba(190, 110, 40, .13) 1px, transparent 1px),
    linear-gradient(to bottom, rgba(190, 110, 40, .13) 1px, transparent 1px);
  background-size: auto, auto, 30px 30px, 30px 30px;
}
/* Calculus III: a cool teal wash over a fine isometric-ish lattice — the
   three-dimensional cousin of the Calculus II graph paper */
.pane.p3 {
  background-image:
    radial-gradient(circle at 90% 8%, rgba(13, 148, 136, .17), rgba(13, 148, 136, 0) 46%),
    radial-gradient(circle at 8% 92%, rgba(8, 110, 120, .13), rgba(8, 110, 120, 0) 46%),
    linear-gradient(60deg,  rgba(20, 130, 120, .10) 1px, transparent 1px),
    linear-gradient(120deg, rgba(20, 130, 120, .10) 1px, transparent 1px),
    linear-gradient(to right, rgba(20, 130, 120, .10) 1px, transparent 1px);
  background-size: auto, auto, 30px 30px, 30px 30px, 30px 30px;
}
/* rows sit on the tint, so their separators need to be a touch stronger */
.pane .unit-row { border-bottom-color: var(--line); }

/* section labels inside a pane — h2 is taken by the course title */
.pane > h3 {
  margin: 34px 0 4px;
  font-size: .76rem;
  font-weight: 700;
  letter-spacing: .11em;
  text-transform: uppercase;
  color: var(--muted);
}
.pane > h3:first-of-type { margin-top: 26px; }
.pane .grid { margin: 14px 0 8px; }
.pane .lesson-links { margin: 12px 0 4px; }
.pane .callout { margin: 24px 0; }
.pane-foot {
  margin: 30px 0 0;
  padding-top: 18px;
  border-top: 1px solid var(--line);
  font-size: .9rem;
}

.unit-row {
  display: flex;
  gap: 16px;
  padding: 15px 0;
  border-bottom: 1px solid var(--line-soft);
}
.unit-row:last-child { border-bottom: 0; }
.unit-n {
  flex: 0 0 52px;
  font-size: .72rem;
  font-weight: 700;
  letter-spacing: .07em;
  text-transform: uppercase;
  color: var(--accent);
  padding-top: 3px;
}
.unit-row.planned .unit-n { color: var(--muted); }
.unit-body { flex: 1; min-width: 0; }
.unit-body h4 {
  margin: 0 0 4px;
  font-size: 1rem;
  text-transform: none;
  letter-spacing: -.01em;
  color: var(--ink);
}
.unit-row.planned .unit-body h4 { color: var(--muted); font-weight: 500; }
.unit-body > p { margin: 0; font-size: .87rem; color: var(--muted); }
.lesson-links {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 10px;
}
.lesson-links a {
  font-size: .83rem;
  text-decoration: none;
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 5px 12px;
  color: var(--ink);
  background: var(--panel);
  transition: border-color .15s, color .15s;
}
.lesson-links a:hover { border-color: var(--accent); color: var(--accent); }
.lesson-links a.is-exam { border-style: dashed; }

@media (max-width: 860px) {
  .course-browser { grid-template-columns: 1fr; gap: 18px; }
  .course-list {
    position: static;
    border-right: 0;
    padding-right: 0;
    border-bottom: 1px solid var(--line);
    padding-bottom: 20px;
  }
  .course-panes { padding-left: 0; }
  .pane { padding: 20px 18px 22px; }
}

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

.topbar {
  border-bottom: 1px solid var(--line);
  background: var(--panel);
  position: sticky;
  top: 0;
  z-index: 10;
}
.topbar-inner {
  max-width: 860px;
  margin: 0 auto;
  padding: 12px 22px;
  display: flex;
  align-items: center;
  gap: 14px;
  flex-wrap: wrap;
}
.brand {
  font-weight: 700;
  letter-spacing: -.01em;
  text-decoration: none;
  color: var(--ink);
  display: inline-flex;
  align-items: center;
  gap: 8px;
}
.brand .glyph { font-size: 1.15rem; }

/* outlined round "back" control, sits left of the brand */
.back-bubble {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  border: 1px solid var(--line);
  border-radius: 50%;
  background: transparent;
  color: var(--muted);
  text-decoration: none;
  transition:
    transform .28s cubic-bezier(.4, 0, .2, 1),
    border-color .28s cubic-bezier(.4, 0, .2, 1),
    color .28s cubic-bezier(.4, 0, .2, 1),
    background-color .28s cubic-bezier(.4, 0, .2, 1);
}
.back-bubble svg { display: block; width: 17px; height: 17px; }
.back-bubble:hover {
  color: var(--accent);
  border-color: var(--accent);
  background: var(--accent-soft);
  transform: translateX(-3px);   /* nudges the way it sends you */
}
.back-bubble:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
@media (prefers-reduced-motion: reduce) {
  .back-bubble:hover { transform: none; }
}
.crumbs {
  font-size: .84rem;
  color: var(--muted);
  display: flex;
  gap: 7px;
  flex-wrap: wrap;
}
.crumbs a { color: var(--muted); }
.crumbs a:hover { color: var(--accent); }
.crumbs .sep { opacity: .55; }

.wrap {
  max-width: 860px;
  margin: 0 auto;
  padding: 34px 22px 80px;
}
.wrap.lab-wrap { max-width: 1180px; }
/* full-bleed: content runs to the page gutters instead of a centred column */
.wrap.full {
  max-width: none;
  padding-left: clamp(14px, 2vw, 32px);
  padding-right: clamp(14px, 2vw, 32px);
}
/* keep the header aligned with full-bleed content instead of the 860px column */
.topbar-inner.full {
  max-width: none;
  padding-left: clamp(14px, 2vw, 32px);
  padding-right: clamp(14px, 2vw, 32px);
}

/* ---------- type ---------- */

h1, h2, h3, h4 { letter-spacing: -.018em; line-height: 1.25; }
h1 { font-size: 2rem; margin: 0 0 6px; }
h2 {
  font-size: 1.32rem;
  margin: 46px 0 14px;
  padding-bottom: 7px;
  border-bottom: 1px solid var(--line);
}
h3 { font-size: 1.06rem; margin: 30px 0 10px; }
h4 { font-size: .94rem; margin: 22px 0 8px; text-transform: uppercase; letter-spacing: .06em; color: var(--muted); }
p { margin: 0 0 15px; }
a { color: var(--accent); text-decoration-thickness: 1px; text-underline-offset: 2px; }
ul, ol { margin: 0 0 16px; padding-left: 22px; }
li { margin-bottom: 7px; }
li > ul, li > ol { margin-top: 7px; }
.lede { font-size: 1.06rem; color: var(--muted); margin-bottom: 26px; }
.eyebrow {
  font-size: .78rem;
  font-weight: 700;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--accent);
  margin: 0 0 8px;
}
hr { border: 0; border-top: 1px solid var(--line); margin: 40px 0; }
small, .small { font-size: .86rem; }

/* ---------- math ---------- */

var, .v {
  font-family: "Cambria Math", "Latin Modern Math", Cambria, Georgia, serif;
  font-style: italic;
  font-size: 1.06em;
}
.eq {
  background: var(--panel);
  border: 1px solid var(--line);
  border-left: 3px solid var(--accent);
  border-radius: 8px;
  padding: 14px 18px;
  margin: 18px 0;
  font-family: "Cambria Math", Cambria, Georgia, serif;
  font-size: 1.16rem;
  overflow-x: auto;
}
.eq .why {
  display: block;
  margin-top: 7px;
  font-family: ui-sans-serif, system-ui, sans-serif;
  font-size: .84rem;
  font-style: normal;
  color: var(--muted);
}
.frac {
  display: inline-flex;
  flex-direction: column;
  vertical-align: -0.5em;
  text-align: center;
  font-size: .86em;
  margin: 0 .12em;
}
.frac > span:first-child {
  border-bottom: 1px solid currentColor;
  padding: 0 .35em 1px;
}
.frac > span:last-child { padding: 1px .35em 0; }

/* big operators: sum, product, integral with stacked limits */
.bigop {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  vertical-align: middle;
  line-height: 1;
  margin: 0 .18em;
}
.bigop .top, .bigop .bot { font-size: .58em; white-space: nowrap; }
.bigop .mid { font-size: 1.45em; line-height: 1; }
.intop { font-size: 1.5em; vertical-align: -.22em; line-height: 0; }
.vec { font-weight: 700; font-style: normal; }

/* ---------- exams ---------- */

.exam-meta {
  display: flex;
  gap: 22px;
  flex-wrap: wrap;
  background: var(--panel-2);
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 14px 20px;
  margin: 22px 0 30px;
  font-size: .9rem;
}
.exam-meta div { min-width: 90px; }
.exam-meta .k {
  display: block;
  font-size: .72rem;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 2px;
}
.q {
  border: 1px solid var(--line);
  border-left: 3px solid var(--accent);
  border-radius: 10px;
  background: var(--panel);
  padding: 16px 20px 6px;
  margin: 20px 0;
}
.q-head {
  display: flex;
  align-items: baseline;
  gap: 10px;
  margin-bottom: 10px;
}
.q-num {
  font-weight: 700;
  font-size: 1rem;
  color: var(--accent);
  white-space: nowrap;
}
.q-topic {
  font-size: .74rem;
  letter-spacing: .07em;
  text-transform: uppercase;
  color: var(--muted);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 2px 10px;
}
.q-pts { margin-left: auto; font-size: .8rem; color: var(--muted); white-space: nowrap; }
.q > p:last-of-type { margin-bottom: 14px; }
.q details.ex { margin: 0 -20px -6px; border: 0; border-top: 1px solid var(--line-soft); border-radius: 0 0 9px 9px; }

/* ---------- boxes ---------- */

.grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(250px, 100%), 1fr));
  gap: 16px;
  margin: 22px 0;
}
/* Unit cards use the same lift-and-cross-fade-to-blue treatment as the launcher
   tiles and the course picker, so every card on the site behaves alike. */
.unit {
  position: relative;
  isolation: isolate;
  display: block;
  text-decoration: none;
  color: inherit;
  background: var(--tile-bg);
  border: 1px solid var(--line);
  border-radius: 12px;
  padding: 18px 20px;
  box-shadow: var(--tile-shadow);
  transition:
    transform .34s cubic-bezier(.4, 0, .2, 1),
    box-shadow .34s cubic-bezier(.4, 0, .2, 1),
    border-color .34s cubic-bezier(.4, 0, .2, 1),
    opacity .34s cubic-bezier(.4, 0, .2, 1);
}
.unit::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  border-radius: inherit;
  background: linear-gradient(150deg, var(--tile-blue-a) 0%, var(--tile-blue-b) 100%);
  opacity: 0;
  transition: opacity .34s cubic-bezier(.4, 0, .2, 1);
}
.unit .n {
  font-size: .76rem;
  font-weight: 700;
  letter-spacing: .09em;
  text-transform: uppercase;
  color: var(--accent);
  transition: color .34s cubic-bezier(.4, 0, .2, 1);
}
.unit h3 {
  margin: 6px 0 6px;
  font-size: 1.02rem;
  transition: color .34s cubic-bezier(.4, 0, .2, 1);
}
.unit p {
  margin: 0;
  font-size: .89rem;
  color: var(--muted);
  transition: color .34s cubic-bezier(.4, 0, .2, 1);
}
.unit:hover,
a.unit:focus-visible {
  transform: translateY(-6px) scale(1.03);
  border-color: var(--tile-blue-b);
  box-shadow: var(--tile-shadow-hover);
}
.unit:hover::before,
a.unit:focus-visible::before { opacity: 1; }
.unit:hover .n, a.unit:focus-visible .n { color: rgba(255, 255, 255, .82); }
.unit:hover h3, a.unit:focus-visible h3 { color: #ffffff; }
.unit:hover p,  a.unit:focus-visible p  { color: rgba(255, 255, 255, .87); }

/* planned units stay dimmed at rest but light up like the rest on hover */
.unit.soon { opacity: .6; }
.unit.soon:hover { opacity: 1; }
.unit.soon .n { color: var(--muted); }

@media (prefers-reduced-motion: reduce) {
  .unit:hover, a.unit:focus-visible { transform: none !important; }
}

.callout {
  border: 1px solid var(--line);
  border-left: 3px solid var(--muted);
  background: var(--panel-2);
  border-radius: 8px;
  padding: 14px 18px;
  margin: 20px 0;
}
.callout > :last-child { margin-bottom: 0; }
.callout .tag {
  display: block;
  font-size: .76rem;
  font-weight: 700;
  letter-spacing: .08em;
  text-transform: uppercase;
  margin-bottom: 6px;
}
.callout.note { border-left-color: var(--info); background: var(--info-soft); }
.callout.note .tag { color: var(--info); }
.callout.warn { border-left-color: var(--warn); background: var(--warn-soft); }
.callout.warn .tag { color: var(--warn); }
.callout.tip { border-left-color: var(--ok); background: var(--ok-soft); }
.callout.tip .tag { color: var(--ok); }
.callout.exam { border-left-color: var(--accent); background: var(--accent-soft); }
.callout.exam .tag { color: var(--accent); }

.badge {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: .76rem;
  font-weight: 600;
  letter-spacing: .05em;
  text-transform: uppercase;
  color: var(--muted);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 4px 12px;
}
.badge .dot { width: 7px; height: 7px; border-radius: 50%; background: var(--accent); }

/* ---------- tables ---------- */

.table-scroll { overflow-x: auto; margin: 20px 0; }
table {
  border-collapse: collapse;
  width: 100%;
  font-size: .92rem;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 10px;
}
caption {
  caption-side: bottom;
  text-align: left;
  font-size: .84rem;
  color: var(--muted);
  padding-top: 9px;
}
th, td {
  border-bottom: 1px solid var(--line-soft);
  padding: 9px 13px;
  text-align: left;
  vertical-align: top;
}
thead th {
  background: var(--panel-2);
  font-size: .8rem;
  letter-spacing: .05em;
  text-transform: uppercase;
  color: var(--muted);
  border-bottom: 1px solid var(--line);
  white-space: nowrap;
}
tbody tr:last-child td { border-bottom: 0; }
td.num, th.num { text-align: right; font-variant-numeric: tabular-nums; }
.blank td { height: 32px; }

/* ---------- figures ---------- */

figure {
  margin: 26px 0;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 12px;
  padding: 18px 18px 14px;
}
figure svg { display: block; width: 100%; height: auto; max-width: 440px; margin: 0 auto; }
figcaption {
  font-size: .87rem;
  color: var(--muted);
  margin-top: 12px;
  padding-top: 10px;
  border-top: 1px solid var(--line-soft);
}
.figrow {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(250px, 100%), 1fr));
  gap: 18px;
  margin: 26px 0;
}
.figrow figure { margin: 0; }

/* svg helpers */
.ax { stroke: var(--ink); stroke-width: 1.6; fill: none; }
.gridline { stroke: var(--grid); stroke-width: 1; stroke-dasharray: 3 4; }
.curve { stroke: var(--accent); stroke-width: 2.4; fill: none; stroke-linecap: round; stroke-linejoin: round; }
.curve2 { stroke: var(--info); stroke-width: 2.2; fill: none; stroke-linecap: round; stroke-linejoin: round; }
.dash { stroke-dasharray: 6 4; }
.fillw { fill: var(--accent); opacity: .15; }
.fillw2 { fill: var(--info); opacity: .15; }
.lbl { fill: var(--ink); font: 12px ui-sans-serif, system-ui, sans-serif; }
.lbl-m { fill: var(--muted); font: 11px ui-sans-serif, system-ui, sans-serif; }
.lbl-a { fill: var(--accent); font: 12px ui-sans-serif, system-ui, sans-serif; font-weight: 600; }
.lbl-i { fill: var(--ink); font: italic 13px "Cambria Math", Cambria, Georgia, serif; }
.dotm { fill: var(--accent); }
.box { fill: var(--panel-2); stroke: var(--ink); stroke-width: 1.5; }

/* ================= LABS ================= */

.lab-shell {
  display: grid;
  grid-template-columns: 264px 1fr;
  gap: 30px;
  align-items: start;
  margin-top: 26px;
}
.lab-nav {
  position: sticky;
  top: 68px;
  align-self: start;
  max-height: calc(100vh - 92px);
  overflow: auto;
  border-right: 1px solid var(--line);
  padding-right: 18px;
}
.lab-nav .list-head {
  display: block;
  font-size: .72rem;
  font-weight: 700;
  letter-spacing: .11em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 10px;
}
details.nav-course {
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--panel);
  margin-bottom: 10px;
}
details.nav-course > summary {
  cursor: pointer;
  list-style: none;
  padding: 11px 14px;
  font-weight: 700;
  font-size: .92rem;
  display: flex;
  align-items: center;
  gap: 9px;
}
details.nav-course > summary::-webkit-details-marker { display: none; }
details.nav-course > summary::before {
  content: "\25B8";
  color: var(--accent);
  font-size: .9em;
  transition: transform .15s;
}
details.nav-course[open] > summary::before { transform: rotate(90deg); }
details.nav-course > summary:hover { color: var(--accent); }
.nav-unit { padding: 0 11px 11px; }
.nav-unit-title {
  display: block;
  font-size: .7rem;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--muted);
  margin: 9px 0 6px 3px;
}
a.nav-lab, span.nav-lab {
  display: block;
  padding: 7px 10px;
  border-radius: 8px;
  font-size: .87rem;
  text-decoration: none;
  color: var(--ink);
}
a.nav-lab:hover { background: var(--accent-soft); color: var(--accent); }
span.nav-lab { color: var(--muted); opacity: .7; }

/* min-width:0 lets this grid child shrink; without it the canvases and
   control grids force the column wider than the viewport. */
.lab-main { min-width: 0; }
.lab-section { margin-bottom: 52px; scroll-margin-top: 76px; }
.sim {
  border: 1px solid var(--line);
  border-radius: 14px;
  background: var(--panel);
  padding: 22px;
  box-shadow: var(--tile-shadow);
}
.sim-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; }
.sim-stage {
  background: var(--panel-2);
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 10px;
}
.sim-stage canvas { display: block; width: 100%; height: auto; border-radius: 6px; }
.sim-cap { font-size: .79rem; color: var(--muted); margin: 9px 0 0; line-height: 1.5; }
.sim-controls { margin-top: 18px; }
.ctrl {
  display: grid;
  grid-template-columns: 128px 1fr 92px;
  gap: 12px;
  align-items: center;
  margin-bottom: 11px;
  font-size: .88rem;
}
.ctrl > span:first-child { color: var(--muted); }
.ctrl input[type="range"] { width: 100%; accent-color: var(--accent); }
.ctrl output {
  font-variant-numeric: tabular-nums;
  text-align: right;
  font-weight: 700;
}
.ctrl select,
.ctrl input[type="number"] {
  width: 100%;
  padding: 6px 9px;
  border: 1px solid var(--line);
  border-radius: 7px;
  background: var(--panel);
  color: var(--ink);
  font-size: .88rem;
  font-family: inherit;
}
.ctrl.wide { grid-template-columns: 128px 1fr; }
.chip-row { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 14px; }
.chip {
  font-size: .82rem;
  padding: 6px 13px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--panel);
  color: var(--ink);
  cursor: pointer;
  font-family: inherit;
  transition: border-color .15s, background .15s, color .15s;
}
.chip:hover { border-color: var(--accent); }
.chip.on {
  background: var(--accent-soft);
  border-color: var(--accent);
  color: var(--accent);
  font-weight: 600;
}
.readouts {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(104px, 100%), 1fr));
  gap: 10px;
  margin-top: 16px;
}
.readout {
  background: var(--panel-2);
  border: 1px solid var(--line);
  border-radius: 9px;
  padding: 9px 11px;
}
.readout .k {
  display: block;
  font-size: .67rem;
  letter-spacing: .07em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 2px;
}
.readout .v {
  font-size: 1.02rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.readout.pos .v { color: var(--ok); }
.readout.neg .v { color: var(--warn); }
/* vector strings are longer than a number; a step down keeps them on one line */
.readout { min-width: 0; overflow-wrap: anywhere; }
.readout .v.small { font-size: .88rem; }

/* ---- vector lab ---- */
a.nav-lab[aria-current="page"] { background: var(--accent-soft); color: var(--accent); font-weight: 600; }
/* the 3D stage earns more width than the graph beside it */
.sim-grid.vec-grid { grid-template-columns: minmax(0, 3fr) minmax(0, 2fr); align-items: start; }
/* an orbit control has to own the touch: without this the first drag on a
   phone scrolls the page instead of the scene */
.sim-stage canvas.grab { cursor: grab; touch-action: none; }
.sim-stage canvas.grab:active { cursor: grabbing; }
.chip-label {
  align-self: center;
  font-size: .68rem;
  font-weight: 700;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--muted);
  margin-right: 4px;
}
.ctrl.vec { grid-template-columns: 44px 1fr 84px; }
.ctrl.vec > span:first-child { font-weight: 600; color: var(--ink); }
.ctrl.vec sub { font-size: .75em; }
.vec-a { color: var(--accent); }
.vec-b { color: var(--info); }
.vec-x { color: var(--vec-cross); }
h4.vec-a, h4.vec-b { margin-top: 4px; }
.vec-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 5px 16px;
  margin-top: 9px;
  font-size: .78rem;
  color: var(--muted);
}
.vec-legend i {
  display: inline-block;
  width: 18px;
  height: 3px;
  border-radius: 2px;
  vertical-align: middle;
  margin-right: 6px;
  background: currentColor;
}
.vec-legend .sw-a { color: var(--accent); }
.vec-legend .sw-b { color: var(--info); }
.vec-legend .sw-sum { color: var(--ok); }
.vec-legend .sw-diff { color: var(--warn); }
.vec-legend .sw-cross { color: var(--vec-cross); }
.vec-legend .sw-proj { color: var(--muted); height: 5px; opacity: .55; }
/* live worked arithmetic under the readouts */
.work {
  margin-top: 16px;
  padding: 11px 14px;
  background: var(--panel-2);
  border: 1px solid var(--line);
  border-radius: 9px;
  font-family: ui-monospace, "Cascadia Mono", Consolas, Menlo, monospace;
  font-size: .78rem;
  line-height: 1.75;
  color: var(--ink);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  font-variant-numeric: tabular-nums;
}

@media (max-width: 940px) {
  .lab-shell { grid-template-columns: 1fr; gap: 20px; }
  .lab-nav {
    position: static;
    max-height: none;
    border-right: 0;
    padding-right: 0;
    border-bottom: 1px solid var(--line);
    padding-bottom: 16px;
  }
  .sim-grid { grid-template-columns: 1fr; }
  .sim-grid.vec-grid { grid-template-columns: 1fr; }
}

/* ================= LESSON PAGES ================= */

/* Per-unit colour themes. Each lesson page carries one of these on <body>
   alongside .lesson-page; everything else reads from --u-accent / --u-a / --u-b
   so a unit is re-skinned by changing three values. */
body.u1 { --u-accent: #0d9488; --u-a: rgba(13,148,136,.15);  --u-b: rgba(45,180,160,.11); }
body.u2 { --u-accent: #b45309; --u-a: rgba(202,138,4,.16);   --u-b: rgba(217,119,6,.11);  }
body.u3 { --u-accent: #7c3aed; --u-a: rgba(124,58,237,.15);  --u-b: rgba(147,84,240,.11); }
body.u4 { --u-accent: #2563eb; --u-a: rgba(37,99,235,.15);   --u-b: rgba(59,130,246,.11); }
body.u5 { --u-accent: #db2777; --u-a: rgba(219,39,119,.14);  --u-b: rgba(236,72,153,.10); }
body.u6 { --u-accent: #0891b2; --u-a: rgba(8,145,178,.15);   --u-b: rgba(6,182,212,.11);  }

@media (prefers-color-scheme: dark) {
  body.u1 { --u-accent: #5eead4; }
  body.u2 { --u-accent: #fbbf24; }
  body.u3 { --u-accent: #c4b5fd; }
  body.u4 { --u-accent: #93c5fd; }
  body.u5 { --u-accent: #f9a8d4; }
  body.u6 { --u-accent: #67e8f9; }
}

.lesson-page {
  background-color: var(--bg);
  background-image:
    radial-gradient(56% 44% at 6% 0%,  var(--u-a), transparent 68%),
    radial-gradient(48% 40% at 97% 18%, var(--u-b), transparent 70%);
  background-attachment: fixed;
  background-repeat: no-repeat;
}

.lesson-shell {
  display: grid;
  grid-template-columns: 268px minmax(0, 1fr);
  gap: 36px;
  align-items: start;
}
.lesson-body { min-width: 0; }
.lesson-toc {
  position: sticky;
  top: 68px;
  align-self: start;
  max-height: calc(100vh - 92px);
  overflow: auto;
}

details.toc-drop {
  border: 1px solid var(--line);
  border-radius: 11px;
  background: var(--panel);
  margin-bottom: 10px;
  box-shadow: var(--tile-shadow);
}
details.toc-drop > summary {
  cursor: pointer;
  list-style: none;
  padding: 11px 14px;
  font-weight: 700;
  font-size: .84rem;
  letter-spacing: .02em;
  display: flex;
  align-items: center;
  gap: 9px;
}
details.toc-drop > summary::-webkit-details-marker { display: none; }
details.toc-drop > summary::before {
  content: "\25B8";
  color: var(--u-accent, var(--accent));
  font-size: .9em;
  transition: transform .18s;
}
details.toc-drop[open] > summary::before { transform: rotate(90deg); }
details.toc-drop > summary:hover { color: var(--u-accent, var(--accent)); }
.toc-body {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 2px 10px 11px;
}
a.toc-box {
  display: block;
  padding: 8px 11px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--tile-bg);
  color: var(--ink);
  font-size: .83rem;
  line-height: 1.35;
  text-decoration: none;
  transition:
    transform .26s cubic-bezier(.4, 0, .2, 1),
    border-color .26s cubic-bezier(.4, 0, .2, 1),
    color .26s cubic-bezier(.4, 0, .2, 1);
}
a.toc-box .toc-n {
  display: inline-block;
  min-width: 1.4em;
  font-weight: 700;
  color: var(--u-accent, var(--accent));
}
a.toc-box:hover {
  transform: translateX(4px);
  border-color: var(--u-accent, var(--accent));
  color: var(--u-accent, var(--accent));
}
a.toc-box.here {
  border-color: var(--u-accent, var(--accent));
  font-weight: 700;
}
a.toc-box.here::after { content: " ·"; }

/* unit tint bleeds into the headings so the page feels themed, not painted */
.lesson-body h2 { border-bottom-color: var(--u-accent, var(--line)); }
.lesson-body .eyebrow { color: var(--u-accent, var(--accent)); }

/* ---- LaTeX blocks (KaTeX) ---- */
.eqbox {
  background: var(--panel);
  border: 1px solid var(--line);
  border-left: 3px solid var(--u-accent, var(--accent));
  border-radius: 8px;
  padding: 15px 18px;
  margin: 20px 0;
  overflow-x: auto;
  box-shadow: var(--tile-shadow);
}
.eqbox .why {
  display: block;
  margin-top: 9px;
  font-size: .85rem;
  color: var(--muted);
  line-height: 1.55;
}
.eqbox .katex-display { margin: 0; }
.eqbox .katex-display + .katex-display { margin-top: .7em; }
/* long equations scroll rather than overflowing the page */
.katex-display { overflow-x: auto; overflow-y: hidden; padding: 2px 0; }
/* This one line is load-bearing, and it is not cosmetic.
   KaTeX emits a visually hidden MathML copy of every expression as an
   absolutely positioned sibling of the visual one, and nothing in its own
   stylesheet positions .katex. So the hidden copy resolved against the page
   itself: a wide equation, or inline maths inside a scrolling table, parked a
   1px box out past the right edge and stretched document.scrollWidth, which on
   a phone reads as the whole page sliding sideways. Positioning .katex keeps
   the copy inside whichever scroller the equation lives in.
   Safe to do: .katex-mathml is the only thing KaTeX positions absolutely
   without a nearer relative ancestor of its own. */
.katex { position: relative; }

@media (max-width: 1000px) {
  .lesson-shell { grid-template-columns: 1fr; gap: 18px; }
  .lesson-toc {
    position: static;
    max-height: none;
    overflow: visible;
    border-bottom: 1px solid var(--line);
    padding-bottom: 14px;
  }
}

/* ---------- code ---------- */

code {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: .86em;
  background: var(--code-bg);
  border: 1px solid var(--line);
  border-radius: 5px;
  padding: 1px 5px;
}
pre {
  background: var(--code-bg);
  border: 1px solid var(--line);
  border-radius: 9px;
  padding: 14px 16px;
  overflow-x: auto;
  font-size: .87rem;
  line-height: 1.55;
}
pre code { background: none; border: 0; padding: 0; font-size: 1em; }

/* ---------- worked examples ---------- */

details.ex {
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--panel);
  margin: 18px 0;
}
details.ex > summary {
  cursor: pointer;
  padding: 13px 18px;
  font-weight: 600;
  font-size: .95rem;
  list-style: none;
  display: flex;
  align-items: center;
  gap: 9px;
}
details.ex > summary::-webkit-details-marker { display: none; }
details.ex > summary::before {
  content: "\25B8";
  color: var(--accent);
  font-size: .9em;
  transition: transform .15s;
}
details.ex[open] > summary::before { transform: rotate(90deg); }
details.ex > summary:hover { color: var(--accent); }
.ex-body {
  padding: 4px 18px 16px;
  border-top: 1px solid var(--line-soft);
}
.ex-body > :first-child { margin-top: 13px; }
.ex-body > :last-child { margin-bottom: 0; }

/* ---------- misc ---------- */

.toc {
  background: var(--panel-2);
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 16px 22px;
  margin: 24px 0 30px;
}
.toc h4 { margin: 0 0 8px; }
.toc ol { margin: 0; padding-left: 20px; }
.toc li { margin-bottom: 4px; }

.pager {
  display: flex;
  justify-content: space-between;
  gap: 14px;
  margin-top: 48px;
  padding-top: 20px;
  border-top: 1px solid var(--line);
  font-size: .92rem;
  flex-wrap: wrap;
}
.pager a { text-decoration: none; }
.pager .dir { display: block; font-size: .76rem; color: var(--muted); text-transform: uppercase; letter-spacing: .07em; }

footer.site {
  border-top: 1px solid var(--line);
  color: var(--muted);
  font-size: .84rem;
  padding: 22px;
}
footer.site .inner { max-width: 860px; margin: 0 auto; }

@media print {
  .topbar, .pager, footer.site { display: none; }
  body { background: #fff; }
  figure, table, .eq { box-shadow: none; }
  details.ex { break-inside: avoid; }
  details.ex > summary { display: none; }
  .ex-body { display: block !important; border-top: 0; }
  details.ex .ex-body { display: block !important; }
}

/* =====================================================================
   MOBILE AND TOUCH
   Appended last on purpose: every rule here is a narrowing override of
   something declared above, so source order does the work and no
   !important is needed. Breakpoints used, and why:
     1000px  the lesson TOC has already stacked above the lesson by here
      860px  the course picker has already stacked above the panes by here
      620px  phones and small tablets in portrait: dense controls, tight padding
      560px  phones: type scale, hero, topbar
      520px  where the launcher tiles go one-up and switch to a sideways layout
     480px tall + landscape: a phone held sideways, where 58vh of hero is too much
   ===================================================================== */

/* ---------- 1. nothing may push the page sideways ---------- */

img, svg, canvas, video { max-width: 100%; }
body { overflow-wrap: break-word; }
/* KaTeX builds maths out of inline-blocks with negative margins, so letting
   the browser break inside one mangles the spacing. Long expressions scroll
   instead — .eqbox, .katex-display, .eq and pre all allow that already. */
.katex, .katex-display, .eq, pre, code, .frac, .bigop { overflow-wrap: normal; }
/* momentum scrolling inside the horizontal scrollers on iOS */
.table-scroll, .katex-display, .eqbox, .eq, pre { -webkit-overflow-scrolling: touch; }

/* ---------- 2. touch devices ---------- */

/* On a touchscreen :hover latches after a tap and can stay latched when you
   navigate back, which left cards frozen blue and lifted. Unwind the hover
   treatment where there is no real pointer, and give a short press response
   instead so taps still feel answered. */
@media (hover: none) {

  /* the three card treatments share a resting state, so they reset together */
  .tile:hover,
  .course-list label:hover,
  .unit:hover {
    transform: none;
    box-shadow: var(--tile-shadow);
    border-color: var(--line);
    background: var(--tile-bg);
    color: inherit;
  }
  /* these two rest differently — no shadow on a contents box, no fill on the
     back bubble — so resetting them with the cards would have added both */
  a.toc-box:hover { transform: none; border-color: var(--line); color: inherit; }
  a.toc-box.here:hover { border-color: var(--u-accent, var(--accent)); }
  .back-bubble:hover { transform: none; border-color: var(--line); color: var(--muted); background: transparent; }
  .tile:hover::before,
  .course-list label:hover::before,
  .unit:hover::before { opacity: 0; }
  .tile:hover h3,
  .unit:hover h3 { color: inherit; }
  .tile:hover p,
  .unit:hover p,
  .course-list label:hover small { color: var(--muted); }
  .unit:hover .n { color: var(--accent); }
  .unit.soon:hover .n { color: var(--muted); }
  .tile:hover .soon-badge { color: var(--muted); border-color: var(--line); }
  .tile.soon-tile:hover { opacity: .58; }
  .unit.soon:hover { opacity: .6; }
  /* :not(.on) is not decoration — a flat reset lands later in the file at
     equal specificity and would strip the selected chip of its accent. */
  .lesson-links a:hover,
  .chip:hover:not(.on) { color: var(--ink); border-color: var(--line); }
  a.nav-lab:hover { background: none; color: var(--ink); }
  details.ex > summary:hover,
  details.toc-drop > summary:hover,
  details.nav-course > summary:hover,
  .crumbs a:hover { color: inherit; }

  /* The selected course card is styled with :not(:hover) so that a real
     pointer can still lift it. With no pointer that exclusion would blank the
     selection the instant you tapped it, so restate it unconditionally. */
  #c1:checked ~ .course-list label[for="c1"],
  #c2:checked ~ .course-list label[for="c2"] {
    border-color: var(--accent);
    background: var(--accent-soft);
    color: var(--accent);
  }

  /* Press response. Listed after the resets above so it wins while held. */
  a.tile:active,
  a.unit:active,
  .course-list label:active,
  a.toc-box:active,
  .lesson-links a:active,
  .chip:active,
  .pager a:active,
  .back-bubble:active {
    transform: scale(.975);
    transition-duration: .07s;
  }
  a.tile, a.unit, .course-list label, a.toc-box, .lesson-links a,
  .chip, .pager a, .back-bubble, details > summary {
    -webkit-tap-highlight-color: rgba(42, 112, 224, .12);
  }
}

/* ---------- 3. landing page ---------- */

@media (max-width: 560px) {
  .hero { height: 52vh; min-height: 300px; }
  .hero-inner { padding: 26px 20px; }
  /* the description that used to sit under the title is gone, so its gap is
     dead space on a screen this small */
  .site-title { font-size: clamp(2.05rem, 11.5vw, 3.6rem); margin-bottom: 0; }

  .launcher {
    min-height: 0;
    margin-top: -22px;
    border-radius: 20px 20px 0 0;
    padding: 32px 16px 54px;
  }
  .launcher h2 { font-size: .8rem; letter-spacing: .12em; margin-bottom: 16px; }
  /* the engineering grid is scaled to the panel, not to the page */
  .launcher::before {
    background-size: 30px 30px, 30px 30px, 120px 120px, 120px 120px, auto, auto;
  }
}

/* One tile per row, laid out sideways: icon in a fixed gutter, text beside it.
   Five 214px-tall cards stacked would be most of a metre of scrolling. */
@media (max-width: 520px) {
  .tiles { gap: 12px; }
  .tile {
    min-height: 0;
    padding: 15px 16px 16px;
    display: grid;
    grid-template-columns: 42px minmax(0, 1fr);
    column-gap: 14px;
    align-items: start;
  }
  .tile .tile-icon {
    grid-column: 1;
    grid-row: 1 / span 3;
    align-self: center;
    font-size: 1.85rem;
    margin-bottom: 0;
  }
  .tile h3 { grid-column: 2; margin: 0 0 3px; font-size: 1.06rem; }
  .tile p { grid-column: 2; font-size: .85rem; }
  .tile .soon-badge { grid-column: 2; margin-top: 8px; }
}

/* A phone on its side: 58vh of hero plus a 380px floor is the whole screen. */
@media (max-height: 480px) and (orientation: landscape) {
  .hero { height: 78vh; min-height: 210px; }
  .hero-inner { padding: 18px clamp(20px, 6vw, 64px); }
  .site-title { font-size: clamp(1.9rem, 6.4vw, 3.2rem); margin-bottom: 0; }
  .launcher { min-height: 0; padding-top: 34px; }
}

/* ---------- 4. chrome and type ---------- */

@media (max-width: 560px) {
  .topbar-inner { padding: 9px 14px; gap: 10px; }
  .brand { font-size: .95rem; }
  /* 34px is under a comfortable tap target; 40px is not */
  .back-bubble { width: 40px; height: 40px; }
  .crumbs { font-size: .8rem; }

  .wrap { padding: 24px 16px 60px; }
  h1 { font-size: 1.62rem; }
  h2 { font-size: 1.18rem; }
  h3 { font-size: 1.02rem; }
  .lede { font-size: 1rem; margin-bottom: 20px; }
  .eq { font-size: 1.05rem; padding: 12px 14px; }
  figure { padding: 14px 14px 12px; }
  .callout { padding: 13px 15px; }

  /* pill links become rows you can actually hit */
  .lesson-links a { padding: 8px 14px; font-size: .86rem; }
  .pager { flex-direction: column; gap: 10px; margin-top: 36px; }
  .pager a {
    border: 1px solid var(--line);
    border-radius: 10px;
    background: var(--panel);
    padding: 11px 14px;
  }
  footer.site { padding: 20px 16px; }
}

/* ---------- 5. course browser ---------- */

/* Below 860px the picker is already stacked above the panes. Make it a grid so
   two courses sit side by side on a tablet and one-up on a phone, instead of a
   tall column of full-width cards to scroll past on every visit. */
@media (max-width: 860px) {
  .course-list {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(220px, 100%), 1fr));
    gap: 12px;
    align-items: start;
  }
  .courses-head, .courses-sub { grid-column: 1 / -1; }
  .courses-head { font-size: 1.2rem; }
  .courses-sub { margin: -4px 0 2px; }
}
@media (max-width: 620px) {
  .pane { padding: 18px 14px 20px; border-radius: 12px; }
  .pane > h3:first-of-type { margin-top: 20px; }
  .unit { padding: 15px 16px; }
}

/* ---------- 6. labs ---------- */

@media (max-width: 620px) {
  .sim { padding: 14px; border-radius: 12px; }
  .sim-stage { padding: 7px; }
  /* Label on its own line so the slider keeps the full width. At three
     columns it was down to roughly 70px of travel on a phone. */
  .ctrl {
    grid-template-columns: minmax(0, 1fr) 84px;
    column-gap: 10px;
    row-gap: 2px;
    margin-bottom: 15px;
  }
  .ctrl > span:first-child { grid-column: 1 / -1; }
  .ctrl.wide { grid-template-columns: minmax(0, 1fr); }
  .ctrl input[type="range"] { height: 30px; margin: 0; }
  .ctrl select, .ctrl input[type="number"] { padding: 9px 10px; }
  .chip { padding: 9px 15px; }
  .lab-section { margin-bottom: 38px; }
  /* the component label is two characters wide, so it can keep its column */
  .ctrl.vec { grid-template-columns: 34px minmax(0, 1fr) 74px; row-gap: 0; }
  .ctrl.vec > span:first-child { grid-column: auto; }
  .work { font-size: .72rem; padding: 9px 11px; }
}

/* ---------- 7. exams ---------- */

@media (max-width: 620px) {
  .exam-meta { gap: 14px; padding: 12px 15px; }
  .exam-meta div { min-width: 78px; }
  /* the negative margin has to track the padding, or the answer drawer stops
     lining up with the card edges */
  .q { padding: 14px 15px 6px; }
  .q details.ex { margin: 0 -15px -6px; }
  .q-head { flex-wrap: wrap; gap: 8px; }
  details.ex > summary { padding: 12px 15px; }
  .ex-body { padding: 4px 15px 14px; }
}

/* ---------- 8. lesson pages ---------- */

/* Stacked, the contents drawer sits between the header and the lesson, and one
   drawer starts open — on a phone that pushed the first heading off screen.
   Cap it and let it scroll inside itself instead. */
@media (max-width: 1000px) {
  .lesson-toc {
    max-height: 42vh;
    overflow: auto;
    -webkit-overflow-scrolling: touch;
  }
}
@media (max-width: 620px) {
  .lesson-shell { gap: 14px; }
  .eqbox { padding: 13px 14px; margin: 16px 0; }
  a.toc-box { padding: 10px 11px; }
  /* a fixed background repaints on every scroll frame on mobile GPUs */
  .lesson-page { background-attachment: scroll; }
}

/* ================= LABS EMBEDDED IN LESSONS ================= */
/* Calculus III drops .sim workbenches straight into the lesson body. The
   workbench styles above are global, so only the framing is new here. */
.lab-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px 12px;
  margin: 30px 0 8px;
}
.lab-head h3 { margin: 0; }
.lab-tag {
  font-size: .66rem;
  font-weight: 700;
  letter-spacing: .11em;
  text-transform: uppercase;
  color: var(--u-accent, var(--accent));
  border: 1px solid currentColor;
  border-radius: 999px;
  padding: 3px 10px;
  white-space: nowrap;
}
.lesson-body .sim { margin: 12px 0 30px; }
.lesson-body .sim h4 { margin: 12px 0 6px; font-size: .9rem; }
/* maths inside a lab heading must keep its case: ax + by + cz, not AX + BY */
.lesson-body .sim h4 .katex { text-transform: none; letter-spacing: 0; }
/* 3D stage wide, companion graph narrower */
.sim-grid.three { grid-template-columns: minmax(0, 3fr) minmax(0, 2fr); align-items: start; }
.sim-grid.one { grid-template-columns: 1fr; }
.sim-stage canvas.grab { cursor: grab; touch-action: none; }
.sim-stage canvas.grab:active { cursor: grabbing; }
.sim-stage canvas.point { cursor: crosshair; touch-action: none; }
.ctrl.narrow { grid-template-columns: 64px 1fr 76px; }
.ctrl.narrow > span:first-child { font-weight: 600; color: var(--ink); }
.ctrl-group[hidden] { display: none; }
/* live equation line under the readouts, rendered by KaTeX */
.live-eq {
  margin-top: 14px;
  padding: 10px 14px;
  background: var(--panel-2);
  border: 1px solid var(--line);
  border-left: 3px solid var(--u-accent, var(--accent));
  border-radius: 9px;
  font-size: .95rem;
  overflow-x: auto;
  min-height: 1.4em;
}
.live-eq .k {
  display: block;
  font-size: .66rem;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 4px;
}
.readout.ok .v { color: var(--ok); }
.readout.bad .v { color: var(--accent); }
.readout.info .v { color: var(--info); }
/* per-face flux table in the divergence-theorem lab */
.mini-table { font-size: .82rem; margin-top: 12px; }
.mini-table td, .mini-table th { padding: 5px 9px; }
@media (max-width: 940px) {
  .sim-grid.three { grid-template-columns: 1fr; }
}
