/* ============================================================================
   The house system — "Terminal".

   Near-monochrome on true black. A hairline frame with registration marks
   holds the whole screen; a fixed nav rail above and a status line below,
   both dense mono. Display type is a bitmap face.

   The accent is white and never varies. A guide claims a low-chroma tint,
   which appears on its address mark and the code prompt and nowhere else:
       <body class="guide" style="--accent: var(--teal)">
   ========================================================================== */

/* --- the faces -------------------------------------------------------------
   Self-hosted, not fetched from Google Fonts: a guide has to typeset on a
   plane, and a bitmap display face silently replaced by Courier New is a
   broken page rather than a slower one. These are the latin and latin-ext
   subsets Google serves, byte for byte, plus greek for JetBrains Mono because
   the maths guides set pi and theta in code.

   `unicode-range` is what keeps this cheap: a browser downloads a face only
   when a glyph on the page actually needs it, so a guide still never pulls a
   display face it does not set. JetBrains Mono and Bitcount are variable
   fonts -- one file covers every weight, and the ranges below are read from
   their own fvar tables. Box drawing and the maths symbols are in no subset
   Google publishes; they fall back to a system mono, as they always have. */

@font-face {
  font-family: 'Atkinson Hyperlegible';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/fonts/atkinson-hyperlegible-400-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'Atkinson Hyperlegible';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/fonts/atkinson-hyperlegible-400-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: 'Atkinson Hyperlegible';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('/fonts/atkinson-hyperlegible-700-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'Atkinson Hyperlegible';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('/fonts/atkinson-hyperlegible-700-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: 'JetBrains Mono';
  font-style: normal;
  font-weight: 400 800;
  font-display: swap;
  src: url('/fonts/jetbrains-mono-var-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'JetBrains Mono';
  font-style: normal;
  font-weight: 400 800;
  font-display: swap;
  src: url('/fonts/jetbrains-mono-var-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: 'JetBrains Mono';
  font-style: normal;
  font-weight: 400 800;
  font-display: swap;
  src: url('/fonts/jetbrains-mono-var-greek.woff2') format('woff2');
  unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
}

@font-face {
  font-family: 'Silkscreen';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/fonts/silkscreen-400-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'Silkscreen';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/fonts/silkscreen-400-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: 'Silkscreen';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('/fonts/silkscreen-700-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'Silkscreen';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('/fonts/silkscreen-700-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: 'Bitcount Prop Single';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url('/fonts/bitcount-prop-single-var-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'Bitcount Prop Single';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url('/fonts/bitcount-prop-single-var-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

:root {
  --bg:      #000000;
  --panel:   #0B0B0D;
  --panel-2: #131418;
  --line:    #26282C;   /* the hairline everything is drawn with */
  --line-2:  #43464C;

  --tx:      #E8EAEC;
  --tx-2:    #92979D;
  --tx-3:    #7A7F85;   /* safe on every ground: 5.20 bg / 4.87 panel / 4.56 panel-2 */

  /* Topic tints. Low chroma on purpose — they separate the guides without
     competing with the white. */
  --teal:   #8FB3AC;
  --rose:   #C295A6;
  --violet: #A49CC0;
  --ember:  #C4A084;

  --sig:    var(--tx);      /* the accent is white; it never varies per guide */
  --on-sig: #000000;
  --accent: var(--tx-2);    /* a guide overrides this with its tint */

  --pixel: 'Silkscreen', 'Courier New', monospace;
  --mono:  'JetBrains Mono', ui-monospace, 'SF Mono', Menlo, Consolas, monospace;
  --body:  'Atkinson Hyperlegible', 'Helvetica Neue', Arial, sans-serif;

  --inset: 10px;        /* how far the frame sits from the viewport edge */
  --bar: 10px;          /* the bottom bar: a progress rail, nothing more */
  --rail: 15rem;
  --measure: 68ch;
  --ease: cubic-bezier(0.16, 1, 0.3, 1);

  color-scheme: dark;
}

:root[data-theme='light'] {
  --bg:      #FFFFFF;
  --panel:   #F2F3F4;
  --panel-2: #E7E9EB;
  --line:    #C9CCD0;
  --line-2:  #9AA0A6;

  --tx:      #08090B;
  --tx-2:    #4E545C;
  --tx-3:    #5E646C;

  --teal:   #3D6B64;
  --rose:   #7A4E5C;
  --violet: #514A70;
  --ember:  #6F553C;

  --on-sig: #FFFFFF;

  color-scheme: light;
}

*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }

html, body { height: 100%; overflow: hidden; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--tx);
  font: 400 16.5px/1.78 var(--body);
  -webkit-font-smoothing: antialiased;
}

/* --- the frame ------------------------------------------------------------
   One hairline rectangle holding the screen, with registration marks at the
   corners. Fixed, inert, and drawn over everything. */

.frame {
  position: fixed;
  inset: var(--inset);
  border: 1px solid var(--line);
  pointer-events: none;
  z-index: 42;          /* above the bars, or they erase the marks */
}
.frame i { position: absolute; width: 9px; height: 9px; border: 0 solid var(--tx-3); }
.frame i:nth-child(1) { top: -1px;    left: -1px;    border-top-width: 1px; border-left-width: 1px; }
.frame i:nth-child(2) { top: -1px;    right: -1px;   border-top-width: 1px; border-right-width: 1px; }
.frame i:nth-child(3) { bottom: -1px; left: -1px;    border-bottom-width: 1px; border-left-width: 1px; }
.frame i:nth-child(4) { bottom: -1px; right: -1px;   border-bottom-width: 1px; border-right-width: 1px; }

/* --- the bottom bar --------------------------------------------------------
   A guide's only chrome below the text: a reading-progress line drawn edge to
   edge along its top. Wide enough for the rail to be a column, that is all it
   is, so the bar is a few pixels tall. Below 60rem it grows to hold the icon
   that opens the contents over the page. */

.bar {
  position: fixed;
  left: var(--inset);
  right: var(--inset);
  height: var(--bar);
  display: flex;
  align-items: center;
  gap: 1.5rem;
  padding: 0 0.85rem;
  background: var(--bg);
  font: 400 10px/1 var(--mono);
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--tx-3);
  white-space: nowrap;
  z-index: 41;
}
.bar-bot { bottom: var(--inset); border-top: 1px solid var(--line); }

/* Full-bleed: left/right 0 against the bar's padding box, so the line runs the
   whole width of the frame rather than stopping at the bar's own padding. The
   track is the bar's top border; only the fill is drawn. */
.prog {
  position: absolute;
  left: 0;
  right: 0;
  top: -1px;
  height: 3px;
  pointer-events: none;
}
.prog i {
  display: block;
  width: 0;
  height: 100%;
  background: var(--sig);
}

/* One control, two glyphs; aria-expanded picks which is drawn, so the state
   lives in the attribute a screen reader is already reading. */
.navtog svg,
.themetog svg,
.instbtn svg {
  display: block;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.5;
  stroke-linecap: square;
}
.navtog svg { width: 22px; height: 22px; }
.themetog svg,
.instbtn svg { width: 15px; height: 15px; stroke-linecap: round; }
.navtog .i-shut,
.navtog[aria-expanded='true'] .i-open { display: none; }
.navtog[aria-expanded='true'] .i-shut { display: block; }

/* --- browser surfaces ---------------------------------------------------- */

::selection { background: var(--sig); color: var(--on-sig); }
:focus-visible { outline: 1px solid var(--sig); outline-offset: 3px; }

html { scrollbar-color: var(--line-2) var(--bg); }
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: var(--bg); }
::-webkit-scrollbar-thumb { background: var(--line-2); }

a { color: inherit; text-decoration-thickness: 1px; text-underline-offset: 3px; }

/* --- type -----------------------------------------------------------------
   The bitmap face is for display only. Prose stays in a workhorse grotesk —
   a pixel face across two thousand lines of maths would be unreadable. */

h1, h2 { font-family: var(--pixel); font-weight: 400; margin: 0; letter-spacing: 0; }
h1 { font-size: clamp(1.75rem, 4.2vw, 3.25rem); line-height: 1.15; }
h2 { font-size: clamp(1.125rem, 1.9vw, 1.5rem); line-height: 1.25; }
h3 { font: 500 14px/1.35 var(--mono); margin: 0; letter-spacing: -0.01em; }

p { margin: 0 0 1.15em; max-width: var(--measure); }
code, kbd, samp, pre { font-family: var(--mono); }
code { font-size: 0.875em; color: var(--tx); background: var(--panel-2); padding: 0.08em 0.3em; }

.lbl {
  font: 400 10px/1.5 var(--mono);
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--tx-3);
  margin: 0 0 0.6rem;
}

/* --- shell ---------------------------------------------------------------- */

/* The index has no bars: the frame is the whole page. */
.shell {
  position: fixed;
  top: var(--inset);
  bottom: var(--inset);
  left: var(--inset);
  right: var(--inset);
  overflow-y: auto;
  overscroll-behavior: contain;
  display: grid;
  grid-template-columns: var(--rail) minmax(0, 1fr);
  /* Named areas rather than source order: the masthead is written first so a
     phone, which reads this as one column, opens on the library rather than on
     its controls. The rail spans both rows to stay a full-height column here. */
  grid-template-areas:
    "side head"
    "side main";
  align-items: start;
}

/* Child-scoped to .shell: .side and .main name the guide shell's rail and
   column too, and .gshell declares no areas -- unscoped, these three put the
   contents rail into an implicit area and break every guide at desktop width.
   The mirror of the .gshell-scoping rule in CLAUDE.md. */
.shell > .masthead { grid-area: head; }
.shell > .side { grid-area: side; }
.shell > .main { grid-area: main; }

/* The rail does not scroll as a whole. It is a column: a scrolling region for
   however many entries there are, and a foot that stays put, so the theme
   control is in the same place whether a guide has twelve chapters or forty. */
.side {
  border-right: 1px solid var(--line);
  padding: 1.75rem 1.25rem 1.1rem 0.85rem;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  position: sticky;
  top: 0;
  align-self: start;
  height: calc(100vh - var(--inset) * 2);
}
/* min-height:0 or the flex item refuses to shrink below its content and the
   overflow lands on the page instead of inside this box. */
.side-scroll { flex: 1 1 auto; min-height: 0; overflow-y: auto; }
.side-foot { flex: none; margin-top: 1.25rem; }
/* The index's foot holds two controls, a guide's holds one. `.shell >`-scoped,
   because .side is a guide's contents rail too and this must not reach it. */
.shell > .side .side-foot { display: flex; gap: 0.5rem; }

/* The masthead is its own grid item now, so it carries the top and side
   padding the main column used to give it. */
.masthead { padding: 1.75rem clamp(0.85rem, 3vw, 3rem) 2.5rem; max-width: 60rem; }
.main { min-width: 0; padding: 0 clamp(0.85rem, 3vw, 3rem) 4rem; max-width: 60rem; }

/* --- slash nav -------------------------------------------------------------
   Every navigable thing is a path. A library of documents is a filesystem. */

.nav-head { font: 400 10px/1.5 var(--mono); letter-spacing: 0.11em; text-transform: uppercase; color: var(--tx-3); margin-top: 1.5rem; }

/* --- theme toggle ------------------------------------------------------------ */

/* One control, not two: the icon names the theme a press would switch to, so
   the sun shows in the dark and the moon in the light. */
/* Sits in the rail's foot, so it carries no margin of its own. --line is the
   hairline the frame is drawn with and reads as scenery on black; a control
   you go looking for gets the panel fill and the heavier line. */
/* .instbtn is the index's install control and shares this box exactly. It is a
   name neither shell already uses, so unlike .side it cannot leak between them. */
.themetog,
.instbtn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  background: var(--panel-2);
  border: 1px solid var(--line-2);
  color: var(--tx);
  padding: 0;
  cursor: pointer;
  transition: background 0.15s var(--ease), color 0.15s var(--ease),
              border-color 0.15s var(--ease);
}
.themetog:hover,
.instbtn:hover { background: var(--sig); border-color: var(--sig); color: var(--on-sig); }
/* `hidden` loses to the display above, and the button must stay gone until the
   browser says the library is installable. */
.instbtn[hidden] { display: none; }
.themetog .i-moon { display: none; }
:root[data-theme='light'] .themetog .i-sun { display: none; }
:root[data-theme='light'] .themetog .i-moon { display: block; }

/* --- the title types on ----------------------------------------------------
   title.js adds .tanim to :root before first paint, so the title is hidden
   from the start rather than painted and then taken away. No class means no
   script, or a reader who asked for less motion -- either way the title is an
   ordinary heading and nothing here applies. */

:root.tanim [data-type-on] { visibility: hidden; }

[data-type-on] { position: relative; }
[data-type-on] .w { display: inline-block; white-space: nowrap; }
[data-type-on] .ch { display: inline-block; }
[data-type-on] .ch.sp { display: inline; }

/* Positioned, not in the flow: between characters it would change the width of
   the word being typed and bounce the last word between lines. */
.tcur {
  position: absolute;
  top: 0.16em;
  left: 0;
  width: 0.55em;
  height: 0.78em;
  background: var(--sig);
  pointer-events: none;
}
.tcur.blink { animation: tcur-blink 0.5s steps(1) infinite; }
@keyframes tcur-blink { 50% { opacity: 0; } }

/* --- masthead ----------------------------------------------------------------- */

.masthead { border-bottom: 1px solid var(--line); position: relative; }
.masthead h1 { margin-bottom: 1.25rem; }
.stand { color: var(--tx-2); max-width: 60ch; }

/* --- figures ---------------------------------------------------------------
   A five-step series for diagrams with multiple traces. The per-guide tint
   cannot cover this: a chart needs steps that stay distinct from each other. */

.fig {
  --f1: var(--tx);
  --f2: var(--tx-2);
  --f3: var(--teal);
  --f4: var(--rose);
  --f5: var(--ember);
  background: var(--panel);
  border: 1px solid var(--line);
}

.prose { max-width: var(--measure); min-width: 0; }

/* --- callouts ---------------------------------------------------------------------
   A mono label in the signal's colour and a hairline box. No fills, no stripes. */

.note {
  --note: var(--ember);
  border: 1px solid var(--line);
  padding: 1rem 1.15rem;
  margin: 0 0 1.4rem;
  max-width: var(--measure);
}
.note.mistake { --note: var(--rose); }
.note.aside   { --note: var(--teal); }
.note .lbl { color: var(--note); }
.note p:last-child { margin: 0; color: var(--tx-2); font-size: 15.5px; }

/* --- the library index ------------------------------------------------------------ */

.track { margin-top: 3rem; }
.track > h2 { padding-bottom: 0.85rem; border-bottom: 1px solid var(--line-2); }

.band {
  display: grid;
  grid-template-columns: 4.5rem minmax(0, 1fr);
  gap: 1.25rem;
  padding: 1.75rem 0.4rem;
  margin: 0 -0.4rem;
  border-bottom: 1px solid var(--line);
  text-decoration: none;
  transition: background 0.2s var(--ease);
}
.band:hover { background: var(--panel); }
.band > .n {
  font: 400 11px var(--mono);
  letter-spacing: 0.05em;
  color: var(--accent);
  align-self: start;
  padding: 0.3rem 0.4rem;
  margin: 0 -0.4rem;
  transition: background 0.25s var(--ease), color 0.25s var(--ease);
}
.band:hover > .n, .band:focus-visible > .n { background: var(--accent); color: var(--on-sig); }
.band h3 {
  font-family: var(--pixel);
  font-size: clamp(1rem, 1.7vw, 1.375rem);
  line-height: 1.25;
  letter-spacing: 0;
  text-transform: uppercase;
  color: var(--tx);
  margin-bottom: 0.65rem;
}
.band p { color: var(--tx-2); font-size: 15.5px; margin: 0; max-width: 58ch; }
/* Last-modified stamp, the one piece of metadata a card carries besides its
   number. Mono and --tx-3 so it reads as a quiet timestamp under the blurb. */
.band .when { margin: 0.7rem 0 0; font: 400 11px/1.5 var(--mono); letter-spacing: 0.04em; color: var(--tx-3); }

/* --- filters ----------------------------------------------------------------------- */

.filters { display: flex; flex-direction: column; align-items: stretch; gap: 0.1rem; margin: 0.85rem 0 0; padding: 0; border: 0; }
.filters legend { padding: 0; }
.filter {
  font: 400 11px/1.5 var(--mono);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  text-align: left;
  background: none;
  border: 0;
  color: var(--tx-2);
  padding: 0.3rem 0.4rem;
  margin: 0 -0.4rem;
  cursor: pointer;
  transition: background 0.15s var(--ease), color 0.15s var(--ease);
}
.filter::before { content: '/'; color: var(--tx-3); margin-right: 0.3em; }
.filter:hover { background: var(--panel); color: var(--tx); }
.filter[aria-pressed='true'] { background: var(--sig); color: var(--on-sig); }
.filter[aria-pressed='true']::before { color: var(--on-sig); }

/* --- responsive ---------------------------------------------------------------------- */

@media (max-width: 60rem) {
  :root { --inset: 6px; }
  /* One column is not a grid. Collapsing to block also sidesteps a row-sizing
     quirk where the <fieldset> rail overflowed its track and painted over the
     masthead. */
  .shell { display: block; }

  /* A row of controls under the heading rather than a column above it: four
     guides do not need a filter to cost a third of the first screen. There is
     no rail to pin a foot to here, so the theme control sits in the row.
     .shell >-scoped: .side is a guide's contents rail too. */
  .shell > .side {
    position: static;
    height: auto;
    overflow: visible;
    border-right: 0;
    border-bottom: 1px solid var(--line);
    padding: 0.9rem 0.85rem 1rem;
    flex-direction: row;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem 1rem;
  }
  .shell > .side .side-scroll { overflow: visible; flex: 1 1 auto; }
  .shell > .side .side-foot { margin: 0; }
  .filters { flex-direction: row; flex-wrap: wrap; gap: 0.3rem; margin: 0; }
  .filters legend { width: 100%; margin-bottom: 0.4rem; }
  .filter { padding: 0.3rem 0.6rem; }

  .masthead { padding: 1.5rem 0.85rem 1.25rem; }
  .main { padding: 1.5rem 0.85rem 3rem; }
  /* The filter's own border already separates the controls from the library;
     a track's desktop top margin on top of it is a screenful of nothing. */
  .main > .track:first-child { margin-top: 0; }
  .band, .row { grid-template-columns: minmax(0, 1fr); gap: 0.45rem; }
  .row > .n { padding-top: 0; }
  .band > .n { justify-self: start; }
}

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

/* ============================================================================
   THE GUIDE SHELL
   One layout for every guide. The index uses .shell; a guide uses .gshell, so
   the two cannot collide on .side.
   ========================================================================= */

/* Dark carries a warm title; light does not. A gold that reads on black
   measures 1.7:1 on white, and darkening it far enough to pass stops it being
   gold. Light uses the ink tone instead, so it inherits --tx and cannot drift
   out of contrast. Do not make this symmetric. */
:root { --title: #E8C46A; }
:root[data-theme='light'] { --title: var(--tx); }

/* There is no top bar: the breadcrumb replaced it. The status line stays. */
.gshell {
  position: fixed;
  top: var(--inset);
  bottom: calc(var(--inset) + var(--bar));
  left: var(--inset);
  right: var(--inset);
  display: grid;
  grid-template-columns: 16rem minmax(0, 1fr);
  overflow: hidden;
}

/* .side is declared above for the index, inside a scrolling .shell. Here it is
   a grid child of a fixed container and scrolls itself, so the sticky
   positioning it inherits has to go. */
.gshell > .side {
  position: static;
  align-self: stretch;   /* the shell has a definite height; fill it */
  height: auto;
  border-right: 1px solid var(--line);
  padding: 1.5rem 1.1rem 1.1rem;
}

/* Everything below is scoped under .gshell. The index uses .shell, so an
   unscoped selector here silently restyles every guide through class names the
   two share (.side, .theme, .bar). Nothing enforces this — keep the prefix. */

.gshell .doc { overflow-y: auto; overscroll-behavior: contain; padding: 1.5rem clamp(1rem, 3vw, 3rem) 5rem; }
.gshell .doc:focus { outline: none; }
.gshell .doc-in { max-width: 60rem; margin: 0 auto; }

/* --- head: heading and breadcrumb ----------------------------------------- */

.gshell h1.title {
  color: var(--title);
  font-family: var(--display);
  font-variation-settings: var(--display-vf, normal);
  font-size: clamp(1.7rem, 3.6vw, 2.6rem);
  line-height: 1.12;
  margin: 1.4rem 0 0;
}
.gshell .crumb { font: 400 13.5px/1.5 var(--body); color: var(--tx-3); margin: 0.7rem 0 0; }
.gshell .crumb a { color: var(--tx-2); text-decoration: none; }
.gshell .crumb a:hover { color: var(--tx); }
.gshell .crumb span { margin: 0 0.45em; color: var(--line-2); }
/* Last-modified stamp under the breadcrumb, same quiet mono timestamp the
   index card carries. */
.gshell .when { margin: 0.55rem 0 0; font: 400 12px/1.5 var(--mono); letter-spacing: 0.05em; color: var(--tx-3); }
.gshell .headwrap { padding-bottom: 1.5rem; border-bottom: 1px solid var(--line); margin-bottom: 2.5rem; }

/* --- side nav: body face, sentence case ----------------------------------- */

.gshell .nav-h {
  font: 400 10.5px/1.5 var(--mono);
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--tx-3);
  margin: 0 0 0.7rem;
}
.gshell .nav-g { margin: 0 0 1.5rem; }
.gshell .nav-g ul { list-style: none; margin: 0; padding: 0; }
.gshell .nav-g a {
  display: block;
  font: 400 14.5px/1.45 var(--body);
  color: var(--tx-2);
  text-decoration: none;
  padding: 0.28rem 0.45rem;
  margin: 0 -0.45rem;
}
.gshell .nav-g a:hover { background: var(--panel); color: var(--tx); }
.gshell .nav-g a[aria-current='true'] { background: var(--sig); color: var(--on-sig); }
/* --- part tags: an optional kicker above a chapter heading ---------------- */

.gshell .part-tag {
  display: block;
  font: 400 10.5px/1.5 var(--mono);
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--tx-3);
  margin-bottom: 0.6rem;
}
/* R1: the display face is for h1 only. A bitmap face at reading size is the
   same mistake the contents nav had. */
.gshell .part-head h2 { font: 700 1.5rem/1.2 var(--body); margin: 0 0 0.7rem; }
.gshell .part-head p { color: var(--tx-2); max-width: var(--measure); margin: 0; }

/* --- chapters -------------------------------------------------------------- */

.gshell .cap { font: 400 12.5px/1.6 var(--mono); color: var(--tx-3); margin: -1rem 0 1.4rem; }
/* A caption is a <div>, not a <p> — Markdown parses block content (its own
   inline code/bold) inside a raw <div> but not inside a raw <p>, which is
   why it is one. Its text lands on the page as a nested <p> that would
   otherwise inherit .prose's paragraph margin and double the gap below. */
.gshell .cap p { margin: 0; }
/* A caption naming what a code block is for reads above the block it
   documents (house style, CLAUDE.md), the position linux's extractor writes
   it in. .cap's default margin above
   pulls it UP to hug the block it follows — right for the common case, a
   caption below its block, but wrong here: pulled up, it collides with
   whatever precedes the caption itself, a heading in 15 of 60 cases. :has(+
   pre) selects exactly this one case — a caption immediately before a <pre>
   — and gives it the opposite margins: none to pull up with (the preceding
   element's own bottom margin already spaces it out, the same convention
   pre.astro-code's own zero top margin relies on), and a small bottom margin
   to stay snug against the code below instead. */
.gshell .cap:has(+ pre) { margin: 0 0 0.5rem; }

/* --- copy ------------------------------------------------------------------ */

.gshell .copy {
  background: none;
  border: 1px solid var(--line);
  color: var(--tx-2);
  font: 400 10.5px/1 var(--mono);
  letter-spacing: 0.11em;
  text-transform: uppercase;
  padding: 0.4rem 0.6rem;
  cursor: pointer;
}
.gshell .copy:hover { border-color: var(--line-2); color: var(--tx); }
.gshell .copy.done { background: var(--sig); border-color: var(--sig); color: var(--on-sig); }
.gshell .row-top { display: flex; align-items: baseline; justify-content: space-between; gap: 1rem; }

/* Every guide closes with a caution about acting on it carelessly. It is a
   house commitment, so the layout requires it rather than offering it. */
.gshell .fin {
  margin-top: 4rem;
  padding-top: 1.5rem;
  border-top: 1px solid var(--line);
  color: var(--tx-2);
  font-size: 15px;
  max-width: var(--measure);
}

/* --- figures ----------------------------------------------------------------
   The SVGs carry only a viewBox, so the width rule is load-bearing: without it
   every figure falls back to the replaced-element default of 300x150. Colour
   comes from the --f1..--f5 series on .fig, never from attributes in the SVG.
   Values here are pinned to the original guide's audited figure CSS, which
   clears 4.5:1 on --panel in both themes. */

.gshell .fig { padding: 14px 16px 8px; margin: 22px 0; }
.gshell .fig svg { width: 100%; height: auto; display: block; }
.gshell .fig .gl { stroke: var(--line-2); stroke-width: 1; }
.gshell .fig .ax { stroke: var(--tx-2); stroke-width: 1.4; }
.gshell .fig .cv { fill: none; stroke-width: 2.2; stroke-linecap: round; stroke-linejoin: round; }
.gshell .fig .cv.thin { stroke-width: 1.4; }
.gshell .fig .dash { stroke-dasharray: 4 4; stroke-width: 1.6; }
.gshell .fig .arw { stroke-width: 2.6; }
/* The house .lbl is an uppercase mono chrome label. Inside a figure a .lbl is
   a plotted annotation, and text-transform does apply to SVG <text>. It sets the
   untinted default, so it is declared before .t1..t5 — those carry the same
   specificity, and a `class="lbl t4"` annotation means to take t4's colour. */
.gshell .fig .lbl {
  font: 500 11px/1.2 var(--mono);
  letter-spacing: 0;
  text-transform: none;
  margin: 0;
  color: inherit;
  fill: var(--tx-2);
}
.gshell .fig .c1 { stroke: var(--f1); } .gshell .fig .t1 { fill: var(--f1); }
.gshell .fig .c2 { stroke: var(--f2); } .gshell .fig .t2 { fill: var(--f2); }
.gshell .fig .c3 { stroke: var(--f3); } .gshell .fig .t3 { fill: var(--f3); }
.gshell .fig .c4 { stroke: var(--f4); } .gshell .fig .t4 { fill: var(--f4); }
.gshell .fig .c5 { stroke: var(--f5); } .gshell .fig .t5 { fill: var(--f5); }
.gshell .fig .ax-lbl { font-size: 10.5px; fill: var(--tx-2); }
.gshell .fig .dot { fill: var(--f1); stroke: var(--panel); stroke-width: 1.5; }
.gshell .fig .dot.hit { fill: var(--f4); }

/* --- prose: everything rendered Markdown can emit --------------------------
   A guide body now renders through <Content /> straight into .prose,
   unstyled. Nothing here is optional: an author reaching for a table or a
   blockquote must not be the first person to notice it has no rule. .prose
   already carries the base measure and min-width:0 (declared once, above,
   for the index); this only adds what .gshell needs beyond that. */

.gshell .prose > :first-child { margin-top: 0; }

/* R1: headings use the body face at weight 700. The pixel face is for
   h1.title only — see the note above the shell banner. */
.gshell .prose h1,
.gshell .prose h2,
.gshell .prose h3,
.gshell .prose h4,
.gshell .prose h5,
.gshell .prose h6 {
  font-family: var(--body);
  font-weight: 700;
  color: var(--tx);
  letter-spacing: 0;
  margin: 2rem 0 0.7rem;
}
.gshell .prose h1 { font-size: 1.75rem; line-height: 1.25; }
.gshell .prose h2 { font-size: 1.4rem; line-height: 1.28; }
.gshell .prose h3 { font-size: 1.15rem; line-height: 1.3; }
.gshell .prose h4 { font-size: 1rem; line-height: 1.4; }
.gshell .prose h5 { font-size: 0.9rem; line-height: 1.4; letter-spacing: 0.01em; }
.gshell .prose h6 {
  font-size: 0.8rem;
  line-height: 1.4;
  color: var(--tx-2);
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

/* A reference entry's name is written as code, because it is a command. At
   inline code's 0.875em and chip background it would set smaller than the
   description beneath it -- the one place code must not shrink. The tint is
   the address mark the old reference row gave each entry. */
.gshell .prose :is(h1, h2, h3, h4, h5, h6) code {
  font-size: 1em;
  background: none;
  padding: 0;
  color: var(--accent);
}

.gshell .prose ul,
.gshell .prose ol {
  margin: 0 0 1.15em;
  padding-left: 1.3rem;
  max-width: var(--measure);
  color: var(--tx);
}
.gshell .prose li { margin-bottom: 0.4em; }
.gshell .prose li:last-child { margin-bottom: 0; }
.gshell .prose li > ul,
.gshell .prose li > ol { margin: 0.4em 0 0; }
.gshell .prose ul { list-style: disc; }
.gshell .prose ol { list-style: decimal; }
.gshell .prose ul ul { list-style: circle; }
.gshell .prose li::marker { color: var(--tx-3); }

.gshell .prose blockquote {
  margin: 0 0 1.15em;
  padding: 0.05rem 0 0.05rem 1.1rem;
  border-left: 2px solid var(--line-2);
  color: var(--tx-2);
  max-width: var(--measure);
}
.gshell .prose blockquote > :last-child { margin-bottom: 0; }

.gshell .prose hr {
  border: 0;
  border-top: 1px solid var(--line);
  margin: 2rem 0;
}

.gshell .prose a {
  color: var(--tx);
  text-decoration: underline;
  text-decoration-color: var(--line-2);
  text-underline-offset: 3px;
  text-decoration-thickness: 1px;
  transition: text-decoration-color 0.15s var(--ease);
}
.gshell .prose a:hover { text-decoration-color: var(--tx); }

.gshell .prose strong { font-weight: 700; color: var(--tx); }
.gshell .prose em { font-style: italic; }
.gshell .prose del { color: var(--tx-3); text-decoration-color: var(--tx-3); }

.gshell .prose img {
  display: block;
  max-width: 100%;
  height: auto;
  border: 1px solid var(--line);
  margin: 1.4rem 0;
}

/* --- tables: no precedent elsewhere in this system ------------------------
   The table element itself is the scroll container — display:block moves it
   out of table layout for sizing purposes only; its thead/tbody/tr/td
   children keep their own table-* display values, so the browser still lays
   them out as a table, just inside a block box that overflow-x can clip and
   scroll. That is what keeps a wide table off the page's own scrollbar. */
.gshell .prose table {
  display: block;
  max-width: 100%;
  overflow-x: auto;
  border-collapse: collapse;
  margin: 0 0 1.4rem;
  font-size: 14.5px;
  border: 1px solid var(--line);
}
.gshell .prose th,
.gshell .prose td {
  padding: 0.6rem 0.85rem;
  text-align: left;
  vertical-align: top;
  border-bottom: 1px solid var(--line);
}
.gshell .prose thead th {
  font: 400 10.5px/1.4 var(--mono);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--tx-3);
  border-bottom: 1px solid var(--line-2);
  white-space: nowrap;
}
.gshell .prose td { color: var(--tx-2); }
.gshell .prose tbody tr:last-child td { border-bottom: 0; }
.gshell .prose tbody tr:hover { background: var(--panel); }
/* A mono first column, the way a reference row sets its command apart from
   its description. Header cells stay out of this — the header row reads as
   one uniform instrument-label strip, not one column standing out from it. */
.gshell .prose td:first-child {
  font-family: var(--mono);
  color: var(--tx);
}

/* --- footnotes: remark-gfm's own markup, styled to read as a quiet close -- */
.gshell .prose sup a[data-footnote-ref] {
  color: var(--tx-2);
  text-decoration: none;
}
.gshell .prose sup a[data-footnote-ref]:hover { color: var(--tx); text-decoration: underline; }
.gshell .prose .footnotes {
  margin-top: 2.5rem;
  padding-top: 1.25rem;
  border-top: 1px solid var(--line);
  font-size: 14px;
  color: var(--tx-2);
  max-width: var(--measure);
}
.gshell .prose .footnotes ol { padding-left: 1.25rem; margin: 0; }
.gshell .prose .footnotes li { margin-bottom: 0.6em; }
.gshell .prose .footnotes li:last-child { margin-bottom: 0; }
.gshell .prose a.data-footnote-backref {
  margin-left: 0.4em;
  color: var(--tx-3);
  text-decoration: none;
}
.gshell .prose a.data-footnote-backref:hover { color: var(--tx); }
/* remark-gfm's own visually-hidden section label — no existing utility for
   this in the system, so it is defined here, scoped to where it is used. */
.gshell .prose .sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* --- code: Shiki's css-variables theme, mapped onto our own tokens --------
   theme:'css-variables' (astro.config.mjs) makes every Shiki colour a
   var() reference instead of a hex literal, so a token kind left unmapped
   here inherits --astro-code-foreground rather than falling back to a
   hardcoded, unaudited colour. Restrained on purpose: full-spectrum
   highlighting fights a near-monochrome palette. Contrast against --panel
   for every mapped colour, in both palettes, is recorded in the task report. */
.gshell .prose pre.astro-code {
  --astro-code-background: var(--panel);
  --astro-code-foreground: var(--tx);
  --astro-code-token-comment: var(--tx-3);
  --astro-code-token-string: var(--tx-2);
  --astro-code-token-string-expression: var(--tx-2);
  --astro-code-token-keyword: var(--accent);
  --astro-code-token-function: var(--tx);
  --astro-code-token-constant: var(--tx-2);
  --astro-code-token-parameter: var(--tx-2);
  --astro-code-token-punctuation: var(--tx-2);
  background: var(--panel);
  border: 1px solid var(--line);
  color: var(--tx);
  padding: 1rem 1.15rem;
  margin: 0 0 1.4rem;
  font-size: 13px;
  line-height: 1.75;
  overflow-x: auto;
}
.gshell .prose pre.astro-code code {
  background: none;
  padding: 0;
  font-size: inherit;
  color: inherit;
}
/* guide.js inserts a .copy button as every Markdown pre's next sibling (see
   its own comment for why it can't be a child of <pre>). Pulled up into the
   pre's own bottom margin the same way .cap pulls up to hug the block above
   it, then re-opens the usual 1.4rem gap below itself. width: fit-content +
   margin-left: auto right-aligns it without introducing a wrapper element
   around <pre> -- simpler, and it leaves whatever else already borders the
   code block (a .cap caption, in a guide's prose
   chapters) exactly where it sits, with nothing new landing between them.
   Sitting in its own row below the block, this can never overlap the code
   above it, however narrow the viewport. */
.gshell .prose pre.astro-code + .copy {
  display: block;
  width: fit-content;
  margin: -0.9rem 0 1.4rem auto;
}

/* --- the contents rail on a narrow screen --------------------------------- */

/* Wide enough for the rail to be a permanent column, and the toggle does not
   exist. Below that the rail is the only navigation a guide has -- 12 to 17
   chapters, and hundreds of reference entries, are not scrollable by hand --
   so it becomes a sheet over the page instead of being hidden. Kept as a class on
   :root for the same reason the theme is: a guide's shell CSS must not reach
   the index, and :root is the one prefix that cannot. */
:root .navtog { display: none; }

@media (max-width: 60rem) {
  .gshell { grid-template-columns: minmax(0, 1fr); }

  /* Floating over the page at the top left, so the contents is one tap away
     from anywhere in a guide rather than only from its head. 44px is the floor
     for a control a thumb has to hit; the fill and hairline are what stop the
     text underneath from reading through it. Above the open sheet's z-index,
     so the cross that closes it stays on top of what it opened. */
  :root .navtog {
    display: flex;
    align-items: center;
    justify-content: center;
    position: fixed;
    top: calc(var(--inset) + 0.6rem);
    left: calc(var(--inset) + 0.6rem);
    width: 44px;
    height: 44px;
    color: var(--tx-2);
    background: var(--panel-2);
    border: 1px solid var(--line-2);
    padding: 0;
    cursor: pointer;
    z-index: 42;
    transition: opacity 0.18s var(--ease), transform 0.18s var(--ease);
  }
  /* Gone while you are reading down the page; back the moment you scroll up. */
  :root .navtog.away {
    opacity: 0;
    transform: translateY(-0.85rem);
    pointer-events: none;
  }
  :root[data-nav='open'] .navtog {
    background: var(--sig);
    border-color: var(--sig);
    color: var(--on-sig);
  }

  /* The sheet is not the document, so it has no reading position to report.
     The bar goes with it, and comes back when it closes. */
  :root[data-nav='open'] .bar-bot { display: none; }

  .gshell > .side { display: none; }
  :root[data-nav='open'] .gshell > .side {
    /* flex, not block: the sheet is the same column as the rail -- a scrolling
       list with the theme control pinned under it. As a block the list simply
       overflowed and took the control off the bottom of the screen. */
    display: flex;
    position: absolute;
    inset: 0;
    /* An absolutely-positioned grid child still honours alignment, and .side
       carries align-self: start for its column layout -- which shrink-wraps the
       sheet to its content instead of filling the shell. */
    align-self: stretch;
    max-height: none;
    z-index: 2;
    background: var(--bg);
    border-right: 0;
    padding: 4.4rem 0.9rem 1.1rem;
  }
  /* Clear of the floating button, which overlaps this corner. */
  .gshell .doc { padding: 4.4rem 0.85rem 3rem; }
}

/* ============================================================================
   THE ROADMAP
   A second index-shaped page: it reuses .shell, so the frame, the rail, the
   theme toggle and the responsive collapse are the index's, already proven.
   Everything below is the board itself -- two lanes, Engineering and
   Mathematics, each read foundation-first down a spine, converging into one
   applied-AI apex. Prefixed `.rm-` throughout, a name neither shell uses, so
   none of it can leak the way .side would.

   A node is a guide you have (solid card, a checkbox that remembers itself) or
   a gap you don't (dashed card, "proposed"). The one relationship the roadmap
   argues lives in the confluence below the lanes: engineering feeds the apex on
   a solid line -- the critical path -- and mathematics on a dashed one, because
   it strengthens the AI work without gating it.
   ========================================================================= */

/* Intent, under the title: the why and the end goal, the standfirst's purpose
   without its usage notes. The north-star node at the foot closes the thought. */
.rm-intent { max-width: 58ch; margin: 1.35rem 0 0; }
.rm-intent-k { display: block; font: 400 10px/1.5 var(--mono); letter-spacing: 0.11em; text-transform: uppercase; color: var(--tx-3); margin-bottom: 0.55rem; }
.rm-intent p { color: var(--tx-2); font-size: 15px; margin: 0; }
.rm-intent strong { color: var(--tx); font-weight: 700; }

.rm-prog { display: flex; align-items: center; gap: 0.9rem; margin: 0 0 2.75rem; max-width: 46rem; }
.rm-count { font: 400 10px/1 var(--mono); letter-spacing: 0.11em; text-transform: uppercase; color: var(--tx-3); white-space: nowrap; }
.rm-count b { color: var(--tx); font-weight: 400; }
.rm-bar { flex: 1 1 auto; height: 3px; background: var(--line); }
.rm-bar i { display: block; height: 100%; width: 0; background: var(--sig); transition: width 0.35s var(--ease); }

/* --- the board: two lanes, side by side ---------------------------------- */
.rm-board { display: grid; grid-template-columns: 1fr 1fr; gap: 1.75rem; align-items: start; }

.rm-lane { --lane: var(--tx-2); min-width: 0; }
.rm-lane.eng  { --lane: var(--teal); }
.rm-lane.math { --lane: var(--rose); }

.rm-lane-h {
  font-family: var(--pixel);
  font-size: clamp(0.95rem, 1.6vw, 1.2rem);
  line-height: 1.15;
  text-transform: uppercase;
  color: var(--tx);
  margin: 0 0 0.35rem;
  display: flex;
  align-items: center;
  gap: 0.55rem;
}
.rm-lane-h::before { content: ''; width: 0.72rem; height: 0.72rem; background: var(--lane); flex: none; }
.rm-lane-sub { font: 400 10px/1.5 var(--mono); letter-spacing: 0.09em; text-transform: uppercase; color: var(--tx-3); margin: 0 0 1.5rem; }

/* the spine the markers ride on: one line behind the marker column */
.rm-nodes { position: relative; display: flex; flex-direction: column; gap: 0.85rem; }
.rm-nodes::before { content: ''; position: absolute; left: 0.6rem; top: 0.5rem; bottom: 0.5rem; width: 1px; background: var(--line-2); }

.rm-node { position: relative; display: grid; grid-template-columns: 1.25rem minmax(0, 1fr); gap: 0.9rem; align-items: start; }
/* Marker on the spine, ringed in --bg so the line reads as passing behind it.
   Hollow by default; filled once the topic is covered. */
.rm-mark { width: 0.72rem; height: 0.72rem; margin: 0.55rem 0 0 0.24rem; background: var(--bg); border: 1px solid var(--lane); box-shadow: 0 0 0 4px var(--bg); }
.rm-mark.covered { background: var(--lane); }

/* A card is a topic: dashed and open by default -- a gap -- and solid once a
   guide covers it. The checkbox drives the .covered class both ways. */
.rm-card { border: 1px dashed var(--line-2); background: none; padding: 0.7rem 0.85rem; }
.rm-card.covered { border-style: solid; border-color: var(--line); background: var(--panel); }
.rm-card-top { display: flex; align-items: baseline; justify-content: space-between; gap: 0.7rem; }
.rm-ttl { font: 500 13.5px/1.35 var(--mono); color: var(--tx); letter-spacing: -0.01em; min-width: 0; }
a.rm-ttl { text-decoration: none; }
a.rm-ttl:hover { text-decoration: underline; text-underline-offset: 3px; text-decoration-color: var(--line-2); }
.rm-note { color: var(--tx-2); font-size: 13px; line-height: 1.5; margin: 0.45rem 0 0; }
/* the tier chip and the checkbox share the right edge of a card's top row */
.rm-right { display: flex; align-items: center; gap: 0.5rem; flex: none; }

/* chips: a gap's tier reads here -- next up, strengthens, or optional. Once the
   topic is covered the tier is moot, so the chip gives way to the checkbox. */
.rm-chip { flex: none; font: 400 9px/1.4 var(--mono); letter-spacing: 0.1em; text-transform: uppercase; padding: 0.15rem 0.4rem; border: 1px solid var(--line-2); color: var(--tx-3); }
.rm-chip.next { color: var(--lane); border-color: var(--lane); }
.rm-card.covered .rm-chip { display: none; }

/* the read checkbox: the visual box is the span; the input is only the state */
.rm-check { flex: none; display: inline-flex; align-items: center; gap: 0.4rem; cursor: pointer; font: 400 9px/1.4 var(--mono); letter-spacing: 0.1em; text-transform: uppercase; color: var(--tx-3); user-select: none; }
.rm-check input { position: absolute; opacity: 0; width: 0; height: 0; }
.rm-box { width: 13px; height: 13px; border: 1px solid var(--line-2); position: relative; transition: background 0.15s var(--ease), border-color 0.15s var(--ease); }
.rm-check:hover .rm-box { border-color: var(--tx-2); }
.rm-check input:checked + .rm-box { background: var(--sig); border-color: var(--sig); }
.rm-check input:checked + .rm-box::after { content: ''; position: absolute; left: 4px; top: 1px; width: 3px; height: 7px; border: solid var(--on-sig); border-width: 0 1.6px 1.6px 0; transform: rotate(45deg); }
.rm-check input:focus-visible + .rm-box { outline: 1px solid var(--sig); outline-offset: 2px; }

/* --- the confluence: the two lanes merge into the apex -------------------- */
.rm-confluence { display: block; width: 100%; max-width: 30rem; height: 54px; margin: 1.25rem auto 0.35rem; }
.rm-confluence .solid { stroke: var(--teal); stroke-width: 1.5; fill: none; }
.rm-confluence .dash  { stroke: var(--rose); stroke-width: 1.5; fill: none; stroke-dasharray: 4 4; }
.rm-confluence .arw   { stroke: var(--tx-2); stroke-width: 1.5; fill: none; }
.rm-conf-cap { text-align: center; font: 400 10px/1.6 var(--mono); letter-spacing: 0.08em; text-transform: uppercase; color: var(--tx-3); margin: 0 0 1.5rem; }
.rm-conf-cap b { color: var(--teal); font-weight: 400; }
.rm-conf-cap em { color: var(--rose); font-style: normal; }

/* --- the apex: applied AI, read as a ladder ------------------------------ */
.rm-apex { --lane: var(--ember); border: 1px solid var(--line-2); background: var(--panel); padding: 1.6rem 1.4rem 1.7rem; }
.rm-apex-h { font-family: var(--pixel); font-size: clamp(1rem, 1.9vw, 1.4rem); line-height: 1.15; text-transform: uppercase; color: var(--tx); margin: 0 0 0.4rem; display: flex; align-items: center; gap: 0.55rem; }
.rm-apex-h::before { content: ''; width: 0.72rem; height: 0.72rem; background: var(--lane); flex: none; }
.rm-apex-sub { color: var(--tx-2); font-size: 14px; line-height: 1.6; margin: 0 0 1.6rem; max-width: 62ch; }
.rm-level { font: 400 10px/1.6 var(--mono); letter-spacing: 0.09em; text-transform: uppercase; color: var(--lane); margin: 1.5rem 0 0.8rem; max-width: 66ch; }
.rm-level b { color: var(--tx); }
.rm-level span { color: var(--tx-3); }
.rm-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: 0.85rem; }

/* the north star at the foot of the apex */
.rm-goal { margin-top: 1.6rem; border: 1px solid var(--sig); padding: 1.1rem 1.15rem; text-align: center; }
.rm-goal-lbl { font: 400 9px/1.4 var(--mono); letter-spacing: 0.14em; text-transform: uppercase; color: var(--tx-3); }
.rm-goal-ttl { font-family: var(--pixel); text-transform: uppercase; color: var(--tx); font-size: clamp(0.9rem, 1.5vw, 1.15rem); line-height: 1.2; margin-top: 0.5rem; }

/* --- the rail: a way home, a legend, and jumps --------------------------- */
.rm-home {
  display: inline-block;
  font: 400 11px/1.5 var(--mono);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--tx-2);
  text-decoration: none;
  margin: 0 0 1.6rem;
}
.rm-home::before { content: '\2190\00a0'; color: var(--tx-3); }
.rm-home:hover { color: var(--tx); }

.rm-legend { display: flex; flex-direction: column; gap: 0.6rem; margin: 0.85rem 0 0; }
.rm-leg { display: flex; align-items: center; gap: 0.55rem; font: 400 10px/1.3 var(--mono); letter-spacing: 0.05em; text-transform: uppercase; color: var(--tx-2); }
.rm-leg .sw { width: 0.85rem; height: 0.85rem; flex: none; }
.rm-leg .sw.done { background: var(--tx-2); }
.rm-leg .sw.gap { border: 1px dashed var(--line-2); }
.rm-leg .ln { width: 0.95rem; flex: none; border-top: 1.5px solid var(--tx-2); }
.rm-leg .ln.dash { border-top-style: dashed; }
.rm-jump { display: flex; flex-direction: column; align-items: stretch; gap: 0.1rem; margin-top: 0.5rem; }
.rm-jump a {
  font: 400 11px/1.5 var(--mono);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--tx-2);
  text-decoration: none;
  padding: 0.3rem 0.4rem;
  margin: 0 -0.4rem;
  transition: background 0.15s var(--ease), color 0.15s var(--ease);
}
.rm-jump a::before { content: '/'; color: var(--tx-3); margin-right: 0.3em; }
.rm-jump a:hover { background: var(--panel); color: var(--tx); }

@media (max-width: 60rem) {
  .rm-board { grid-template-columns: minmax(0, 1fr); gap: 1.25rem; }
  /* The rail is a control row here; the jumps aren't worth the height on a
     page this short, so the legend and the theme control carry it alone. */
  .rm-jump-sec { display: none; }
}
