/* ============================================================================
   iol-shell.css — the front page's calibrated look, distilled for every page.
   Load LAST (after the page's own styles). Pairs with iol-shell.js.
   Ground · type scale · figures · the badge · board-edge footer · back-to-top.
   ============================================================================ */

/* ---- 1 · GROUND: true black + icon lattice + PCB traces (from Template 01) */
body{
  background-color:#000 !important;
  background-image:
    url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='288' height='288' viewBox='0 0 288 288'%3E%3Cg fill='none' stroke='%23ffffff' opacity='.105' stroke-width='1.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cg transform='translate(28,30)'%3E%3Crect x='7' y='7' width='10' height='10' rx='2'/%3E%3Cpath d='M10 4v3M14 4v3M10 17v3M14 17v3M4 10h3M4 14h3M17 10h3M17 14h3'/%3E%3C/g%3E%3Cg transform='translate(160,58)'%3E%3Cpath d='M9 3h6M10 3v5.5L4.5 18a2 2 0 0 0 1.8 3h11.4a2 2 0 0 0 1.8-3L14 8.5V3'/%3E%3Cpath d='M7 15h10'/%3E%3C/g%3E%3Cg transform='translate(70,150)'%3E%3Cpath d='M8 7h5.5a2.5 2.5 0 0 1 0 5H8zM8 12h6a2.5 2.5 0 0 1 0 5H8zM8 7v10'/%3E%3Cpath d='M10.5 5v2M12.6 5v2M10.5 17v2M12.6 17v2'/%3E%3C/g%3E%3Cg transform='translate(210,178)'%3E%3Crect x='2' y='7' width='20' height='11' rx='4'/%3E%3Cline x1='7' y1='11' x2='7' y2='14'/%3E%3Cline x1='5.5' y1='12.5' x2='8.5' y2='12.5'/%3E%3Ccircle cx='16' cy='11.5' r='1'/%3E%3Ccircle cx='18.5' cy='13.5' r='1'/%3E%3C/g%3E%3Cg transform='translate(24,232)'%3E%3Crect x='3.5' y='3.5' width='17' height='17' rx='4'/%3E%3Ccircle cx='8' cy='8' r='1.1'/%3E%3Ccircle cx='16' cy='8' r='1.1'/%3E%3Ccircle cx='12' cy='12' r='1.1'/%3E%3Ccircle cx='8' cy='16' r='1.1'/%3E%3Ccircle cx='16' cy='16' r='1.1'/%3E%3C/g%3E%3Cg transform='translate(150,258) scale(.9)'%3E%3Cpath d='M13 2 5 13h5l-1 9 8-11h-5z'/%3E%3C/g%3E%3Cg transform='translate(104,96)'%3E%3Cpath d='M3 21h13M6 21v-6'/%3E%3Ccircle cx='6' cy='13' r='1.8'/%3E%3Cpath d='M7.3 11.8 13 6.5'/%3E%3Ccircle cx='14' cy='5.8' r='1.5'/%3E%3Cpath d='M15.2 6.8 18.5 10'/%3E%3Cpath d='M18 11.5l3-1.2M18.6 12.8l2.6 1.8'/%3E%3C/g%3E%3Cg transform='translate(238,22)'%3E%3Ccircle cx='12' cy='3' r='1'/%3E%3Cpath d='M12 4v2'/%3E%3Crect x='6' y='6' width='12' height='9' rx='2'/%3E%3Ccircle cx='9.5' cy='10.5' r='1'/%3E%3Ccircle cx='14.5' cy='10.5' r='1'/%3E%3Cpath d='M4 9v4M20 9v4'/%3E%3Crect x='8' y='17' width='8' height='4' rx='1.5'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E"),
    url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='384' height='384' viewBox='0 0 384 384'%3E%3Cg fill='none' stroke='%23ffffff' opacity='.085' stroke-width='1.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='40' cy='60' r='2.6'/%3E%3Cpath d='M40 60H120L150 90V144'/%3E%3Ccircle cx='150' cy='150' r='2.6'/%3E%3Ccircle cx='300' cy='50' r='2.6'/%3E%3Cpath d='M300 50V110L260 150H196'/%3E%3Ccircle cx='190' cy='150' r='2.6'/%3E%3Ccircle cx='60' cy='300' r='2.6'/%3E%3Cpath d='M60 300H150L210 240H324'/%3E%3Ccircle cx='330' cy='240' r='2.6'/%3E%3Ccircle cx='90' cy='190' r='2.6'/%3E%3Cpath d='M90 190H128'/%3E%3Ccircle cx='133' cy='190' r='1.5'/%3E%3Ccircle cx='250' cy='96' r='1.5'/%3E%3Ccircle cx='104' cy='247' r='1.5'/%3E%3Crect x='230' y='300' width='26' height='18' rx='2'/%3E%3Cpath d='M226 304h4M226 309h4M226 314h4M256 304h4M256 309h4M256 314h4'/%3E%3C/g%3E%3Cg fill='none' stroke='%23ff8a3d' opacity='.095' stroke-width='1.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='344' cy='120' r='2.6'/%3E%3Cpath d='M344 120V170L310 204H262'/%3E%3Ccircle cx='256' cy='204' r='2.6'/%3E%3C/g%3E%3C/svg%3E") !important;
  background-size:288px 288px, 384px 384px !important;
  transform:none !important;   /* guard: a transformed body hijacks position:fixed */
}

/* ---- 2 · TYPE SCALE: the front page's small-type law ---------------------- */
h1, .hero h1{font-size:clamp(22px, 2.2vw, 30px) !important; line-height:1.15 !important;}
h2{font-size:clamp(16px, 1.6vw, 19px) !important;}
h3{font-size:14.5px !important;}
.prose{font-size:15.5px !important; line-height:1.78 !important;}
.dek, .lede{font-size:14px !important;}

/* ---- 3 · FIGURES: floating spec-sheet cards (DESIGN.md §3.1, Technical
        Brand Geometry) — hairline frame, registration mark, auto-numbered
        FIG chip. Every figure stands out like an instrument. ---------------- */
body{counter-reset:iolfig;}
.fig{
  max-width:680px !important; margin:26px auto !important;
  position:relative;
  background:#0b0d12 !important;              /* solid instrument surface */
  border:1px solid rgba(255,255,255,.2) !important;
  border-radius:12px;
  padding:14px 18px 26px;
  box-shadow:0 24px 60px -44px rgba(0,0,0,.9), inset 0 1px 0 rgba(255,255,255,.05);
  counter-increment:iolfig;
  /* NO counter-zoom. It made figures render at true 1:1 while the prose was
     scaled by the fit law, so diagram labels ended up ~40% LARGER than body
     text and every drawing looked oversized. Figures now scale with the page,
     which keeps label size in proportion to the prose at every window width.
     Declared explicitly (not just deleted) so any cached older stylesheet
     cannot keep applying the old counter-zoom. */
  zoom:1 !important;
  max-width:var(--iolFigMax,771px) !important;
  margin-left:auto; margin-right:auto;
}
/* the counter-zoom is for DIAGRAMS — reading text inside a figure must stay
   at reading size, not inherit the magnification */
.fig figcaption, .fig .figcap, .fig .cap, .fig .fig-caption{
  font-size:13.5px !important; line-height:1.65 !important;}
.fig figcaption b, .fig figcaption strong{font-size:inherit !important;}
/* the instrument header: what this figure IS + how to use it
   SIZE. 9px was the smallest type on the whole site once the fit law had been
   applied to it. Measured on algorithms/chapter-08: at innerWidth 1050 and 980 the
   body zoom sits on its 0.6 floor, so 9px * 0.6 = 5.40px on the eye, on all six
   figures at once — and .ft in this slot is the figure's AUTHORED HEADLINE, the
   sentence carrying its teaching claim, not decoration. The whole band from 901 to
   1422 renders it under 7px (at 1280, zoom 0.6996, it measured 6.30px). 11.5px puts
   the floor case at 6.90px and 1280 at 8.05px. It does not fully clear 7px at the
   0.6 floor — nothing under 11.67px can — because the remaining gap belongs to the
   fit law's floor, not to this type. Left as a lead rather than changed here:
   iolFit() clamps to 0.6 above innerWidth 900 and clears zoom entirely at 900 and
   below, so 900 -> 901 drops the page from zoom 1 to 0.6 in one pixel. */
.fig .fig-head{
  display:flex; align-items:center; justify-content:space-between; gap:12px;
  margin:0 0 12px; padding-bottom:9px;
  border-bottom:1px dashed rgba(255,255,255,.1);
  font-family:ui-monospace,Consolas,monospace; font-size:11.5px;
  letter-spacing:.16em; text-transform:uppercase;}
/* TWO KINDS OF TEXT LAND IN THIS SLOT, and they need opposite treatment.
   iol-shell.js fills .ft from data-title if the figure has one, and otherwise
   falls back to an svg's aria-label. Those are not the same object:
     · a data-title is an AUTHORED HEADLINE carrying the figure's teaching claim
       (algorithms chapters: 7 figures, longest 62 chars). Truncating it to an
       ellipsis destroys the sentence — measured on algorithms/chapter-07, all
       seven titles lost 158-298px at 375 (28-43% left visible), all seven lost
       25-165px at 520, and a7fig02 lost 12px at 980. So it WRAPS.
     · an aria-label is an ACCESSIBILITY DESCRIPTION written for a screen reader
       and never meant to be seen (probability + quant: 42 pages, no data-title,
       labels running 130-593 chars). Wrapping those is the regression this
       comment exists to prevent: measured on probability/chapter-14 at 375, the
       worst head became 557px tall / 38 lines and the page's 23 heads grew from
       690px total to 4,748px — about four extra screens of screen-reader prose
       above the drawings. So it stays CLAMPED, exactly as it shipped.
   The head is static markup injected once at load, so neither can shift anything
   under the reader's hands. */
.fig .fig-head .ft{
  color:#eaeef6; font-weight:700; min-width:0;
  white-space:normal; overflow-wrap:anywhere;}
.fig .fig-head.from-aria .ft{
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;}
.fig .fig-head .fh{
  flex:0 0 auto; display:inline-flex; align-items:center; gap:6px;
  color:#8ef0a9; border:1px solid rgba(142,240,169,.35);
  border-radius:999px; padding:2px 9px;}
.fig .fig-head .fh i{
  width:5px; height:5px; border-radius:50%;
  background:#8ef0a9; box-shadow:0 0 5px #8ef0a9;
  animation:figHint 1.8s ease-in-out infinite;}
.fig .fig-head .fh.static{color:rgba(255,255,255,.45); border-color:rgba(255,255,255,.16);}
.fig .fig-head .fh.static i{background:rgba(255,255,255,.4); box-shadow:none; animation:none;}
@keyframes figHint{0%,100%{opacity:1}50%{opacity:.35}}
/* fifa-style start cue: every interactive figure points at its first move
   (bobbing hand + pulsing gold chip; wording comes from data-start) */
.fig .iol-start{
  display:inline-flex; align-items:center; gap:8px;
  margin:0 0 12px;
  /* 10.5px measured 6.30px on the eye at every width from 901 to 1422 (zoom floor
     0.6 through 0.7); 12px puts the floor case at 7.20px. This chip is the sentence
     that tells the reader what to commit, so it is the last thing that should be
     the smallest type on the figure. */
  font-family:ui-monospace,Consolas,monospace; font-size:12px; font-weight:700;
  letter-spacing:.09em; text-transform:uppercase;
  color:#ffb454; background:rgba(255,180,84,.07);
  border:1px solid rgba(255,180,84,.45); border-radius:999px;
  padding:4px 13px 4px 9px;
  animation:iolChipPulse 2.2s ease-in-out infinite;}
.fig .iol-start .hand{
  font-size:16px; line-height:1; display:inline-block;
  animation:iolHandBob 1s ease-in-out infinite;
  filter:drop-shadow(0 2px 5px rgba(0,0,0,.7));}
@keyframes iolHandBob{0%,100%{transform:translateY(-2px)}50%{transform:translateY(3px)}}
@keyframes iolChipPulse{
  0%,100%{border-color:rgba(255,180,84,.45); box-shadow:0 0 0 0 rgba(255,180,84,0);}
  50%{border-color:rgba(255,180,84,.95); box-shadow:0 0 12px -3px rgba(255,180,84,.55);}}
.fig::before{
  content:""; position:absolute; top:5px; left:5px; width:10px; height:10px;
  border-top:1px solid rgba(255,255,255,.5); border-left:1px solid rgba(255,255,255,.5);
  pointer-events:none;}
.fig::after{
  content:"fig " counter(iolfig, decimal-leading-zero);
  position:absolute; right:10px; bottom:7px;
  font-family:ui-monospace,Consolas,monospace; font-size:8.5px;
  letter-spacing:.22em; text-transform:uppercase; color:rgba(255,180,84,.6);
  pointer-events:none;}
.fig:hover{border-color:rgba(255,180,84,.4) !important;}
.fig svg, .fig img, .fig canvas{max-width:100% !important; height:auto !important;}

/* ---- 3.5 · cross-page signatures (DESIGN.md §4) ---------------------------- */
::selection{background:rgba(255,180,84,.32); color:#fff;}
:focus-visible{outline:1px solid rgba(255,180,84,.65); outline-offset:2px; border-radius:2px;}
*{scrollbar-width:thin; scrollbar-color:rgba(255,255,255,.16) transparent;}
::-webkit-scrollbar{width:10px; height:10px;}
::-webkit-scrollbar-thumb{background:rgba(255,255,255,.12); border-radius:8px;}
::-webkit-scrollbar-thumb:hover{background:rgba(255,138,61,.5);}
input, textarea{caret-color:#ff8a3d;}
.eyebrow, .kicker, .tag{letter-spacing:.2em !important; text-transform:uppercase !important;}
.prose a{
  background:linear-gradient(currentColor,currentColor) no-repeat 0 100%/0 1px;
  transition:background-size .22s ease; text-decoration:none;}
.prose a:hover{background-size:100% 1px;}

/* ---- 4 · THE BADGE: .lockup becomes the iolinked badge -------------------- */
.lockup{
  border:1px solid rgba(255,255,255,.22) !important;
  background:#000 !important;
  border-radius:9px !important;
  height:44px !important; box-sizing:border-box !important;
  padding:0 15px !important; gap:10px !important;
  position:relative !important;
  align-items:center !important;
  text-decoration:none !important;
  transition:border-color .18s ease, box-shadow .18s ease !important;
}
.lockup:hover{
  transform:none !important;
  border-color:rgba(255,77,87,.45) !important;
  box-shadow:0 0 16px -6px rgba(255,77,87,.65) !important;
}
.lockup::before{
  content:""; position:absolute; top:3px; left:3px; width:8px; height:8px;
  border-top:1px solid rgba(225,6,0,.75); border-left:1px solid rgba(225,6,0,.75);
  pointer-events:none;
}
.lockup::after{
  content:""; position:absolute; left:49px; right:12px; bottom:6px; height:7px;
  pointer-events:none;
  background:
    radial-gradient(circle 3px at 8px 50%, #ffb454 98%, transparent),
    radial-gradient(circle 2.7px at 8px 50%, #8ef0a9 98%, transparent),
    radial-gradient(circle 1.5px at 1.5px 50%, rgba(255,255,255,.35) 98%, transparent),
    radial-gradient(circle 1.5px at calc(100% - 1.5px) 50%, rgba(255,255,255,.35) 98%, transparent),
    linear-gradient(rgba(255,255,255,.16), rgba(255,255,255,.16)) 0 50%/100% 1px no-repeat;
  background-size:16px 7px, 16px 7px, 3px 3px, 3px 3px;
  background-position:-16px 50%, calc(100% + 16px) 50%, 0 50%, 100% 50%;
  background-repeat:no-repeat;
  animation:ioHandshake 7s linear .8s infinite;
}
@keyframes ioHandshake{
  0%   {background-position:-16px 50%,            calc(100% + 16px) 50%, 0 50%, 100% 50%;}
  17%  {background-position:-16px 50%,            calc(100% + 16px) 50%, 0 50%, 100% 50%;}
  33%  {background-position:calc(100% + 16px) 50%, calc(100% + 16px) 50%, 0 50%, 100% 50%;}
  38%  {background-position:calc(100% + 16px) 50%, calc(100% + 16px) 50%, 0 50%, 100% 50%;}
  54%  {background-position:calc(100% + 16px) 50%, -16px 50%,             0 50%, 100% 50%;}
  99.9%{background-position:calc(100% + 16px) 50%, -16px 50%,             0 50%, 100% 50%;}
  100% {background-position:-16px 50%,            calc(100% + 16px) 50%, 0 50%, 100% 50%;}
}
.lockup svg{
  width:22px !important; height:22px !important;
  padding:4px; box-sizing:border-box;
  background:#000; border:1px solid rgba(255,255,255,.16); border-radius:5px;
  color:#fff !important; filter:none !important;
}
.lockup svg circle{
  transform-box:fill-box; transform-origin:center;
  animation:chadCycle 7s cubic-bezier(.3,.85,.35,1.18) .8s infinite;
}
@keyframes chadCycle{
  0%{transform:translate(-7.4px,-7.4px); opacity:.95;}
  5%{transform:translate(-7.4px,-7.4px);}
  11%{transform:translate(.9px,.9px);}
  13.5%{transform:translate(-.2px,-.2px);}
  15.5%{transform:translate(0,0); opacity:1;}
  92%{transform:translate(0,0); opacity:1;}
  100%{transform:translate(-7.4px,-7.4px); opacity:.95;}
}
.lockup .wm{
  font-size:15.5px !important; font-weight:700 !important; letter-spacing:0 !important;
  font-family:"Cascadia Code","JetBrains Mono",ui-monospace,Consolas,monospace !important;
}
.lockup .cr{
  width:5.5px !important; height:12px !important; margin-left:1px !important;
  background:#ff4d57 !important; border-radius:0 !important;
  box-shadow:0 0 6px rgba(255,77,87,.55);
}

/* ---- 3.8 · TERM COLOURS: the front page's exact palette, everywhere -------
        (kit classes b0/b1/b2/bg realigned; every coloured word matches home) */
b.b0, .b0{color:#7fc8ff !important;}   /* cyan — maths / signals */
b.b1, .b1{color:#ffb454 !important;}   /* gold — key concepts    */
b.b2, .b2{color:#ff5470 !important;}   /* red  — warnings / code */
b.bg, .bg{color:#6be58b !important;}   /* green — outcomes / life */

/* ---- 4.5 · CHAPTER RAIL, compacted: one tight, aligned nav column --------- */
.rail{gap:18px !important; padding-top:14px !important;
      display:flex !important; flex-direction:column !important;}
.rail .lockup{margin:0 auto 6px !important;}
/* air under the logo — the whole navigation stack drops down a step so the
   lockup reads as a masthead, not the first row of the nav (owner 2026-07-26) */
.rail .navbox{margin-top:26px !important;}
.rail .tocbox{margin-top:2px !important;}
/* the up-next card gets clear air on BOTH sides so it reads as its own thing */
.rail .nextcard{margin-top:26px !important; margin-bottom:10px !important;}
/* ORDER: nav · up-next · [section meter] · section list — the meter sits
   directly above the list it counts (owner's call) */
.rail .lockup{order:1;}
.rail .navbox{order:2;}
.rail .nextcard{order:3;}
.rail .secmeter{order:4; margin-bottom:0 !important;}
.rail #iolSecRail{order:5;}
.rail .kbdhint{order:6;}

/* ---- 4.6 · NAVIGATOR polish: rail boxes join the badge family -------------- */
.navbox, .tocbox, .nextcard{
  background:#0b0d12 !important;
  border:1px solid rgba(255,255,255,.2) !important;
  border-radius:10px !important;
  box-shadow:inset 0 1px 0 rgba(255,255,255,.05) !important;}
/* the navigator wears the front-page gold frame: gold outside, a hair of
   white just inside it, and white-bordered buttons within */
.navbox{
  border:1px solid rgba(255,180,84,.5) !important;
  box-shadow:inset 0 0 0 1px rgba(255,255,255,.07),
             inset 0 1px 0 rgba(255,255,255,.05),
             0 0 14px -8px rgba(255,180,84,.45) !important;}
.navbox a{
  border:1px solid rgba(255,255,255,.16) !important;
  border-radius:8px !important;
  background:rgba(255,255,255,.02) !important;
  transition:border-color .15s ease, color .15s ease, transform .15s ease !important;}
.navbox a:hover{
  border-color:rgba(255,180,84,.55) !important;
  transform:translateY(-1px);}
.nextcard{transition:border-color .15s ease, box-shadow .15s ease !important;}
.nextcard:hover{
  border-color:rgba(255,180,84,.5) !important;
  box-shadow:0 0 14px -6px rgba(255,180,84,.5) !important;}
.poschip{
  background:#000 !important;
  border:1px solid rgba(255,255,255,.22) !important;
  border-radius:9px !important;
  padding:9px 12px !important;
  transition:border-color .15s ease, box-shadow .15s ease, transform .15s ease;}
.poschip:hover{
  border-color:rgba(255,180,84,.55) !important;
  box-shadow:0 0 13px -4px rgba(255,180,84,.55);
  transform:translateY(-1px);}
.poschip .pc-map{
  color:#ffb454 !important; font-weight:700 !important; letter-spacing:.13em;}
.poschip .pc-dots i{
  width:5px !important; height:5px !important; border-radius:50%;
  background:rgba(255,255,255,.18) !important;}
.poschip .pc-dots i.here{
  background:#ffb454 !important;
  box-shadow:0 0 6px rgba(255,180,84,.9);
  animation:figHint 1.8s ease-in-out infinite;}

/* the chapter-name slab that sits under the map chip */
#iolChapCard{
  position:fixed; z-index:59;
  background:#000; border:1px solid rgba(255,255,255,.22);
  border-radius:9px; padding:7px 11px 8px;
  box-shadow:inset 0 1px 0 rgba(255,255,255,.05);
  display:flex; flex-direction:column; gap:3px;
  pointer-events:none;}
#iolChapCard .cc-k{
  font-family:ui-monospace,Consolas,monospace; font-size:8px;
  letter-spacing:.18em; text-transform:uppercase; color:rgba(255,255,255,.42);}
#iolChapCard .cc-t{
  font-family:ui-monospace,Consolas,monospace; font-size:11.5px; font-weight:700;
  letter-spacing:.02em; color:#ffb454; line-height:1.3;}
@media (max-width:1000px){#iolChapCard{display:none !important;}}

/* you-are-here: the chapter you came from glows on the map */
a.iol-here{
  position:relative;
  border-color:rgba(255,180,84,.65) !important;
  box-shadow:0 0 0 1px rgba(255,180,84,.35), 0 0 16px -6px rgba(255,180,84,.6) !important;}
.iol-here-tag{
  position:absolute; top:-9px; right:10px;
  font-family:ui-monospace,Consolas,monospace; font-size:8.5px; font-weight:700;
  letter-spacing:.14em; text-transform:uppercase;
  color:#ffb454; background:#000;
  border:1px solid rgba(255,180,84,.55); border-radius:999px;
  padding:2px 8px;
  animation:figHint 1.8s ease-in-out infinite;}

/* hub/map heroes were designed for display type — the chapter h1 cap
   squashed them; let ONLY heroes breathe (still no billboard sizes) */
.hero-main h1{
  font-size:clamp(32px, 3.2vw, 42px) !important;
  line-height:1.14 !important; letter-spacing:-.01em;}

/* ---- 4.60 · PRE-PAINT FIT (kills the big→small flash on every page load) ---- */
/* The fit law used to arrive only when the deferred script ran, so each new
   page painted huge, then snapped small. These buckets approximate
   z = clamp(.60, 68vw/1244, 1.35) in pure CSS — applied before first paint —
   and iolFit() then nudges to the exact value (a ≤4% change nobody sees).
   Figure counter-zoom vars get the matching bucket so instruments never pop. */
@media (min-width:901px)  and (max-width:1150px){body{zoom:.60;} body{--iolFigZoom:1.667;}}
@media (min-width:1151px) and (max-width:1250px){body{zoom:.655;} body{--iolFigZoom:1.527;}}
@media (min-width:1251px) and (max-width:1350px){body{zoom:.71;} body{--iolFigZoom:1.408;}}
@media (min-width:1351px) and (max-width:1450px){body{zoom:.765;} body{--iolFigZoom:1.307;}}
@media (min-width:1451px) and (max-width:1550px){body{zoom:.82;} body{--iolFigZoom:1.220;}}
@media (min-width:1551px) and (max-width:1650px){body{zoom:.875;} body{--iolFigZoom:1.143;}}
@media (min-width:1651px) and (max-width:1750px){body{zoom:.93;} body{--iolFigZoom:1.075;}}
@media (min-width:1751px) and (max-width:1850px){body{zoom:.985;} body{--iolFigZoom:1.015;}}
@media (min-width:1851px) and (max-width:2000px){body{zoom:1.05;} body{--iolFigZoom:.952;}}
@media (min-width:2001px) and (max-width:2200px){body{zoom:1.148;} body{--iolFigZoom:.871;}}
@media (min-width:2201px) and (max-width:2470px){body{zoom:1.277;} body{--iolFigZoom:.783;}}
@media (min-width:2471px){body{zoom:1.35;} body{--iolFigZoom:.741;}}

/* ---- 4.61 · STANDALONE WIDGETS (trace3 banners, CodeRun) --------------------- */
/* hand-crafted debuggers live OUTSIDE figures; iolFit() gives each an inline
   zoom that lifts it toward true size, borrowing the free right margin.
   Panels get real height and widgets get air between them. */
.t3banner, .crgallery{margin-top:38px !important; margin-bottom:38px !important;}
.t3banner .nbox{min-height:104px !important;}
.t3banner .nbox, .crgallery .cr{line-height:1.55;}

/* ---- 4.62 · TRACE DIAGRAMS AT READABLE SIZE --------------------------------- */
/* the step-debugger grids used fixed heights, so their logical-diagram SVGs
   (call stacks, memory maps) shrank to fit and became unreadable. Panels now
   grow with content and the SVG fills the panel WIDTH at its natural aspect. */
/* applies to EVERY trace3 — inside figures and as standalone banners.
   NOTE the `.t3 .t3grid` selector: the pages set the height with an
   !important rule at that specificity, so a bare `.t3grid` loses. */
.t3grid,
.t3 .t3grid,
.t3-heap .t3grid{
  /* The widget = header (~180) + this grid + controls. Sizing the grid off
     the viewport keeps the WHOLE widget on screen, so you can read the
     panels and press Next without scrolling. Bigger than the old 665 by a
     wide margin, but never so big that the controls fall off the screen. */
  height:min(calc(95vh - 148px), 820px) !important;
  max-height:none !important;
  /* the drawing row gets the lion's share; the code/memory row keeps just
     what it needs (owner: "the top drawing session gets the full space") */
  grid-template-rows:1.42fr 1fr !important;}
.t3viz{min-height:0 !important;}
.t3viz svg{
  width:100% !important;
  height:auto !important;
  max-height:70vh !important;}
/* NOTE: do NOT force min-heights on .nbox/.alu — measured, that INFLATES the
   right column past its grid row and creates the very clipping it was meant
   to cure (124px of hidden text). The extra grid height above is enough:
   the panels then size naturally (~85-90px) and nothing is cut off.
   Scroll stays as a backstop for unusually long beats. */
.t3grid .nbox, .t3grid .alu,
.t3banner .nbox, .t3banner .alu{overflow-y:auto !important;}
/* the memory/registers column keeps pace so the two sides stay level */
.t3grid .mem, .t3grid .t3mem, .t3grid .memcol{min-height:210px !important;}

/* ---- 4.65 · REMOVALS + STABILITY (owner rulings 2026-07-26) ----------------- */
/* the growing read-snake reads ugly — gone everywhere */
.pysnake, .pysnake-lane, #pysnake, #pysnake-lane{display:none !important;}
/* scrollbar appearing/vanishing mid-interaction re-fired the fit law and made
   pages pump big→small; a permanent gutter keeps innerWidth rock steady */
html{overflow-y:scroll;}
/* the old IO mark that sat between the chapter lede and the first section.
   The brand is already in the rail lockup two inches away, so repeating it
   mid-column added a second logo and a hard stop the reader had to step over.
   Hidden, not deleted: it is decorative (aria-hidden) and lives once in each
   of the 63 chapter files, so one rule retires it everywhere and one deleted
   rule brings it back. */
.divider{display:none !important;}

/* ---- 4.68 · SECTION DOT RAIL (replaces the cramped on-this-page box) -------- */
.tocbox{display:none !important;}
/* LEFT-EDGE placement: the rail lives in the vacant left strip, pads first,
   labels sliding out to the right on hover (owner's call, 2026-07-26) */
#iolSecRail{
  position:fixed; left:18px; right:auto; top:50%; transform:translateY(-50%);
  z-index:7900; display:flex; flex-direction:column; gap:16px; align-items:flex-start;}
/* the hit area is the PAD ONLY — the label is absolutely positioned so the
   rail can never swallow clicks meant for the navigator beside it */
#iolSecRail a{
  position:relative; display:flex; align-items:center;
  width:8px; height:8px;
  text-decoration:none; color:rgba(255,255,255,.42);
  font-family:ui-monospace,Consolas,monospace; font-size:9.5px;
  letter-spacing:.12em; text-transform:uppercase;}
/* the copper trace that ties the pads together, like a via run on a board */
#iolSecRail::before{
  content:""; position:absolute; left:3.5px; top:4px; bottom:4px; width:1px;
  background:linear-gradient(180deg,
    rgba(255,180,84,.10), rgba(255,180,84,.45), rgba(255,180,84,.10));
  pointer-events:none;}
#iolSecRail a i{                               /* the via pad */
  width:8px; height:8px; border-radius:50%; flex:0 0 auto;
  background:#0b0d12; border:1px solid rgba(255,180,84,.5);
  box-shadow:0 0 0 2px rgba(0,0,0,.85);
  transition:background .2s ease, border-color .2s ease, box-shadow .2s ease;}
/* NO floating label: on the left edge any pop-out lands on the navigator's
   up-next card. The pad's own lit state is the indicator; the section name
   is exposed as a native tooltip (title=), which the browser paints above
   everything and can never disturb the layout. */
#iolSecRail a .lbl{
  position:absolute !important; width:1px; height:1px;
  overflow:hidden; clip-path:inset(50%); white-space:nowrap;}
#iolSecRail a.on{color:#ffb454;}

/* ---- the rail docked in the navigator column: all six always readable ---- */
/* the meter + the section list, boxed as one card in the badge family */
#iolSecRail.in-rail{
  position:static; transform:none; left:auto; top:auto;
  width:100%; gap:0; margin-top:6px;
  align-items:stretch;
  background:#0b0d12; border:1px solid rgba(255,255,255,.2);
  border-radius:10px; padding:10px 12px 11px;
  box-shadow:inset 0 1px 0 rgba(255,255,255,.05);}
#iolSecRail.in-rail::before{display:none;}   /* no copper line inside the card */
/* The navigator must never CLIP its own content. Hiding the scrollbar made
   the section list unreachable on shorter windows, so instead the rail
   sizes to its content and simply scrolls with the page when it is tall. */
.rail{
  max-height:none !important;
  overflow:visible !important;
  scrollbar-width:none !important;}
.rail::-webkit-scrollbar{width:0 !important; height:0 !important; display:none !important;}
/* compact the section list so the whole rail fits a laptop screen */
#iolSecRail.in-rail a{padding-top:4px !important; padding-bottom:4px !important;}
/* the meter becomes the card's header strip */
#iolSecRail.in-rail .secmeter{
  order:-1 !important;                 /* header, not footer */
  border:0 !important; background:none !important;
  padding:0 0 8px !important; margin:0 0 6px !important;
  border-bottom:1px dashed rgba(255,255,255,.14) !important;
  display:flex !important; justify-content:space-between !important;}
#iolSecRail.in-rail a{
  width:auto; height:auto; gap:10px;
  flex-direction:row !important;      /* pad on the LEFT, name to its right */
  justify-content:flex-start;
  padding:5px 2px 5px 0; font-size:9px; letter-spacing:.1em;
  color:rgba(255,255,255,.46);}
#iolSecRail.in-rail a i{margin-left:-15px;}
#iolSecRail.in-rail a .lbl{               /* the name, permanently visible */
  position:static !important; width:auto; height:auto;
  clip-path:none; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
  background:none; border:0; padding:0; box-shadow:none; opacity:1;
  transform:none; max-width:100%;}
#iolSecRail.in-rail a:hover{color:#eaeef6;}
#iolSecRail.in-rail a.on{color:#ffb454; font-weight:700;}   /* you are here */
/* a slightly bigger, brighter pad ring on hover so the rail still feels alive */
#iolSecRail a:hover i{border-color:#ffb454; box-shadow:0 0 0 2px rgba(0,0,0,.85), 0 0 8px rgba(255,180,84,.6);}
#iolSecRail a.on i{
  background:#ffb454; border-color:#ffb454;
  box-shadow:0 0 0 2px rgba(0,0,0,.85), 0 0 9px rgba(255,180,84,.75);}
/* the active pad is shown by its GOLD LIT STATE, never by a persistent label:
   on the left edge a always-on label printed straight over the navigator's
   up-next card and hid the chapter name. Labels are hover-only now. */
#iolSecRail a.on .lbl{border-color:rgba(255,180,84,.4);}
#iolSecRail a:hover .lbl, #iolSecRail a:focus-visible .lbl{opacity:1; transform:none;}
@media (max-width:1000px){#iolSecRail{display:none;}}
/* the page shifts left into the reclaimed space, leaving a clear lane for the
   pad column; the navigator now sits hard against that lane instead of
   floating in the middle of an empty margin */
/* BALANCED THREE-BEAT LAYOUT (owner's call): [gap] navigator [same gap]
   reading column, with the reading column dead-centre in the viewport.
   The fit law pins the local viewport to ~1829px for any normal window,
   so these fixed values hold: 40 + 260 + 40 + 1149 centres at 914.5,
   and 1829/2 = 914.7. */
@media (min-width:1001px){
  .layout{
    /* PRE-PAINT DEFAULT for the left-wall shift: inside the fit law's linear
       regime (1097-2470px screens) the local viewport is a constant ~1829px,
       so the solved shift is a constant 148px — declare it in CSS so even a
       paint that happens BEFORE the script runs starts in the right place.
       Outside that regime iol-shell.js corrects by a few px at most (inline
       style wins over this declaration), which is imperceptible. */
    --iolShiftX:148px;
    /* auto, NEVER 0: the page sizes .layout with width:94vw and relies on
       auto margins to centre it. Forcing 0 pinned the whole page to the
       left edge — that was the "everything to the side" bug. */
    margin-left:auto !important; margin-right:auto !important;
    /* NOT vw: the body is zoomed by the fit law, and vw resolves against the
       UNZOOMED viewport, so the page's width:94vw was leaving ~180px of the
       real width unused. width:auto makes the layout fill its container
       exactly — correct at every window size, by construction. */
    width:auto !important;
    /* NO PAGE-LEVEL CAP: the computers pages declared max-width:1240px on
       .layout, which strangled the shell's wide grid — the 696px of side
       columns came out of the READING column (517px of prose in a 1090px
       track) and pushed the rail to ~18%, which then fought the 148px
       pre-paint shift and produced the right-then-snap on every page change.
       Uncapping makes every course page geometrically identical to the
       python exemplar: same columns, same 10% wall, same no-op shift. */
    max-width:none !important;
    padding-inline:26px !important;
    column-gap:0 !important;
    /* equal 1fr on BOTH sides => the reading column is centred by
       construction at ANY window width. The previous fixed 40px margin
       only centred it at one specific viewport and drifted everywhere else. */
    /* Side columns are a FIXED 348px (260 navigator + 88 gap) and the grid
       centres itself. Equal sides => the reading column is always centred;
       fixed sides => the navigator-to-text distance is IDENTICAL at every
       screen width instead of drifting wider on big monitors. */
    /* LEFT-WALL BREATHING ROOM — the whole page slides right as one piece.
       --iolShiftX is added to the left column and subtracted from the (empty)
       right one, so the COLUMN SUM IS UNCHANGED. That matters: the middle
       column is already squeezed below its 1090px ideal at common widths, so
       any rule that changed the sum would steal width from the prose. Because
       the sum holds, the reading column keeps its exact width and the rail
       keeps its exact offset — the whole layout is translated, not reflowed.
       Every internal relationship (rail<->prose gap, rail width, prose width)
       is bit-for-bit what it was. iol-shell.js §0 measures and sets the value. */
    grid-template-columns:
      calc(348px + var(--iolShiftX,0px))
      minmax(0,1090px)
      calc(348px - var(--iolShiftX,0px)) !important;
    justify-content:center !important;}
  /* the navigator hugs the right edge of the left space, a fixed 40px off
     the reading column, so the same rhythm holds on every monitor */
  /* a page that caps its own <main> (computers: 940px) must sit CENTRED in
     the reading track, not hug its left edge — python mains are uncapped and
     fill the track, so this is a no-op for them */
  .layout > main{ width:100% !important; justify-self:center !important; }
  .layout > .rail{
    justify-self:end !important;
    /* the 88px gap is PROTECTED: on a narrow window the navigator gives up
       width first, so the space between it and the text never collapses. */
    width:min(260px, calc(100% - 88px)) !important;
    margin-right:88px !important;}
}

/* ---- 4.7 · SUPER NAV — the rail as one instrument --------------------------- */
/* the TOC is a PCB net: one copper trace down the left, a via pad per section.
   pad states: hollow copper (unread) → gold, lit (the section under the eye) */
.tocbox{position:relative !important; padding-left:26px !important; overflow:hidden;}
.tocbox::before{                              /* the vertical copper run */
  content:""; position:absolute; left:13px; top:38px; bottom:14px;
  width:1px; background:linear-gradient(180deg,
    rgba(255,180,84,.55), rgba(255,180,84,.14) 60%, rgba(255,255,255,.08));}
.tocbox .grp{position:relative;}
.tocbox .grp::before{                         /* the via pad */
  content:""; position:absolute; left:-17px; top:13px;
  width:7px; height:7px; border-radius:50%;
  background:#0b0d12; border:1px solid rgba(255,180,84,.4);
  box-shadow:0 0 0 2px #0b0d12;
  transition:background .2s ease, border-color .2s ease, box-shadow .2s ease;}
.tocbox .grp.active::before{
  background:#ffb454; border-color:#ffb454;
  box-shadow:0 0 0 2px #0b0d12, 0 0 8px rgba(255,180,84,.7);}
.tocbox .grp .grph{transition:color .15s ease;}
.tocbox .grp:hover .grph{color:#ffb454 !important;}
.tocbox .grp.active .gn{color:#ffb454 !important;}
/* section meter reads like a panel instrument */
.secmeter{
  font-family:ui-monospace,Consolas,monospace !important;
  letter-spacing:.14em !important; text-transform:uppercase;
  border:1px solid rgba(255,255,255,.14) !important; border-radius:8px !important;
  background:#0b0d12 !important;}
.secmeter b, .secmeter #sm-pct{color:#ffb454 !important;}
/* next chapter: the arrow rides forward on hover */
.nextcard .ar{display:inline-block; transition:transform .18s ease;}
.nextcard:hover .ar{transform:translateX(5px);}
/* prev/next carry their key hints — MIRRORED: Prev wears its ← on the left,
   Next wears its → on the right, so the two buttons read as reflections. */
.navbox .seq a{position:relative;}
.navbox .seq a[data-kbd]:last-child::after,
.navbox .seq a[data-kbd]:first-child::before{
  content:attr(data-kbd);
  padding:1px 5px;
  font-size:8.5px; font-family:ui-monospace,Consolas,monospace;
  color:rgba(255,255,255,.45);
  border:1px solid rgba(255,255,255,.18); border-radius:4px;}
.navbox .seq a[data-kbd]:last-child::after{margin-left:7px;}
.navbox .seq a[data-kbd]:first-child::before{margin-right:7px;}
.navbox .seq a[data-kbd]:last-child:hover::after,
.navbox .seq a[data-kbd]:first-child:hover::before{color:#ffb454; border-color:rgba(255,180,84,.4);}
/* the "← prev · next → · scroll to explore" caption under the section card —
   retired (owner ruling 2026-07-26): the buttons now carry their own key
   badges, so the caption said nothing the rail wasn't already showing. */
.kbdhint{display:none !important;}
/* the read-progress trace: one crisp gold line across the top of the viewport */
#iolReadTrace{
  position:fixed; top:0; left:0; height:2px; width:0;
  background:linear-gradient(90deg, #b87333, #ffb454);
  box-shadow:0 0 6px -1px rgba(255,180,84,.8);
  z-index:9500; pointer-events:none;}

/* the autorouter gutter: copper traces chase the pointer in the vacant strip
   right of the reading column. Purely decorative, never interactive. */
#iolRouteFx{
  position:fixed; top:0; left:0; z-index:3; pointer-events:none; opacity:1;}
/* the star field sits behind the copper, in the same side gutters */
#iolStarFx{
  position:fixed; top:0; left:0; z-index:2; pointer-events:none; opacity:1;}
@media (max-width:1000px){#iolRouteFx,#iolStarFx{display:none !important;}}

/* ---- 5 · BOARD EDGE (injected by iol-shell.js) ----------------------------- */
.boardedge{margin:30px 0 0;}
.silk{
  display:flex; align-items:center; justify-content:center; gap:20px; flex-wrap:wrap;
  font-family:ui-monospace,Consolas,monospace; font-size:8px; letter-spacing:.18em;
  text-transform:uppercase; color:rgba(255,255,255,.38); margin-bottom:9px;}
.silk span{display:inline-flex; align-items:center; gap:5px;}
.silk i{width:5px; height:5px; border-radius:50%; border:1px solid rgba(255,255,255,.3);}
.silk .s-tx i{border-color:rgba(255,180,84,.7);}
.silk .s-rx i{border-color:rgba(142,240,169,.7);}
.silk .rev{color:rgba(255,180,84,.5);}
.edgefingers{
  height:11px; position:relative; overflow:hidden;
  border-top:1px solid rgba(255,255,255,.28);
  background:repeating-linear-gradient(90deg, rgba(255,180,84,.5) 0 22px, transparent 22px 36px);}
.edgefingers::after{
  content:""; position:absolute; inset:0;
  background:linear-gradient(90deg, transparent, rgba(255,255,255,.4), transparent);
  background-size:120px 100%; background-repeat:no-repeat;
  background-position:-140px 0;
  animation:contactWipe 9s linear 2.4s infinite;}
@keyframes contactWipe{
  0%{background-position:-140px 0;}
  11%{background-position:calc(100% + 140px) 0;}
  100%{background-position:calc(100% + 140px) 0;}
}

/* ---- 6 · BACK-TO-TOP ring (injected by iol-shell.js) ----------------------- */
#toTop{
  position:fixed; left:14px; bottom:16px; z-index:8000;
  width:46px; height:46px; display:grid; place-items:center;
  color:#9ba3b2; background:rgba(10,12,17,.72);
  -webkit-backdrop-filter:blur(6px); backdrop-filter:blur(6px);
  border:1px solid rgba(255,255,255,.14); border-radius:50%;
  padding:0; cursor:pointer; opacity:0; transform:translateY(8px);
  transition:opacity .22s ease, transform .22s ease, color .15s, border-color .15s, box-shadow .18s;
  pointer-events:none;}
#toTop.on{opacity:1; transform:none; pointer-events:auto;}
#toTop:hover{color:#ffb454; border-color:rgba(255,180,84,.45);
  box-shadow:0 0 14px -4px rgba(255,180,84,.5);}
#toTop svg{display:block;}
#ttProg{transition:stroke-dashoffset .15s linear;}

/* ---- 7 · guards ------------------------------------------------------------ */
@media (prefers-reduced-motion:reduce){
  .lockup::after, .lockup svg circle, .edgefingers::after, .fig .fig-head .fh i,
  .fig .iol-start, .fig .iol-start .hand{animation:none !important;}
}

/* ============================================================================
   5 · PRODUCTION POLISH — the 50-element pass (owner: "50 nice powerful
   elements... which hold as part of the template", "be very creative").
   Every rule keys off structural hooks all 82 chapters already share, so new
   tutorial writing inherits the whole set for free. JS partners: iol-shell.js
   §3. Numbering matches the JS side. §5.50 guards silence all motion under
   prefers-reduced-motion and hide fixed ambience under 1001px.
   ========================================================================== */

/* 1 · drop cap on every chapter lede (pages that hand-rolled one keep theirs) */
.layout main p.lede:not([dropcap])::first-letter{
  font-family:var(--font-serif,Georgia,serif); font-weight:600;
  font-size:3.05em; line-height:.83; float:left;
  padding:2px 10px 0 0; color:#ffb454;
  text-shadow:0 0 22px rgba(255,180,84,.35);}

/* 2 · headline balancing — no more one-word orphan lines */
.layout main h1, .layout main h2, .layout main h3{text-wrap:balance;}
.layout main p.prose, .layout main p.lede{text-wrap:pretty;}

/* 3 · section underline draws in as the section arrives (.iolin from JS §29) */
.layout main h2{position:relative;}
.layout main h2::after{
  content:""; position:absolute; left:0; bottom:-7px; height:1.5px; width:0;
  background:linear-gradient(90deg,#b87333,#ffb454 60%,transparent);
  transition:width .8s cubic-bezier(.22,1,.36,1);}
.layout main h2.iolin::after{width:170px;}

/* 4 · ghost section numerals — RETIRED (owner 2026-07-26: "not good").
   The data-gn attribute is still written by JS; nothing renders it. */

/* 5 · prose links: gradient underline that fills on hover */
.layout main p a:not(.iolext), .layout main li a:not(.iolext){
  text-decoration:none;
  background:linear-gradient(90deg,#ffb454,#b87333) left bottom / 0 1px no-repeat,
             linear-gradient(90deg,rgba(255,180,84,.25),rgba(184,115,51,.25)) left bottom / 100% 1px no-repeat;
  transition:background-size .35s ease;
  padding-bottom:1px;}
.layout main p a:not(.iolext):hover,
.layout main li a:not(.iolext):hover{background-size:100% 1px, 100% 1px;}

/* 6 · external links carry their own away-arrow (class from JS §6) */
a.iolext::after{content:"\2197"; font-size:.78em; margin-left:3px;
  color:rgba(255,180,84,.75); font-family:ui-monospace,monospace;}

/* 7 · emphasis reads warm, not just slanted */
.layout main p.prose em, .layout main p.prose i,
.layout main p.lede em, .layout main p.lede i{color:#e8c9a8;}

/* 8 · colour-coded key terms glow from beneath on hover */
.layout main b.b0, .layout main b.b1, .layout main b.b2, .layout main b.bg{
  border-radius:3px; transition:box-shadow .25s ease, filter .25s ease;}
.layout main b.b0:hover, .layout main b.b1:hover,
.layout main b.b2:hover, .layout main b.bg:hover{
  filter:brightness(1.22);
  box-shadow:inset 0 -8px 12px -10px currentColor;}

/* 9 · blockquote: copper rail + a Spectral quote mark set into the margin */
.layout main blockquote{
  position:relative; margin:26px 0; padding:14px 20px 14px 26px;
  border-left:2px solid #b87333; border-radius:0 var(--r-m,10px) var(--r-m,10px) 0;
  background:linear-gradient(90deg,rgba(255,180,84,.055),transparent 70%);}
.layout main blockquote::before{
  content:"\201C"; position:absolute; left:8px; top:-6px;
  font-family:var(--font-serif,Georgia,serif); font-size:44px; line-height:1;
  color:rgba(255,180,84,.4); pointer-events:none;}

/* 10 · kbd = a real keycap */
.layout kbd{
  display:inline-block; padding:2px 7px 3px; margin:0 1px;
  font-family:ui-monospace,Consolas,monospace; font-size:.82em;
  color:#e6ebf5; background:linear-gradient(#181c26,#0d1017);
  border:1px solid rgba(255,255,255,.22); border-bottom-width:2.5px;
  border-radius:5px; box-shadow:0 2px 0 rgba(0,0,0,.5);}

/* 11 · mark = solder glow, never highlighter yellow */
.layout main mark{
  background:linear-gradient(180deg,transparent 58%,rgba(255,180,84,.28) 58%);
  color:inherit; padding:0 2px; border-radius:2px;}

/* 12 · hr = a circuit trace with a via at its centre */
.layout main hr{
  position:relative; border:0; height:1px; margin:34px auto; max-width:520px;
  background:linear-gradient(90deg,transparent,rgba(184,115,51,.55),transparent);}
.layout main hr::after{
  content:""; position:absolute; left:50%; top:50%; width:7px; height:7px;
  transform:translate(-50%,-50%); border-radius:50%;
  background:#0b0d12; border:1.5px solid rgba(255,180,84,.6);}

/* 13/14 · lists: solder-via bullets, copper mono numerals (plain lists only —
   widget lists all carry classes and are untouched) */
.layout main ul:not([class]) > li::marker{content:"\25C9  "; color:rgba(255,180,84,.62); font-size:.8em;}
.layout main ol:not([class]) > li::marker{
  font-family:ui-monospace,Consolas,monospace; font-weight:700;
  color:rgba(255,180,84,.75);}
.layout main ul:not([class]) > li, .layout main ol:not([class]) > li{margin:6px 0; padding-left:4px;}

/* 15 · tables: instrument-grade */
.layout main table:not([class]){border-collapse:collapse; font-variant-numeric:tabular-nums;}
.layout main table:not([class]) th{
  border-bottom:1.5px solid rgba(184,115,51,.6);
  font-family:ui-monospace,Consolas,monospace; font-size:.82em;
  letter-spacing:.14em; text-transform:uppercase; color:#ffb454;
  padding:8px 14px; text-align:left;}
.layout main table:not([class]) td{padding:7px 14px; border-bottom:1px solid rgba(255,255,255,.08);}
.layout main table:not([class]) tbody tr:hover td{background:rgba(255,180,84,.05);}

/* 16 · captions open with a copper tick */
.fig figcaption::before{
  content:""; display:inline-block; width:14px; height:1.5px;
  background:linear-gradient(90deg,#b87333,#ffb454);
  margin-right:8px; vertical-align:3px;}

/* 17 · the chapter title settles into place on load — a keyframe, not a
   class-gated transition: an animation ALWAYS runs to completion, so a lost
   callback can only cost the effect, never the title itself. */
@keyframes iolH1{from{opacity:0; letter-spacing:.06em;}}
html.ioljs .layout main h1{animation:iolH1 1.1s cubic-bezier(.22,1,.36,1) .08s both;}

/* 18 · anchor jumps land with breathing room */
.layout main [id]{scroll-margin-top:26px;}

/* 19 · selecting code feels like a terminal, not a document */
.codewrap ::selection, .termcard ::selection, pre ::selection, code ::selection{
  background:rgba(94,222,120,.28); color:#eaffea;}

/* 20 · kickers carry a live copper gradient */
.layout main .kicker{
  background:linear-gradient(90deg,#ffb454,#e8b04b 40%,#b87333);
  -webkit-background-clip:text; background-clip:text; -webkit-text-fill-color:transparent;}
.layout main .kicker .iolrt{-webkit-text-fill-color:#8a93a6; margin-left:10px;}

/* 21 · every code card gets a copy button (JS injects) */
.iolcopy{
  margin-left:auto; padding:3px 10px; cursor:pointer;
  font-family:ui-monospace,Consolas,monospace; font-size:9.5px;
  letter-spacing:.18em; text-transform:uppercase;
  color:rgba(255,255,255,.55); background:transparent;
  border:1px solid rgba(255,255,255,.18); border-radius:5px;
  transition:color .2s, border-color .2s;}
.iolcopy:hover{color:#ffb454; border-color:rgba(255,180,84,.5);}
.iolcopy.ok{color:#5ede78; border-color:rgba(94,222,120,.5);}

/* 22 · language chips wear their language's colour (class from JS) */
.codehead .lang.iolpy{color:#f4c370 !important;}
.codehead .lang.iolsh{color:#5ede78 !important;}
.codehead .lang.iolc{color:#6095f4 !important;}

/* 23 · hovering a code card lights its left rail */
.codewrap{transition:box-shadow .3s ease;}
.codewrap:hover{box-shadow:inset 2px 0 0 rgba(255,180,84,.55);}

/* 24 · long listings fold (JS clamps past ~26 lines) */
.codewrap.iolclamp{position:relative; padding-bottom:34px;}
.codewrap.iolclamp pre{max-height:480px; overflow:hidden;}
.codewrap.iolclamp::after{
  content:""; position:absolute; left:0; right:0; bottom:34px; height:70px;
  background:linear-gradient(transparent,rgba(7,8,12,.94)); pointer-events:none;}
.codewrap.iolclamp.open pre{max-height:none;}
.codewrap.iolclamp.open::after{display:none;}
.iolexpand{
  position:absolute; left:50%; bottom:6px; transform:translateX(-50%);
  padding:3px 14px; cursor:pointer; z-index:2;
  font-family:ui-monospace,Consolas,monospace; font-size:9.5px;
  letter-spacing:.18em; text-transform:uppercase;
  color:#ffb454; background:#0b0d12;
  border:1px solid rgba(255,180,84,.35); border-radius:99px;}
.iolexpand:hover{border-color:rgba(255,180,84,.7);}

/* 25 · terminal cards: one CRT sheen sweep on hover */
.termcard{position:relative; overflow:hidden;}
.termcard::after{
  content:""; position:absolute; top:0; bottom:0; left:-40%; width:32%;
  background:linear-gradient(105deg,transparent,rgba(255,255,255,.045),transparent);
  transform:skewX(-12deg); pointer-events:none; transition:left .8s ease;}
.termcard:hover::after{left:118%;}

/* 26 · a terminal on screen shows a live status dot */
.termcard.iolin .tbar .dot{animation:iolLive 2.4s ease-in-out infinite;}
@keyframes iolLive{0%,100%{box-shadow:0 0 0 0 rgba(94,222,120,0);}
  50%{box-shadow:0 0 7px 1px rgba(94,222,120,.65);}}

/* 27 · (JS) double-click selects the whole listing — no CSS needed */

/* 28 · inline code underlines copper on hover */
.layout main p code, .layout main li code{transition:box-shadow .2s ease;}
.layout main p code:hover, .layout main li code:hover{
  box-shadow:inset 0 -1.5px 0 rgba(255,180,84,.55);}

/* 29 · scroll-reveal: content rises into place (elements tagged by JS) */
html.ioljs .iolrvl{opacity:0; transform:translateY(12px);
  transition:opacity .65s ease, transform .65s cubic-bezier(.22,1,.36,1);}
html.ioljs .iolrvl.iolin{opacity:1; transform:none;}
html.ioljs .iolrvl.iolnow, html.ioljs .iolrvl.iolnow.iolin{transition:none;}

/* 30 · the read-trace carries a bright bead at its tip */
#iolReadTrace::after{
  content:""; position:absolute; right:-2px; top:-2px; width:6px; height:6px;
  border-radius:50%; background:#ffb454;
  box-shadow:0 0 10px 2px rgba(255,180,84,.8);}

/* 31 · the active section pad breathes */
#iolSecRail.in-rail a.on .pad{animation:iolPad 2.6s ease-in-out infinite;}
@keyframes iolPad{0%,100%{box-shadow:0 0 0 0 rgba(255,180,84,.5);}
  50%{box-shadow:0 0 0 4px rgba(255,180,84,.12);}}

/* 32 · up-next card: copper sweep builds under the cursor */
.rail .nextcard{position:relative; overflow:hidden;
  transition:border-color .25s ease, box-shadow .25s ease;}
.rail .nextcard:hover{border-color:rgba(255,180,84,.55) !important;
  box-shadow:0 6px 22px -12px rgba(255,180,84,.35);}
.rail .nextcard::before{
  content:""; position:absolute; inset:0;
  background:linear-gradient(100deg,transparent 30%,rgba(255,180,84,.07) 50%,transparent 70%);
  transform:translateX(-100%); transition:transform .7s ease; pointer-events:none;}
.rail .nextcard:hover::before{transform:translateX(100%);}

/* 33 · prev/next press like real buttons */
.navbox .seq a{transition:transform .12s ease, color .2s, border-color .2s;}
.navbox .seq a:active{transform:translateY(1.5px);}

/* 34 · the map chip answers the cursor */
.poschip{transition:border-color .25s ease, box-shadow .25s ease;}
.poschip:hover{border-color:rgba(255,180,84,.55) !important;
  box-shadow:0 4px 18px -10px rgba(255,180,84,.4);}

/* 35 · reading time chip — styled with the kicker (see 20); JS injects */

/* 36 · the section meter blips when you cross into a new section */
#sm-cur, #sm-pct{transition:color .8s ease, text-shadow .8s ease;}
.secmeter.iolblip #sm-cur, .secmeter.iolblip #sm-pct{
  color:#ffb454 !important; text-shadow:0 0 12px rgba(255,180,84,.7);
  transition:none;}

/* 37 · toc jumps nudge toward their section */
.tocbox .gjump{transition:transform .18s ease, color .18s ease;}
.tocbox .gjump:hover{transform:translateX(4px); color:#ffb454 !important;}

/* 38 · the brand mark glows once under the cursor */
.rail .lockup svg{transition:filter .3s ease;}
.rail .lockup:hover svg{filter:drop-shadow(0 0 8px rgba(255,60,40,.55));}

/* 39 · back-to-top: ring brightens on approach */
#toTop{transition:border-color .25s ease, color .25s ease;}
#toTop:hover{border-color:rgba(255,180,84,.55) !important; color:#ffb454;}

/* 40 · jumping to an anchor flashes its heading once */
@keyframes iolTgt{0%{background:rgba(255,180,84,.18);}100%{background:transparent;}}
.layout main :target{animation:iolTgt 1.4s ease 1; border-radius:6px;}

/* 41-44 · ambience: vignette, film grain, wall power-rails, ghost chapter
   numeral (elements injected by JS §3; all pointer-transparent) */
@media (min-width:1001px){
  #iolVig{position:fixed; inset:0; z-index:1; pointer-events:none;
    background:radial-gradient(ellipse 120% 90% at 50% 42%, transparent 62%, rgba(0,0,0,.32));}
  #iolGrain{position:fixed; inset:0; z-index:1; pointer-events:none; opacity:.07;
    background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)' opacity='.55'/%3E%3C/svg%3E");}
  #iolWallL,#iolWallR{position:fixed; top:0; bottom:0; width:1px; z-index:1; pointer-events:none;
    background:linear-gradient(180deg,transparent,rgba(184,115,51,.55) 30%,rgba(184,115,51,.55) 70%,transparent);}
  #iolWallL{left:1.8%;} #iolWallR{right:1.8%;}
  /* 44 · giant chapter numeral — RETIRED (owner: "a bit odd") */
  #iolGhostCh{display:none;}
}
@media (max-width:1000px){#iolVig,#iolGrain,#iolWallL,#iolWallR,#iolGhostCh{display:none !important;}}

/* 45 · terminal cards carry a powered top rail */
.termcard{border-top:1px solid rgba(184,115,51,.45) !important;}

/* 46 · code cards grow corner solder ticks on hover */
.codewrap:hover{
  background-image:
    linear-gradient(rgba(255,180,84,.7),rgba(255,180,84,.7)),
    linear-gradient(rgba(255,180,84,.7),rgba(255,180,84,.7)),
    linear-gradient(rgba(255,180,84,.7),rgba(255,180,84,.7)),
    linear-gradient(rgba(255,180,84,.7),rgba(255,180,84,.7));
  background-repeat:no-repeat;
  background-size:10px 1px, 1px 10px, 10px 1px, 1px 10px;
  background-position:top 4px left 4px, top 4px left 4px,
                      bottom 4px right 4px, bottom 4px right 4px;}

/* 47 · hovering a figure brightens its fig-number plate */
.fig:hover::after{color:#ffb454;}

/* 48 · print: the chapter, and only the chapter */
@media print{
  .rail, .poschip, #iolChapCard, #iolReadTrace, #iolSecRail, #toTop,
  #iolRouteFx, #iolStarFx, #iolVig, #iolGrain, #iolWallL, #iolWallR,
  #iolGhostCh, .iolcopy, .iolexpand{display:none !important;}
  body{zoom:1 !important;}
  .layout{display:block !important; width:auto !important; padding:0 !important;}
  .layout main{max-width:100% !important;}
}

/* 49 · rail links answer keyboard focus like they answer the mouse */
.rail a:focus-visible, .navbox .seq a:focus-visible{
  background:rgba(255,180,84,.08); border-color:rgba(255,180,84,.5) !important;}

/* 50 · guards: reduced motion silences every §5 animation; transforms gone */
@media (prefers-reduced-motion:reduce){
  .layout main h2::after{transition:none;}
  html.ioljs .iolrvl{opacity:1 !important; transform:none !important; transition:none !important;}
  html.ioljs .layout main h1{animation:none;}
  .termcard::after, .rail .nextcard::before{display:none;}
  .termcard.iolin .tbar .dot, #iolSecRail.in-rail a.on .pad,
  .layout main :target{animation:none !important;}
}

/* ---- 5.51 · RAIL LOCKUP = FOOTER BRAND (owner 2026-07-26) ------------------
   "use the same logo used in the bottom, that same size, at the top" — the
   rail's mark now wears the footer .iol tile treatment exactly: 27px outer,
   5px padding, hairline border, 5px radius, black ground. And the blinking
   caret steps back from the wordmark so the two read as separate signals. */
.rail .lockup svg{
  width:27px !important; height:27px !important; flex:0 0 auto;
  padding:5px; box-sizing:border-box;
  background:#000;
  border:1px solid rgba(255,255,255,.16);
  border-radius:5px;
  transition:border-color .18s ease;}
.rail .lockup:hover svg{border-color:rgba(255,77,87,.5);}
.rail .lockup .cr{margin-left:9px !important;}
