/* ============================================================================
   COMPONENTS — Notebook Design System
   ----------------------------------------------------------------------------
   Class-backed styles for the React primitives in components/**. Shipped to
   consumers via styles.css so :hover / :focus / :disabled all work. Components'
   .jsx files are thin: they emit these classNames and pass content through.

   Tone: hairline rules over shadows, near-square corners, quiet hover (border
   or colour shift — never a lift), low-motion transitions. This is the
   deliberate toning-down of the prototype's SaaS-flavoured card behaviour.
   ========================================================================== */

/* ─────────────────────────────  Kicker / label  ───────────────────────── */
.nb-kicker {
  font-family: var(--font-mono);
  font-size: var(--fs-kicker);
  font-weight: var(--w-med);
  letter-spacing: var(--tk-kicker);
  text-transform: uppercase;
  color: var(--accent);
  margin: 0;
}
.nb-kicker--muted { color: var(--text-muted); }

/* ─────────────────────────────────  Button  ───────────────────────────── */
.nb-btn {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  font-family: var(--font-mono);
  font-size: 0.72rem;
  letter-spacing: var(--tk-nav);
  text-transform: uppercase;
  line-height: 1;
  padding: 0.62em 1.05em;
  border: var(--bw) solid var(--border-strong);
  border-radius: var(--r-1);
  background: transparent;
  color: var(--text-dim);
  cursor: pointer;
  text-decoration: none;
  transition: color var(--dur-fast) var(--ease),
              border-color var(--dur-fast) var(--ease),
              background var(--dur-fast) var(--ease);
}
.nb-btn:hover { color: var(--accent); border-color: var(--accent); text-decoration: none; }
.nb-btn:disabled, .nb-btn[aria-disabled="true"] {
  opacity: 0.45; cursor: not-allowed; color: var(--text-muted); border-color: var(--border);
}
/* primary = filled accent, paper-coloured text */
.nb-btn--primary {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--surface);
  font-weight: var(--w-med);
}
.nb-btn--primary:hover {
  background: var(--accent-bright);
  border-color: var(--accent-bright);
  color: var(--surface);
}
/* ghost = no border, just text */
.nb-btn--ghost { border-color: transparent; padding-left: 0.4em; padding-right: 0.4em; }
.nb-btn--ghost:hover { border-color: transparent; }
.nb-btn--sm { font-size: 0.66rem; padding: 0.5em 0.85em; }

/* ──────────────────────────────  Tag / pill  ──────────────────────────── */
.nb-tag {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-family: var(--font-mono);
  font-size: 0.62rem;
  letter-spacing: var(--tk-label);
  text-transform: uppercase;
  color: var(--text-dim);
  border: var(--bw) solid var(--border-strong);
  border-radius: var(--r-1);
  padding: 0.34em 0.6em;
  background: transparent;
}
.nb-tag .nb-tag__sw {
  width: 9px; height: 9px; border-radius: var(--r-1); flex: none;
  background: var(--accent);
}
/* legend variant: no border, just swatch + label */
.nb-tag--legend { border: 0; padding: 0; }

/* ─────────────────────────────  Badge / status  ───────────────────────── */
.nb-badge {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-family: var(--font-mono);
  font-size: 0.6rem;
  letter-spacing: var(--tk-label);
  text-transform: uppercase;
  padding: 0.4em 0.7em;
  border: var(--bw) solid currentColor;
  border-radius: var(--r-1);
  color: var(--accent);
  background: color-mix(in srgb, currentColor 8%, transparent);
}
.nb-badge--pos { color: var(--pos); }
.nb-badge--neg { color: var(--neg); }
.nb-badge--caution { color: var(--caution); }
.nb-badge--muted { color: var(--text-muted); }
.nb-badge__dot { width: 6px; height: 6px; border-radius: var(--r-round); background: currentColor; flex: none; }

/* ───────────────────────────────  Spec card  ──────────────────────────── */
/* The project / specimen card. Toned down: no translateY lift, no drop-shadow.
   Hover = border goes accent + a hairline top accent. That's it. */
.nb-card {
  display: flex;
  flex-direction: column;
  background: var(--surface-card);
  border: var(--bw) solid var(--border-strong);
  border-radius: var(--r-1);
  color: var(--text);
  text-decoration: none;
  padding: var(--sp-5) var(--sp-5) var(--sp-5);
  position: relative;
  transition: border-color var(--dur-fast) var(--ease);
}
a.nb-card:hover { text-decoration: none; border-color: var(--accent); }
a.nb-card:hover .nb-card__go { color: var(--accent-bright); }
.nb-card--draft { border-style: dashed; }
.nb-card__tag {
  display: flex; justify-content: space-between; gap: 12px;
  font-family: var(--font-mono); font-size: 0.6rem; letter-spacing: var(--tk-label);
  text-transform: uppercase; color: var(--text-muted); margin-bottom: var(--sp-3);
}
.nb-card__tag .st { color: var(--accent); }
.nb-card__tag .st--draft { color: var(--caution); }
.nb-card h3 { font-size: 1.4rem; line-height: 1.08; margin: 0 0 var(--sp-2); font-weight: var(--w-med); }
.nb-card__dek { color: var(--text-dim); font-size: var(--fs-sm); line-height: 1.45; margin: 0 0 var(--sp-4); }
.nb-card__find {
  font-family: var(--font-mono); font-size: 0.72rem; line-height: 1.5; color: var(--text-dim);
  border-top: var(--bw) solid var(--border); padding-top: var(--sp-3); margin-top: auto;
}
.nb-card__find b { color: var(--text); font-weight: var(--w-semi); }
.nb-card__go {
  font-family: var(--font-mono); font-size: 0.66rem; letter-spacing: var(--tk-label);
  text-transform: uppercase; color: var(--accent); margin-top: var(--sp-4);
  transition: color var(--dur-fast) var(--ease);
}

/* ───────────────────────────────  Stat strip  ─────────────────────────── */
.nb-stats {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
  border-top: var(--bw) solid var(--border-strong);
  border-bottom: var(--bw) solid var(--border-strong);
}
.nb-stat { padding: var(--sp-4) var(--sp-4); border-right: var(--bw) solid var(--border); }
.nb-stat:last-child { border-right: 0; }
.nb-stat__num {
  font-family: var(--font-serif); font-weight: var(--w-reg); font-size: 2.1rem; line-height: 1;
  letter-spacing: -0.02em; color: var(--text); display: block; margin-bottom: 6px;
  font-feature-settings: "onum" 1;
}
.nb-stat__num small { font-size: 0.9rem; color: var(--text-muted); font-style: italic; }
.nb-stat__label {
  font-family: var(--font-mono); font-size: 0.62rem; letter-spacing: var(--tk-label);
  text-transform: uppercase; color: var(--text-muted); line-height: 1.4;
}
@media (max-width: 720px) {
  .nb-stats { grid-auto-flow: row; grid-auto-columns: auto; }
  .nb-stat { border-right: 0; border-bottom: var(--bw) solid var(--border); }
  .nb-stat:last-child { border-bottom: 0; }
}

/* ───────────────────────────────  Marginalia  ─────────────────────────── */
.nb-marginalia {
  display: grid; grid-template-columns: 8.5rem 1fr; gap: 0 var(--sp-6);
  align-items: start; border-top: var(--bw) solid var(--border);
  padding-top: var(--sp-5);
}
.nb-marginalia__label {
  font-family: var(--font-mono); font-size: var(--fs-label); letter-spacing: var(--tk-label);
  text-transform: uppercase; color: var(--text-muted); padding-top: 0.2rem;
}
.nb-marginalia__body { max-width: var(--measure); }
@media (max-width: 720px) {
  .nb-marginalia { grid-template-columns: 1fr; gap: var(--sp-2) 0; }
}

/* ───────────────────────────────  Pull-quote  ─────────────────────────── */
.nb-pull {
  font-family: var(--font-serif); font-style: italic; font-weight: var(--w-reg);
  font-size: 1.55rem; line-height: 1.24; letter-spacing: -0.005em; color: var(--text);
  border-top: var(--bw) solid var(--border); border-bottom: var(--bw) solid var(--border);
  padding: var(--sp-5) 0; margin: var(--sp-6) 0; text-wrap: balance; max-width: 28ch;
}
.nb-pull--wide { max-width: var(--measure); }
.nb-pull cite {
  display: block; font-style: normal; font-family: var(--font-mono);
  font-size: 0.66rem; letter-spacing: var(--tk-label); text-transform: uppercase;
  color: var(--text-muted); margin-top: var(--sp-3);
}

/* ───────────────────────────────  Scan-plate  ─────────────────────────── */
.nb-plate {
  background: var(--surface-sunken);
  border: var(--bw) solid var(--border-strong);
  border-radius: var(--r-2);
  padding: var(--sp-4);
}
.nb-plate img, .nb-plate svg { display: block; width: 100%; height: auto; }
.nb-plate__note {
  font-family: var(--font-mono); font-size: 0.66rem; letter-spacing: var(--tk-label);
  text-transform: uppercase; color: var(--cause); margin: 0 0 var(--sp-3);
}
.nb-figcap {
  font-family: var(--font-mono); font-size: var(--fs-fig); color: var(--text-muted);
  line-height: 1.45; margin-top: var(--sp-3); max-width: var(--measure);
}
.nb-figcap b { color: var(--text); font-weight: var(--w-semi); }

/* ───────────────────────────────  Data table  ─────────────────────────── */
/* overflow-x scrolls a too-wide table in place on mobile instead of blowing
   out the document width; hidden scrollbar matches .page-toc's treatment. */
.nb-table {
  display: block; overflow-x: auto; width: 100%; max-width: 100%;
  border-collapse: collapse; font-family: var(--font-mono); font-size: 0.78rem;
  scrollbar-width: none;
}
.nb-table::-webkit-scrollbar { display: none; }

/* ────────────────────────────────  Code block  ────────────────────────── */
/* pre.code had no rules at all before this — pure UA default (white-space:
   pre, no wrap), which let long lines blow out the document width on
   mobile instead of scrolling. Same hidden-scrollbar treatment as above. */
pre.code {
  overflow-x: auto; max-width: 100%; scrollbar-width: none;
}
pre.code::-webkit-scrollbar { display: none; }
.nb-table th, .nb-table td { padding: 8px 12px; text-align: left; border-bottom: var(--bw) solid var(--border); }
.nb-table th {
  font-weight: var(--w-semi); color: var(--text); text-transform: uppercase;
  font-size: 0.62rem; letter-spacing: var(--tk-label); border-bottom: var(--bw-strong) solid var(--border-strong);
}
.nb-table td.num, .nb-table th.num { text-align: right; font-feature-settings: "tnum" 1; }
.nb-table .pos { color: var(--pos); }
.nb-table .neg { color: var(--neg); }
.nb-table .game { color: var(--caution); font-style: italic; }
.nb-table tbody tr:hover td { background: color-mix(in srgb, var(--accent) 6%, transparent); }

/* ──────────────────────────────  Specimen box  ────────────────────────── */
/* A quoted model output / failure-mode exhibit. Coloured left rule by verdict. */
.nb-specimen {
  background: var(--surface-card);
  border: var(--bw) solid var(--border);
  border-left: 3px solid var(--accent);
  border-radius: 0 var(--r-1) var(--r-1) 0;
  padding: var(--sp-4) var(--sp-4) var(--sp-3);
  margin: var(--sp-4) 0;
}
.nb-specimen--win { border-left-color: var(--pos); }
.nb-specimen--crash { border-left-color: var(--neg); }
.nb-specimen--weird { border-left-color: var(--caution); }
.nb-specimen__head {
  display: flex; justify-content: space-between; align-items: baseline; gap: 10px;
  font-family: var(--font-mono); font-size: 0.62rem; letter-spacing: var(--tk-label);
  text-transform: uppercase; margin-bottom: var(--sp-2);
}
.nb-specimen__head .lid { font-weight: var(--w-semi); color: var(--text); }
.nb-specimen__head .t { color: var(--text-muted); }
.nb-specimen__quote {
  font-family: var(--font-mono); font-size: 0.76rem; line-height: 1.55; color: var(--text-dim);
  white-space: pre-wrap; word-break: break-word; margin: 0;
}
.nb-specimen__quote .hl { background: color-mix(in srgb, var(--accent) 18%, transparent); padding: 0 2px; }
.nb-specimen__gloss { font-family: var(--font-serif); font-style: italic; font-size: var(--fs-sm); color: var(--text-dim); margin: var(--sp-2) 0 0; }

/* ════════════════════════════════════════════════════════════════════════
   NOTEBOOK IDENTITY COMPONENTS
   The deliberate anti-"tasteslop" set: a page is a RECORD, not a pitch.
   Use these instead of a hero + stat-cell row. Uncertainty, corrections and
   provenance are first-class *visual* elements, not buried prose.
   ════════════════════════════════════════════════════════════════════════ */

/* ── Notebook header — replaces the SaaS hero/stats-strip ─────────────────
   A masthead that reads like the top of a lab page: specimen · status ·
   last-revised · confidence · caveats. Mono labels, hairline rules, no
   big-number theatre. */
.nb-record { border-top: var(--bw-strong) solid var(--text); padding-top: var(--sp-4); }
.nb-record__title { font-size: var(--fs-h2); font-weight: var(--w-med); margin: 0 0 var(--sp-4); }
.nb-record__grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: var(--sp-3) var(--sp-5);
  border-bottom: var(--bw) solid var(--border-strong);
  padding-bottom: var(--sp-4);
}
/* min-width:0 overrides the grid item's default min-width:auto — without it,
   a long unbroken value (a filename, a dotted path) forces its track past
   the minmax(150px,…) minimum to its own content width, blowing out
   .nb-record__grid (and the document) at narrow widths. overflow-wrap on
   the value lets long tokens actually break instead of just being allowed
   to overflow now that the column can shrink. */
.nb-record__field { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.nb-record__k {
  font-family: var(--font-mono); font-size: var(--fs-micro); letter-spacing: var(--tk-label);
  text-transform: uppercase; color: var(--text-muted);
}
.nb-record__v {
  font-family: var(--font-mono); font-size: 0.78rem; color: var(--text); line-height: 1.4;
  overflow-wrap: break-word;
}
/* ── Correction / Dead-End card — the site's identity made structural ─────
   Hypothesis → why it looked true → why it failed → disposition. Violet/neg
   keyed. Place it NEAR THE TOP of a page; falsification is rewarded, not buried. */
.nb-correction {
  border: var(--bw) solid var(--border-strong);
  border-left: 3px solid var(--neg);
  border-radius: 0 var(--r-1) var(--r-1) 0;
  background: color-mix(in srgb, var(--neg) 6%, var(--surface-card));
  padding: var(--sp-4) var(--sp-5) var(--sp-4);
  margin: var(--sp-5) 0;
}
.nb-correction__head {
  display: flex; align-items: baseline; justify-content: space-between; gap: 12px;
  font-family: var(--font-mono); font-size: 0.64rem; letter-spacing: var(--tk-kicker);
  text-transform: uppercase; color: var(--neg); margin-bottom: var(--sp-3);
}
.nb-correction__disp {
  font-family: var(--font-mono); font-size: 0.6rem; letter-spacing: var(--tk-label);
  text-transform: uppercase; color: var(--neg); border: 1px solid var(--neg);
  border-radius: var(--r-1); padding: 0.3em 0.55em;
}
.nb-correction__row { display: grid; grid-template-columns: 9rem 1fr; gap: 4px var(--sp-4); padding: var(--sp-2) 0; }
.nb-correction__row + .nb-correction__row { border-top: 1px dotted var(--border); }
.nb-correction__k {
  font-family: var(--font-mono); font-size: var(--fs-micro); letter-spacing: var(--tk-label);
  text-transform: uppercase; color: var(--text-muted); padding-top: 2px;
}
.nb-correction__b { font-family: var(--font-serif); font-size: var(--fs-sm); color: var(--text-dim); line-height: 1.5; }
.nb-correction__b b { color: var(--text); }
@media (max-width: 600px) { .nb-correction__row { grid-template-columns: 1fr; gap: 1px; } }
/* ── Caveats walkback list — quiet successor to .nb-limits ────────────────
   Confidence lives in a prose lead-in, not a chip; hairline left rules, no box.
   (Originated page-local on hrm.html — its copy is identical and shadows this.) */
.walkback { list-style: none; padding: 0; margin: var(--sp-5) 0 0; max-width: var(--measure); }
.walkback li { font-size: var(--fs-sm); line-height: 1.6; color: var(--text-dim); padding-left: var(--sp-5); border-left: 1px solid var(--border); margin-bottom: var(--sp-4); }
.wbhead { display: block; color: var(--text); font-weight: 600; }

/* ── Provenance caption — "these charts came from files, not Illustrator" ──*/
.nb-prov {
  font-family: var(--font-mono); font-size: 0.62rem; letter-spacing: 0.02em;
  color: var(--text-muted); margin-top: var(--sp-2); display: flex; flex-wrap: wrap; gap: 0 0.9em;
}
/* was white-space:nowrap — a long value (e.g. a source filename + long
   description) couldn't shrink and blew out the document width on mobile.
   A viewport-width media query can't gate this reliably: once content
   overflows, mobile browsers widen the reported viewport past the
   breakpoint to fit it, undoing the fix. Just allow wrap unconditionally;
   flex-wrap on the parent already keeps "label value" together when
   there's room. */
.nb-prov span { white-space: normal; }
.nb-prov b { color: var(--text-dim); font-weight: var(--w-med); }

/* ──────────────────────────────  Lab nav  ─────────────────────────────── */
.nb-nav {
  display: flex; align-items: center; flex-wrap: wrap; gap: 0;
  position: sticky; top: 0; z-index: var(--z-nav);
  border-bottom: var(--bw) solid var(--border-strong);
  background: color-mix(in srgb, var(--surface) 90%, transparent);
  backdrop-filter: blur(8px);
  font-family: var(--font-mono); font-size: 0.7rem; letter-spacing: var(--tk-nav);
  text-transform: uppercase;
}
.nb-nav a { display: block; padding: 0.7rem 1rem; color: var(--text-dim); white-space: nowrap; }
.nb-nav a:hover, .nb-nav a.is-active { color: var(--accent); text-decoration: none; }
.nb-nav .nb-nav__home { color: var(--text); font-weight: var(--w-semi); letter-spacing: 0.12em; }
.nb-nav .nb-nav__spacer { flex: 1; }

/* ── PROTOTYPE: site bar + page TOC ───────────────────────────────────────
   .site-bar = constant site chrome (same markup every page). .page-toc =
   per-page section nav as a sticky left sidebar on wide screens only. On
   mobile the section links collapse into a no-JS <details> disclosure.
   Scroll-spy highlight (.is-active) is set by nb.js (IntersectionObserver);
   links still work with JS off. */
/* .nb-header-sticky wraps .site-bar + .page-toc as the single sticky
   ancestor (< 1500px). Previously each bar was independently
   `position: sticky` with .page-toc's offset hardcoded to `top: 2.6rem`,
   assuming .site-bar is always exactly one row tall — but .site-bar wraps
   to two rows on medium-narrow widths, so it grew taller than that
   assumption and covered .page-toc on scroll. Stacking them in normal flow
   under one sticky parent removes the hardcoded offset entirely, so there's
   nothing to go stale. */
.nb-header-sticky { position: sticky; top: 0; z-index: var(--z-nav); }
.site-bar {
  display: flex; align-items: center; flex-wrap: wrap; gap: 0;
  /* both bars' backdrop-filter creates a stacking context; without this the
     later-in-DOM .page-toc paints over open dropdown menus */
  position: relative; z-index: 1;
  border-bottom: var(--bw) solid var(--border-strong);
  background: color-mix(in srgb, var(--surface) 90%, transparent);
  backdrop-filter: blur(8px);
  font-family: var(--font-mono); font-size: 0.7rem; letter-spacing: var(--tk-nav);
  text-transform: uppercase;
}
.site-bar a { display: block; padding: 0.7rem 1rem; color: var(--text-dim); white-space: nowrap; }
.site-bar a:hover, .site-bar a.is-active { color: var(--accent); text-decoration: none; }
/* the Font/Light toggles are <button>s (they change state, they don't navigate);
   dressed to sit flush with the nav links. */
.site-bar button {
  background: none; border: 0; font: inherit;
  text-transform: inherit; letter-spacing: inherit;
  display: block; padding: 0.7rem 1rem; color: var(--text-dim);
  white-space: nowrap; cursor: pointer;
}
.site-bar button:hover { color: var(--accent); }
.site-bar__home { color: var(--text); font-weight: var(--w-semi); letter-spacing: 0.12em; }
/* per-page category label: markup stays identical everywhere (the bar is synced
   from one fragment); each page names its category via a body.cat-* class. */
.site-bar__cat::after { content: " · Experimentation Notebooks"; color: var(--text-dim); font-weight: var(--w-reg); }
body.cat-intervention .site-bar__cat::after { content: " · Intervention"; }
body.cat-observation  .site-bar__cat::after { content: " · Observation"; }
body.cat-method       .site-bar__cat::after { content: " · Method"; }
body.cat-systems      .site-bar__cat::after { content: " · Systems"; }
.site-bar__spacer { flex: 1; }
.site-bar__links { display: flex; flex-wrap: wrap; }

/* category dropdowns — native <details>, work with JS off (click/tap to open).
   nb.js adds accordion + click-away as progressive enhancement. */
/* plain top-level site-bar link (no dropdown) — matches a nav-group summary */
.site-bar__link {
  display: block; padding: 0.7rem 1rem; color: var(--text-dim);
  white-space: nowrap; cursor: pointer;
}
.nav-group { position: relative; }
.nav-group > summary {
  display: block; padding: 0.7rem 1rem; color: var(--text-dim);
  cursor: pointer; white-space: nowrap; list-style: none; user-select: none;
}
.nav-group > summary::-webkit-details-marker { display: none; }
.nav-group > summary::after { content: " ▾"; opacity: 0.55; }
.nav-group > summary:hover, .nav-group[open] > summary { color: var(--accent); }
/* hub scroll indicator: active category + a progress underline that fills as
   you move through its section (--seg set by nb.js navProgress) */
.site-bar .nav-group > summary.is-active { color: var(--accent-bright); }
.site-bar .nav-group > summary.is-active::before {
  content: ""; position: absolute; left: 0; bottom: 0; height: 2px;
  width: var(--seg, 0); background: var(--accent); transition: width 0.12s linear;
}
.nav-group > ul {
  list-style: none; margin: 0; padding: var(--sp-2) 0;
  position: absolute; top: 100%; left: 0; min-width: 12rem;
  background: var(--surface); border: var(--bw) solid var(--border-strong); border-top: 0;
}
.nav-group > ul a { padding: 0.5rem 1rem; text-transform: none; letter-spacing: 0; font-size: 0.72rem; white-space: nowrap; }
.nav-group > ul a:hover { background: color-mix(in srgb, var(--accent) 12%, transparent); }

/* mobile: checkbox-hack hamburger reveals the categories stacked full-width.
   The checkbox is visually hidden but stays FOCUSABLE — display:none (or the
   `hidden` attribute) would cut keyboard users off from the menu entirely.
   Its focus ring is painted onto the burger label via the sibling selector. */
.site-bar__toggle { display: none; }
.site-bar__burger { display: none; cursor: pointer; padding: 0.7rem 1rem; }
@media (max-width: 760px) {
  .site-bar__toggle {
    display: block; position: absolute;
    width: 1px; height: 1px; margin: -1px; padding: 0;
    overflow: hidden; clip-path: inset(50%); border: 0;
  }
  .site-bar__toggle:focus-visible ~ .site-bar__burger {
    outline: 2px solid var(--focus-ring); outline-offset: -2px;
  }
  .site-bar__burger { display: block; }
  .site-bar__cat { display: none; }
  .site-bar__links { order: 10; width: 100%; display: none; flex-direction: column; }
  .site-bar__toggle:checked ~ .site-bar__links { display: flex; }
  .nav-group > ul { position: static; border: 0; min-width: 0; padding-left: var(--sp-4); }
}

/* Default (< 1400px): a slim horizontal strip stuck just below the site-bar,
   so in-page section nav is present at laptop/tablet/phone widths too. */
.page-toc {
  display: flex; align-items: center; overflow-x: auto;
  border-bottom: var(--bw) solid var(--border);
  background: color-mix(in srgb, var(--surface) 92%, transparent);
  backdrop-filter: blur(6px);
  font-family: var(--font-mono); font-size: 0.62rem;
  letter-spacing: var(--tk-nav); text-transform: uppercase;
  scrollbar-width: none;
}
.page-toc::-webkit-scrollbar { display: none; }
/* first item sits at 1rem, matching the site-bar's home-link inset, so the two
   stacked chrome bars share a left edge (the article column is centered below). */
.page-toc__label { margin: 0; color: var(--text-muted); white-space: nowrap; padding: 0 var(--sp-2) 0 1rem; }
.page-toc a { position: relative; display: block; padding: 0.55rem 0.8rem; color: var(--text-dim); white-space: nowrap; }
.page-toc a:hover { color: var(--accent); text-decoration: none; }
.page-toc a.is-active { color: var(--accent-bright); }
/* progress underline that fills through the active section (--seg from nb.js) */
.page-toc a.is-active::before {
  content: ""; position: absolute; left: 0; bottom: 0; height: 2px;
  width: var(--seg, 0); background: var(--accent); transition: width 0.12s linear;
}

/* Wide (>= 1500px): enough left margin to float it into a fixed sidebar
   without hugging the window edge. Below this it stays the sticky strip. */
@media (min-width: 1500px) {
  .page-toc {
    display: block; position: fixed; top: 5.5rem;
    left: calc((100vw - var(--page-max)) / 2 - 10.5rem);
    width: 9rem; overflow: visible; padding: 0;
    border: 0; background: none; backdrop-filter: none;
    font-size: 0.66rem;
  }
  .page-toc__label { padding: 0; margin-bottom: var(--sp-2); }
  .page-toc a { padding: 0.35rem 0; border-left: 2px solid transparent; padding-left: var(--sp-3); }
  .page-toc a.is-active { border-left-color: var(--accent); }
  .page-toc a.is-active::before { display: none; }   /* sidebar uses the left keyline, not the underline */
}

/* ── nb-spec: specimen readout ────────────────────────────────────────────
   Compact mono key/value strip for teardown cards (arch / runtime / op — the
   facts a research index gets scanned for; the op value carries the single
   accent). Used WHERE those facts exist; essay/systems cards keep the plain
   .nb-card__tag genre label. Promoted from the hub page 2026-07-03. */
.nb-spec {
    display: flex; flex-wrap: wrap; gap: 0;
    margin: var(--sp-3) 0 0;
    border: 1px solid var(--border);
    border-left: 2px solid var(--accent);
    font-family: var(--font-mono);
    font-size: var(--fs-micro);
    line-height: 1.4;
}
.nb-spec > div { display: flex; gap: .5ch; padding: .25rem .6rem; border-right: 1px solid var(--border); }
.nb-spec > div:last-child { border-right: 0; }
.nb-spec dt { margin: 0; color: var(--text-muted); text-transform: lowercase; }
.nb-spec dd { margin: 0; color: var(--text-dim); }
.nb-spec dd.op { color: var(--accent-bright); }   /* the pop */
.nb-spec dd.draft { color: var(--caution); }      /* only the exception is flagged; published is implicit */

/* ── nb-card__head: title + spec sidebar ─────────────────────────────────
   Header row for teardown cards: title takes the left ~66%, the spec strip
   stacks vertically in the right column. The card stays a flex column, so
   .nb-card__find keeps its margin-top:auto bottom-pin across a row. On
   mobile the head stacks and the strip returns to a horizontal band. */
.nb-card__head {
    display: flex;
    align-items: flex-start;
    gap: var(--sp-5);
    margin-bottom: var(--sp-3);
}
.nb-card__head h3 { flex: 1 1 auto; max-width: 66%; margin: 0; }
.nb-card__head .nb-spec {
    flex: 0 0 auto;
    max-width: 36%;
    margin: 0 0 0 auto;          /* pin to the right */
    flex-direction: column;
}
.nb-card__head .nb-spec > div {
    border-right: 0;
    border-bottom: 1px solid var(--border);
    justify-content: flex-start;
}
.nb-card__head .nb-spec > div:last-child { border-bottom: 0; }

@media (max-width: 760px) {
    .nb-card__head { flex-direction: column; gap: var(--sp-3); }
    .nb-card__head h3 { max-width: 100%; }
    .nb-card__head .nb-spec {
        max-width: 100%; margin: 0;
        flex-direction: row; flex-wrap: wrap;
    }
    .nb-card__head .nb-spec > div { border-right: 1px solid var(--border); border-bottom: 0; }
    .nb-card__head .nb-spec > div:last-child { border-right: 0; }
}

/* ── skip link: first tab stop, off-screen until focused ─────────────────
   Markup lives at the top of <body> before the site-bar, targeting the page's
   <main id="main">. Kept out of the synced nav fragment so a page only carries
   it once it has a matching main landmark. */
.skip-link {
  position: absolute; left: var(--sp-3); top: var(--sp-3);
  z-index: calc(var(--z-nav) + 1);
  transform: translateY(-160%);
  background: var(--surface); color: var(--accent);
  border: var(--bw) solid var(--border-strong);
  padding: 0.5rem 0.9rem;
  font-family: var(--font-mono); font-size: 0.72rem;
  letter-spacing: var(--tk-nav); text-transform: uppercase;
  transition: transform var(--dur-fast) var(--ease);
}
.skip-link:focus { transform: translateY(0); outline: 2px solid var(--focus-ring); text-decoration: none; }
