/* Expected Learning - the editorial datasheet.
   -------------------------------------------------------------------------
   The page is not a centred column of cards. It is a 12 column spread with a
   full-bleed gutter, thin mono rules that label their sections the way a
   datasheet does, display type set large enough to BE the layout, and a
   handful of signature objects: the editor window, the lesson ticker, the
   syllabus chapters, the verdict ledger.
   Tokens live in tokens.css and are consumed here only via var(--*) -
   nothing in this file redefines a token.
   ------------------------------------------------------------------------- */

*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; }
html { scroll-behavior: smooth; }
img, picture, video, canvas, svg { max-width: 100%; display: block; }
html, body { max-width: 100%; }

body {
  background: var(--bg);
  color: var(--ink);
  font-family: var(--sans);
  font-size: var(--step-0);
  line-height: 1.62;
  -webkit-font-smoothing: antialiased;
}

/* Atmosphere: ember glow fields plus a fine grain, both painted from
   gradients only - no image request, no external URL of any kind. */
body::before,
body::after {
  content: "";
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 0;
}
body::before {
  background:
    radial-gradient(70rem 44rem at 88% -16%, rgba(168, 216, 240, 0.13), transparent 62%),
    radial-gradient(56rem 40rem at -14% 24%, rgba(255, 139, 61, 0.06), transparent 58%),
    radial-gradient(50rem 36rem at 42% 108%, rgba(140, 230, 143, 0.05), transparent 62%);
}
body::after {
  opacity: 0.5;
  background:
    repeating-linear-gradient(37deg, rgba(255, 255, 255, 0.016) 0 1px, transparent 1px 3px),
    repeating-linear-gradient(113deg, rgba(0, 0, 0, 0.05) 0 1px, transparent 1px 4px);
}
body > * { position: relative; z-index: 1; }

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

h1, h2, h3 { line-height: 1.02; font-weight: 700; }
h1 {
  font-family: var(--disp);
  font-size: var(--disp-2);
  letter-spacing: -0.035em;
  margin: 0 0 var(--s4);
}
h2 {
  font-family: var(--disp);
  font-size: var(--step-3);
  letter-spacing: -0.025em;
  margin: 0 0 var(--s3);
}
h3 { font-size: var(--step-1); letter-spacing: -0.01em; margin: 0 0 var(--s2); }

p { max-width: 74ch; }
.muted { color: var(--mut); }
.legal { max-width: 68ch; color: var(--mut); }
.legal + .legal { margin-top: var(--s3); }
.note { font-family: var(--mono); font-size: var(--step--1); color: var(--mut); }

/* -----------------------------------------------------------------------
   The frame: a 12 column spread, and the full-bleed escape hatch
   ----------------------------------------------------------------------- */
.frame {
  width: 100%;
  max-width: var(--maxw);
  margin-inline: auto;
  padding-inline: var(--pad);
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  column-gap: clamp(0.75rem, 1.6vw, 2rem);
  row-gap: var(--s4);
}
.bleed { width: 100%; }

/* Pages that are a straight run of prose (changelog, pledges, the legal
   pages) emit no frame of their own. Give every direct child of main that is
   not already full-bleed the same gutter and measure, so those pages need no
   markup change at all. */
main > :not(.frame):not(.bleed) {
  width: 100%;
  max-width: var(--maxw);
  margin-inline: auto;
  padding-inline: var(--pad);
}

/* Column spans. Mobile-first: every spread column is full width by default
   (source order stacks top to bottom), then the 12-col spans layer in at
   desktop widths only - the approved desktop layout is unchanged. */
.c-1,
.c-l7, .c-l8, .c-l6, .c-r5, .c-r6, .c-r4, .c-mid, .c-in7, .c-in9 {
  grid-column: 1 / -1;
}
@media (min-width: 60rem) {
  .c-l7 { grid-column: 1 / span 7; }
  .c-l8 { grid-column: 1 / span 8; }
  .c-l6 { grid-column: 1 / span 6; }
  .c-r5 { grid-column: 8 / span 5; }
  .c-r6 { grid-column: 7 / span 6; }
  .c-r4 { grid-column: 9 / span 4; }
  .c-mid { grid-column: 3 / span 8; }
  .c-in7 { grid-column: 2 / span 7; }
  .c-in9 { grid-column: 2 / span 9; }
}

/* -----------------------------------------------------------------------
   Skip link, header, footer
   ----------------------------------------------------------------------- */
.skip {
  position: absolute;
  left: var(--s3);
  top: -60px;
  z-index: 60;
  background: var(--acc);
  color: var(--bg);
  padding: 10px 18px;
  font-family: var(--mono);
  font-weight: 700;
  text-decoration: none;
  transition: top 0.15s ease;
}
.skip:focus { top: var(--s2); }

.site-head {
  /* zoom-safe: at high zoom (a 320px effective viewport, WCAG 1.4.10) a
     sticky header with a wrapped two-row nav consumes most of the screen
     and the content can never be seen - owner-reported. Static by default;
     sticky only where the viewport provably has room for it. */
  position: static;
  z-index: 50;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s3);
  width: 100%;
  max-width: var(--maxw);
  margin-inline: auto;
  padding: var(--s3) var(--pad);
  background: color-mix(in srgb, var(--bg) 82%, transparent);
  -webkit-backdrop-filter: blur(14px);
  backdrop-filter: blur(14px);
  border-bottom: 1px solid transparent;
  transition: border-color 0.25s ease;
}
@supports not (background: color-mix(in srgb, red 50%, blue)) {
  .site-head { background: var(--bg); }
}
.site-head.scrolled { border-bottom-color: var(--line); }
.site-head .brand {
  font-family: var(--disp);
  font-weight: 700;
  font-size: var(--step-1);
  letter-spacing: -0.03em;
  color: var(--ink);
  text-decoration: none;
  white-space: nowrap;
  flex-shrink: 0;
}
.site-head .brand::after {
  content: "";
  display: block;
  height: 2px;
  width: 0;
  background: var(--acc);
  transition: width 0.25s ease;
}
.site-head .brand:hover::after,
.site-head .brand[aria-current="page"]::after { width: 100%; }
.site-head nav {
  display: flex;
  flex-wrap: nowrap;
  align-items: center;
  gap: var(--s3);
  min-width: 0;
}
/* Below the desktop breakpoint the 7 language/product links do not fit one
   screen at phone widths (measured: ~460px of content in a 320px viewport -
   see mobile-nav-research.md option 1). Rather than let flex-wrap decide an
   accidental, content-driven two-row break (the old behaviour), the links
   ride a single never-wrapping scroll strip - same pattern already proven by
   .mq-track / .ed-code on this site - and the theme toggle is pulled OUT of
   the scrolling strip entirely so it can never be scroll-hidden (the
   research's a11y ruling: "recommend the latter, it's a persistent,
   non-taxonomy control and hiding it via scroll position would be a
   functional regression, not just cosmetic"). */
/* no horizontal scroll on the nav, ever (owner, from a real phone,
   2026-08-30): the languages fit one tightened row on phones and are
   ALLOWED to wrap into designed rows at ultra-narrow - wrap, never clip,
   never scroll. */
.site-head .nav-scroll {
  display: flex;
  flex-wrap: wrap;
  gap: 0.1rem 0.35rem;
  justify-content: space-between;
  width: 100%;
  min-width: 0;
}
.site-head nav a {
  font-family: var(--mono);
  font-size: var(--step--1);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--mut);
  text-decoration: none;
  padding: 2px 0.25rem;
  border-bottom: 2px solid transparent;
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  flex: 0 0 auto;
}
@media (max-width: 47.99rem) {
  /* tightened phone metrics so six languages fit one 390px row */
  .site-head nav a { letter-spacing: 0.04em; font-size: max(0.72rem, calc(var(--step--1) * 0.92)); }
}
@media (min-width: 48rem) {
  /* Tablets and up fit all 7 + toggle on one row natively (768 measured
     77px pre-strip) - the strip mechanics fall away from 48rem, not 60
     (owner 2026-08-30: tablets have the space). */
  .site-head .nav-scroll { gap: clamp(0.6rem, 1.6vw, 1.6rem); justify-content: flex-start; }
  .site-head nav a { min-height: 0; padding: 2px 0; }
}
.site-head nav a:hover { color: var(--ink); }
.site-head nav a[aria-current="page"] { color: var(--acc); border-bottom-color: var(--acc); }

main { display: block; padding-bottom: var(--s7); }

.site-foot {
  width: 100%;
  max-width: var(--maxw);
  margin-inline: auto;
  padding: var(--s5) var(--pad) var(--s6);
  border-top: 1px solid var(--line);
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--s4);
}
.site-foot nav { display: flex; gap: var(--s4); flex-wrap: wrap; }
.site-foot nav a {
  font-family: var(--mono);
  font-size: var(--step--1);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--mut);
  text-decoration: none;
}
.site-foot nav a:hover { color: var(--acc); }
.site-foot nav a[aria-current="page"] { color: var(--acc); }
.site-foot .foot-line {
  font-family: var(--disp);
  font-size: var(--step-2);
  letter-spacing: -0.03em;
  color: var(--ink);
  margin: 0;
}

/* -----------------------------------------------------------------------
   Sections and their mono rule labels
   ----------------------------------------------------------------------- */
.section { margin-block: var(--s7); /* never the shorthand: it zeroed margin-inline and beat .frame centering at ultrawide */ }
.section:first-child { margin-top: var(--s5); }

.sec-tag {
  grid-column: 1 / -1;
  display: flex;
  align-items: center;
  gap: var(--s3);
  font-family: var(--mono);
  font-size: var(--step--1);
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--mut);
  margin: 0 0 var(--s2);
  max-width: none;
}
.sec-tag::after {
  content: "";
  flex: 1;
  height: 1px;
  background: linear-gradient(90deg, var(--line), transparent);
}
.sec-tag b { color: var(--acc); font-weight: 700; }

.eyebrow, .kicker {
  font-family: var(--mono);
  font-size: var(--step--1);
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--mut);
  margin: 0 0 var(--s2);
}

.lede {
  font-size: var(--step-1);
  color: var(--mut);
  max-width: 58ch;
  line-height: 1.5;
}

/* A section heading with its own oversized numeral sitting behind it. */
.big-head { position: relative; }
.big-head h2 {
  font-size: var(--disp-3);
  letter-spacing: -0.04em;
  position: relative;
  z-index: 1;
}
.big-head .ghost-num {
  position: absolute;
  right: 0;
  top: -0.32em;
  font-family: var(--disp);
  font-weight: 700;
  font-size: clamp(5rem, 13vw, 12rem);
  line-height: 0.8;
  /* normal tracking: negative tracking merged the "1" into the "0" on
     two-digit counts (php's 10 tracks) at display size - owner-reported */
  letter-spacing: normal;
  color: transparent;
  /* the FULL numeral must always read, deliberately overflowing the
     section rule line (owner 2026-08-29); z-index 0 keeps it above the
     divider chrome but below the heading text, and the stroke leans on
     --mut so the whole glyph is visible, not a whisper */
  -webkit-text-stroke: 1.5px color-mix(in srgb, var(--mut) 45%, transparent);
  pointer-events: none;
  user-select: none;
  z-index: 0;
}
@supports not (color: color-mix(in srgb, red 50%, blue)) {
  .big-head .ghost-num { -webkit-text-stroke: 1.5px var(--mut); }
}

/* -----------------------------------------------------------------------
   HERO - the display headline wrapping an off-axis editor window
   ----------------------------------------------------------------------- */
.hero { margin-top: var(--s5); margin-bottom: var(--s7); }
.hero-h1 {
  font-family: var(--disp);
  font-size: var(--disp-1);
  font-weight: 700;
  line-height: 0.88;
  letter-spacing: -0.045em;
  margin: 0;
  text-wrap: balance;
}
.hero-h1 .thin { font-weight: 300; letter-spacing: -0.03em; }
.hero-h1 .lit {
  color: var(--acc);
  background: linear-gradient(96deg, var(--acc) 0%, var(--test) 44%, var(--acc) 100%);
  background-size: 260% 100%;
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  animation: sweep 9s ease-in-out infinite;
}
/* With JS the highlight is driven by scroll position instead of a timer:
   site.js sets --sweep (0..1) on .hero-h1 and it inherits down to here. */
.js .hero-h1 .lit {
  animation: none;
  background-position: calc(var(--sweep, 0) * 100%) 50%;
}
@supports not ((-webkit-background-clip: text) or (background-clip: text)) {
  .hero-h1 .lit { color: var(--acc); -webkit-text-fill-color: currentColor; }
}
@keyframes sweep {
  0%, 100% { background-position: 0% 50%; }
  50% { background-position: 100% 50%; }
}
.hero-h1 .off { display: block; margin-left: clamp(0px, 5vw, 5rem); }
/* short display lines (two words) stall the sweep gradient mid-wash and
   read as a mistake - they carry solid accent instead (owner 2026-08-29) */
.hero-h1 .lit-solid { color: var(--acc); }
.hero-h1 { margin-bottom: clamp(1.2rem, 3vw, 2rem); }

.hero-annot {
  font-family: var(--mono);
  font-size: var(--step--1);
  line-height: 1.7;
  letter-spacing: 0.02em;
  color: var(--mut);
  max-width: 50ch;
  border-left: 2px solid var(--acc);
  padding-left: var(--s3);
}
.hero-annot b { color: var(--ink); font-weight: 700; }

.hero-wedge {
  font-size: var(--step-2);
  font-family: var(--disp);
  font-weight: 300;
  letter-spacing: -0.02em;
  line-height: 1.22;
  color: var(--ink);
  max-width: 42ch;
}

/* -----------------------------------------------------------------------
   The editor window - drawn entirely in CSS
   ----------------------------------------------------------------------- */
.editor {
  background: var(--term-bg);
  border: 1px solid var(--con-border);
  border-radius: 8px;
  box-shadow: var(--shadow), 0 0 0 1px rgba(255, 144, 102, 0.10), 0 0 90px rgba(255, 144, 102, 0.14);
  overflow: hidden;
  font-family: var(--mono);
  font-size: clamp(0.66rem, 0.55rem + 0.4vw, 0.82rem);
  line-height: 1.65;
  color: var(--ink);
  width: 100%;
  max-width: 100%;
}
/* The warm amber glow (rgba(255,144,102,...)) reads as an ambient light-source
   halo on the dark --term-bg it was tuned against, but composited straight
   over the cream page background it becomes a brown smudge, not a glow.
   Neutralize it to a cool, near-invisible shadow on light instead - the
   --shadow token itself already carries the theme's warm drop-shadow tone,
   so only the two glow layers need overriding here. */
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) .editor {
    box-shadow: var(--shadow), 0 0 0 1px rgba(11, 60, 90, 0.06), 0 0 40px rgba(11, 60, 90, 0.06);
  }
}
/* Mobile-first: the editor sits flat, full width, below the headline. Only
   at desktop widths does it go off-axis. */
.editor.tilt { transform: none; }
@media (min-width: 60rem) {
  .editor { transition: transform 0.5s cubic-bezier(0.2, 0.7, 0.2, 1); }
  /* owner 2026-08-29: no rotation on the editor - it sits square */
  .editor.tilt { transform: translateY(-1.5rem); }
}
.ed-bar {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 9px 12px;
  background: var(--con-bg2);
  border-bottom: 1px solid var(--con-border);
}
.ed-bar .dot { width: 10px; height: 10px; border-radius: 50%; background: var(--con-dot-off); flex: none; }
.ed-bar .dot.r { background: #e5534b; }
.ed-bar .dot.y { background: #d4a72c; }
.ed-bar .dot.g { background: #3fb950; }
.ed-tab {
  margin-left: 6px;
  padding: 3px 12px;
  background: var(--term-bg);
  border: 1px solid var(--con-border);
  border-bottom-color: transparent;
  border-radius: 5px 5px 0 0;
  color: var(--mut);
  font-size: 0.92em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.ed-tab .tab-mark { color: var(--acc); }
/* dark 7.05:1 on #11161d; light 5.80:1 on --term-bg #ffffff (--con-where #5a6773) */
.ed-where { margin-left: auto; color: var(--con-where); font-size: 0.86em; white-space: nowrap; }

/* Mobile-first: code lines never truncate - the list scrolls horizontally
   instead. At desktop widths (where every real line already fits) the
   original ellipsis clipping is restored, unchanged. */
.ed-code {
  list-style: none;
  margin: 0;
  padding: var(--s3) 0;
  counter-reset: ln;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}
.ed-code li {
  counter-increment: ln;
  position: relative;
  padding: 0 var(--s3) 0 4.3em;
  white-space: pre;
  overflow: visible;
  text-overflow: clip;
  width: max-content;
  min-width: 100%;
  /* an empty line (a blank in the real source) has no line box under
     white-space: pre - give every row one line of height so the numbering
     stays continuous and matches the source file line for line */
  min-height: 1.6em;
  min-height: 1lh;
}
@media (min-width: 60rem) {
  .ed-code { overflow-x: hidden; }
  .ed-code li { overflow: hidden; text-overflow: ellipsis; width: auto; min-width: 0; }
}
/* Gutter geometry: a dedicated dot column (0 to 1.5em; the dot itself at
   0.5em so it clears the 3px hit bar) sits to the LEFT of the line-number
   column (1.5em to 3.8em, right-aligned so it lines up whether the number
   is one or two digits), then a 0.5em gap before code starts at 4.3em -
   mirrors a real IDE gutter. All boxes are em-sized so the split scales
   with font-size instead of depending on a pixel nudge tuned for one line
   length. */
.ed-code li::before {
  content: counter(ln);
  position: absolute;
  left: 1.5em;
  width: 2.3em;
  text-align: right;
  color: var(--con-ln);
}
.ed-code li.bp::after {
  content: "";
  position: absolute;
  left: 0.5em;
  top: 0.575em;
  width: 0.7em;
  height: 0.7em;
  border-radius: 50%;
  background: #ff5f56;
  box-shadow: 0 0 12px rgba(255, 95, 86, 0.9);
}
.ed-code li.hit {
  background: rgba(255, 144, 102, 0.10);
  box-shadow: inset 3px 0 0 var(--acc);
}
/* dark on #0b0f14 / light on --term-bg #ffffff (both AA):
   k b8380a 5.82, s(=test) 8f4508 6.95, c(=con-tag) 5a6773 5.80,
   f 0c6f7c 5.87, n 8a2ec0 6.50 */
.ed-code .k { color: var(--con-k); }
.ed-code .s { color: var(--con-s); }
.ed-code .c { color: var(--con-tag); }
.ed-code .f { color: var(--con-f); }
.ed-code .n { color: var(--con-n); }

.ed-panel {
  border-top: 1px solid var(--con-border);
  background: var(--con-panel);
  padding: var(--s3) var(--s4);
  overflow-x: auto;
}
.ed-panel-tag {
  display: block;
  /* dark 6.27:1 on #0a0d12; light 5.11:1 on --con-panel #eef1f4 */
  color: var(--con-tag);
  letter-spacing: 0.2em;
  text-transform: uppercase;
  font-size: 0.82em;
  margin-bottom: var(--s2);
  max-width: none;
}
.ed-strip {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: var(--s2) var(--s3);
  padding: var(--s3) var(--s4);
  border-top: 1px solid var(--con-border);
  background:
    linear-gradient(90deg, rgba(95, 242, 176, 0.16), transparent 70%),
    var(--con-panel);
}
.ed-strip.no {
  background:
    linear-gradient(90deg, rgba(255, 122, 122, 0.16), transparent 70%),
    var(--con-panel);
}
.ed-verdict {
  font-family: var(--disp);
  font-weight: 700;
  font-size: clamp(1.6rem, 1rem + 2.2vw, 3.1rem);
  line-height: 1;
  letter-spacing: -0.04em;
  color: var(--pass);
  text-shadow: 0 0 40px rgba(95, 242, 176, 0.45);
}
.ed-strip.mid {
  background:
    linear-gradient(90deg, rgba(255, 199, 107, 0.16), transparent 70%),
    var(--con-panel);
}
.ed-strip.no .ed-verdict { color: var(--fail); text-shadow: 0 0 40px rgba(255, 122, 122, 0.4); }
.ed-strip.mid .ed-verdict { color: var(--warn); text-shadow: 0 0 40px rgba(255, 199, 107, 0.35); }
.ed-strip-note { color: var(--mut); max-width: 44ch; }

/* -----------------------------------------------------------------------
   Status rail - product id, stage, lesson count, in mono
   ----------------------------------------------------------------------- */
/* S1: dock the rail flush beneath the editor window so hero terminal +
   status rail read as ONE dark panel, the way /products/ pairs its hero
   assert console with the family status. */
.editor.docked { border-radius: 8px 8px 0 0; border-bottom: none; }
.rail-dock {
  background: var(--term-bg);
  border: 1px solid var(--con-border);
  border-top: none;
  border-radius: 0 0 8px 8px;
  padding: var(--s4) 1rem;
  box-shadow: var(--shadow);
}
.rail-dock .eyebrow { color: var(--mut); }
.rail-dock .rail { border-top: 1px solid var(--line); }
/* S2: decision load - the row the owner is calling next (java, per
   upcoming.html) carries primary weight so it reads before the six-way
   choice, without reordering or rewording any row. */

.rail { border-top: 1px solid var(--line); }
.rail-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto auto;
  align-items: baseline;
  gap: var(--s3);
  min-height: 44px;
  padding: 0.7rem 0;
  border-bottom: 1px solid var(--line);
  font-family: var(--mono);
  font-size: var(--step--1);
  text-decoration: none;
  color: var(--mut);
  transition: color 0.2s ease, padding-left 0.2s ease;
}
.rail-row:hover { color: var(--ink); padding-left: var(--s2); }
.rail-name {
  color: var(--ink);
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
}
.rail-row:hover .rail-name { color: var(--acc); }
.rail-count { color: var(--mut); font-variant-numeric: tabular-nums; }

/* -----------------------------------------------------------------------
   The ticker - real lesson names, decorative, aria-hidden
   ----------------------------------------------------------------------- */
.marquee {
  overflow: hidden;
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  padding: var(--s3) 0;
  margin-block: var(--s7); /* never the shorthand: it zeroed margin-inline and beat .frame centering at ultrawide */
  display: flex;
  gap: 0;
  mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
}
.mq-track {
  display: flex;
  flex: none;
  align-items: center;
  gap: var(--s4);
  padding-right: var(--s4);
  white-space: nowrap;
  animation: mq 92s linear infinite;
}
.marquee:hover .mq-track { animation-play-state: paused; }
@keyframes mq { from { transform: translateX(0); } to { transform: translateX(-100%); } }
.mq-item {
  font-family: var(--disp);
  font-weight: 300;
  font-size: clamp(1.1rem, 0.7rem + 1.5vw, 2.1rem);
  letter-spacing: -0.02em;
  color: var(--mut);
}
.mq-item.free { color: var(--pass); font-weight: 500; }
.mq-sep { color: var(--acc); font-family: var(--mono); font-size: 1rem; }
@media (prefers-reduced-motion: reduce) {
  .mq-track { animation: none; }
  .marquee { flex-wrap: wrap; }
  .mq-track + .mq-track { display: none; }
}

/* -----------------------------------------------------------------------
   Syllabus - editorial chapters, lesson names as dense text runs
   ----------------------------------------------------------------------- */
.syllabus { display: block; }
.chapter, .track-box {
  border-top: 1px solid var(--line);
  padding: var(--s5) 0;
}
.chapter:last-child, .track-box:last-child { border-bottom: 1px solid var(--line); }

.ch-head {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: baseline;
  gap: var(--s2) var(--s4);
  margin-bottom: var(--s4);
}
.ch-head .ch-meta { grid-column: 1 / -1; text-align: left; }
@media (min-width: 40rem) {
  .ch-head { grid-template-columns: auto minmax(0, 1fr) auto; }
  .ch-head .ch-meta { grid-column: auto; text-align: right; }
}
.ch-num {
  font-family: var(--disp);
  font-weight: 700;
  font-size: clamp(2rem, 1.2rem + 5.5vw, 6rem);
  line-height: 0.8;
  letter-spacing: -0.05em;
  color: transparent;
  -webkit-text-stroke: 1.5px var(--acc);
  font-variant-numeric: tabular-nums;
}
@supports not (-webkit-text-stroke: 1px red) {
  .ch-num { color: var(--acc); }
}
.ch-name {
  font-family: var(--disp);
  font-weight: 700;
  font-size: clamp(1.35rem, 0.9rem + 2.6vw, 3.2rem);
  letter-spacing: -0.035em;
  line-height: 1;
  color: var(--ink);
  margin: 0;
}
.ch-meta {
  font-family: var(--mono);
  font-size: var(--step--1);
  color: var(--mut);
  text-align: right;
  white-space: nowrap;
  margin: 0;
  max-width: none;
}
.ch-meta b { color: var(--acc); font-weight: 700; }
.ch-meta .free-n { color: var(--pass); font-weight: 700; }

.ch-runs {
  max-width: none;
  font-size: clamp(0.95rem, 0.9rem + 0.35vw, 1.16rem);
  line-height: 1.85;
  color: var(--mut);
  overflow-wrap: break-word;
}
.run { color: var(--mut); overflow-wrap: break-word; }
.run.is-free { color: var(--pass); font-weight: 600; }
.run-free {
  font-family: var(--mono);
  font-size: 0.56em;
  letter-spacing: 0.14em;
  vertical-align: 0.55em;
  margin-left: 0.35em;
  color: var(--pass);
}
.run-sep {
  color: var(--line);
  padding: 0 0.45em;
  font-family: var(--mono);
}
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) .run-sep { color: var(--mut); opacity: 0.45; }
}

/* Collapsed tracks keep the same typography behind a real disclosure. */
details.track-box > summary {
  list-style: none;
  cursor: pointer;
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: baseline;
  gap: var(--s2) var(--s4);
  min-height: 44px;
}
details.track-box > summary .ch-meta { grid-column: 1 / -1; text-align: left; }
@media (min-width: 40rem) {
  details.track-box > summary { grid-template-columns: auto minmax(0, 1fr) auto; }
  details.track-box > summary .ch-meta { grid-column: auto; text-align: right; }
}
details.track-box > summary::-webkit-details-marker { display: none; }
details.track-box > summary::after {
  content: "+";
  font-family: var(--mono);
  color: var(--acc);
  font-size: var(--step-2);
  align-self: center;
}
details.track-box[open] > summary::after { content: "-"; }
details.track-box[open] > summary { margin-bottom: var(--s4); }

/* -----------------------------------------------------------------------
   Ledger - receipts with oversized verdict typography
   ----------------------------------------------------------------------- */
.receipts {
  background: var(--term-bg);
  border: 1px solid var(--con-border);
  border-radius: 8px;
  color: var(--ink);
  overflow: hidden;
}
.receipts p {
  display: grid;
  grid-template-columns: minmax(5.5rem, auto) minmax(0, 1fr);
  align-items: start;
  gap: var(--s2) var(--s4);
  max-width: none;
  padding: var(--s4) clamp(1rem, 3vw, 2.5rem);
  border-bottom: 1px solid var(--con-border-2);
  font-size: var(--step-0);
}
.receipts p:last-child { border-bottom: none; }
.receipts .v-rail {
  grid-row: span 2;
  display: flex;
  align-items: center;
  justify-content: center;
  border-right: 1px solid var(--con-border);
  padding-right: var(--s3);
}
.receipts .v {
  font-family: var(--disp);
  font-weight: 700;
  font-size: clamp(1.1rem, 0.7rem + 1.1vw, 1.6rem);
  line-height: 0.9;
  letter-spacing: 0.06em;
  color: var(--pass);
  writing-mode: vertical-rl;
  transform: rotate(180deg);
  text-transform: uppercase;
}
.receipts .v.pass { color: var(--pass); text-shadow: 0 0 34px rgba(95, 242, 176, 0.35); }
.receipts .v.open { color: var(--warn); }
.receipts .v.pledge { color: var(--acc); }
.receipts .ev {
  grid-column: 2;
  display: block;
  color: var(--mut);
  font-size: 0.92em;
  line-height: 1.55;
  margin-top: 6px;
  padding-left: var(--s3);
  border-left: 1px solid var(--con-border);
}
/* A bordered pill inside a receipt would fight the ledger numerals. */
.receipts .v.pill { border: none; padding: 0; background: none; }

.receipts-note {
  color: var(--mut);
  font-size: var(--step--1);
  margin-bottom: var(--s3);
}
.receipts-note a { color: var(--ink); }

.receipts.receipts-ghost {
  background: transparent;
  border: 1px dashed var(--line);
  border-radius: 8px;
}
.receipts.receipts-ghost p {
  display: block;
  grid-template-columns: none;
  color: var(--mut);
  padding: clamp(1.5rem, 4vw, 2.5rem);
  border-bottom: none;
  font-size: var(--step-0);
}

.verify-tag {
  display: inline-block;
  font-family: var(--mono);
  font-size: var(--step--1);
  letter-spacing: 0.1em;
  border: 1px solid var(--line);
  padding: 2px 10px;
  color: var(--mut);
}
.verify-tag.ok { color: var(--pass); border-color: var(--pass); }

/* -----------------------------------------------------------------------
   Console blocks kept from the datasheet vocabulary
   ----------------------------------------------------------------------- */
.term {
  background: var(--term-bg);
  border: 1px solid var(--con-border);
  border-radius: 8px;
  overflow: hidden;
  box-shadow: var(--shadow);
}
.term-bar {
  display: flex; align-items: center; gap: 8px;
  padding: 9px 12px; background: var(--con-bg2); border-bottom: 1px solid var(--con-border);
}
.term-bar .dot { width: 10px; height: 10px; border-radius: 50%; background: var(--con-dot-off); }
.term-bar .dot.r { background: #e5534b; }
.term-bar .dot.y { background: #d4a72c; }
.term-bar .dot.g { background: #3fb950; }
.term-title { margin-left: 6px; font-family: var(--mono); font-size: var(--step--1); color: var(--mut); }
.term-body { padding: var(--s4); font-family: var(--mono); color: var(--ink); overflow-x: auto; }

/* the test-output console is prose (expected/actual/verdict lines, never
   aligned columns) - it wraps instead of growing a horizontal scrollbar
   (owner 2026-08-29); code editors (.ed-code) keep their scroll */
[data-typed] { white-space: pre-wrap; word-break: break-word; }
[data-typed] .caret {
  display: inline-block; width: 0.6em; height: 1.05em; vertical-align: -0.16em;
  background: var(--acc); animation: caret-blink 1s steps(1) infinite;
}
@media (prefers-reduced-motion: reduce) { [data-typed] .caret { animation: none; opacity: 1; } }
@keyframes caret-blink { 0%, 49% { opacity: 1; } 50%, 100% { opacity: 0; } }

.assert {
  background: var(--term-bg);
  border: 1px solid var(--con-border);
  border-radius: 8px;
  color: var(--ink);
  font-family: var(--mono);
  font-size: var(--step-0);
  line-height: 1.85;
  white-space: pre;
  overflow-x: auto;
  padding: var(--s4) clamp(1rem, 3vw, 2.5rem);
}
.assert .pass { color: var(--pass); font-weight: 700; }
.assert .fail { color: var(--fail); font-weight: 700; }

/* -----------------------------------------------------------------------
   Stat numerals - enormous, tabular, sitting under mono labels
   ----------------------------------------------------------------------- */
.bento {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 0;
  border-top: 1px solid var(--line);
  border-left: 1px solid var(--line);
}
@media (min-width: 40rem) {
  .bento { grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr)); }
}
.tile {
  border-right: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  padding: var(--s4) var(--s3) var(--s3);
  display: flex;
  flex-direction: column;
  gap: 2px;
  background: linear-gradient(180deg, rgba(255, 144, 102, 0.05), transparent 55%);
}
.tile-num {
  font-family: var(--disp);
  font-weight: 700;
  font-size: clamp(2.4rem, 1.2rem + 3.4vw, 4.6rem);
  line-height: 0.85;
  letter-spacing: -0.05em;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
  overflow-wrap: anywhere;
}
.tile-num .unit { font-size: 0.34em; color: var(--mut); font-weight: 600; margin-left: 4px; }
.tile-label {
  font-family: var(--mono);
  font-size: var(--step--1);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--mut);
}
.tile-accent .tile-num { color: var(--acc); text-shadow: 0 0 44px rgba(255, 144, 102, 0.35); }
.tile-free .tile-num { color: var(--pass); text-shadow: 0 0 44px rgba(95, 242, 176, 0.30); }
.tile-stage .tile-num {
  font-family: var(--mono); font-size: var(--step-2); letter-spacing: 0.06em; line-height: 1.2;
}

/* -----------------------------------------------------------------------
   Cards - bordered fields in a rule grid, never rounded boxes
   ----------------------------------------------------------------------- */
.card {
  padding: var(--s4) 0 var(--s5);
  border-top: 1px solid var(--line);
  background: transparent;
}
.card h2, .card h3 {
  font-family: var(--disp);
  font-size: var(--step-2);
  letter-spacing: -0.03em;
  margin: 0 0 var(--s2);
}
.card p { color: var(--mut); }
.card p:last-child { margin-bottom: 0; }
.card.feature {
  border: 1px solid var(--acc);
  padding: var(--s5);
  background: linear-gradient(150deg, var(--acc-soft), transparent 60%);
}
.card.feature h2 { font-size: var(--disp-3); }
.card.dim { border-top-color: var(--line); }
.card.dim h2 { color: var(--mut); }

.grid2 { display: grid; grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr)); gap: 0 var(--s5); }
.grid2 .card { border-top: 2px solid var(--acc); }

.step-n {
  display: block;
  font-family: var(--mono);
  font-size: var(--step--1);
  letter-spacing: 0.2em;
  color: var(--acc);
  margin-bottom: var(--s2);
}

/* -----------------------------------------------------------------------
   Product index - an editorial list, not a card grid
   ----------------------------------------------------------------------- */
.product-grid { display: block; border-top: 1px solid var(--line); }
.product-grid .card {
  border-top: none;
  border-bottom: 1px solid var(--line);
  padding: var(--s5) 0;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--s3);
  align-items: start;
}
@media (min-width: 62rem) {
  .product-grid .card {
    grid-template-columns: minmax(0, 7fr) minmax(0, 5fr);
    column-gap: var(--s6);
  }
  /* the heading owns a full row so every product name sits on ONE line at
     the same size (owner 2026-08-30); pill and stats reflow beneath it */
  .product-grid .card > h2 { grid-column: 1 / -1; grid-row: 1; white-space: nowrap; }
  .product-grid .card > .stage-pill { grid-column: 2; grid-row: 2; justify-self: start; }
  .product-grid .card > .muted { grid-column: 1; grid-row: 2 / span 2; }
  .product-grid .card > .pg-stats { grid-column: 2; grid-row: 3; }
  .product-grid .card > .btn { grid-column: 2; grid-row: 4; justify-self: start; }
}
.product-grid .card h2 {
  font-size: clamp(1.9rem, 1rem + 3.4vw, 4.2rem);
  letter-spacing: -0.045em;
  line-height: 0.95;
  color: var(--mut);
  font-weight: 300;
  margin: 0;
  transition: color 0.25s ease;
}
.product-grid .card h2 b { color: var(--ink); font-weight: 700; white-space: nowrap; }
.product-grid .card:hover h2 b { color: var(--acc); }
.pg-stats { display: flex; gap: var(--s5); flex-wrap: wrap; }
.pg-stat {
  font-family: var(--mono);
  font-size: var(--step--1);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--mut);
  display: flex;
  flex-direction: column;
}
.pg-stat b {
  font-family: var(--disp);
  font-size: clamp(1.5rem, 1.1rem + 1.4vw, 2.1rem);
  line-height: 1;
  letter-spacing: -0.04em;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}
.pg-stat.pill-free { color: var(--pass); border: none; padding: 0; }
.pg-stat.pill-free b { color: var(--pass); }

.product-grid .card.card-dim { border-bottom-color: var(--line); }
.product-grid .card.card-dim h2 { opacity: 0.55; }
.product-grid .card.card-dim h2 b { color: var(--mut); }
.product-grid .card.card-dim:hover h2 b { color: var(--mut); }
.product-grid .card.card-dim .muted { opacity: 0.6; }
.product-grid .card.card-dim .pg-stat { opacity: 0.6; }
.product-grid .card.card-dim .pg-stat b { color: var(--mut); }
.product-grid .card.card-dim .pg-stat.pill-free { color: var(--mut); }
.product-grid .card.card-dim .pg-stat.pill-free b { color: var(--mut); }
.product-grid .card.card-dim .btn { opacity: 0.7; }
.product-grid .card.card-dim .stage-pill { opacity: 1; }

/* -----------------------------------------------------------------------
   Chips, stage words, legend
   ----------------------------------------------------------------------- */
.chip {
  display: inline-block;
  font-family: var(--mono);
  font-size: var(--step--1);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  border: 1px solid var(--line);
  padding: 3px 12px;
  color: var(--mut);
  white-space: nowrap;
}
.chip.live { color: var(--pass); border-color: var(--pass); }
.chip.dev { color: var(--acc); border-color: var(--acc); }
.chip.draft { color: var(--warn); border-color: var(--warn); }
.chip.soon { color: var(--mut); border-color: var(--line); }
.st-next { color: var(--acc); }
.st-drafted { color: var(--mut); }
.st-built { color: var(--warn); }
.st-tested { color: var(--pass); }
.st-live { color: var(--pass); }
.chip:not([class*="st-"]):not(.draft):not(.dev):not(.live):not(.soon) {
  color: var(--test); border-color: var(--test); background: rgba(255, 139, 61, 0.08);
}
.chip.st-next { color: var(--acc); border-color: var(--acc); }
.chip.st-drafted { color: var(--mut); border-color: var(--line); }
.chip.st-built { color: var(--warn); border-color: var(--warn); }
.chip.st-tested { color: var(--pass); border-color: var(--pass); }
.chip.st-live { color: var(--pass); border-color: var(--pass); }
.chip.st-in-development { color: var(--warn); border-color: var(--warn); }
.stage-pill { align-self: start; }

.legend { display: flex; flex-wrap: wrap; align-items: center; gap: 6px var(--s3); margin: var(--s3) 0; }
.legend .lg {
  font-family: var(--mono);
  font-size: var(--step--1);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--mut);
}
.legend .lg-arrow { color: var(--acc); font-family: var(--mono); }

/* -----------------------------------------------------------------------
   Pills - verdicts and small facts, no longer a pill wall
   ----------------------------------------------------------------------- */
.pill {
  display: inline-block;
  font-family: var(--mono);
  font-size: var(--step--1);
  letter-spacing: 0.06em;
  border: 1px solid var(--line);
  padding: 2px 10px;
  color: var(--mut);
}
.pill-free { border-color: var(--pass); color: var(--pass); }
.pill-free-mark { font-size: 0.72em; letter-spacing: 0.16em; margin-left: 6px; color: var(--pass); }
.pill-pass { border-color: var(--pass); color: var(--pass); }
.pill-warn, .pill-open { border-color: var(--warn); color: var(--warn); }
.pill-acc { border-color: var(--acc); color: var(--acc); }

/* -----------------------------------------------------------------------
   Buttons - calm, legible, never the drama
   ----------------------------------------------------------------------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;
  font-family: var(--mono);
  font-size: var(--step-0);
  letter-spacing: 0.04em;
  font-weight: 700;
  text-decoration: none;
  padding: 0.85rem 1.6rem;
  background: var(--acc);
  color: var(--bg);
  border: 1px solid var(--acc);
  transition: filter 0.2s ease, transform 0.2s ease;
}
:root:not([data-theme="dark"]) .btn { color: #ffffff; }
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) .btn { color: var(--bg); } }
:root[data-theme="dark"] .btn { color: var(--bg); }
:root[data-theme="light"] .btn { color: #ffffff; }
.btn:hover { filter: brightness(1.1); transform: translateY(-1px); }
.btn.ghost { background: transparent; border: 1px solid var(--acc); }
:root .btn.ghost { color: var(--acc); }
.btn[aria-disabled="true"] {
  background: transparent;
  border-color: var(--line);
  cursor: default;
  transform: none;
  filter: none;
}
:root .btn[aria-disabled="true"] { color: var(--mut); }

/* -----------------------------------------------------------------------
   Platform section - deliberately calm and plainly legible
   ----------------------------------------------------------------------- */
.platform-card { border: 1px solid var(--line); background: var(--panel); padding: var(--s4) var(--s5) var(--s5); }
.platform-card [role="tablist"] { display: none; }
.platform-card.js-tabs [role="tablist"] {
  display: flex; flex-wrap: wrap; gap: var(--s4);
  border-bottom: 1px solid var(--line); margin-bottom: var(--s4);
}
.platform-card [role="tab"] {
  background: none; border: none; border-bottom: 2px solid transparent;
  font-family: var(--mono); font-size: var(--step-0); letter-spacing: 0.08em;
  color: var(--mut); padding: var(--s2) 0; cursor: pointer;
  min-height: 44px; display: inline-flex; align-items: center;
}
.platform-card [role="tab"]:hover { color: var(--ink); }
.platform-card [role="tab"][aria-selected="true"] { color: var(--acc); border-bottom-color: var(--acc); }
.platform-card [role="tabpanel"] h3 { font-family: var(--mono); font-size: var(--step-0); margin: 0 0 var(--s2); }
.platform-card [role="tabpanel"] p { color: var(--mut); }
.platform-card [role="tabpanel"] + [role="tabpanel"] {
  margin-top: var(--s4); border-top: 1px solid var(--line); padding-top: var(--s4);
}
.platform-card.js-tabs [role="tabpanel"] + [role="tabpanel"] { margin-top: 0; border-top: none; padding-top: 0; }
.platform-card [role="tabpanel"] .note { margin: var(--s3) 0 0; }

.get-line { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s4); margin-top: var(--s4); max-width: none; }
.get-line .lede { max-width: 36ch; margin: 0; }

/* -----------------------------------------------------------------------
   OS matrix - per-track OS-runnability, generated from lesson manifests
   ----------------------------------------------------------------------- */
.mono { font-family: var(--mono); }
.mut { color: var(--mut); }
.os-matrix {
  /* separated from the platform card by a rule and clear air (owner spec
     2026-08-30) */
  margin-top: var(--s5);
  margin-bottom: var(--s6);
  padding-top: var(--s4);
  border-top: 1px solid var(--line);
 margin-top: var(--s5); padding-top: var(--s4); border-top: 1px solid var(--line); }
.os-matrix h3 { font-family: var(--mono); font-size: var(--step-0); letter-spacing: 0.04em; margin: 0 0 var(--s4); color: var(--mut); }
.os-matrix-table { width: 100%; border-collapse: collapse; font-family: var(--mono); table-layout: fixed; }
.os-matrix-table caption {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}
.os-matrix-table th[scope="col"] { text-align: left; color: var(--mut); font-weight: 400; padding: var(--s2) var(--s3); border-bottom: 1px solid var(--line); }
.os-matrix-table th[scope="row"] { text-align: left; font-weight: 700; padding: var(--s2) var(--s3) var(--s2) 0; }
.os-matrix-table td { padding: var(--s2) var(--s3); border-bottom: 1px solid var(--line); }
.os-cell-full { color: var(--pass); }
.os-cell-zero { color: var(--warn); }
.os-cell-pending { color: var(--mut); }
.os-cell .skip { color: var(--mut); font-size: 0.85em; margin-left: 0.4em; }
.os-matrix-note-row td { border-bottom: none; color: var(--mut); }
.os-matrix .note { margin-top: var(--s4); }

@media (min-width: 60rem) {
  .os-matrix-table th:first-child, .os-matrix-table td:first-child { width: 14rem; }
}

@media (max-width: 59.99rem) {
  .os-matrix-table, .os-matrix-table tbody, .os-matrix-table tr, .os-matrix-table th, .os-matrix-table td { display: block; width: auto; }
  .os-matrix-table thead { display: none; }
  .os-matrix-table tbody tr { padding: var(--s3) 0; border-bottom: 1px solid var(--line); }
  .os-matrix-table th[scope="row"] { padding: 0 0 var(--s2); border-bottom: none; }
  .os-matrix-table td { display: flex; justify-content: space-between; padding: 2px 0; border: none; }
  .os-matrix-table td::before { content: attr(data-os); color: var(--mut); font-size: 0.85em; }
  .os-matrix-note-row td { display: block; }
  .os-matrix-note-row td::before { content: none; }
}

/* -----------------------------------------------------------------------
   FAQ
   ----------------------------------------------------------------------- */
.faq details, details.faq-item { border-bottom: 1px solid var(--line); padding: var(--s4) 0; }
.faq details:first-of-type, details.faq-item:first-of-type { border-top: 1px solid var(--line); }
.faq summary, details.faq-item summary {
  cursor: pointer; list-style: none;
  font-family: var(--disp); font-weight: 700; font-size: var(--step-2);
  letter-spacing: -0.02em; display: flex; justify-content: space-between; gap: var(--s3);
}
.faq summary::-webkit-details-marker, details.faq-item summary::-webkit-details-marker { display: none; }
.faq summary::after, details.faq-item summary::after { content: "+"; color: var(--acc); font-family: var(--mono); }
.faq details[open] summary::after, details.faq-item[open] summary::after { content: "-"; }
.faq details p, details.faq-item p { color: var(--mut); margin: var(--s3) 0 0; }
dl.faq dt { font-weight: 700; margin-top: var(--s4); font-family: var(--mono); }
dl.faq dd { margin: 4px 0 0; color: var(--mut); }

/* -----------------------------------------------------------------------
   Trigger words - they ignite as their section reveals
   ----------------------------------------------------------------------- */
.kw { font-family: var(--mono); color: var(--acc); }
.hl { color: var(--acc); font-weight: 700; --ig: var(--acc); }
.grn { color: var(--pass); font-weight: 700; --ig: var(--pass); }
.amb { color: var(--warn); font-weight: 700; --ig: var(--warn); }
.tst { color: var(--test); font-weight: 700; --ig: var(--test); }
.hardpass { color: var(--fail); font-weight: 700; font-family: var(--mono); --ig: var(--fail); }

.js .reveal :is(.grn, .amb, .tst, .hardpass, .hl) {
  color: inherit; transition: color 0.6s ease, text-shadow 0.6s ease;
}
.js .reveal.in :is(.grn, .amb, .tst, .hardpass, .hl) { color: var(--ig); animation: ignite 0.7s ease 1; }
@keyframes ignite {
  0% { text-shadow: none; }
  40% { text-shadow: 0 0 26px var(--ig); }
  100% { text-shadow: none; }
}
@media (prefers-reduced-motion: reduce) {
  .js .reveal :is(.grn, .amb, .tst, .hardpass, .hl) { color: var(--ig); transition: none; }
  .js .reveal.in :is(.grn, .amb, .tst, .hardpass, .hl) { animation: none; }
}
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) .reveal.in :is(.grn, .amb, .tst, .hardpass, .hl) { animation: none; }
}
:root[data-theme="light"] .reveal.in :is(.grn, .amb, .tst, .hardpass, .hl) { animation: none; }

/* -----------------------------------------------------------------------
   Reveal - a clipped wipe with a soft focus, not a fade-up
   ----------------------------------------------------------------------- */
.reveal { opacity: 1; transform: none; }
.js .reveal {
  opacity: 0;
  filter: blur(6px);
  clip-path: inset(0 0 14% 0);
  transform: translateY(12px);
  transition: opacity 0.55s ease, filter 0.55s ease, clip-path 0.7s cubic-bezier(0.2, 0.7, 0.2, 1), transform 0.55s ease;
}
.js .reveal.in { opacity: 1; filter: none; clip-path: inset(0 0 0 0); transform: none; }
@media (prefers-reduced-motion: reduce) {
  .js .reveal { opacity: 1; filter: none; clip-path: none; transform: none; transition: none; }
}

/* The lesson runs arrive a beat after their chapter. */
.js .reveal .run { opacity: 0; transition: opacity 0.5s ease; }
.js .reveal.in .run { opacity: 1; }
.js .reveal.in .run:nth-of-type(4n+2) { transition-delay: 0.06s; }
.js .reveal.in .run:nth-of-type(4n+3) { transition-delay: 0.12s; }
.js .reveal.in .run:nth-of-type(4n+4) { transition-delay: 0.18s; }
@media (prefers-reduced-motion: reduce) {
  .js .reveal .run { opacity: 1; transition: none; }
}

/* -----------------------------------------------------------------------
   Pull quotes - ink earned by scrolling
   ----------------------------------------------------------------------- */
.pull {
  font-family: var(--disp);
  font-weight: 700;
  font-size: var(--disp-2);
  line-height: 0.95;
  letter-spacing: -0.045em;
  margin-block: var(--s7); /* never the shorthand: it zeroed margin-inline and beat .frame centering at ultrawide */
  max-width: 22ch;
}
.pull .fill {
  --fill: 0;
  /* the unfilled stop must still clear AA on its own (it is the rest state
     for every no-scroll visitor, and the permanent state without JS) - 90%
     mut opacity computes 4.63:1 light / 6.53:1 dark against page bg, vs the
     45% this used to ship at (1.95:1 light / 2.49:1 dark, a WCAG AA fail) */
  background: linear-gradient(90deg, var(--acc), var(--acc) calc(var(--fill) * 100%), color-mix(in srgb, var(--mut) 90%, transparent) calc(var(--fill) * 100%));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
.pull .fill b { font-weight: 700; }
/* no-JS (html lacks .js because site.js never ran, per E1's html.js-in-site.js
   fix): the scroll-driven fill can never advance, so default to fully
   painted rather than stuck at the low-contrast --fill:0 rest state */
html:not(.js) .pull .fill { --fill: 1; }
@supports not (color: color-mix(in srgb, red 50%, blue)) { .pull .fill { background: none; color: var(--ink); } }
@media (prefers-reduced-motion: reduce) { .pull .fill { background: none; color: var(--ink); } }
/* S6: dead-zone compression - the promise pull-quote stretch ran nearly a
   full empty viewport; cut its vertical rhythm ~35% and give the eye one
   concrete data-driven stat instead of empty space. */
.pull.compact { margin: calc(var(--s7) * 0.65) 0 var(--s3); }
.pull-stat {
  font-family: var(--mono);
  font-size: var(--step--1);
  color: var(--mut);
  letter-spacing: 0.04em;
  margin: 0 0 var(--s7);
}

/* -----------------------------------------------------------------------
   Pipeline - a horizontal editorial timeline
   ----------------------------------------------------------------------- */
.pipeline {
  display: flex;
  gap: 0;
  overflow-x: auto;
  padding-bottom: var(--s3);
  border-top: 1px solid var(--line);
  scrollbar-width: thin;
  /* the rail scrolls; fade the clipped edge so it reads as scrollable */
  -webkit-mask-image: linear-gradient(90deg, #000 92%, transparent);
  mask-image: linear-gradient(90deg, #000 92%, transparent);
}
.pipeline::-webkit-scrollbar { height: 10px; }
.pipeline::-webkit-scrollbar-thumb { background: var(--line); }
.pipeline .node {
  flex: 0 0 min(23rem, 78vw);
  padding: var(--s4) var(--s4) var(--s5) 0;
  border-right: 1px solid var(--line);
  margin-right: var(--s4);
}
.pipeline .node:last-child { border-right: none; }
.pipeline .ntag {
  display: block;
  font-family: var(--mono);
  font-size: var(--step--1);
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--mut);
  margin-bottom: var(--s2);
}
.pipeline .node.now .ntag { color: var(--acc); }
.pipeline .ntag.st-next { color: var(--acc); }
.pipeline .ntag.st-drafted { color: var(--mut); }
.pipeline .ntag.st-built { color: var(--warn); }
.pipeline .ntag.st-tested { color: var(--pass); }
.pipeline .ntag.st-live { color: var(--pass); }
.pipeline .ntag.st-dev { color: var(--warn); }
.pipeline .nname {
  display: block;
  font-family: var(--disp);
  font-weight: 700;
  font-size: var(--step-3);
  letter-spacing: -0.035em;
  line-height: 1;
  color: var(--ink);
}
.pipeline .nname a { color: inherit; text-decoration: none; }
.pipeline .nname a:hover { color: var(--acc); }
.pipeline .node.dim .nname { color: var(--mut); }
.pipeline .ndesc { display: block; color: var(--mut); font-size: var(--step--1); line-height: 1.6; margin-top: var(--s3); }
.pipeline .node.now { border-top: 2px solid var(--acc); margin-top: -1px; }

.js .pipeline .node { opacity: 0; transform: translateY(14px); transition: opacity 0.4s ease, transform 0.4s ease; }
.js .pipeline.in .node { opacity: 1; transform: none; }
.js .pipeline.in .node:nth-child(1) { transition-delay: 0.00s; }
.js .pipeline.in .node:nth-child(2) { transition-delay: 0.06s; }
.js .pipeline.in .node:nth-child(3) { transition-delay: 0.12s; }
.js .pipeline.in .node:nth-child(4) { transition-delay: 0.18s; }
.js .pipeline.in .node:nth-child(5) { transition-delay: 0.24s; }
.js .pipeline.in .node:nth-child(6) { transition-delay: 0.30s; }
@media (prefers-reduced-motion: reduce) {
  .js .pipeline .node { opacity: 1; transform: none; transition: none; }
}

/* -----------------------------------------------------------------------
   Changelog log entries
   ----------------------------------------------------------------------- */
.log-nav {
  font-family: var(--mono); font-size: var(--step--1); color: var(--mut);
  display: flex; gap: var(--s3); flex-wrap: wrap; margin: var(--s4) 0 0;
}
.log-nav a { color: var(--mut); text-decoration: none; border-bottom: 1px solid var(--line); }
.log-nav a:hover { color: var(--acc); border-bottom-color: var(--acc); }
.log-month {
  font-family: var(--mono); font-size: var(--step--1); color: var(--mut);
  letter-spacing: 0.14em; text-transform: uppercase; font-weight: 400;
  margin: var(--s6) 0 0; scroll-margin-top: var(--s5);
}
.log-month:first-of-type { margin-top: var(--s5); }
.log-entry { padding: var(--s5) 0; border-bottom: 1px solid var(--line); }
.log-entry:first-of-type { border-top: 1px solid var(--line); }
.log-v {
  font-family: var(--disp);
  font-weight: 700;
  font-size: var(--step-2);
  letter-spacing: -0.03em;
  line-height: 1.1;
}
.log-v b { color: var(--acc); font-weight: 700; }
.log-v a { color: inherit; text-decoration: none; }
.log-v a:hover { text-decoration: underline; text-decoration-color: var(--acc); }
.log-d { font-family: var(--mono); color: var(--mut); font-size: var(--step--1); margin-left: var(--s3); }
.log-s {
  font-family: var(--mono); font-size: var(--step--1); letter-spacing: 0.14em; text-transform: uppercase;
  border: 1px solid; padding: 1px 10px; font-weight: 400;
  vertical-align: middle; margin-left: var(--s2);
}
/* 1.4.10 reflow: the heading row must wrap at 320px, so date and badge
   carry no nowrap; the badge keeps its shape via inline-block instead */
.log-s { display: inline-block; }
@media (min-width: 40rem) { .log-d, .log-s { white-space: nowrap; } }
.log-s.live { color: var(--pass); border-color: var(--pass); }
.log-s.built { color: var(--test); border-color: var(--test); }
.log-s.dev { color: var(--warn); border-color: var(--warn); }
.log-s.soon { color: var(--acc); border-color: var(--acc); }
.log-entry ul { margin: var(--s3) 0 0; padding-left: 20px; max-width: 74ch; }
.log-entry li { margin: var(--s2) 0; color: var(--mut); }

/* -----------------------------------------------------------------------
   View transitions
   ----------------------------------------------------------------------- */
@view-transition { navigation: auto; }
main { view-transition-name: main-content; }
@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(*), ::view-transition-old(*), ::view-transition-new(*) { animation: none !important; }
}

/* -----------------------------------------------------------------------
   Light theme adjustments that are structural rather than palette
   ----------------------------------------------------------------------- */
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) body::before {
    background:
      radial-gradient(66rem 42rem at 88% -16%, rgba(184, 56, 10, 0.10), transparent 62%),
      radial-gradient(52rem 38rem at -14% 24%, rgba(122, 83, 6, 0.06), transparent 58%);
  }
  :root:not([data-theme="dark"]) body::after { opacity: 0.35; }
  :root:not([data-theme="dark"]) .tile-num,
  :root:not([data-theme="dark"]) .ed-verdict,
  :root:not([data-theme="dark"]) .receipts .v.pass { text-shadow: none; }
  :root:not([data-theme="dark"]) .big-head .ghost-num { -webkit-text-stroke-color: var(--panel-2); }
  /* .skip (the accessibility "Skip to content" link, base.njk) and the
     unrelated os-matrix "N skip" count badge (product.njk, also class
     "skip") collide on the class name: the skip-link rule paints
     background: var(--acc); the os-cell rule (mut text) only overrides
     color, so the background bleeds through onto the count badge, which
     in light mode reads var(--mut) grey text on var(--acc) blue - a
     failure (audit-contrast.cjs: 1.06:1, span.skip in td.os-cell-partial,
     /c/ and /java/ #get). Dark mode's grey-on-light-blue combination for
     this same accidental background is unaudited pre-existing behavior
     and is left untouched (out of scope, not a light-on-light bug) - this
     rule only removes the bleed under the light guard. */
  :root:not([data-theme="dark"]) .os-cell .skip { background: none; }
}
:root[data-theme="light"] .tile-num,
:root[data-theme="light"] .ed-verdict,
:root[data-theme="light"] .receipts .v.pass { text-shadow: none; }
:root[data-theme="light"] .os-cell .skip { background: none; }
@media (prefers-reduced-motion: reduce) {
  .site-head { -webkit-backdrop-filter: none; backdrop-filter: none; }
  .hero-h1 .lit { animation: none; }
  .editor.tilt { transform: none; }
}

/* Stage tile numerals carry the stage verdict color (owner 2026-08-29). */
.tile-num.stage-tested, .tile-num.stage-live { color: var(--pass); text-shadow: 0 0 30px rgba(140, 230, 143, 0.35); }
.tile-num.stage-drafted { color: var(--mut); }
.tile-num.stage-in-development, .tile-num.stage-built { color: var(--warn); }


/* the header may pin only when the screen has space to give it */
@media (min-width: 48rem) and (min-height: 32rem) {
  .site-head { position: sticky; top: 0; }
}

.plat-pending { padding: var(--s4); }
.plat-pending .muted { max-width: 60ch; }

/* -----------------------------------------------------------------------
   Scroll-advance FAB and header theme toggle (sweep45/fab-research.md,
   design S3: the two controls occupy fully separate screen regions - the
   FAB stays bottom-right and scroll-gated, the toggle lives in the
   persistent header - so there is never a stacking/gap decision between
   them, and no floating-layer CSS at all for the toggle.
   ----------------------------------------------------------------------- */

/* Footer occlusion guard: the footer nav (.site-foot nav a, ~22px tall,
   no min-height rule of its own - sweep45/26-touch.md #2) is trailing
   content the FAB can end up fixed over once the reader scrolls to the
   true bottom of the page. Rather than rely on which side of the footer's
   flex row a link happens to land on at a given viewport width, the
   document is given extra room below the footer equal to the FAB's own
   footprint (48px + gap + safe-area), so once fully scrolled nothing
   tappable can ever sit under the fixed FAB. */
.site-foot {
  padding-bottom: calc(var(--s6) + 128px + env(safe-area-inset-bottom, 0px));
}

/* Mobile-only paging stack (dual-fab-research.md): desktop keeps
   PageUp/PageDown and a draggable scrollbar - both strictly faster than
   reaching for a screen-corner control - so the stack is CSS-absent
   (not just visually hidden) unless the device has a coarse (touch)
   pointer AND the viewport is narrow. `pointer: coarse` targets the real
   reason (no direct-manipulation surface without it); the width clause is
   the belt-and-braces fallback for touch+mouse 2-in-1s, expressed at the
   same 60rem cutover the header nav already uses for its own mobile
   style, so the whole site changes mobile behavior at one width. */
.fab-stack { display: none; }
@media (pointer: coarse) and (max-width: 80rem) { /* phones AND tablets (owner) */
  .fab-stack {
    display: flex;
    flex-direction: column;
    gap: 12px;
    position: fixed;
    right: max(1rem, env(safe-area-inset-right));
    bottom: max(1rem, env(safe-area-inset-bottom));
    z-index: 40;
  }
}

/* Two DISTINCT, always-independently-gated controls (not one button that
   flips at the end) - dual-fab-research.md section 2. Translucent at
   rest (reads as floating rather than solid over content), opaque on
   hover/focus/active so intent is unambiguous right when it's about to
   be used; the 72% figure is a solid-color alpha (not blur-only) so the
   effective composited color still carries the panel's own audited
   contrast against --ink, not just whatever happens to sit underneath. */
.fab-scroll {
  width: 48px;
  height: 48px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 999px;
  border: 1px solid var(--line);
  background: color-mix(in srgb, var(--panel) 72%, transparent);
  color: var(--ink);
  box-shadow: var(--shadow);
  cursor: pointer;
  opacity: 1;
  transform: translateY(0);
  transition: opacity 0.2s ease, transform 0.2s ease, background-color 0.2s ease;
}
@supports not (background: color-mix(in srgb, red 50%, blue)) {
  .fab-scroll { background: var(--panel); }
}
/* A plain `.fab-scroll { display: flex }` in an author stylesheet beats
   the UA stylesheet's `[hidden] { display: none }` on a cascade-origin
   tie, not a specificity one - author rules always win over UA rules at
   equal specificity, so [hidden] alone would silently stop hiding
   anything the moment .fab-scroll sets its own display. This rule
   restores it explicitly, so JS toggling the hidden attribute is the
   single source of truth for both visibility and click-throughability. */
.fab-scroll[hidden] {
  display: none;
  opacity: 0;
  pointer-events: none;
  transform: translateY(8px);
}
.fab-scroll:hover {
  background: color-mix(in srgb, var(--panel) 92%, transparent);
  border-color: var(--acc);
  color: var(--acc);
}
.fab-scroll:focus-visible,
.fab-scroll:active {
  background: var(--panel);
}
@media (prefers-reduced-motion: reduce) {
  .fab-scroll { transition: none; }
}

.theme-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  min-height: 44px;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: transparent;
  color: var(--mut);
  cursor: pointer;
  flex-shrink: 0;
}
@media (min-width: 48rem) {
  .theme-toggle { min-height: 32px; height: 32px; }
}
.theme-toggle:hover { color: var(--ink); border-color: var(--acc); }
.theme-toggle svg { display: none; }
.theme-toggle .i-moon { display: block; }
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) .theme-toggle .i-moon { display: none; }
  :root:not([data-theme="dark"]) .theme-toggle .i-sun { display: block; }
}
:root[data-theme="light"] .theme-toggle .i-moon { display: none; }
:root[data-theme="light"] .theme-toggle .i-sun { display: block; }
:root[data-theme="dark"] .theme-toggle .i-moon { display: block; }
:root[data-theme="dark"] .theme-toggle .i-sun { display: none; }


/* two-row mobile nav (owner 2026-08-30): languages ride the top rail
   (scroll only if they overflow), PRODUCTS + theme toggle get their own
   line. At desktop the meta row dissolves back into the single line. */
@media (max-width: 47.99rem) {
  .site-head { flex-wrap: wrap; }
  .site-head nav[aria-label="Primary"] { flex: 1 1 100%; width: 100%; flex-direction: column; align-items: stretch; gap: 2px; }
  .nav-meta { display: flex; align-items: center; justify-content: flex-end; gap: var(--s3); min-height: 44px; }
  .nav-meta a { display: inline-flex; align-items: center; min-height: 44px; padding-inline: 0.5rem; }
}
@media (min-width: 48rem) {
  .nav-meta { display: contents; }
}

/* -----------------------------------------------------------------------
   The IntelliJ design study - src/_includes/jetbrains-study.njk

   An IDE tool window drawn entirely in markup + these tokens, no image. It
   paints on the same --con-* console chrome as .editor/.term/.assert, so it
   inverts to a light panel in light mode with the rest of the consoles and
   stays AA on both paths (audit:contrast walks it).

   The loop is CSS keyframes only - no JS orchestration - so it runs with
   scripting off. Every animated property's BASE value is the FINAL state
   (breakpoint bound, execution paused on 16, goals 1 and 2 passed), which
   is what a reduced-motion visitor gets instantly when the animations are
   switched off at the bottom of this block. Nothing here responds to a
   click: the tree's own scroll is the only live affordance.
   ----------------------------------------------------------------------- */
.jb-legal { margin: var(--s3) 0 0; color: var(--mut); font-size: var(--step--1); max-width: 68ch; }
.jb-study { margin: var(--s5) 0 0; }
.jb-ide {
  background: var(--term-bg);
  border: 1px solid var(--con-border);
  border-radius: 8px;
  overflow: hidden;
  font-family: var(--mono);
  color: var(--ink);
  box-shadow: var(--shadow);
}
.jb-title {
  display: flex;
  align-items: center;
  gap: var(--s2);
  padding: 9px 12px;
  background: var(--con-bg2);
  border-bottom: 1px solid var(--con-border);
  font-size: 0.78rem;
}
.jb-dots { display: flex; gap: 6px; flex: none; }
.jb-dot { width: 10px; height: 10px; border-radius: 50%; background: var(--con-dot-off); }
.jb-dot.r { background: #e5534b; }
.jb-dot.y { background: #d4a72c; }
.jb-dot.g { background: #3fb950; }
.jb-title-name { color: var(--con-where); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.jb-main { display: flex; flex-direction: column; }
.jb-editor { min-width: 0; display: flex; flex-direction: column; }
.jb-tabs { display: flex; gap: 0; padding: 6px 8px 0; background: var(--con-bg2); }
.jb-tab {
  padding: 4px 12px;
  border: 1px solid var(--con-border);
  border-bottom-color: transparent;
  border-radius: 5px 5px 0 0;
  color: var(--mut);
  font-size: 0.74rem;
  white-space: nowrap;
}
.jb-tab.active { background: var(--term-bg); color: var(--ink); }

.jb-code { counter-reset: ln 0; font-size: 0.72rem; line-height: 1.85; flex: 1 1 auto; }
@media (min-width: 60rem) { .jb-code { font-size: 0.78rem; } }
.jb-status {
  display: flex;
  gap: var(--s3);
  padding: 7px 12px;
  border-top: 1px solid var(--con-border);
  background: var(--con-bg2);
  color: var(--con-where);
  font-size: 0.7rem;
}
.jb-status-end { margin-left: auto; }

.jb-panel {
  display: flex;
  flex-direction: column;
  background: var(--con-panel);
  border-top: 1px solid var(--con-border);
}
.jb-panel-head {
  margin: 0;
  padding: 9px 14px;
  background: var(--con-bg2);
  border-bottom: 1px solid var(--con-border);
  color: var(--acc);
  font-size: 0.68rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}

/* The lesson tree: every lesson of the real Java debugging track, in order.
   Only the active row carries an affordance; the rest are inert by design. */
.jb-tree {
  max-height: 9rem;
  overflow-y: auto;
  border-bottom: 1px solid var(--con-border);
  padding-bottom: 4px;
}
@media (min-width: 60rem) { .jb-tree { max-height: 11rem; } }
.jb-tree:focus-visible { outline: 2px solid var(--acc); outline-offset: -2px; }
.jb-tree-head {
  margin: 0;
  padding: 8px 14px 6px;
  color: var(--con-tag);
  font-size: 0.64rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}
.jb-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 4px 14px;
  font-size: 0.7rem;
  color: var(--mut);
  cursor: default;
}
/* con-where (5.11:1 on the light --con-panel #eef1f4), not con-ln:
   con-ln is audited at 4.52:1 on white and dips under AA on the panel tint. */
.jb-num { color: var(--con-where); flex: none; }
.jb-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.jb-chip {
  margin-left: auto;
  flex: none;
  padding: 0 6px;
  border: 1px solid var(--con-pass);
  border-radius: 2px;
  color: var(--con-pass);
  font-size: 0.62rem;
  letter-spacing: 0.08em;
}
.jb-row.active {
  background: var(--acc-soft);
  box-shadow: inset 2px 0 0 var(--acc);
  color: var(--ink);
  cursor: pointer;
}
.jb-row.active .jb-num { color: var(--acc); }

.jb-prose { padding: 14px; border-bottom: 1px solid var(--con-border); font-family: var(--sans); }
.jb-kicker {
  margin: 0 0 6px;
  font-family: var(--mono);
  font-size: 0.64rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--acc);
}
.jb-lesson-name { margin: 0 0 8px; font-family: var(--disp); font-weight: 700; font-size: 0.98rem; color: var(--ink); }
.jb-prose p:last-child { margin: 0; color: var(--mut); font-size: 0.78rem; line-height: 1.65; max-width: 46ch; }
.jb-scene, .jb-why { margin: 0 0 8px; color: var(--mut); font-size: 0.78rem; line-height: 1.65; max-width: 46ch; }
.jb-scene code, .jb-why code { font-family: var(--mono); font-size: 0.72rem; color: var(--con-f); }
.jb-tasks {
  margin: 0 0 8px;
  padding-left: 1.25rem;
  color: var(--mut);
  font-size: 0.78rem;
  line-height: 1.65;
  max-width: 46ch;
}
.jb-tasks li { margin-bottom: 6px; }
.jb-tasks li:last-child { margin-bottom: 0; }
.jb-tasks code { font-family: var(--mono); font-size: 0.72rem; color: var(--con-f); }

.jb-goals { padding: 14px; }
.jb-goals-head {
  margin: 0 0 10px;
  font-size: 0.64rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--con-tag);
}
.jb-goal {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 7px 9px;
  margin-bottom: 6px;
  border: 1px solid var(--con-border);
  border-radius: 3px;
  background: var(--con-bg2);
  font-size: 0.72rem;
}
.jb-check {
  position: relative;
  width: 15px;
  height: 15px;
  flex: none;
  border: 1px solid var(--con-border);
  border-radius: 3px;
}
.jb-check::after {
  content: "\2713";
  text-align: center;
  line-height: 15px;
  font-size: 10px;
  font-weight: 700;
  color: var(--con-panel);
  position: absolute;
  inset: -1px;
  border-radius: 3px;
  background: var(--con-pass);
  opacity: 1;
}
.jb-check.off::after { opacity: 0; }
.jb-goal-label { flex: 1 1 auto; color: var(--ink); font-family: var(--sans); min-width: 0; }
.jb-verdicts { display: grid; flex: none; }
.jb-verdict {
  grid-area: 1 / 1;
  padding: 1px 7px;
  border: 1px solid var(--con-border);
  border-radius: 2px;
  font-size: 0.62rem;
  letter-spacing: 0.06em;
  text-align: center;
}
.jb-verdict.pending { color: var(--mut); opacity: 0; }
.jb-verdict.pending.only { opacity: 1; }
.jb-verdict.pass { color: var(--con-pass); border-color: var(--con-pass); opacity: 1; }

.jb-hint {
  margin: 10px 0 0;
  padding: 8px 10px;
  border: 1px solid var(--con-border);
  border-radius: 3px;
  color: var(--mut);
  font-size: 0.68rem;
}
.jb-caret { color: var(--acc); margin-right: 7px; }

/* Mocked quiz - task 4 of the real lesson ("answer the quiz below, then
   press Continue"). Inert: disabled/aria-disabled inputs, never focusable,
   showing the honest answers so the study reads as complete. */
.jb-quiz { padding: 0 14px 14px; }
.jb-quiz-head {
  margin: 0 0 8px;
  font-size: 0.64rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--con-tag);
}
.jb-quiz-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin-bottom: 6px;
}
.jb-quiz-row:last-child { margin-bottom: 0; }
.jb-quiz-label {
  flex: 1 1 auto;
  min-width: 0;
  color: var(--mut);
  font-family: var(--sans);
  font-size: 0.68rem;
  line-height: 1.3;
}
.jb-quiz-input {
  flex: none;
  width: 3.6em;
  padding: 3px 6px;
  border: 1px solid var(--con-border);
  border-radius: 3px;
  background: var(--con-bg2);
  color: var(--ink);
  font-family: var(--mono);
  font-size: 0.7rem;
  text-align: center;
  cursor: default;
}

.jb-actions { display: flex; gap: 8px; padding: 12px 14px; border-top: 1px solid var(--con-border); }
.jb-btn {
  font-family: var(--mono);
  font-size: 0.68rem;
  letter-spacing: 0.06em;
  padding: 7px 15px;
  border: 1px solid var(--con-border);
  border-radius: 3px;
  background: var(--con-bg2);
  color: var(--ink);
  opacity: 1;
}
.jb-btn.primary { background: var(--acc); border-color: var(--acc); color: var(--term-bg); font-weight: 700; }

.jb-cap {
  margin-top: 10px;
  font-family: var(--mono);
  font-size: var(--step--1);
  color: var(--mut);
}

@media (min-width: 60rem) {
  .jb-main { flex-direction: row; align-items: stretch; }
  .jb-editor { flex: 1 1 auto; }
  .jb-panel { flex: 0 0 23rem; border-top: none; border-left: 1px solid var(--con-border); }
}

/* The loop. 11s, one pass: breakpoint sets, execution pauses on 16, goal 1
   ticks, a beat, goal 2 flips to PASSED, a long hold, a graceful reset. */
@keyframes jb-bp-in {
  0%, 6% { opacity: 0; transform: scale(0.4); }
  11% { opacity: 1; transform: scale(1.7); }
  17%, 95% { opacity: 1; transform: scale(1); }
  100% { opacity: 0; transform: scale(0.4); }
}
@keyframes jb-hit-in {
  0%, 18% { background-color: transparent; box-shadow: inset 0 0 0 transparent; }
  26%, 95% { background-color: var(--acc-soft); box-shadow: inset 3px 0 0 var(--acc); }
  100% { background-color: transparent; box-shadow: inset 0 0 0 transparent; }
}
@keyframes jb-tick-1 { 0%, 32% { opacity: 0; } 38%, 95% { opacity: 1; } 100% { opacity: 0; } }
@keyframes jb-pend-1 { 0%, 32% { opacity: 1; } 38%, 95% { opacity: 0; } 100% { opacity: 1; } }
@keyframes jb-tick-2 { 0%, 50% { opacity: 0; } 56%, 95% { opacity: 1; } 100% { opacity: 0; } }
@keyframes jb-pend-2 { 0%, 50% { opacity: 1; } 56%, 95% { opacity: 0; } 100% { opacity: 1; } }

.jb-bp::after { animation: jb-bp-in 11s linear infinite; }
.jb-hit { background-color: var(--acc-soft); box-shadow: inset 3px 0 0 var(--acc); animation: jb-hit-in 11s linear infinite; }
.jb-goal-1 .jb-check::after { animation: jb-tick-1 11s linear infinite; }
.jb-goal-1 .jb-verdict.pass { animation: jb-tick-1 11s linear infinite; }
.jb-goal-1 .jb-verdict.pending { animation: jb-pend-1 11s linear infinite; }
.jb-goal-2 .jb-check::after { animation: jb-tick-2 11s linear infinite; }
.jb-goal-2 .jb-verdict.pass { animation: jb-tick-2 11s linear infinite; }
.jb-goal-2 .jb-verdict.pending { animation: jb-pend-2 11s linear infinite; }

@media (prefers-reduced-motion: reduce) {
  .jb-bp::after,
  .jb-hit,
  .jb-goal-1 .jb-check::after,
  .jb-goal-1 .jb-verdict.pass,
  .jb-goal-1 .jb-verdict.pending,
  .jb-goal-2 .jb-check::after,
  .jb-goal-2 .jb-verdict.pass,
  .jb-goal-2 .jb-verdict.pending { animation: none; }
}

.pipeline .ntag.st-researching { color: var(--warn); }
.st-researching { color: var(--warn); }
.pipeline .ndesc b { color: var(--ink); font-weight: 600; }
.pipeline .ndesc.jb-note { font-family: var(--mono); font-size: 0.72rem; margin-top: var(--s2); }

/* -----------------------------------------------------------------------
   VS Code design study - the twin of the jb-* block above, but this one
   dramatizes the family's own REAL shipped UI (ExpectedC IS a VS Code
   extension). Reuses the site's own .dot / .bp / .hit / .ed-code
   breakpoint-gutter treatment already proven in the hero editor, so the
   editor half of this study is literally the same visual language as the
   real product, not a re-derived approximation. The reduced-motion /
   no-JS baseline is the "hold" frame (breakpoint bound, execution paused,
   goals 1 and 2 passed) - identical pattern to the jb- block. Mobile
   collapses the editor pane and activity bar to a single breadcrumb line
   (Sec 5 of the ide-study research) and keeps only the panel: title,
   prose, goals, verdict. Nothing here responds to a click except the
   tree's own scroll. ----------------------------------------------------- */
.vs-study { margin: var(--s5) 0 0; }
.vs-ide {
  background: var(--term-bg);
  border: 1px solid var(--con-border);
  border-radius: 8px;
  overflow: hidden;
  font-family: var(--mono);
  color: var(--ink);
  box-shadow: var(--shadow);
}
.vs-title {
  display: flex;
  align-items: center;
  gap: var(--s2);
  padding: 9px 12px;
  background: var(--con-bg2);
  border-bottom: 1px solid var(--con-border);
  font-size: 0.78rem;
}
.vs-dots { display: flex; gap: 6px; flex: none; }
.vs-title-name { color: var(--con-where); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Mobile breadcrumb: path context above the stacked editor below 60rem.
   Hidden at desktop where the full chrome already carries this context. */
.vs-crumb {
  display: block;
  padding: 7px 14px;
  background: var(--con-bg2);
  border-bottom: 1px solid var(--con-border);
  color: var(--con-where);
  font-size: 0.68rem;
  overflow-x: auto;
  white-space: nowrap;
}
@media (min-width: 60rem) { .vs-crumb { display: none; } }

.vs-main { display: flex; flex-direction: column; }

/* Activity bar: decorative only (aria-hidden), the one chrome element
   research flags as VS-Code-specific vs IntelliJ's edge tool-window bars.
   Dropped entirely on mobile - no informational content to preserve. */
.vs-actbar {
  display: none;
  flex: none;
  flex-direction: column;
  align-items: center;
  gap: 14px;
  padding: 10px 0;
  width: 2.4rem;
  background: var(--con-bg2);
  border-right: 1px solid var(--con-border);
}
.vs-acticon {
  width: 18px;
  height: 18px;
  border-radius: 3px;
  border: 1px solid var(--con-border);
  opacity: 0.6;
}
.vs-acticon.active { border-color: var(--acc); opacity: 1; box-shadow: inset 2px 0 0 var(--acc); }

.vs-editor { display: flex; min-width: 0; flex-direction: column; }
/* The editor pane stacks ABOVE the lesson panel on phones, exactly as the
   IntelliJ study does - the breakpoint and the dot are the selling point
   and must survive every width. Only the decorative activity bar drops. */
.vs-tabs { display: flex; gap: 0; padding: 6px 8px 0; background: var(--con-bg2); }
.vs-tab {
  padding: 4px 12px;
  border: 1px solid var(--con-border);
  border-bottom-color: transparent;
  border-radius: 5px 5px 0 0;
  color: var(--mut);
  font-size: 0.74rem;
  white-space: nowrap;
}
.vs-tab.active { background: var(--term-bg); color: var(--ink); }

.vs-code { counter-reset: ln 0; font-size: 0.72rem; line-height: 1.85; flex: 1 1 auto; }
@media (min-width: 60rem) { .vs-code { font-size: 0.78rem; } }

.vs-status {
  display: flex;
  gap: var(--s3);
  padding: 7px 12px;
  border-top: 1px solid var(--con-border);
  background: var(--con-bg2);
  color: var(--con-where);
  font-size: 0.7rem;
}
.vs-status-hit { color: var(--warn); opacity: 0; }
.vs-status-end { margin-left: auto; }

.vs-panel {
  display: flex;
  flex-direction: column;
  background: var(--con-panel);
  border-top: 1px solid var(--con-border);
}
.vs-panel-head {
  margin: 0;
  padding: 9px 14px;
  background: var(--con-bg2);
  border-bottom: 1px solid var(--con-border);
  color: var(--acc);
  font-size: 0.68rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}

/* The lesson tree: every lesson of the real C debugging track, in order.
   Only the active row carries an affordance; the rest are inert by design.
   This scroll is the artifact's one genuinely live control. */
.vs-tree {
  max-height: 9rem;
  overflow-y: auto;
  border-bottom: 1px solid var(--con-border);
  padding-bottom: 4px;
}
@media (min-width: 60rem) { .vs-tree { max-height: 11rem; } }
.vs-tree:focus-visible { outline: 2px solid var(--acc); outline-offset: -2px; }
.vs-tree-head {
  margin: 0;
  padding: 8px 14px 6px;
  color: var(--con-tag);
  font-size: 0.64rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}
.vs-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 4px 14px;
  font-size: 0.7rem;
  color: var(--mut);
  cursor: default;
}
.vs-num { color: var(--con-where); flex: none; }
.vs-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.vs-chip {
  margin-left: auto;
  flex: none;
  padding: 0 6px;
  border: 1px solid var(--con-pass);
  border-radius: 2px;
  color: var(--con-pass);
  font-size: 0.62rem;
  letter-spacing: 0.08em;
}
.vs-row.active {
  background: var(--acc-soft);
  box-shadow: inset 2px 0 0 var(--acc);
  color: var(--ink);
  cursor: pointer;
}
.vs-row.active .vs-num { color: var(--acc); }

.vs-prose { padding: 14px; border-bottom: 1px solid var(--con-border); font-family: var(--sans); }
.vs-kicker {
  margin: 0 0 6px;
  font-family: var(--mono);
  font-size: 0.64rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--acc);
}
.vs-lesson-name { margin: 0 0 8px; font-family: var(--disp); font-weight: 700; font-size: 0.98rem; color: var(--ink); }
.vs-prose p:last-child { margin: 0; color: var(--mut); font-size: 0.78rem; line-height: 1.65; max-width: 46ch; }
.vs-scene, .vs-why { margin: 0 0 8px; color: var(--mut); font-size: 0.78rem; line-height: 1.65; max-width: 46ch; }
.vs-scene code, .vs-why code { font-family: var(--mono); font-size: 0.72rem; color: var(--con-f); }
.vs-tasks {
  margin: 0 0 8px;
  padding-left: 1.25rem;
  color: var(--mut);
  font-size: 0.78rem;
  line-height: 1.65;
  max-width: 46ch;
}
.vs-tasks li { margin-bottom: 6px; }
.vs-tasks li:last-child { margin-bottom: 0; }
.vs-tasks code { font-family: var(--mono); font-size: 0.72rem; color: var(--con-f); }

.vs-goals { padding: 14px; }
.vs-goals-head {
  margin: 0 0 10px;
  font-size: 0.64rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--con-tag);
}
.vs-goal {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 7px 9px;
  margin-bottom: 6px;
  border: 1px solid var(--con-border);
  border-radius: 3px;
  background: var(--con-bg2);
  font-size: 0.72rem;
}
.vs-check {
  position: relative;
  width: 15px;
  height: 15px;
  flex: none;
  border: 1px solid var(--con-border);
  border-radius: 3px;
}
.vs-check::after {
  content: "\2713";
  text-align: center;
  line-height: 15px;
  font-size: 10px;
  font-weight: 700;
  color: var(--con-panel);
  position: absolute;
  inset: -1px;
  border-radius: 3px;
  background: var(--con-pass);
  opacity: 1;
}
.vs-check.off::after { opacity: 0; }
.vs-goal-label { flex: 1 1 auto; color: var(--ink); font-family: var(--sans); min-width: 0; }
.vs-verdicts { display: grid; flex: none; }
.vs-verdict {
  grid-area: 1 / 1;
  padding: 1px 7px;
  border: 1px solid var(--con-border);
  border-radius: 2px;
  font-size: 0.62rem;
  letter-spacing: 0.06em;
  text-align: center;
}
.vs-verdict.pending { color: var(--mut); opacity: 0; }
.vs-verdict.pending.only { opacity: 1; }
.vs-verdict.pass { color: var(--con-pass); border-color: var(--con-pass); opacity: 1; }

.vs-hint {
  margin: 10px 0 0;
  padding: 8px 10px;
  border: 1px solid var(--con-border);
  border-radius: 3px;
  color: var(--mut);
  font-size: 0.68rem;
}
.vs-caret { color: var(--acc); margin-right: 7px; }

/* Mocked quiz - task 4 of the real lesson ("answer the quiz"). Inert:
   the inputs are disabled/aria-disabled and never receive focus, they
   only show the honest answers so the study reads as complete. */
.vs-quiz { padding: 0 14px 14px; }
.vs-quiz-head {
  margin: 0 0 8px;
  font-size: 0.64rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--con-tag);
}
.vs-quiz-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin-bottom: 6px;
}
.vs-quiz-row:last-child { margin-bottom: 0; }
.vs-quiz-label {
  flex: 1 1 auto;
  min-width: 0;
  color: var(--mut);
  font-family: var(--sans);
  font-size: 0.68rem;
  line-height: 1.3;
}
.vs-quiz-input {
  flex: none;
  width: 3.6em;
  padding: 3px 6px;
  border: 1px solid var(--con-border);
  border-radius: 3px;
  background: var(--con-bg2);
  color: var(--ink);
  font-family: var(--mono);
  font-size: 0.7rem;
  text-align: center;
  cursor: default;
}

.vs-actions { display: flex; gap: 8px; padding: 12px 14px; border-top: 1px solid var(--con-border); }
.vs-btn {
  font-family: var(--mono);
  font-size: 0.68rem;
  letter-spacing: 0.06em;
  padding: 7px 15px;
  border: 1px solid var(--con-border);
  border-radius: 3px;
  background: var(--con-bg2);
  color: var(--ink);
  opacity: 1;
}
.vs-btn.primary { background: var(--acc); border-color: var(--acc); color: var(--term-bg); font-weight: 700; }

.vs-cap {
  margin-top: 10px;
  font-family: var(--mono);
  font-size: var(--step--1);
  color: var(--mut);
  max-width: 68ch;
}
.vs-cap a { color: var(--acc); }

@media (min-width: 60rem) {
  .vs-actbar { display: flex; }
  .vs-main { flex-direction: row; align-items: stretch; }
  .vs-editor { flex: 1 1 auto; }
  .vs-panel { flex: 0 0 23rem; border-top: none; border-left: 1px solid var(--con-border); }
}

/* The loop. 11s, one pass: breakpoint sets, execution pauses (status text
   flips to the paused/SIGSEGV read), goal 1 ticks, a beat, goal 2 flips to
   PASSED, a long hold, a graceful reset. Same cadence as the jb- block so
   the two studies read as one system. */
@keyframes vs-bp-in {
  0%, 6% { opacity: 0; transform: scale(0.4); }
  11% { opacity: 1; transform: scale(1.7); }
  17%, 95% { opacity: 1; transform: scale(1); }
  100% { opacity: 0; transform: scale(0.4); }
}
@keyframes vs-hit-in {
  0%, 18% { background-color: transparent; box-shadow: inset 0 0 0 transparent; }
  26%, 95% { background-color: var(--acc-soft); box-shadow: inset 3px 0 0 var(--acc); }
  100% { background-color: transparent; box-shadow: inset 0 0 0 transparent; }
}
@keyframes vs-status-in { 0%, 22% { opacity: 0; } 28%, 95% { opacity: 1; } 100% { opacity: 0; } }
@keyframes vs-tick-1 { 0%, 32% { opacity: 0; } 38%, 95% { opacity: 1; } 100% { opacity: 0; } }
@keyframes vs-pend-1 { 0%, 32% { opacity: 1; } 38%, 95% { opacity: 0; } 100% { opacity: 1; } }
@keyframes vs-tick-2 { 0%, 50% { opacity: 0; } 56%, 95% { opacity: 1; } 100% { opacity: 0; } }
@keyframes vs-pend-2 { 0%, 50% { opacity: 1; } 56%, 95% { opacity: 0; } 100% { opacity: 1; } }

.vs-bp::after { animation: vs-bp-in 11s linear infinite; }
.vs-hit { background-color: var(--acc-soft); box-shadow: inset 3px 0 0 var(--acc); animation: vs-hit-in 11s linear infinite; }
.vs-status-hit { animation: vs-status-in 11s linear infinite; }
.vs-goal-1 .vs-check::after { animation: vs-tick-1 11s linear infinite; }
.vs-goal-1 .vs-verdict.pass { animation: vs-tick-1 11s linear infinite; }
.vs-goal-1 .vs-verdict.pending { animation: vs-pend-1 11s linear infinite; }
.vs-goal-2 .vs-check::after { animation: vs-tick-2 11s linear infinite; }
.vs-goal-2 .vs-verdict.pass { animation: vs-tick-2 11s linear infinite; }
.vs-goal-2 .vs-verdict.pending { animation: vs-pend-2 11s linear infinite; }

@media (prefers-reduced-motion: reduce) {
  .vs-bp::after,
  .vs-hit,
  .vs-status-hit,
  .vs-goal-1 .vs-check::after,
  .vs-goal-1 .vs-verdict.pass,
  .vs-goal-1 .vs-verdict.pending,
  .vs-goal-2 .vs-check::after,
  .vs-goal-2 .vs-verdict.pass,
  .vs-goal-2 .vs-verdict.pending { animation: none; }
  .vs-hit { background-color: var(--acc-soft); box-shadow: inset 3px 0 0 var(--acc); }
  .vs-status-hit { opacity: 1; }
  .vs-goal-1 .vs-check::after,
  .vs-goal-2 .vs-check::after { opacity: 1; }
  .vs-goal-1 .vs-verdict.pass,
  .vs-goal-2 .vs-verdict.pass { opacity: 1; }
  .vs-goal-1 .vs-verdict.pending,
  .vs-goal-2 .vs-verdict.pending { opacity: 0; }
}
