/* ===========================================================================
   STEPPER · instrument console, in two lights.

   The whole page is one machine: a quiet ground, hairline rules, and a single
   green that means exactly one thing: a signal that is live. Amber means the
   machine stopped. Nothing else gets colour.

   THEMES.

     :root                    light, the default, no condition attached
     :root[data-theme=dark]   dark, reached only through the switch

   Light carries near-black 1.5px rules; dark goes back to a hairline, because
   a heavy line on a near-black ground reads as a wall rather than an edge.
   ======================================================================== */

/* --------------------------------------------------------------- light ---
   LIGHT IS THE DEFAULT. The bare :root carries it, so a reader with no
   stored choice gets light and nothing has to load before that is true.
   Dark is a deliberate second palette reached only through the switch: the
   operating system preference is not consulted, because a default that
   changes depending on whose machine it is on is not a default.

   Two accent tokens, and the distinction matters:
     --signal      an accent used as a FILL or a BORDER  (LEDs, bars, rules)
     --signal-ink  an accent used as TEXT on the ground  (must stay readable)
   In dark they are nearly the same. In light they are not, and using the
   wrong one is how a green-on-white site becomes unreadable.
   ======================================================================== */

:root {
  color-scheme: light;

  color-scheme: light;

  --void:      #ebedec;
  --panel:     #ffffff;
  --panel-2:   #f3f4f3;
  --panel-3:   #e3e6e4;
  --well:      #f8f9f8;
  --substrate: #d1d5d2;
  --line:      #181b1a;
  --line-2:    #4b504d;
  --line-soft: rgba(24, 27, 26, .26);

  --text:      #101413;
  --text-2:    #525855;
  --dim:       #70766f;
  --on-signal: #031a08;   /* dark ink: white on this green is 2.3:1 */

  --signal:    #00c805;
  --signal-2:  #00a00a;
  --signal-ink:#007a05;
  --tint:      rgba(0, 168, 12, 0.11);
  --tint-soft: rgba(0, 168, 12, 0.045);
  --tint-line: rgba(0, 168, 12, 0.26);
  --grid:      rgba(0, 150, 20, 0.042);
  --glow:      rgba(0, 200, 5, 0.26);
  --amber:     #b26a00;
  --amber-glow:rgba(178, 106, 0, .28);

  --g1: #00dc12;
  --g2: #00c805;
  --g3: #00a30c;
  --halo:   radial-gradient(ellipse 880px 520px at 76% 4%,
              rgba(0, 200, 5, .065), transparent 60%);
  --halo-2: radial-gradient(ellipse 620px 380px at 12% 96%,
              rgba(0, 200, 5, .035), transparent 62%);

  --scrim:  rgba(235, 237, 236, 0.88);
  --lift:   0 1px 2px rgba(20, 26, 23, .06), 0 8px 24px -14px rgba(20, 26, 23, .18);
  --lift-2: 0 1px 3px rgba(20, 26, 23, .08), 0 18px 40px -22px rgba(20, 26, 23, .30);

  /* Rules are 1.5px and close to black. A pale hairline disappears on a
     bright ground and makes a panel look unfinished; this is an instrument
     face, and the lines on one are drawn to be seen. */
  /* Derived from whichever ramp is live, so they belong here rather than in
     both palettes. Custom properties resolve on the element they are read
     from, and both palettes live on :root, so these follow the theme without
     being written twice. */
  --grad:      linear-gradient(135deg, var(--g1) 0%, var(--g2) 50%, var(--g3) 100%);
  --grad-flat: linear-gradient(90deg,  var(--g2) 0%, var(--g3) 100%);

  --display: "Chakra Petch", "Arial Narrow", sans-serif;
  /* The second display face, expanded. Chakra Petch runs the whole page and
     after a while reads as one voice saying everything; a wide gothic is the
     other half of an instrument's typography, the kind silkscreened onto a
     panel beside a switch. It carries the showcase and nothing else. */
  --display-2: "Archivo", "Helvetica Neue", Arial, sans-serif;
  --sans:    "Spline Sans", system-ui, -apple-system, "Segoe UI", sans-serif;
  --mono:    "Spline Sans Mono", ui-monospace, "SF Mono", Menlo, Consolas, monospace;

  /* MOTION: latch, do not ease.
     A flip-flop has no in-between value. It holds, then on the edge it is
     something else. So nothing on this page glides: transitions advance in
     discrete stages, which is both what the machine does and the clearest way
     to not look like every other site easing things in on a cubic curve.
     --latch  arriving content, five stages
     --snap   pointer feedback, two stages, close enough to instant
     --draw   a rule drawing itself, one segment at a time */
  --latch: steps(5, end);
  --snap:  steps(2, end);
  --draw:  steps(14, end);

  --gutter: clamp(16px, 4vw, 52px);
  --maxw: 1360px;
  --rule: 1.5px solid var(--line);
}

/* ---------------------------------------------------------------- dark ---
   A token swap and nothing else. No rule below this block knows which theme
   is running, which is the only way a two-theme site stays maintainable.
   In the dark the rules go back to a hairline: on a near-black ground a
   heavy line reads as a wall rather than an edge. */

:root[data-theme="dark"] {
  color-scheme: dark;

  color-scheme: dark;

  /* ground */
  --void:      #05070a;
  --panel:     #0a0e13;
  --panel-2:   #10161d;
  --panel-3:   #161f28;
  --well:      #04070a;   /* inside an instrument: canvas, <pre> */
  --substrate: #0e161c;   /* a gate that is not switching */
  --line:      #1c2630;
  --line-2:    #2a3844;
  --line-soft: rgba(28, 38, 48, .65);

  /* ink */
  --text:      #cfdae2;
  --text-2:    #93a3af;
  --dim:       #5e6d79;
  --on-signal: #04120a;   /* text printed on top of a signal fill */

  /* signal, rationed */
  --signal:    #00e015;
  --signal-2:  #00b80f;
  --signal-ink:#2ee23c;
  --tint:      rgba(0, 224, 21, 0.11);
  --tint-soft: rgba(0, 224, 21, 0.038);
  --tint-line: rgba(0, 224, 21, 0.18);
  --grid:      rgba(0, 224, 21, 0.032);
  --glow:      rgba(0, 224, 21, 0.30);
  --amber:     #ffb020;
  --amber-glow:rgba(255, 176, 32, .35);

  /* THE RAMP. Three stops, always used as one object, never sampled apart.
     It is the brand's only gradient and it means one thing: a signal getting
     stronger. Deep green is a settled gate, bright green is one that just
     switched. The trace on the analyser, the rail over the stats and a lit
     LED are all showing that same idea, which is why it is defined once here
     and referenced everywhere else. */
  --g1: #4dfa5c;
  --g2: #00e015;
  --g3: #00a80c;
  --halo:      radial-gradient(ellipse 880px 520px at 76% 4%,
                 rgba(0, 224, 21, .13), transparent 64%);
  --halo-2:    radial-gradient(ellipse 620px 380px at 12% 96%,
                 rgba(0, 224, 21, .07), transparent 66%);

  --scrim:     rgba(5, 7, 10, 0.88);   /* the sticky topbar */
  --lift:      none;                   /* panels need no shadow in the dark */
  --lift-2:    none;

  --rule: 1px solid var(--line);
}

* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }

body {
  margin: 0;
  background-color: var(--void);
  /* faint engineering grid: the surface the instrument sits on */
  background-image:
    linear-gradient(to right,  var(--grid) 1px, transparent 1px),
    linear-gradient(to bottom, var(--grid) 1px, transparent 1px);
  background-size: 56px 56px;
  color: var(--text);
  font-family: var(--sans);
  font-size: 16px;
  line-height: 1.62;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
  transition: background-color .28s ease, color .28s ease;
}

h1, h2, h3, h4 { margin: 0; line-height: 1.04; font-family: var(--display); }
p, dl, dd, dt, ol, ul { margin: 0; padding: 0; }
li { list-style: none; }
a { color: inherit; text-decoration: none; }
img, svg { max-width: 100%; }
:focus-visible { outline: 2px solid var(--signal); outline-offset: 3px; }

code {
  font-family: var(--mono);
  font-size: 0.88em;
  color: var(--signal-ink);
  background: var(--tint-soft);
  border: 1px solid var(--tint-line);
  padding: 1px 5px;
}

/* Off-screen until it is focused, then it lands in the corner like any other
   control. Never display:none, because a hidden link cannot be tabbed to, which
   defeats the entire point of having one. */
.skip {
  position: absolute; z-index: 100;
  top: 8px; left: 8px;
  transform: translateY(-160%);
  padding: 11px 18px;
  font-family: var(--mono); font-size: 12px; font-weight: 600;
  letter-spacing: .13em; text-transform: uppercase;
  background: var(--grad); color: var(--on-signal);
  transition: transform .14s var(--snap);
}
.skip:focus { transform: none; }

/* visible to a screen reader, to nobody else */
.sr {
  position: absolute; width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%);
  white-space: nowrap;
}

.wrap { max-width: var(--maxw); margin: 0 auto; padding: 0 var(--gutter); }
.stack { display: flex; flex-direction: column; }
.g6 { gap: 6px; } .g10 { gap: 10px; } .g14 { gap: 14px; }
.g20 { gap: 20px; } .g28 { gap: 28px; } .g40 { gap: 40px; }

/* text that carries the gradient instead of a flat accent, used only on the
   words a heading turns on, never on running copy */
.grad {
  background: var(--grad);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
@supports not (background-clip: text) { .grad { color: var(--signal-ink); } }

/* ---------------------------------------------------------------- labels */

.ck {
  font-family: var(--mono);
  font-size: 10.5px;
  font-weight: 500;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--dim);
}
.ck-sig { color: var(--signal-ink); }
.ck-amber { color: var(--amber); }

/* the recurring 6px signal square that prefixes labels */
.sq {
  display: inline-block;
  width: 6px; height: 6px;
  background: var(--grad);
  margin-right: 8px;
  vertical-align: 1px;
  box-shadow: 0 0 7px var(--glow);
}
.sq-off { background: var(--line-2); box-shadow: none; }

/* ---------------------------------------------------------------- buttons */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 9px;
  font-family: var(--mono);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  padding: 13px 22px;
  border: 1px solid var(--signal);
  background: transparent;
  color: var(--signal-ink);
  cursor: pointer;
  position: relative;
  overflow: hidden;
  transition: color .12s var(--snap), background-color .12s var(--snap),
              border-color .12s var(--snap), transform .12s var(--snap),
              box-shadow .12s var(--snap);
}
/* a scan line sweeps the button on hover: the probe passing over a pad */
.btn::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(100deg, transparent 40%, var(--glow) 50%, transparent 60%);
  transform: translateX(-120%);
  transition: transform .5s steps(7, end);
  pointer-events: none;
}
.btn:hover::after, .btn:focus-visible::after { transform: translateX(120%); }
.btn:hover { background: var(--tint); }

.btn-solid {
  background: var(--grad);
  background-size: 180% 180%;
  border-color: transparent;
  color: var(--on-signal);
  box-shadow: 0 0 0 rgba(0,0,0,0);
}
.btn-solid:hover {
  background-position: 100% 0;
  transform: translateY(-1px);
  box-shadow: 0 8px 22px -10px var(--glow);
}
.btn-solid:active { transform: translateY(0); }
.btn-ghost { border-color: var(--line-2); color: var(--text-2); background: transparent; }
.btn-ghost:hover { border-color: var(--text-2); color: var(--text); background: var(--panel-2); }
.btn-sm { padding: 9px 15px; font-size: 11px; }
.btn[disabled] {
  border-color: var(--line-2);
  color: var(--dim);
  cursor: not-allowed;
  background: transparent;
  transform: none;
  box-shadow: none;
}
.btn[disabled]::after { display: none; }

/* ---------------------------------------------------------------- topbar */

.topbar {
  position: sticky; top: 0; z-index: 60;
  background: var(--scrim);
  backdrop-filter: blur(9px);
  border-bottom: var(--rule);
}
.topbar-in {
  max-width: var(--maxw); margin: 0 auto;
  padding: 0 var(--gutter);
  height: 58px;
  display: flex; align-items: center; gap: 26px;
}
.brandmark { display: flex; align-items: center; gap: 10px; flex-shrink: 0; }
/* The mark is wider than it is tall, so the box has to be too. A square box
   letterboxes it and leaves the wordmark sitting against dead space. */
.brandmark img { width: 26px; height: 26px; display: block; }

/* ------------------------------------------------------------- page menu -
 *
 * One button of fixed width, whatever the site grows to. It replaces the
 * cross-page links that used to sit in .topnav and compete with the section
 * anchors for a row that cannot grow — and unlike that row, it survives the
 * phone breakpoint, where the nav is hidden and there was otherwise no way
 * off a page but the back button.
 *
 * The button carries the name of the page you are on, so the bar answers
 * "where am I" without being opened.
 */
.pagenav { position: relative; flex: none; }

.pagenav-btn {
  display: inline-flex; align-items: center; gap: 8px;
  height: 32px; padding: 0 10px 0 12px;
  font-family: var(--mono); font-size: 12px; letter-spacing: .04em;
  color: var(--text-2); background: var(--panel-2);
  border: 1px solid var(--line-2); border-radius: 6px;
  cursor: pointer; white-space: nowrap;
}
.pagenav-btn:hover { color: var(--text); border-color: var(--signal); }
.pagenav-btn .pagenav-caret { display: flex; opacity: .7; }
.pagenav-btn .pagenav-caret svg { width: 13px; height: 13px; display: block; }
.pagenav-btn[aria-expanded="true"] { color: var(--text); border-color: var(--signal); }
.pagenav-btn[aria-expanded="true"] .pagenav-caret { transform: rotate(180deg); }

.pagemenu {
  position: absolute; left: 0; top: calc(100% + 8px);
  z-index: 80; min-width: 300px;
  padding: 8px;
  background: var(--panel); border: 1px solid var(--line-2); border-radius: 8px;
  box-shadow: 0 22px 48px rgba(0, 0, 0, .34);
}
.pagemenu-head {
  padding: 9px 10px 5px; color: var(--dim);
  font-size: 10px; letter-spacing: .2em; text-transform: uppercase;
}
.pagemenu-head:first-child { padding-top: 4px; }

.pagemenu-item {
  display: flex; flex-direction: column; gap: 1px;
  padding: 8px 10px; border-radius: 6px;
  text-decoration: none; color: var(--text-2);
  /* A whole row is the target, not the word inside it. */
  min-height: 34px; justify-content: center;
}
.pagemenu-item b {
  font-family: var(--display); font-size: 14px; font-weight: 600; color: var(--text);
}
.pagemenu-item span {
  font-family: var(--mono); font-size: 10.5px; letter-spacing: .04em; color: var(--dim);
}
.pagemenu-item:hover { background: var(--panel-2); }
.pagemenu-item:hover b { color: var(--signal-ink); }

/* Where you already are, marked rather than removed: a menu that drops the
   current page makes the list a different length on every page, and a reader
   loses the shape they had just learned. */
.pagemenu-item.is-here { background: var(--panel-2); }
.pagemenu-item.is-here b { color: var(--signal-ink); }
.pagemenu-item.is-here::after {
  content: "you are here";
  font-family: var(--mono); font-size: 9.5px; letter-spacing: .16em;
  text-transform: uppercase; color: var(--signal);
  margin-top: 2px;
}

@media (max-width: 560px) {
  /* It stays. This is the one control in the bar that a phone cannot do
     without, which is why the rest of the row is what gets dropped. */
  .pagenav-btn { height: 34px; font-size: 11.5px; padding: 0 8px 0 10px; }
  .pagemenu { min-width: 244px; }
}

/* ------------------------------------------------------- compact editor -
 * The launchpad keeps the smallest editor that still produces a ROM. Tall
 * enough for a program somebody would actually launch, short enough that it
 * does not become the page. */
/* Both selectors are doubled up on purpose. The shared editor sets its own
   height further down the file, and a single class here loses to it on
   source order; the textarea defends its height with an id, which a class
   cannot outweigh at all. */
.wb-code.editor-sm { min-height: 170px; }

.editor-sm #wb-src {
  min-height: 170px;

  /* No drag handle here. The textarea shares a box with the gutter beside
     it; dragging its corner grows one and not the other, and pulls the
     grip out over the panel below. The full workbench is a page away for
     anyone who wants the room. */
  resize: none;
}

/* -------------------------------------------------------- launch figure -
 * The launchpad says one thing the front page does not: two objects come
 * out of one transaction. The figure is that sentence, so it lives here
 * rather than being another copy of the front page hero. */
.launchfig { width: 100%; margin: 6px 0 2px; }
.launchfig canvas { display: block; width: 100%; }

/* ----------------------------------------------------------- the card -
 *
 * Square, because the contract draws a square, and capped so it cannot grow
 * taller than the column it shares with a specification plate.
 */
.cardwrap {
  width: 100%; max-width: 240px; margin: 0 auto;
  aspect-ratio: 1 / 1;
  border: var(--rule); border-radius: 10px;
  overflow: hidden;

  /* The checkerboard every image editor uses for "nothing here yet". A card
     that has been drawn covers it completely; one that has not shows it, so
     an empty frame reads as empty rather than as a black slab somebody forgot
     to fill in. */
  background-color: #f4f5f4;
  background-image:
    linear-gradient(45deg, #dcdedc 25%, transparent 25%, transparent 75%, #dcdedc 75%),
    linear-gradient(45deg, #dcdedc 25%, transparent 25%, transparent 75%, #dcdedc 75%);
  background-size: 16px 16px;
  background-position: 0 0, 8px 8px;
}
.cardwrap canvas { display: block; width: 100%; height: 100%; }
.chip-card { max-width: 200px; margin: 0; flex: none; }

/* An address is printed as it is, never upper-cased.
 *
 * EIP-55 puts the checksum in the letter case, so shouting an address throws
 * away the one thing that lets somebody catch a wrong one. The label style
 * around it upper-cases by default, and this exists to stop it. */
.card-addr {
  text-transform: none;
  font-family: "Spline Sans Mono", monospace;
  letter-spacing: 0;
}

/* ------------------------------------------------------------- token bar -
 *
 * The contract address, pinned under the nav on every page.
 *
 * It sits below the topbar rather than inside it because it is not
 * navigation: it is one string a visitor arrives holding and wants to check
 * against what they were sent. Sticky at the topbar's own height so both stay
 * put, and one z-index below so the nav wins where they meet.
 *
 * The address never wraps and never truncates. A half-shown contract address
 * is worse than none: it is the shape a scam takes. On a narrow screen the
 * row scrolls sideways instead, which keeps every character readable.
 */
.tokenbar {
  position: sticky; top: 58px; z-index: 59;
  background: var(--scrim);
  backdrop-filter: blur(9px);
  border-bottom: var(--rule);
}
.tokenbar-in {
  display: none !important;
  max-width: var(--maxw); margin: 0 auto;
  padding: 7px var(--gutter);
  display: flex; align-items: center; gap: 12px;
  overflow-x: auto; scrollbar-width: none;
}
.tokenbar-in::-webkit-scrollbar { display: none; }

.tokenbar-tag {
  flex: none;
  font-family: "Chakra Petch", sans-serif; font-weight: 700;
  font-size: 13px; letter-spacing: .06em;
  color: var(--signal);
}
.tokenbar-ca {
  flex: none;
  font-family: "Spline Sans Mono", monospace;
  font-size: 12px; letter-spacing: .01em;
  color: var(--text-2);
  white-space: nowrap;
}
.tokenbar-copy {
  flex: none;
  font-family: "Spline Sans Mono", monospace;
  font-size: 11px; letter-spacing: .1em; text-transform: uppercase;
  color: var(--dim); background: none;
  border: var(--rule); border-radius: 5px;
  padding: 3px 9px; cursor: pointer;
  /* Wide enough for the longest thing it ever says, so confirming a copy
     does not shove the row sideways. */
  min-width: 74px;
}
.tokenbar-copy:hover { color: var(--text); border-color: var(--signal); }

.tokenbar-link {
  flex: none;
  font-family: "Spline Sans Mono", monospace;
  font-size: 11px; letter-spacing: .1em; text-transform: uppercase;
  color: var(--dim); text-decoration: none;
}
.tokenbar-link:hover { color: var(--text); }
.tokenbar-buy { color: var(--signal); margin-left: auto; padding-left: 12px; }
.tokenbar-buy:hover { color: var(--signal); text-decoration: underline; }

@media (max-width: 720px) {
  /* The trade link stops being pushed to the far edge once the row scrolls:
     off the end of a scrolling row is off the screen. */
  .tokenbar-buy { margin-left: 0; }
}

/* ---------------------------------------------------------- the die frame -
   The mark carries a lot of internal detail, which on a bare page reads as a
   sticker somebody dropped there. A square well gives it an edge to sit
   inside, and two corner brackets in the signal green echo the alignment
   marks on a reticle — the same vocabulary as the section markers. */

.markframe {
  position: relative;
  display: grid;
  place-items: center;
  width: 38px; height: 38px;
  flex: none;
  background: var(--well);
  border: 1px solid var(--line-2);
  transition: border-color .14s var(--snap), background .14s var(--snap);
}
.markframe::before,
.markframe::after {
  content: "";
  position: absolute;
  width: 7px; height: 7px;
  border: 1px solid var(--signal);
  pointer-events: none;
}
.markframe::before { top: -1px; left: -1px; border-right: 0; border-bottom: 0; }
.markframe::after { bottom: -1px; right: -1px; border-left: 0; border-top: 0; }
.brandmark:hover .markframe {
  border-color: color-mix(in srgb, var(--signal) 45%, var(--line-2));
  background: color-mix(in srgb, var(--signal) 7%, var(--well));
}

/* The footer takes the same frame at a size where the die inside it reads. */
.foot-mark { display: flex; align-items: center; gap: 14px; }
.foot-mark img {
  width: 56px; height: 56px; display: block;
  padding: 7px;
  background: var(--well);
  border: 1px solid var(--line-2);
  box-sizing: content-box;
}
.foot-mark b {
  font-family: var(--display);
  font-size: 27px;
  font-weight: 700;
  letter-spacing: .17em;
  color: var(--text);
}
.brandmark b {
  font-family: var(--display);
  font-size: 16px; font-weight: 700;
  letter-spacing: 0.16em;
  color: var(--text);
}
/* The inline mark is cells, not a line, so it takes fills rather than strokes
   and follows the palette like everything else. The edge is the wavefront; the
   settled cells are gates the edge has already passed. */
.brandmark .m-edge { fill: var(--g1); }
.brandmark .m-set  { fill: var(--g3); }
.brandmark img { image-rendering: auto; }

.topnav { display: flex; gap: 22px; margin-left: auto; }
.topnav a {
  position: relative;
  font-family: var(--mono); font-size: 11px; font-weight: 500;
  letter-spacing: 0.12em; text-transform: uppercase; color: var(--text-2);
  padding: 4px 0;
  transition: color .15s;
}
/* the underline grows out of the gradient rather than switching on */
.topnav a::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 1px;
  background: var(--grad-flat);
  transform: scaleX(0); transform-origin: left;
  transition: transform .2s var(--snap);
}
.topnav a:hover { color: var(--signal-ink); }
.topnav a:hover::after { transform: scaleX(1); }
.top-links { display: flex; gap: 12px; align-items: center; }

#top-links button {
  display: none !important;
}

#foot-social button {
  display: none !important;
}

@media (max-width: 940px) {
  /* The nav carried margin-left:auto, so hiding it left the theme switch
     stranded beside the wordmark. The empty link row takes over the job. */
  .topnav { display: none; }
  .top-links { margin-left: auto; }
  .topbar-in { gap: 14px; }
}

/* ------------------------------------------------------------ theme switch
   Two states, both always visible, with the active one carrying the gradient.
   A switch that shows only the state you are not in is a riddle. */

.themer {
  /* The button size and the pill size are the same size, so they are one
     number. They were two, and they drifted the moment one of them changed. */
  --sw: 30px;
  --sh: 24px;
  display: inline-flex; align-items: center;
  border: 1px solid var(--line-2);
  background: var(--panel-2);
  padding: 2px;
  flex: none;
  position: relative;
}
.themer button {
  position: relative; z-index: 1;
  display: inline-flex; align-items: center; justify-content: center;
  width: var(--sw); height: var(--sh);
  border: 0; background: transparent; cursor: pointer;
  color: var(--dim);
  transition: color .2s ease;
}
.themer button svg { width: 14px; height: 14px; display: block; }
.themer button[aria-pressed="true"] { color: var(--on-signal); }
.themer button:hover { color: var(--text); }
.themer button[aria-pressed="true"]:hover { color: var(--on-signal); }
/* the sliding gradient pill sits behind whichever half is active */
.themer::before {
  content: ""; position: absolute; z-index: 0;
  top: 2px; left: 2px; width: var(--sw); height: var(--sh);
  background: var(--grad);
  transition: transform .18s var(--snap);
}
.themer[data-at="light"]::before { transform: translateX(var(--sw)); }

/* ---------------------------------------------------------------- hero */

.hero { border-bottom: var(--rule); position: relative; isolation: isolate; }
/* THE HALO: one soft light thrown from behind the console, and a second,
   quieter one low on the left so the band does not feel top-heavy. It is the
   ramp again, spread out: the glow a live instrument puts on its own bezel. */
.hero::before {
  content: ""; position: absolute; inset: 0; z-index: -1; pointer-events: none;
  background: var(--halo), var(--halo-2);
}
.hero-in {
  max-width: var(--maxw); margin: 0 auto;
  padding: clamp(30px, 6vw, 88px) var(--gutter) clamp(28px, 4vw, 56px);
  display: grid;
  grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr);
  gap: clamp(28px, 5vw, 72px);
  align-items: center;
}
@media (max-width: 940px) { .hero-in { grid-template-columns: 1fr; } }

.hero h1 {
  font-size: clamp(38px, 5.6vw, 78px);
  font-weight: 700;
  letter-spacing: -0.02em;
  text-wrap: balance;
  margin: 16px 0 0;
}
.hero h1 em {
  font-style: normal;
  background: var(--grad);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
@supports not (background-clip: text) { .hero h1 em { color: var(--signal-ink); } }

.hero-sub {
  max-width: 54ch;
  margin-top: 20px;
  color: var(--text-2);
  font-size: 16.5px;
}
.hero-sub b { color: var(--text); font-weight: 600; }
.hero-cta { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 28px; }
/* Side by side they would each be about 160px wide, which is not a button.
   Stacked and full width, they are two clear targets. */
@media (max-width: 560px) {
  /* A panel head is a label and a note held apart by auto margin. On a phone
     there is no apart: the two collide, both wrap, and the result reads as one
     broken sentence. They stack instead, and the note keeps its own line. */
  .panel-head { flex-wrap: wrap; row-gap: 4px; padding: 10px 14px; }
  .panel-head .grow { margin-left: 0; flex-basis: 100%; }

  /* Same fault, same fix: the drawer summary held a title and a hint on one
     line, and the hint ran under its own underline. */
  .drawer-head { flex-wrap: wrap; row-gap: 6px; }
  .drawer-act { margin-left: 0; flex-basis: 100%; white-space: normal; line-height: 1.5; }
  .drawer-title { flex: 1 1 auto; min-width: 0; }

  .hero-cta { flex-direction: column; align-items: stretch; }
  .hero-cta .btn { width: 100%; }
}

.hero-stats {
  display: grid; grid-template-columns: repeat(4, 1fr);
  border: var(--rule); margin-top: 34px;
  background: var(--panel);
  position: relative;
  box-shadow: var(--lift);
}
/* a 2px gradient rail along the top: the one place the full ramp is shown */
.hero-stats::before {
  content: ""; position: absolute; left: 0; right: 0; top: -1px; height: 2px;
  background: var(--grad-flat);
}
@media (max-width: 640px) { .hero-stats { grid-template-columns: repeat(2, 1fr); } }
.hero-stats div { padding: 14px 16px; border-right: var(--rule); }
.hero-stats div:last-child { border-right: 0; }
@media (max-width: 640px) {
  .hero-stats div:nth-child(2) { border-right: 0; }
  .hero-stats div:nth-child(-n+2) { border-bottom: var(--rule); }
}
.hero-stats b {
  display: block; font-family: var(--display); font-size: 26px;
  font-weight: 700; color: var(--signal-ink); letter-spacing: 0.01em;
  font-variant-numeric: tabular-nums; margin-top: 2px;
}
/* How much of the die did work on the last edge. The bar is the same figure
   as the number beside it, read as a proportion of the whole array. */
.hero-stats .spark {
  display: block; height: 2px; margin-top: 8px;
  background: var(--line); position: relative; overflow: hidden;
}
.hero-stats .spark::after {
  content: ""; position: absolute; top: 0; bottom: 0; left: 0;
  width: var(--w, 0%); background: var(--grad);
  transition: width .12s linear;
}

/* ---------------------------------------------------------------- console */

#machine .console { margin-bottom: clamp(20px, 3vw, 32px); }
.console {
  border: var(--rule);
  background: var(--panel);
  position: relative;
  box-shadow: var(--lift-2);
}
/* corner ticks: an instrument bezel, not a card */
.console::before, .console::after {
  content: ""; position: absolute; width: 9px; height: 9px;
  border: 1px solid var(--signal); pointer-events: none;
}
.console::before { top: -1px; left: -1px; border-right: 0; border-bottom: 0; }
.console::after { bottom: -1px; right: -1px; border-left: 0; border-top: 0; }

.console-bar {
  display: flex; align-items: center; gap: 12px;
  padding: 11px 16px; border-bottom: var(--rule);
  background: var(--panel-2);
}
.console-bar .grow { margin-left: auto; }

.livedot {
  width: 7px; height: 7px; background: var(--signal);
  box-shadow: 0 0 8px var(--glow);
  animation: pulse 1.9s ease-in-out infinite;
}
.livedot.halt { background: var(--amber); box-shadow: 0 0 8px var(--amber-glow); animation: none; }
@keyframes pulse { 0%,100% { opacity: 1; } 50% { opacity: .35; } }

/* the die: 2,368 gates */
.die-wrap { padding: 16px; }
@media (max-width: 560px) {
  .die-wrap, .panel-body { padding: 12px; }
  .readout div { padding: 10px 12px; }
  .console-bar { padding: 10px 12px; }
}
/* Both die maps: the console one and the launchpad one. The canvas carries
   an intrinsic size for the no-CSS case, and the width is taken back here so
   it can never push the page wider than the viewport. */
.die-wrap canvas {
  display: block; width: 100%; max-width: 100%; height: auto;
  background: var(--well); border: var(--rule);
}
/* The caption lives in the label strip now. The rule stays for the launchpad,
   which still draws a die with a note under it. */
.die-note { padding: 0 16px 14px; }

/* In the strip it is a caption, not a heading: it gives way to the part number
   and the cycle count on either side of it, and disappears before it would
   push them apart. */
.bar-note {
  color: var(--dim);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
  padding-left: 4px;
  border-left: 1px solid var(--line-soft);
}
@media (max-width: 1120px) { .bar-note { display: none; } }

/* ---------------------------------------------------------- the readout ---
   Four figures read off the machine on every frame. It is the last band of
   the instrument and the first thing anybody actually looks at, so the cells
   are separated by a hairline rather than boxed, and the numbers are set in
   the display face at a size you can read across a desk. */

.readout {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 1px;
  background: var(--line-soft);
  border-top: var(--rule);
}
.readout div {
  background: var(--panel);
  padding: 14px 16px 16px;
  display: flex; flex-direction: column; gap: 5px;
  min-width: 0;
}
.readout b {
  font-family: var(--display);
  font-size: clamp(20px, 2vw, 27px); font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--signal-ink);
  font-variant-numeric: tabular-nums;
  overflow-wrap: anywhere;
}
/* The output port is the one figure that changes for a reason, so it is the
   one allowed to announce that it changed. */
.readout b.hot { color: var(--g1); }

@media (max-width: 620px) {
  .readout { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* ---------------------------------------------------------------- sections */

.sec { border-bottom: var(--rule); padding: clamp(52px, 7vw, 96px) 0; }
/* A section opens the way a datasheet page does: a running head across the
   full measure with the section number on it, a rule under that, and only
   then the title. The note sits beside the title rather than above it, so the
   eye lands on what the section is called before it lands on the caveat. */
.sec-head {
  display: grid;
  grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr);
  grid-template-areas:
    "index index"
    "title note";
  column-gap: clamp(20px, 4vw, 56px);
  row-gap: clamp(16px, 2.4vw, 30px);
  align-items: start;
  margin-bottom: clamp(28px, 4vw, 48px);
}
.sec-index {
  grid-area: index;
  display: flex; align-items: center;
  padding-bottom: 12px;
  border-bottom: var(--rule);
  font-family: var(--mono); font-size: 10.5px; font-weight: 500;
  letter-spacing: 0.24em; text-transform: uppercase; color: var(--dim);
}
.sec-head h2 { grid-area: title; margin-top: 0; }
.sec-note { grid-area: note; padding-top: 6px; }
@media (max-width: 860px) {
  .sec-head {
    grid-template-columns: 1fr;
    grid-template-areas: "index" "title" "note";
  }
  .sec-note { padding-top: 0; }
}
.sec-num { color: var(--signal-ink); }
.sec-head h2 {
  font-size: clamp(30px, 4.2vw, 54px);
  font-weight: 700; letter-spacing: -0.015em; text-wrap: balance;
  margin-top: 12px;
}
/* The word a heading turns on gets a FIELD behind it, not a highlight.
   The first version was a flat tint hugging the glyphs, and it read exactly
   like a find-in-page match: a rectangle somebody had swiped a marker over.
   This is meant to read as a lit region of the die instead, so it borrows the
   things a die has and a highlighter does not:

     · it squares up to the line box rather than to the letters, with the
       ascender and descender room included, so it is a plate the type sits ON
     · a hairline in the accent, at the alpha the panels use, so it has an edge
     · a dot field, the way a piece of perf board is drilled. A line grid was
       tried first and it fought the letterforms; dots read as a surface
       without competing with the type standing on it
     · a heavier floor rule that runs the full width of the plate

   It still wipes in a segment at a time, because a machine does not fade. */
.sec-head h2 em {
  font-style: normal;
  position: relative;
  display: inline-block;
  padding: .04em .24em .12em;
  background: var(--grad);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  isolation: isolate;
}
@supports not (background-clip: text) { .sec-head h2 em { color: var(--signal-ink); } }

/* the field, and the floor it stands on */
.sec-head h2 em::before,
.sec-head h2 em::after {
  content: ""; position: absolute; z-index: -1;
  transform: scaleX(0); transform-origin: left;
  transition: transform .55s var(--draw);
}
.sec-head h2 em::before {
  /* Squared to the line box and deep enough that a descender sits INSIDE the
     plate rather than hanging through its floor. */
  left: 0; right: 0; top: -.02em; bottom: 0;
  background:
    radial-gradient(var(--tint-line) 1.1px, transparent 1.2px) 0 0 / 9px 9px,
    var(--tint);
  border: 1px solid var(--tint-line);
  border-bottom: 0;
}
/* The floor the plate stands on, arriving a beat after it. It replaces the
   bottom border rather than sitting under one, so the edge is a single line
   of one weight. */
.sec-head h2 em::after {
  left: 0; right: 0; bottom: 0; height: 3px;
  background: var(--grad-flat);
  transition-delay: .12s;
}
.sec-head.in h2 em::before,
.sec-head.in h2 em::after { transform: scaleX(1); }

/* Nothing to wipe if motion is unwelcome: the band is simply there. */
/* ------------------------------------------------------------ the gateway ---
   The door to /launchpad.html. It is one anchor, not a card with a button
   inside it, so the whole plate is the target and the keyboard gets a single
   stop rather than two. */

.nav-go { color: var(--signal-ink) !important; }
.nav-go::after { background: var(--grad) !important; }

.gateway {
  display: grid;
  grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr);
  gap: 0;
  border: var(--rule);
  background: var(--panel);
  box-shadow: var(--lift);
  text-decoration: none; color: inherit;
  position: relative; overflow: hidden;
  transition: box-shadow .18s var(--snap), border-color .18s var(--snap);
}
@media (max-width: 860px) { .gateway { grid-template-columns: 1fr; } }

/* The same 2px gradient rail the cards use, so a hover means one thing across
   the whole page. */
.gateway::before {
  content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 2px;
  background: var(--grad); transform: scaleY(0); transform-origin: top;
  transition: transform .28s var(--latch);
  z-index: 2;
}
.gateway:hover::before, .gateway:focus-visible::before { transform: scaleY(1); }
.gateway:hover { border-color: var(--signal-2); }
.gateway:focus-visible { outline: 2px solid var(--signal); outline-offset: 3px; }

.gateway-face { padding: clamp(22px, 3.4vw, 38px); }
.gateway-face h3 {
  font-family: var(--display);
  font-size: clamp(21px, 2.4vw, 30px); font-weight: 700;
  letter-spacing: -0.01em; line-height: 1.2;
  margin: 12px 0 12px; color: var(--text); text-wrap: balance;
}
.gateway-face h3 em {
  font-style: normal;
  background: var(--grad); -webkit-background-clip: text;
  background-clip: text; color: transparent;
}
@supports not (background-clip: text) { .gateway-face h3 em { color: var(--signal-ink); } }
.gateway-face p {
  font-size: 14.5px; line-height: 1.6; color: var(--text-2);
  max-width: 46ch; margin-bottom: 20px;
}
.gateway-go {
  display: inline-flex; align-items: center; gap: 9px;
  font-family: var(--mono); font-size: 11.5px; font-weight: 600;
  letter-spacing: .12em; text-transform: uppercase;
  color: var(--signal-ink);
  padding-bottom: 4px; border-bottom: 1px solid var(--signal);
}
/* The arrow steps rather than glides: the same motion language as the rest. */
.gateway-go i { font-style: normal; transition: transform .2s var(--snap); }
.gateway:hover .gateway-go i { transform: translateX(5px); }

/* The right half is a specimen of what the launchpad does: four lines of real
   assembly and an output byte. Static, because a moving picture of a program
   on a page nobody asked to run one on is noise. */
.gateway-strip {
  border-left: var(--rule);
  background: var(--well);
  display: flex; flex-direction: column;
  min-width: 0;
}
@media (max-width: 860px) {
  .gateway-strip { border-left: 0; border-top: var(--rule); }
}
.gs-head {
  display: flex; align-items: center; gap: 9px;
  padding: 11px 16px; border-bottom: 1px solid var(--line-soft);
  background: var(--panel-2);
  font-family: var(--mono); font-size: 10px; font-weight: 600;
  letter-spacing: .16em; color: var(--dim);
}
.gs-code {
  flex: 1 1 auto;
  margin: 0; padding: 18px 16px;
  font-family: var(--mono); font-size: 13px; line-height: 1.85;
  color: var(--text-2);
  overflow-x: auto;
}
.gs-code b { color: var(--signal-ink); font-weight: 600; }
.gs-foot {
  display: flex; align-items: center; gap: 12px;
  padding: 12px 16px; border-top: 1px solid var(--line-soft);
  background: var(--panel-2);
}
.gs-bits { display: flex; gap: 3px; }
.gs-bits i {
  width: 12px; height: 12px;
  background: var(--panel-3); border: 1px solid var(--line-soft);
}
.gs-bits i.on { background: var(--grad); border-color: transparent; }
.gs-bits i:nth-child(4) { margin-right: 5px; }

/* ======================================================= THE LAUNCHPAD ===
   A second page, built out of the same parts as the first. Nothing below
   invents a new visual language: it is the panel, the ledger and the plate
   already defined above, arranged around an editor. */

/* The hero here carries a status table rather than a console, so the columns
   sit closer to even than on the front page. */
.lp-hero { grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr); align-items: center; }
@media (max-width: 940px) { .lp-hero { grid-template-columns: 1fr; } }

/* ------------------------------------------------------- the chipset ---
   One package, lifted off the page, with the network it answers to drawn
   around it. It replaced a status table in the launchpad hero, and the status
   moved to a strip underneath where it reads faster anyway.

   The float is a still one: the package sits above its own shadow and stays
   there. A part bobbing on a sine wave would be the only eased thing on this
   site, and it would read as a logo animation rather than as an object. */

.chipset {
  perspective: 1200px;
  display: flex; align-items: center; justify-content: center;
  /* A floating package projects upward: at fifty-six degrees a lift of L
     shows as 0.83L above where the object sits. The top pad is that room. */
  padding: clamp(30px, 6vw, 56px) 0 clamp(16px, 3vw, 30px);
  overflow: hidden;
}
.chip-scene {
  position: relative;
  width: min(430px, 62vw); aspect-ratio: 1;
  /* Two corrections, both because a tilted plane does not fill its own box.
     translateY comes first in the list, so it moves the ALREADY-rotated
     result down: the drawing is thrown upward by the float, and this puts it
     back in the middle of the space it occupies. The negative bottom margin
     then gives back the dead half that the square was still reserving. */
  transform: translateY(15%) rotateX(56deg) rotateZ(-38deg);
  transform-style: preserve-3d;
  margin-bottom: -4%;
}

/* --- the network ------------------------------------------------------- */

.chip-trace {
  position: absolute; left: 50%; top: 50%;
  height: 1px; margin-top: -0.5px;
  transform-origin: left center;
  transform-style: preserve-3d;
  background: var(--line-2);
  opacity: .45;
}
/* The pulse: the trace fills from the package outward as --run climbs. */
.chip-trace::after {
  content: ""; position: absolute; inset: 0;
  background: var(--grad-flat);
  transform: scaleX(var(--run, 0)); transform-origin: left;
}
/* Opacity is set by chipset.js: it carries how far round the orbit this
   trace sits as well as whether a pulse is on it. */

.chip-node {
  position: absolute;
  width: 16px; height: 16px; margin: -8px 0 0 -8px;
  background: var(--panel-3);
  border: 1px solid var(--line-2);
  /* The transform is set by chipset.js: it carries the node's place on the
     orbit as well as whatever the animation is doing to it. */
  transition: box-shadow .14s var(--snap), background-color .1s var(--snap);
}
.chip-node.on {
  background: var(--g2); border-color: transparent;
  box-shadow: 0 10px 18px -10px var(--glow);
}
.chip-node.agreed {
  background: var(--grad); border-color: transparent;
  box-shadow: 0 12px 22px -10px var(--glow);
}

/* --- the package ------------------------------------------------------- */

/* Cast on the ground plane, not on the package, so the gap between them is
   what reads as height. */
.chip-shadow {
  position: absolute; left: 50%; top: 50%;
  width: 42%; height: 42%; margin: -21% 0 0 -21%;
  background: radial-gradient(ellipse at center,
              rgba(16, 22, 19, .22), transparent 70%);
  filter: blur(11px);
  transform: translateZ(1px);
}

.chip-pkg {
  position: absolute; left: 50%; top: 50%;
  width: 48%; height: 48%; margin: -24% 0 0 -24%;
  /* The lift is set by chipset.js, as a fraction of the scene rather than a
     count of pixels: the same drawing has to hold at 430px and at 240. */
  transform-style: preserve-3d;
}

/* The side wall of the package: five outlines stacked down in Z. Cheaper
   than four real faces and, at this angle, indistinguishable from them. */
.chip-slab {
  position: absolute; inset: 0;
  background: var(--panel-3);
  border: 1px solid var(--line-2);
}

.chip-body {
  position: absolute; inset: 0;
  background: linear-gradient(150deg, var(--panel-2), var(--panel-3));
  border: var(--rule);
  box-shadow: var(--lift-2), 0 0 0 1px var(--line-soft) inset;
  display: flex; flex-direction: column;
  padding: 9%;
  gap: 6%;
}

/* The leads. Four banks, seven to a side, sunk a little below the body so
   the package has a thickness rather than being a card. */
.chip-pins { position: absolute; display: flex; transform: translateZ(-9px); }
.chip-pin { background: var(--line-2); border-radius: 1px; }

.chip-pins-n, .chip-pins-s {
  left: 12%; right: 12%; justify-content: space-between;
}
.chip-pins-n { top: -7%; }
.chip-pins-s { bottom: -7%; }
.chip-pins-n .chip-pin, .chip-pins-s .chip-pin { width: 5%; height: 9px; }

.chip-pins-e, .chip-pins-w {
  top: 12%; bottom: 12%; flex-direction: column; justify-content: space-between;
}
.chip-pins-w { left: -7%; }
.chip-pins-e { right: -7%; }
.chip-pins-e .chip-pin, .chip-pins-w .chip-pin { height: 5%; width: 9px; }

/* The die on top of the package: the same diagonal wavefront as the mark. */
.chip-die {
  flex: 1 1 auto;
  display: grid;
  grid-template-columns: repeat(8, 1fr);
  gap: 6%;
  transform-style: preserve-3d;
}
.chip-cell {
  background: var(--panel-3);
  border: 1px solid var(--line-soft);
  transition: box-shadow .14s var(--snap);
}
.chip-cell.set { background: var(--g3); border-color: transparent; opacity: .62; }
.chip-cell.edge {
  background: var(--g1); border-color: transparent; opacity: 1;
  transform: translateZ(9px);
  box-shadow: 0 8px 14px -6px var(--glow);
}
.chip-cell.agreed { background: var(--grad); border-color: transparent; opacity: 1; }

.chip-mark {
  flex: none;
  font-family: var(--mono); font-size: 9px; font-weight: 600;
  letter-spacing: .22em; color: var(--dim);
  text-align: center;
}

@media (max-width: 560px) {
  .chip-node { width: 12px; height: 12px; margin: -6px 0 0 -6px; }
  .chip-pins-n .chip-pin, .chip-pins-s .chip-pin { height: 6px; }
  .chip-pins-e .chip-pin, .chip-pins-w .chip-pin { width: 6px; }
  .chip-mark { font-size: 8px; letter-spacing: .16em; }
}
@media (prefers-reduced-motion: reduce) {
  .chip-cell, .chip-node { transition: none; }
  .chip-cell.edge { transform: none; }
}

/* ------------------------------------------------------ the launch state ---
   Six facts, each either true or not yet. A strip rather than a table: the
   question a reader has here is "what is live", and a row of cells answers it
   in one pass where a two-column table makes them read down. */

.hero .statestrip { margin-top: clamp(6px, 2vw, 18px); }
.statestrip {
  border: var(--rule); background: var(--panel); box-shadow: var(--lift);
  margin-bottom: clamp(18px, 3vw, 30px);
}
.statestrip-head {
  display: flex; align-items: center; gap: 10px;
  padding: 11px 16px; border-bottom: var(--rule); background: var(--panel-2);
}
.statestrip-head .grow { margin-left: auto; }
.statestrip-cells {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(150px, 100%), 1fr));
  gap: 1px; background: var(--line-soft);
}
.statecell {
  background: var(--panel);
  padding: 13px 16px 15px;
  display: flex; flex-direction: column; gap: 6px;
  min-width: 0;
}
.statecell b {
  font-family: var(--mono); font-size: 12.5px; font-weight: 600;
  letter-spacing: .08em; color: var(--dim);
  overflow-wrap: anywhere;
}
.statecell.on b { color: var(--signal-ink); }
/* A live row carries the rail; a pending one does not, so the strip can be
   read by colour alone from across a table. */
.statecell.on { position: relative; }
.statecell.on::before {
  content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 2px;
  background: var(--grad);
}

/* ------------------------------------------------------- the workbench ---
   Editor on the left, the machine it feeds on the right. Both columns are
   min-width: 0 so a long line of assembly scrolls inside the editor instead
   of widening the page. */

.wb {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr);
  gap: 20px; align-items: start;
}
@media (max-width: 1000px) { .wb { grid-template-columns: 1fr; } }

.wb-editor { display: flex; flex-direction: column; }

/* The gutter and the textarea share one box and one scroll position, so the
   numbers stay beside the lines they number. */
.wb-code {
  display: flex; align-items: stretch;
  border: 1px solid var(--line-2);
  background: var(--well);
  min-height: 300px;
}
.wb-gutter {
  flex: none; width: 42px;
  padding: 12px 0;
  background: var(--panel-2);
  border-right: 1px solid var(--line-soft);
  overflow: hidden;
  font-family: var(--mono); font-size: 12.5px; line-height: 20px;
  font-variant-numeric: tabular-nums;
  user-select: none;
}
.wb-gutter .gl {
  display: block; font-style: normal;
  padding-right: 10px; text-align: right;
  color: var(--dim);
}
/* The line the assembler stopped on. One colour, one line, no red wash over
   the whole editor: the error message already says what happened. */
.wb-gutter .gl.bad {
  color: var(--on-signal); background: var(--amber); font-weight: 600;
}

#wb-src {
  flex: 1 1 auto; min-width: 0;
  padding: 12px 14px;
  background: transparent; color: var(--text);
  border: 0; outline: none; resize: vertical;
  font-family: var(--mono); font-size: 12.5px; line-height: 20px;
  tab-size: 2;
  min-height: 300px;
  white-space: pre; overflow-wrap: normal; overflow-x: auto;
}
.wb-code:focus-within { border-color: var(--signal); }
#wb-src::selection { background: var(--tint); }
#wb-src::-webkit-scrollbar,
.listing::-webkit-scrollbar { width: 8px; height: 8px; }
#wb-src::-webkit-scrollbar-track { background: var(--panel-2); }
#wb-src::-webkit-scrollbar-thumb { background: var(--line-2); }

/* The assembler's one line of speech. It is the only thing on the page that
   changes colour to mean something, so it earns the three states. */
.wb-msg {
  font-family: var(--mono); font-size: 11.5px; line-height: 1.5;
  letter-spacing: .03em;
  padding: 10px 12px;
  border-left: 2px solid var(--line-2);
  background: var(--panel-2);
  color: var(--text-2);
  min-height: 40px;
}
.wb-msg.ok { border-left-color: var(--signal); color: var(--text); }
.wb-msg.bad { border-left-color: var(--amber); color: var(--text); }
.wb-msg.warn { border-left-color: var(--amber); color: var(--text-2); }

/* A legend strip along the foot of a panel: the field widths the listing
   above it is drawn to. */
.panel-foot {
  display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
  padding: 10px 12px; border-top: var(--rule); background: var(--panel-2);
}
.panel-foot .grow { margin-left: auto; }

/* ------------------------------------------------- the live opcode table ---
   The table is wired to the processor above it: an instruction lights its own
   row when it executes and the light decays from there, so what you read is
   the instruction mix of whatever is running rather than a list. The tint is
   set per row by app.js, because the value is continuous and CSS has no
   business interpolating a colour it cannot measure. */

.isa-live {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  padding: 11px 14px;
  border: var(--rule); border-bottom: 0;
  background: var(--panel-2);
}
.isa-live .grow {
  margin-left: auto;
  font-weight: 600; letter-spacing: .16em; color: var(--signal-ink);
  font-variant-numeric: tabular-nums;
}
/* The one executing right now, rather than merely warm. */
#isa-body tr { transition: none; }
/* On the executing row the wash IS the accent, so the type stops being one:
   accent green on a green field is the one combination that reads worse the
   brighter it gets. */
#isa-body tr.live td { color: var(--text); }
#isa-body tr.live td.op { color: var(--text); font-weight: 700; }
#isa-body tr.live td.num { color: var(--text); font-weight: 600; }
/* A rail on the left edge, drawn on the first cell because a table row cannot
   carry a pseudo-element reliably. Its colour is set by app.js: it tracks the
   heat rather than the .live flag, so a row that was touched a moment ago is
   still marked after the wash behind it has faded. */
#isa-body tr td:first-child {
  border-left: 2px solid transparent;
  transition: border-left-color .1s var(--snap);
}
/* Brighter still on the row executing right now. */
#isa-body tr.live td:first-child { border-left-width: 3px; }

/* ---------------------------------------------------- instruction table ---
   Four groups, ordered the way somebody writing a program thinks rather than
   by opcode number. Every row is a button: clicking it drops the form into
   the editor, so the table is a keyboard rather than a poster. */

.isa-groups {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(min(260px, 100%), 1fr));
  gap: 18px; align-items: start;
}
.isa-group {
  border: var(--rule); background: var(--panel); box-shadow: var(--lift);
  display: flex; flex-direction: column;
}
.isa-group-head {
  display: flex; align-items: baseline; gap: 10px;
  padding: 12px 14px; border-bottom: var(--rule); background: var(--panel-2);
}
.isa-count {
  margin-left: auto;
  font-family: var(--mono); font-size: 10px; letter-spacing: .12em;
  color: var(--dim);
}
.isa-op {
  display: grid;
  grid-template-columns: minmax(0, auto) minmax(0, 1fr) auto;
  align-items: baseline; gap: 4px 12px;
  width: 100%; text-align: left;
  padding: 9px 14px;
  background: transparent; border: 0;
  border-bottom: 1px solid var(--line-soft);
  border-left: 2px solid transparent;
  cursor: pointer; font: inherit;
  transition: background-color .12s var(--snap), border-left-color .12s var(--snap);
}
.isa-op:last-of-type { border-bottom: 0; }
.isa-op:hover, .isa-op:focus-visible {
  background: var(--tint); border-left-color: var(--signal); outline: none;
}
.isa-op code {
  font-family: var(--mono); font-size: 12.5px; font-weight: 600;
  color: var(--text); background: none; padding: 0; border: 0;
  white-space: nowrap;
}
.isa-op:hover code { color: var(--signal-ink); }
.isa-eff {
  font-size: 12px; color: var(--text-2); line-height: 1.4;
  min-width: 0; overflow-wrap: anywhere;
}
/* The flag column is the same width on every row so C Z reads as a column
   rather than as trailing text. */
.isa-fl {
  font-family: var(--mono); font-size: 9.5px; letter-spacing: .14em;
  color: var(--signal-ink); min-width: 22px; text-align: right;
}
.isa-group-note {
  padding: 10px 14px; border-top: var(--rule); background: var(--panel-2);
  font-size: 12px; color: var(--dim); line-height: 1.45;
}

/* The launchpad pairs the ROM with the register file, and the file is always
   sixteen rows tall. A floor under the listing keeps the two panels the same
   object rather than a tall one beside a stub. */
#lp-listing { min-height: 322px; max-height: 322px; }

/* The mnemonic beside a listing row. */
.listing .src { color: var(--dim); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.listing .line.at .src { color: var(--signal-ink); }

@media (max-width: 560px) {
  .wb-gutter { width: 34px; }
  #wb-src, .wb-gutter { font-size: 11.5px; line-height: 19px; }
}

@media (prefers-reduced-motion: reduce) {
  .sec-head h2 em::before, .sec-head h2 em::after { transform: scaleX(1); }
}
.sec-copy { color: var(--text-2); max-width: 62ch; font-size: 16px; }
.sec-copy + .sec-copy { margin-top: 14px; }
.sec-copy b { color: var(--text); font-weight: 600; }

/* a hairline that draws itself in when the section arrives */
.drawline { height: 1px; background: var(--line); position: relative; overflow: hidden; }
.drawline::after {
  content: ""; position: absolute; inset: 0;
  background: var(--grad-flat);
  transform: scaleX(0); transform-origin: left;
  transition: transform .9s var(--draw);
}
.drawline.in::after { transform: scaleX(1); }

/* blocks that arrive rather than appear. 14px is enough to read as motion
   without the page ever looking like it is assembling itself */
.rise { opacity: 0; transform: translateY(14px); }
.rise.in {
  opacity: 1; transform: none;
  transition: opacity .42s var(--latch), transform .42s var(--latch);
}
/* Reached by scrolling back up, or jumped clean over. The reader has already
   been here; making them watch it arrive again is a stutter, not a flourish. */
.rise.in.instant { transition: none; }
.drawline.in.instant::after { transition: none; }

/* ---------------------------------------------------------------- grids */

.cards {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(232px, 1fr));
  border: var(--rule); border-right: 0; border-bottom: 0;
  background: var(--panel);
  position: relative; box-shadow: var(--lift);
}
/* one rail for the whole set, because the cards are peers rather than steps */
.cards::before {
  content: ""; position: absolute; left: 0; right: 0; top: -1px; height: 2px;
  background: var(--grad-flat); z-index: 1;
}
.cards > div {
  padding: 22px 22px 26px;
  border-right: var(--rule); border-bottom: var(--rule);
  position: relative;
  transition: background-color .12s var(--snap);
}
.cards > div::before {
  content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 2px;
  background: var(--grad); transform: scaleY(0); transform-origin: top;
  transition: transform .24s var(--latch);
}
.cards > div:hover { background: var(--panel-2); }
.cards > div:hover::before { transform: scaleY(1); }
.cards h3 {
  font-family: var(--display); font-size: 19px; font-weight: 600;
  margin: 9px 0 7px; color: var(--text);
}
.cards p { font-size: 14px; color: var(--text-2); line-height: 1.55; }
.cards .big {
  font-family: var(--display); font-size: 34px; font-weight: 700;
  color: var(--signal-ink); letter-spacing: -0.01em; margin: 8px 0 4px;
  font-variant-numeric: tabular-nums;
}

/* ---------------------------------------------------------------- panels */

.panel { border: var(--rule); background: var(--panel); box-shadow: var(--lift); }
.panel-head {
  padding: 12px 16px; border-bottom: var(--rule); background: var(--panel-2);
  display: flex; align-items: center; gap: 10px;
}
/* a trailing label in a panel head belongs at the far end, the same way it
   does in the console bar */
.panel-head .grow { margin-left: auto; }
.panel-body { padding: 16px; }

.split {
  display: grid; grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
  gap: 20px; align-items: start;
}
@media (max-width: 900px) { .split { grid-template-columns: 1fr; } }

/* ------------------------------------------------------- register file ---
   Sixteen rows of eight flip-flops. The bit cells are the same square the
   mark is made of and the same square the die map draws, because they are all
   the same object: one bit of state, lit or not. */

.regs { border: var(--rule); background: var(--panel); }
.reg {
  display: grid;
  grid-template-columns: 30px auto minmax(0, 1fr);
  align-items: center; gap: 12px;
  padding: 5px 10px;
  border-bottom: 1px solid var(--line-soft);
}
.reg:last-child { border-bottom: 0; }
.reg .k {
  font-family: var(--mono); font-size: 10px; letter-spacing: .1em;
  color: var(--dim);
}
/* Fixed cells, not a bar that stretches: a bit is a bit, and eight of them
   should read as eight objects rather than as one meter. */
.reg .bits { display: flex; gap: 3px; flex: none; }
.reg .bit {
  width: 12px; height: 12px; flex: none;
  background: var(--panel-3);
  border: 1px solid var(--line-soft);
  /* No transition: a flip-flop changes on the edge or it does not change. */
}
.reg .bit.on { background: var(--grad); border-color: transparent; }
/* every fourth cell carries a heavier edge, so eight bits stay countable */
.reg .bit:nth-child(4) { margin-right: 5px; }
.reg .v {
  font-family: var(--mono); font-size: 12.5px; font-weight: 600;
  color: var(--text); font-variant-numeric: tabular-nums; text-align: right;
  justify-self: end;
}
.reg.hot .v { color: var(--signal-ink); }

/* ---------------------------------------------------------- ROM listing ---
   One row per word, and the word shown as the four fields the decoder splits
   it into, to scale. A jump spends its width on an address; a mov barely uses
   any of it. The mnemonic follows, for anyone who would rather read English. */

.listing { font-family: var(--mono); font-size: 12px; max-height: 300px; overflow-y: auto; }
.listing .line {
  display: grid;
  grid-template-columns: 30px minmax(90px, 1fr) minmax(0, 1.15fr);
  align-items: center; gap: 10px;
  padding: 4px 12px;
  color: var(--text-2);
  border-left: 2px solid transparent;
}
.listing .line .pc { color: var(--dim); font-variant-numeric: tabular-nums; }
.listing .word { display: flex; gap: 2px; }
.listing .fld {
  flex-grow: 1; flex-basis: 0;
  font-style: normal; font-size: 9px; line-height: 14px; text-align: center;
  color: var(--on-signal); background: var(--g3);
  overflow: hidden;
}
/* the four fields are told apart by weight, not by four different hues:
   colour still means one thing on this page */
.listing .fld-op { background: var(--g2); }
.listing .fld-rd { background: var(--g3); }
.listing .fld-rs { background: var(--g3); opacity: .78; }
.listing .fld-im { background: var(--g3); opacity: .58; }
.listing .fld.zero { background: var(--panel-3); color: var(--dim); }
.listing .line.at {
  background: var(--tint);
  border-left-color: var(--signal);
  color: var(--signal-ink);
}
.listing .line.at .fld-op { background: var(--g1); }
.listing::-webkit-scrollbar { width: 8px; }
.listing::-webkit-scrollbar-track { background: var(--panel-2); }
.listing::-webkit-scrollbar-thumb { background: var(--line-2); }
.listing::-webkit-scrollbar-thumb:hover { background: var(--signal-2); }

/* controls */
.controls { display: flex; flex-wrap: wrap; gap: 8px; }
.chips { display: flex; flex-wrap: wrap; gap: 6px; }
.chip {
  font-family: var(--mono); font-size: 11px; font-weight: 500; letter-spacing: .1em;
  padding: 7px 12px; border: 1px solid var(--line-2); background: transparent;
  color: var(--text-2); cursor: pointer; text-transform: uppercase;
  transition: color .12s var(--snap), border-color .12s var(--snap),
              background-color .12s var(--snap);
}
.chip:hover { border-color: var(--text-2); color: var(--text); }
.chip[aria-pressed="true"] {
  border-color: var(--signal); color: var(--signal-ink);
  background: var(--tint);
}

.field { display: flex; flex-direction: column; gap: 7px; }
/* The link rows take the same definition rather than a copy of it. Left to
   the browser default they came out 21px tall, which is under the size a
   thumb can reliably hit and was found by the layout audit, not by eye. */
.field input, .field select, .linkrow input {
  font-family: var(--mono); font-size: 14px;
  background: var(--panel-2); color: var(--text);
  border: 1px solid var(--line-2); padding: 11px 13px; width: 100%;
}
.field input:focus, .linkrow input:focus { border-color: var(--signal); outline: none; }
.hint { font-family: var(--mono); font-size: 10.5px; color: var(--dim); letter-spacing: .06em; }

/* ------------------------------------------------------ chip economics ---
   A ledger, not a card grid: label left, figure right, and the reason it is
   that figure underneath in the quiet weight. Everything is computed, so the
   numbers move as the chips above are picked. */

.econ { border: var(--rule); background: var(--panel-2); }
.econ-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: baseline;
  gap: 4px 14px;
  padding: 10px 14px;
  border-bottom: 1px solid var(--line-soft);
}
.econ-row:last-child { border-bottom: 0; }
.econ-row b {
  font-family: var(--mono); font-size: 16px; font-weight: 600;
  color: var(--signal-ink); font-variant-numeric: tabular-nums;
  text-align: right;
}
/* the note runs the full width beneath both, so long reasons never squeeze
   the figure into two lines */
.econ-note {
  grid-column: 1 / -1;
  font-family: var(--mono); font-size: 10.5px; letter-spacing: .06em;
  color: var(--dim);
}
/* the last row is the one that matters: it is what a sponsor is paid */
.econ-row:last-child { background: var(--tint-soft); }
.econ-row:last-child b { font-size: 19px; }

/* ----------------------------------------------------- the component shelf
   Volumes on a shelf. Each spine is turned away until you reach for it, then
   it squares up and opens. The rotation is the only place on this page that
   uses perspective, and it earns it: these are objects, and the shelf is the
   one metaphor that makes seven blocks feel countable. */

.shelf-wrap { margin-top: clamp(30px, 4vw, 48px); }
.shelf {
  display: flex; gap: 10px; align-items: stretch;
  margin-top: 20px;
  /* The lean throws each spine's far edge past its own layout box, so the
     last one was being sliced off by the scroll container. The right pad is
     that overhang. */
  /* Vertical room as well as horizontal. A leaning spine's corners travel
     outside its own box, and a scroll container was slicing the top-right
     one clean off, which is what made the lean read as a crop rather than a
     depth. */
  /* The left pad is the other overhang. With the vanishing point at 88% an
     opening spine is pushed AWAY from it, so the leftmost one travels left as
     it comes forward: measured at 40px past the box, which the scroll
     container was slicing off "Register read" with. */
  padding: 30px 54px 30px 52px;
  perspective: 1500px;
  perspective-origin: 88% 50%;
  overflow-x: auto;
}
.spine {
  /* Closed spines share the full width rather than sitting at a fixed 84px,
     which left half the row empty on a wide screen. The open one takes a set
     share and the rest divide what is left, so the shelf always reaches the
     right edge. */
  flex: 1 1 0;
  min-width: 76px;
  height: 404px;
  position: relative;
  padding: 0; margin: 0;
  border: var(--rule); background: var(--panel);
  /* A darker right edge stands in for the thickness of the block. Without it
     the spines read as leaning cards rather than as things with a spine. */
  border-right-width: 4px;
  box-shadow: var(--lift);
  cursor: pointer;
  transform-origin: left center;
  /* --i counts from the left, set by shelf.js. Volumes further from the
     viewer sit back and shrink very slightly, which is what makes the row
     read as a shelf receding rather than seven identical cards on a slant. */
  transform: rotateY(-19deg)
             translateZ(calc(var(--i, 0) * 7px - 42px))
             scale(calc(0.955 + var(--i, 0) * 0.0075));
  transition: flex .3s var(--latch), transform .3s var(--latch),
              box-shadow .3s var(--latch), filter .3s var(--latch);
  text-align: left;
  /* The far end of the row falls into shade, the near end does not. */
  filter: brightness(calc(0.94 + var(--i, 0) * 0.01));
}
/* Opening is driven by a class, never by :hover. Hovering a flex item that
   then grows moves it out from under the pointer, which drops the hover,
   which shrinks it back under the pointer: the spine flickered open and shut
   several times a second. A class set on mouseenter cannot do that. */
.spine.open {
  flex: 0 0 340px;
  transform: rotateY(0deg) translateZ(60px) scale(1);
  border-right-width: 1.5px;
  box-shadow: var(--lift-2), 0 40px 70px -40px rgba(16, 40, 28, .5);
  filter: none;
  z-index: 3;
}
.spine:focus-visible { outline-offset: 4px; }
/* the closed edge: the name read the long way, and the number at the foot */
.spine-edge {
  position: absolute; inset: 0;
  display: flex; flex-direction: column;
  align-items: center; justify-content: space-between;
  padding: 16px 0;
  transition: opacity .16s var(--snap);
}
.spine-name {
  writing-mode: vertical-rl; text-orientation: mixed;
  font-family: var(--display); font-size: 17px; font-weight: 600;
  letter-spacing: .04em; color: var(--text);
  flex: none;
}
/* How much of the die this block takes, drawn against the largest one. The
   cells are the same square as the mark, the die map and the register file,
   because they are all showing the same object: one unit of the processor. */
.spine-gauge {
  flex: 1 1 auto;
  display: flex; flex-direction: column; justify-content: flex-end;
  gap: 3px;
  width: 14px;
  margin: 16px 0;
}
.spine-gauge i {
  display: block; flex: 1 1 0; min-height: 2px;
  background: var(--panel-3);
  border: 1px solid var(--line-soft);
}
.spine-gauge i.on { background: var(--g3); border-color: transparent; }
/* The top of a filled gauge is the live end, so it carries the bright stop. */
.spine-gauge i.on:first-of-type { background: var(--g1); }
.spine:hover .spine-gauge i.on,
.spine:focus-visible .spine-gauge i.on { background: var(--g2); }
.spine-n {
  font-family: var(--mono); font-size: 12px; font-weight: 600;
  color: var(--signal-ink); font-variant-numeric: tabular-nums;
}
.spine.open .spine-edge { opacity: 0; pointer-events: none; }

.spine-face {
  position: absolute; inset: 0;
  display: flex; flex-direction: column;
  padding: 14px;
  overflow: hidden;
  opacity: 0; pointer-events: none;
  transition: opacity .2s var(--snap);
}
.spine.open .spine-face { opacity: 1; }

.spine-head { display: flex; flex-direction: column; gap: 3px; }
/* The locator: the whole die with this block lit, generated per card. */
.loc-wrap { display: flex; flex-direction: column; gap: 5px; margin-top: 12px; }
.loc { display: block; width: 100%; height: auto; border: 1px solid var(--line-soft); }
.spine-head b {
  font-family: var(--display); font-size: 17px; font-weight: 600; color: var(--text);
}

/* ------------------------------------------------------------- blueprint ---
   The sheet the framework is drawn on. A faint grid, a border, registration
   marks in the corners and a title block along the foot. Without them a
   schematic reads as an illustration; with them it reads as a drawing, which
   is what it is. */

.bp {
  display: flex; flex-direction: column; margin-top: 12px; min-height: 0;
  flex: 1 1 auto;
  border: 1px solid var(--line-2);
  background-color: var(--well);
  background-image:
    linear-gradient(to right, var(--grid) 1px, transparent 1px),
    linear-gradient(to bottom, var(--grid) 1px, transparent 1px);
  background-size: 13px 13px;
}
.bp-reg { stroke: var(--line-2); stroke-width: 1.2; }
.bp-title {
  /* Every track floored at zero. Left at auto the three cells hold their
     min-content and the title block grows wider than the sheet it is
     pinned to, which clips the end off the last one. */
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, auto) minmax(0, auto);
  gap: 0; border-top: 1px solid var(--line-2);
  margin-top: auto;   /* pinned to the foot of the sheet, always visible */
}
.bp-cell {
  display: flex; flex-direction: column; gap: 1px;
  padding: 6px 9px; border-left: 1px solid var(--line-2); min-width: 0;
}
.bp-cell:first-child { border-left: 0; }
.bp-cell > * { min-width: 0; overflow-wrap: anywhere; }
.bp-cell b {
  font-family: var(--mono); font-size: 11px; font-weight: 600; color: var(--text);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.bp-cell .ck { font-size: 8.5px; letter-spacing: .16em; }

/* the frameworks themselves: four kinds of mark and nothing else */
.fw { display: block; width: 100%; height: auto; margin: 0; flex: 0 1 auto; min-height: 0; }
.fw-w { stroke: var(--line-2); stroke-width: 1; }
.fw-hot { stroke: var(--signal); stroke-width: 1.5; }
.fw-dead { stroke: var(--line-soft); stroke-width: 1; stroke-dasharray: 2 3; }
.fw-b { fill: none; stroke: var(--text-2); stroke-width: 1.2; }
.fw-dim { stroke: var(--line-2); }
.fw-cell { fill: var(--panel-3); stroke: var(--line-soft); stroke-width: .8; }
.fw-cell.fw-on { fill: var(--signal); stroke: none; }
.fw-t {
  font-family: var(--mono); font-size: 8.5px; letter-spacing: .08em;
  fill: var(--dim); stroke: none;
}

@media (max-width: 700px) {
  /* No room to fan them out on a phone, so they stand square and scroll. */
  .shelf { perspective: none; }
  /* No room to share on a phone: fixed widths and a horizontal scroll. */
  .shelf { padding: 10px 12px 10px 4px; perspective: none; }
  .spine { transform: none; filter: none; flex: 0 0 132px; height: 400px; }
  /* The framework is 260 units wide; anything narrower than 306 clips it. */
  .spine.open { flex: 0 0 306px; transform: none; }
}
@media (prefers-reduced-motion: reduce) { .spine { transform: none; } }

/* --------------------------------------------------------- the symbols ---
   Three objects seen at an angle, one per section that earns one. The tilt is
   the only perspective on this page besides the shelf, and it is here for the
   same reason: these are objects, not charts.

   Cells lift off their plate in Z. That is what makes a wavefront read as
   something passing over a die rather than a colour changing. */

.symbol {
  perspective: 1000px;
  display: flex; justify-content: center; align-items: center;
  /* A guard: a rotated square's diagonal is 1.41 times its side, so the plate
     is sized against that below. This catches whatever the maths misses. */
  overflow: hidden;
  padding: clamp(30px, 5vw, 62px) 0 clamp(18px, 2vw, 26px);
}

.sym-plate {
  display: grid;
  gap: 5px;
  width: min(360px, 52vw); aspect-ratio: 1;
  transform: rotateX(56deg) rotateZ(-43deg);
  transform-style: preserve-3d;
}
.sym-cell {
  background: var(--panel-3);
  border: 1px solid var(--line-soft);
  /* No transition on the fill: a flip-flop has no in-between value. The lift
     is allowed to move, because a physical thing rising does take time. */
  transition: transform .18s var(--snap), box-shadow .18s var(--snap);
}
/* Printed, and recessive. A wafer fills up behind the lens, but the lens is
   what you are meant to be watching, so the finished fields sit back. */
.sym-cell.set { background: var(--g3); border-color: transparent; opacity: .32; }
/* Whatever is under the lens right now. */
.sym-cell.edge, .sym-cell.head {
  background: var(--g1); border-color: transparent; opacity: 1;
  transform: translateZ(22px);
  box-shadow: 0 14px 26px -8px var(--glow);
}

/* Not everything belongs in the middle of the page. A symbol placed off to
   one side, with its caption held against the opposite margin, reads as a
   thing on a bench rather than as a centred illustration. */
.symbol.aside {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(20px, 4vw, 60px);
  align-items: center;
  justify-content: start;
  padding: clamp(24px, 3.5vw, 48px) 0 clamp(16px, 2vw, 24px);
}
/* object right, words left.
   Perspective applies to a direct child only, so it has to live on the box
   that actually holds the transformed thing, not on .symbol above it. */
.symbol.aside > :first-child {
  grid-column: 2; justify-self: center;
  perspective: 1100px;
  display: flex; align-items: center; justify-content: center;
}
.symbol.aside .sym-cap { grid-column: 1; grid-row: 1; }
/* object left, words right */
.symbol.aside.flip > :first-child { grid-column: 1; }
.symbol.aside.flip .sym-cap { grid-column: 2; }

.sym-cap {
  max-width: 46ch;
  font-size: 14.5px; line-height: 1.65; color: var(--text-2);
}
.sym-cap b { color: var(--text); font-weight: 600; }
.sym-cap .ck { display: block; margin-bottom: 10px; }
.sym-cap .sym-legend {
  display: flex; flex-wrap: wrap; gap: 6px 16px; margin-top: 14px;
}
.sym-cap .sym-legend span {
  font-family: var(--mono); font-size: 10.5px; letter-spacing: .08em;
  color: var(--dim); display: inline-flex; align-items: center; gap: 7px;
}
.sym-cap .sym-legend i {
  width: 9px; height: 9px; flex: none;
  border: 1px solid var(--line-2); background: var(--panel-3);
}
.sym-cap .sym-legend i.k-set { background: var(--g3); border-color: transparent; }
.sym-cap .sym-legend i.k-head { background: var(--g1); border-color: transparent; }
.sym-cap .sym-legend i.k-gone { background: transparent; opacity: .35; }

@media (max-width: 820px) {
  .symbol.aside, .symbol.aside.flip {
    grid-template-columns: 1fr;
    justify-items: center;
  }
  .symbol.aside > :first-child,
  .symbol.aside.flip > :first-child { grid-column: 1; grid-row: 1; }
  .symbol.aside .sym-cap,
  .symbol.aside.flip .sym-cap { grid-column: 1; grid-row: 2; text-align: left; }
}

/* § 02 · the skyline ------------------------------------------------------
   Seven towers on a tilted floor, one per block of the processor, each as
   tall as the gates that block really carries. The bricks stack in Z out of
   the floor plane rather than in Y across it, which is the difference between
   a bar chart and a building. */

.sym-floor {
  display: flex; align-items: flex-end; gap: clamp(9px, 1.4vw, 15px);
  width: min(400px, 60vw); height: min(230px, 34vw);
  transform: rotateX(62deg) rotateZ(-38deg);
  transform-style: preserve-3d;
}
.sym-tower {
  position: relative;
  flex: 1 1 0; min-width: 0; aspect-ratio: 1;
  transform-style: preserve-3d;
  /* The footprint: where the block sits on the die, drawn whether or not
     anything is standing on it yet. */
  border: 1px solid var(--line-soft);
  background: var(--panel-2);
}
.sym-brick {
  position: absolute; inset: 0;
  border: 1px solid var(--line-soft);
  background: transparent;
  opacity: 0;
  transition: opacity .12s var(--snap);
}
.sym-brick.set { background: var(--g3); border-color: transparent; opacity: .5; }
.sym-brick.head {
  background: var(--g1); border-color: transparent; opacity: 1;
  box-shadow: 0 12px 22px -10px var(--glow);
}
/* An optimised gate is not dimmed, it is not there. The outline stays for a
   beat so you can see what was taken. */
.sym-brick.gone {
  background: transparent; border-color: var(--line-2);
  opacity: .16;
}

/* § 06 · the consensus ring ----------------------------------------------
   Twelve nodes on a closed circuit, seen at an angle. Nothing counts until
   the result has come all the way round, which is the shape of the argument
   as much as the shape of the object. */

.sym-ring {
  position: relative;
  width: min(320px, 52vw); aspect-ratio: 1;
  transform: rotateX(60deg);
  transform-style: preserve-3d;
}
.sym-node {
  position: absolute; left: 50%; top: 50%;
  width: 30px; height: 30px; margin: -15px 0 0 -15px;
  background: var(--panel-3);
  border: 1px solid var(--line-2);
  transition: box-shadow .16s var(--snap), background-color .1s var(--snap);
}
.sym-node.set { background: var(--g3); border-color: transparent; opacity: .55; }
.sym-node.edge {
  background: var(--g1); border-color: transparent;
  box-shadow: 0 16px 26px -10px var(--glow);
}
.sym-node.agreed {
  background: var(--grad); border-color: transparent;
  box-shadow: 0 10px 20px -12px var(--glow);
}
/* Drawn from the middle outwards, and only when the circuit has closed. */
.sym-spoke {
  position: absolute; left: 50%; top: 50%;
  /* Reaches exactly the node centres: the same 46% the nodes are placed at. */
  width: 46%; height: 1px; margin-top: -0.5px;
  transform-origin: left center;
  background: var(--line-2);
  opacity: 0;
  transition: opacity .18s var(--snap);
}
.sym-spoke.on { background: var(--grad-flat); opacity: .55; }
.sym-hub {
  position: absolute; left: 50%; top: 50%;
  width: 34px; height: 34px; margin: -17px 0 0 -17px;
  border: 1px solid var(--line-2); background: var(--panel);
  transform: translateZ(4px);
  transition: background-color .12s var(--snap);
}
.sym-hub.on {
  background: var(--grad); border-color: transparent;
  transform: translateZ(40px);
  box-shadow: 0 22px 34px -14px var(--glow);
}

@media (max-width: 560px) {
  .sym-node { width: 22px; height: 22px; margin: -11px 0 0 -11px; }
  .sym-hub { width: 26px; height: 26px; margin: -13px 0 0 -13px; }
}
@media (prefers-reduced-motion: reduce) {
  .sym-brick, .sym-node, .sym-spoke, .sym-hub { transition: none; }
  .sym-hub.on { transform: translateZ(4px); }
}

.symbol-cap {
  text-align: center; max-width: 46ch; margin: 0 auto clamp(26px, 3vw, 40px);
  line-height: 1.7;
}

@media (prefers-reduced-motion: reduce) {
  .sym-cell { transition: none; }
  .sym-cell.edge, .sym-cell.head, .sym-cell.gone, .sym-cell.agreed { transform: none; }
}

/* the closing argument, set larger than the body it follows */
.name-body {
  columns: 2; column-gap: clamp(28px, 4vw, 56px);
  margin-bottom: clamp(28px, 3vw, 42px);
}
.name-body .sec-copy { max-width: none; break-inside: avoid; margin-bottom: 14px; }
@media (max-width: 820px) { .name-body { columns: 1; } }

.name-close {
  border: var(--rule); border-left: 0; background: var(--panel);
  padding: 26px 30px; position: relative; box-shadow: var(--lift);
  margin-bottom: clamp(26px, 3vw, 40px);
}
.name-close::before {
  content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 2px;
  background: var(--grad);
}
.name-close p {
  font-family: var(--display); font-size: clamp(19px, 2.2vw, 27px);
  font-weight: 600; line-height: 1.28; color: var(--text); text-wrap: balance;
  margin-bottom: 12px;
}

/* --------------------------------------------------- specification plate ---
   A datasheet header, not a product shot. Rows of fact, then the output port
   drawn as the eight flip-flops it is. */

.plate-id {
  display: flex; align-items: baseline; gap: 12px;
  padding-bottom: 12px; border-bottom: var(--rule);
}
.plate-id b {
  font-family: var(--display); font-size: clamp(20px, 2.4vw, 28px);
  font-weight: 700; color: var(--text); letter-spacing: -.01em;
}
.plate-tick {
  font-family: var(--mono); font-size: 11px; font-weight: 600; letter-spacing: .16em;
  padding: 3px 9px; background: var(--grad); color: var(--on-signal);
  margin-left: auto; white-space: nowrap;
}
.plate-rows { display: grid; }
.plate-row {
  display: flex; align-items: baseline; justify-content: space-between; gap: 16px;
  padding: 9px 0; border-bottom: 1px solid var(--line-soft);
}
.plate-row > * { min-width: 0; }

/* A row holds a label, sometimes a 42-character address, and sometimes a
   note about it. On a phone those three cannot share a line, and left on
   one the address ran 121px past the edge. Wrapping is the cure there and
   a disfigurement anywhere else, so it is scoped to where it is needed. */
@media (max-width: 720px) {
  .plate-row { flex-wrap: wrap; }
  .plate-row > * { overflow-wrap: anywhere; }
}
.plate-row b {
  font-family: var(--mono); font-size: 12.5px; font-weight: 600;
  color: var(--text); text-align: right;
}
.plate-out {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  padding-top: 14px;
}
.plate-bits { display: flex; gap: 4px; margin-left: auto; }
.plate-bits .bit {
  width: 16px; height: 16px;
  background: var(--panel-3); border: 1px solid var(--line-soft);
}
.plate-bits .bit.on { background: var(--grad); border-color: transparent; }
.plate-out b {
  font-family: var(--mono); font-size: 17px; font-weight: 600;
  color: var(--signal-ink); font-variant-numeric: tabular-nums; min-width: 30px;
  text-align: right;
}

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

.tw { overflow-x: auto; border: var(--rule); background: var(--panel); }
table { border-collapse: collapse; width: 100%; font-family: var(--mono); font-size: 12.5px; min-width: 540px; }
th {
  text-align: left; font-size: 10px; font-weight: 600; letter-spacing: .16em;
  text-transform: uppercase; color: var(--dim);
  padding: 12px 14px; border-bottom: var(--rule); background: var(--panel-2);
  white-space: nowrap;
}
td { padding: 8px 14px; border-bottom: 1px solid var(--line-soft); color: var(--text-2); vertical-align: top; }
tbody tr:last-child td { border-bottom: 0; }
tbody tr:hover td { background: var(--tint-soft); }
td.op { color: var(--signal-ink); font-weight: 600; }
td.num, th.num { text-align: right; font-variant-numeric: tabular-nums; }
td b { color: var(--text); font-weight: 600; }

/* ---------------------------------------------------------------- pipeline */

/* A pipeline is a sequence, so it is drawn as one: a single rail runs the
   width of the strip and every step hangs a numbered marker off it. The
   numbers are not decoration here, they are the order the stages happen in. */
.pipe {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(min(190px, 100%), 1fr));
  gap: 1px; background: var(--line); border: var(--rule);
  position: relative; box-shadow: var(--lift);
}
.pipe::before {
  content: ""; position: absolute; left: 0; right: 0; top: -1px; height: 2px;
  background: var(--grad-flat); z-index: 1;
}
.pipe > div {
  background: var(--panel); padding: 26px 20px 24px; position: relative;
  transition: background-color .2s ease;
}
.pipe > div:hover { background: var(--panel-2); }
/* the marker sits on the rail, half in the panel and half out of it */
.pipe .n {
  position: absolute; top: -1px; left: 20px;
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 26px; height: 15px; padding: 0 6px;
  font-family: var(--mono); font-size: 9.5px; font-weight: 600;
  letter-spacing: .14em;
  background: var(--grad); color: var(--on-signal);
}
.pipe h3 { font-size: 18px; font-weight: 600; margin: 12px 0 6px; }
.pipe p { font-size: 13.5px; color: var(--text-2); line-height: 1.5; }

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

pre {
  margin: 0; overflow-x: auto; background: var(--well); border: var(--rule);
  padding: 16px 18px; font-family: var(--mono); font-size: 12.5px; line-height: 1.65;
  color: var(--text-2);
}
pre .c { color: var(--dim); }
pre .k { color: var(--signal-ink); }

/* ---------------------------------------------------------------- roadmap */

/* The mark as our CLI prints it. Terminal cells are twice as tall as they
   are wide, so a square needs two characters, and the whole block sits on the
   well background because that is what a terminal is. */

.ansi {
  border: 1px solid var(--line-2); background: var(--well);
  padding: 16px 18px; margin-bottom: 14px;
  font-family: var(--mono); font-size: 13px; line-height: 1.28;
  overflow-x: auto;
}
.ansi-row { display: flex; align-items: baseline; gap: 22px; white-space: pre; }
.ansi-art { flex: none; letter-spacing: 0; }
.ansi-c { font-style: normal; color: transparent; }
/* The finished fields sit back; the edge is the thing you are watching. Same
   relationship as the moving symbols, in a font. */
.ansi-c.set { color: var(--g3); }
.ansi-c.edge { color: var(--g1); }
.ansi-txt { min-width: 0; }
.ansi-name { color: var(--signal-ink); font-weight: 600; letter-spacing: .12em; }
.ansi-edge { color: var(--signal-ink); font-weight: 600; letter-spacing: .1em; }
.ansi-dim { color: var(--dim); }

/* ------------------------------------------------- run it somewhere else ---
   A tabbed transcript. The tabs are the same chips used everywhere else, and
   the body is a real terminal transcript rather than terminal-shaped art, so
   it gets the well background and nothing more decorative than that. */

.runit {
  border: var(--rule); background: var(--panel); box-shadow: var(--lift);
  margin-bottom: 22px;
}
.runit-head {
  display: flex; align-items: center; gap: 16px; flex-wrap: wrap;
  padding: 12px 16px; border-bottom: var(--rule); background: var(--panel-2);
}
.runit-head .chips { margin-left: auto; }
.runit-body { padding: 16px; }
.runit-code {
  border: 1px solid var(--line-2); background: var(--well);
  padding: 16px 18px; margin: 0;
  font-family: var(--mono); font-size: 12.5px; line-height: 1.75;
  color: var(--text-2); overflow-x: auto;
  white-space: pre;
}
/* A <pre> already is the code block. The inline <code> chip styling would
   otherwise paint a tinted box behind every line of the transcript. */
.runit-code code {
  background: none; border: 0; padding: 0; color: inherit;
  font-size: inherit; font-family: inherit;
}

.runit-note {
  margin-top: 12px; font-size: 13px; color: var(--dim); line-height: 1.55;
  max-width: 80ch;
}
@media (max-width: 620px) {
  .runit-head .chips { margin-left: 0; }
  .runit-code { font-size: 11.5px; padding: 12px 13px; }
}

/* ------------------------------------------------------------ roadmap ---
   The old version was four equal cards, which said the releases were four
   equal things and hid every deliverable inside a paragraph. This is a rail
   with a position on it and a checklist under each stop, because the useful
   question a reader has is not "what is planned" but "what exists today".

   The rail first: a scale with the current release marked. */

.rail { margin-bottom: clamp(26px, 3vw, 38px); position: relative; }
.rail-line {
  height: 2px; background: var(--line-2); position: relative;
  margin: 0 0 14px;
}
/* One fifth, because T-0 is the first of five. It is a position, not a
   percentage of anything, so it does not animate or creep. */
.rail-fill {
  position: absolute; left: 0; top: 0; bottom: 0; width: 10%;
  background: var(--grad-flat);
}
.rail-stops {
  display: grid; grid-template-columns: repeat(5, 1fr);
  gap: 8px;
}
.rail-stop {
  display: flex; flex-direction: column; gap: 3px;
  position: relative; padding-top: 2px;
}
.rail-stop::before {
  content: ""; position: absolute; top: -22px; left: 0;
  width: 9px; height: 9px;
  background: var(--panel); border: 2px solid var(--line-2);
}
.rail-stop.is-at::before { background: var(--signal); border-color: var(--signal); }
/* A release that shipped is filled but not lit: the marker means "here",
   and two lit dots would mean the rail has two heres. */
.rail-stop.is-done::before { background: var(--dim); border-color: var(--dim); }
.rail-stop.is-done b { color: var(--text-2); }
.rail-stop b {
  font-family: var(--mono); font-size: 11px; font-weight: 600;
  letter-spacing: .16em; color: var(--dim);
}
.rail-stop.is-at b { color: var(--signal-ink); }
.rail-stop span { font-size: 12px; color: var(--dim); line-height: 1.35; }
.rail-stop.is-at span { color: var(--text-2); }
@media (max-width: 700px) {
  .rail-stops { grid-template-columns: repeat(5, 1fr); gap: 4px; }
  .rail-stop span { display: none; }
}

/* The releases themselves: a rail down the left, one row per release. */

.rmap { display: flex; flex-direction: column; }
.rmap-row {
  display: grid; grid-template-columns: 72px minmax(0, 1fr);
  gap: clamp(14px, 2vw, 26px);
  padding: 0 0 clamp(24px, 3vw, 36px);
}
.rmap-row:last-child { padding-bottom: 0; }

/* The vertical line is drawn by the marker column, so it stops with the last
   release instead of running off into the footer. */
.rmap-rail {
  display: flex; flex-direction: column; align-items: center; gap: 10px;
  position: relative;
}
.rmap-row:not(:last-child) .rmap-rail::after {
  content: ""; position: absolute; top: 26px; bottom: -36px; width: 1px;
  background: var(--line-2);
}
.rmap-dot {
  width: 14px; height: 14px; flex: none;
  background: var(--panel); border: 2px solid var(--line-2);
  margin-top: 5px;
}
.rmap-row.is-at .rmap-dot {
  background: var(--grad); border-color: transparent;
  box-shadow: 0 0 0 4px var(--tint);
}
.rmap-tag {
  font-family: var(--mono); font-size: 11px; font-weight: 600;
  letter-spacing: .16em; color: var(--dim);
}
.rmap-row.is-at .rmap-tag { color: var(--signal-ink); }

/* A folded release is a closed drawer, not a hidden section: the summary
   stays a full-width target and the body is what opens. */
.rmap-body {
  border: var(--rule); background: var(--panel); box-shadow: var(--lift);
  position: relative; min-width: 0;
}
.rmap-row.is-at .rmap-body { border-color: var(--signal-2); }
.rmap-body::before {
  content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 2px;
  background: var(--grad); transform: scaleY(0); transform-origin: top;
  transition: transform .28s var(--latch);
  z-index: 1;
}
.rmap-body[open]::before,
.rmap-row.is-at .rmap-body::before,
.rmap-body:hover::before { transform: scaleY(1); }

.rmap-head {
  display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
  padding: clamp(15px, 1.8vw, 20px) clamp(18px, 2.2vw, 26px);
  cursor: pointer; list-style: none;
  transition: background-color .14s var(--snap);
}
.rmap-head::-webkit-details-marker { display: none; }
.rmap-head:hover { background: var(--panel-2); }
.rmap-body[open] .rmap-head { border-bottom: 1px solid var(--line-soft); }
.rmap-head:focus-visible { outline: 2px solid var(--signal); outline-offset: -3px; }

/* The open/shut indicator. It has two states and no in-between, which is the
   same rule every other moving thing on this page follows. */
.rmap-mark {
  width: 15px; height: 15px; flex: none; position: relative;
  border: 1px solid var(--line-2);
}
.rmap-mark::before, .rmap-mark::after {
  content: ""; position: absolute; background: var(--text-2);
  left: 50%; top: 50%;
}
.rmap-mark::before { width: 7px; height: 1px; transform: translate(-50%, -50%); }
.rmap-mark::after {
  width: 1px; height: 7px; transform: translate(-50%, -50%);
  transition: transform .16s var(--snap);
}
.rmap-body[open] .rmap-mark::after { transform: translate(-50%, -50%) scaleY(0); }
.rmap-body[open] .rmap-mark { border-color: var(--signal); }
.rmap-body[open] .rmap-mark::before { background: var(--signal-ink); }

.rmap-fold { padding: clamp(16px, 2vw, 22px) clamp(18px, 2.2vw, 26px) clamp(18px, 2.2vw, 24px); }

/* A row of controls above the list, so a reader who wants all of it at once
   does not have to click five times. */
.rmap-tools {
  display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
  margin-bottom: 16px;
}
.rmap-head h3 {
  font-family: var(--display); font-size: clamp(19px, 2vw, 23px);
  font-weight: 600; color: var(--text);
}
/* The state chip is the one place colour is spent in this section, and it
   means exactly one thing: how real is this. */
.rmap-state {
  margin-left: auto;
  font-family: var(--mono); font-size: 9.5px; font-weight: 600;
  letter-spacing: .14em; text-transform: uppercase;
  padding: 4px 8px;
  border: 1px solid var(--line-2); color: var(--dim);
  white-space: nowrap;
}
.rmap-state.building {
  border-color: var(--signal); background: var(--tint); color: var(--signal-ink);
}
.rmap-state.part { border-color: var(--tint-line); color: var(--signal-ink); }
.rmap-state.research { border-style: dashed; }

.rmap-fold > p {
  font-size: 14.5px; color: var(--text-2); line-height: 1.6;
  max-width: 72ch; margin-bottom: 16px;
}

/* The deliverables. A filled marker means it exists today; an open one means
   it does not, and that distinction is the entire value of the list. */
.rmap-list {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(auto-fit, minmax(min(260px, 100%), 1fr));
  gap: 1px; background: var(--line-soft);
  border-top: 1px solid var(--line-soft);
}
.rmap-list li {
  background: var(--panel);
  padding: 10px 12px 10px 30px;
  position: relative;
  font-size: 13.5px; line-height: 1.5; color: var(--dim);
}
.rmap-list li::before {
  content: ""; position: absolute; left: 12px; top: 15px;
  width: 8px; height: 8px;
  border: 1px solid var(--line-2); background: transparent;
}
.rmap-list li.done { color: var(--text-2); }
.rmap-list li.done::before { background: var(--grad); border-color: transparent; }
.rmap-list li code {
  font-size: 12px; padding: 1px 4px;
}

@media (max-width: 620px) {
  .rmap-row { grid-template-columns: 40px minmax(0, 1fr); gap: 12px; }
  .rmap-tag { font-size: 10px; letter-spacing: .1em; }
  .rmap-head { gap: 8px; }
  .rmap-state { margin-left: 0; }
}

/* ---------------------------------------------------------------- note */

.note {
  border: var(--rule); border-left: 0;
  background: var(--panel); padding: 16px 20px;
  font-size: 15px; color: var(--text-2);
  position: relative;
  box-shadow: var(--lift);
}
.note::before {
  content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 2px;
  background: var(--grad);
}
.note b { color: var(--text); }
.note.warn::before { background: var(--amber); }

/* ---------------------------------------------------------------- footer */

/* ---------------------------------------------------------------- footer ---
   Where somebody who has read the page decides what to do next, so it carries
   destinations rather than a signature. Four columns on a desk, one on a
   phone, and a base line that states the part and the chain because those are
   the two facts a reader is most likely to want to copy. */

footer { padding: clamp(44px, 6vw, 72px) 0 clamp(40px, 5vw, 60px); }

.foot-grid {
  display: grid;
  grid-template-columns: minmax(0, 1.5fr) repeat(3, minmax(0, 1fr));
  gap: clamp(26px, 4vw, 56px);
  align-items: start;
}
@media (max-width: 900px) {
  .foot-grid { grid-template-columns: 1fr 1fr; }
  .foot-brand { grid-column: 1 / -1; }
}
@media (max-width: 520px) {
  .foot-grid { grid-template-columns: 1fr; gap: 26px; }
}

.foot-brand { display: flex; flex-direction: column; gap: 16px; align-items: flex-start; }
.foot-logo { width: clamp(180px, 20vw, 240px); height: auto; display: block; }
.foot-line {
  font-size: 13.5px; line-height: 1.6; color: var(--text-2); max-width: 40ch;
}
.foot-social { display: flex; gap: 8px; flex-wrap: wrap; }

.foot-col { display: flex; flex-direction: column; gap: 10px; min-width: 0; }
.foot-col .ck { margin-bottom: 2px; }
.foot-col a {
  font-size: 13.5px; color: var(--text-2); text-decoration: none;
  width: fit-content;
  border-bottom: 1px solid transparent;
  transition: color .12s var(--snap), border-color .12s var(--snap);
}
.foot-col a:hover { color: var(--signal-ink); border-bottom-color: var(--signal); }

.foot-rule {
  height: 1px; background: var(--line-soft);
  margin: clamp(28px, 4vw, 44px) 0 clamp(16px, 2vw, 22px);
}
.foot-base {
  display: flex; flex-wrap: wrap; gap: 10px 26px; align-items: center;
}
.foot-base .grow { margin-left: auto; }

/* A button that is only an icon still needs to be a target. */
.btn-icon { padding: 9px 11px; }
.btn-icon svg { width: 15px; height: 15px; display: block; }
.btn-ghost svg { width: 14px; height: 14px; flex: none; }
.btn-ghost:has(svg) { display: inline-flex; align-items: center; gap: 8px; }
/* A destination that is not set up yet stays legible.
   Fading it to 45% made it invisible against a light bar, so the page looked
   as though it had no social links rather than links that are coming. It keeps
   full contrast and simply does not respond. */
.btn:disabled {
  cursor: default;
  border-style: dashed;
  color: var(--text-2);
}
.btn:disabled:hover {
  transform: none;
  border-color: var(--line-2);
  color: var(--text-2);
  background: transparent;
}
.btn-icon:disabled svg { opacity: .8; }

/* ---------------------------------------------------------------- boot */

/* One short power-on sweep. Everything is legible before it runs and after
   it finishes: it only adds a pass of light, it never hides content. */
.boot { position: relative; }
.boot::after {
  content: ""; position: absolute; inset: 0; z-index: 5; pointer-events: none;
  background: linear-gradient(180deg, transparent 0%, var(--glow) 48%, transparent 55%);
  animation: sweep 1s steps(9, end) 1 forwards;
}
@keyframes sweep {
  from { transform: translateY(-100%); opacity: .9; }
  to   { transform: translateY(100%);  opacity: 0; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
    scroll-behavior: auto !important;
  }
  .drawline::after { transform: scaleX(1); }
  .rise { opacity: 1; transform: none; }
}

/* ------------------------------------------------------- touch targets ---
   Measured on a 390px viewport: the theme switch came out 30x24, the chips
   57x30 and the small buttons 34 tall. All of those are misses under a thumb,
   so on a touch pointer every control grows to at least forty. */

@media (pointer: coarse), (max-width: 760px) {
  .btn-sm { padding: 12px 16px; }
  .chip { padding: 11px 14px; }
  .themer { --sw: 46px; --sh: 40px; }
  .topnav a { padding-top: 7px; padding-bottom: 7px; }
  .isa-op { padding-top: 12px; padding-bottom: 12px; }
  .field input, .field select { padding: 13px 14px; }
  .rmap-head { padding-top: 16px; padding-bottom: 16px; }
  .brandmark { padding: 6px 0; }
  #wb-run, #wb-stop, #wb-reset { padding: 13px 18px; }
}


/* -------------------------------------------------------- the strategy desk -
   A chart the reader chose to load, the rule the chip is running, and the tape
   that came off its output port. The chart is the widest thing on the page, so
   it takes the left column and the two panels stack beside it. */

.desk {
  display: grid;
  grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr);
  gap: 20px;
  align-items: start;
}
@media (max-width: 940px) { .desk { grid-template-columns: minmax(0, 1fr); } }
/* Grid items default to min-width:auto, which is what lets a wide table shove
   the page sideways instead of scrolling inside its own wrapper. */
.desk > *, .desk .panel, .desk .stack { min-width: 0; }

.desk-chart { display: flex; flex-direction: column; min-height: 460px; }
.tv-wrap { flex: 1; display: flex; min-height: 420px; position: relative; }
.tv-box { flex: 1; min-height: 420px; }
.tv-box iframe { width: 100% !important; height: 100% !important; border: 0; }

/* Until somebody asks for it, the panel says what pressing the button costs. */
.tv-hold {
  flex: 1; display: flex; flex-direction: column; gap: 12px;
  align-items: center; justify-content: center; text-align: center;
  padding: 32px 24px;
  background:
    repeating-linear-gradient(90deg, var(--line-soft) 0 1px, transparent 1px 34px),
    repeating-linear-gradient(0deg, var(--line-soft) 0 1px, transparent 1px 34px);
}
.tv-hold .hint { max-width: 34ch; }

/* The bus: converter, die, controller. Three stages and two arrows, because
   that is genuinely the whole architecture. */
.bus {
  display: grid;
  grid-template-columns: 1fr auto 1fr auto 1fr;
  gap: 0 14px; align-items: stretch;
  margin-bottom: 18px;
}
.bus-stage {
  border: 1px solid var(--line-2);
  background: var(--panel);
  padding: 16px 18px 18px;
  display: flex; flex-direction: column; gap: 7px;
}
.bus-stage.is-core {
  border-color: var(--signal);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--signal) 22%, transparent);
}
.bus-stage b { font-size: 15px; letter-spacing: -.01em; }
.bus-stage p { font-size: 13px; line-height: 1.62; color: var(--text-2); margin: 0; }
.bus-arrow { align-self: center; width: 26px; height: 1px; background: var(--line); position: relative; }
.bus-arrow::after {
  content: ""; position: absolute; right: 0; top: -3px;
  border: 3.5px solid transparent; border-left-color: var(--line);
}
@media (max-width: 860px) {
  .bus { grid-template-columns: 1fr; gap: 10px; }
  .bus-arrow { width: 1px; height: 18px; justify-self: center; background: var(--line); }
  .bus-arrow::after { right: -3px; top: auto; bottom: 0; border: 3.5px solid transparent; border-top-color: var(--line); }
}
.bus-foot { margin: 0 0 22px; max-width: 78ch; }

/* The source the chip is actually running, shown rather than described. */
.sd-src {
  margin: 0; padding: 14px 16px;
  border: 1px solid var(--line-soft);
  background: var(--well);
  font: 12px/1.55 var(--mono);
  color: var(--text-2);
  max-height: 260px; overflow: auto;
  white-space: pre; tab-size: 8;
}
.sd-src::-webkit-scrollbar { width: 8px; height: 8px; }
.sd-src::-webkit-scrollbar-thumb { background: var(--line-2); }

/* One row per decision. The verb carries the colour; nothing else needs to. */
.sd-tape thead th { padding: 8px 10px; font-size: 9.5px; }
#sd-tape td { padding: 5px 10px; font-size: 12px; border-bottom: 1px solid var(--line-soft); }
#sd-tape td.dim { color: var(--dim); }
#sd-tape tr:last-child td { border-bottom: 0; }
.act {
  display: inline-block; min-width: 42px; text-align: center;
  padding: 2px 7px; font: 600 10.5px/1.5 var(--mono);
  letter-spacing: .07em; border: 1px solid var(--line-2); color: var(--dim);
}
.act.a1 { border-color: var(--signal); background: var(--signal); color: var(--signal-ink); }
.act.a2 { border-color: var(--amber); color: var(--amber); }

/* --------------------------------------------------------------- the wallet -
   A connect button that answers two questions before it is ever used: which
   chain this is, and whether you are on it. Connected is a quiet state, so it
   is the accent; the wrong chain is the only thing here worth an alarm. */

.top-wallet { display: flex; align-items: center; }
.btn-wallet {
  font-family: var(--mono);
  font-size: 11.5px;
  letter-spacing: .04em;
  white-space: nowrap;
}
.btn-wallet.is-on {
  border-color: color-mix(in srgb, var(--signal) 55%, var(--line-2));
  color: var(--signal-ink);
  background: color-mix(in srgb, var(--signal) 14%, transparent);
}
.btn-wallet.is-wrong {
  border-color: var(--amber);
  color: var(--amber);
  background: color-mix(in srgb, var(--amber) 12%, transparent);
}
@media (max-width: 900px) { .top-wallet { order: 3; } }

/* The top bar has room for the wallet button on a desk and not on a phone,
   where it was 122px wide in a row that had none to give. Below that width the
   hero carries it instead, next to the other things you might actually do. */
.hero-wallet { display: none; }
@media (max-width: 900px) {
  .top-wallet { display: none; }
  .hero-wallet { display: flex; }
}


/* ------------------------------------------------------------ the way back -
   The launchpad is a second page and needs an exit that reads as one. The
   brandmark already links home, but a logo is something people learn is
   clickable rather than something they see is. */

.btn-back {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  flex: none;
  padding-left: 9px;
}
.btn-back svg { width: 14px; height: 14px; flex: none; }
.btn-back:hover svg { transform: translateX(-2px); }
.btn-back svg { transition: transform .12s var(--snap); }
@media (max-width: 760px) { .btn-back span { display: none; } .btn-back { padding: 9px 10px; } }

/* The top bar carries the mark, the way back, two social buttons, the wallet
   and the theme switch. On a narrow phone that row runs out of width, and the
   first thing to go is the wordmark: the framed mark still identifies the
   page, and every button stays reachable. */
@media (max-width: 560px) {
  .brandmark b { display: none; }
  .brandmark { gap: 0; }
}

/* Nav items break across lines before the bar admits it is full, which reads
   as a layout accident rather than a dense bar. Each item stays on one line
   and the row is allowed to be tight instead. */
.topnav a { white-space: nowrap; }


/* ------------------------------------------------------------- the instrument
   The die and the panel that drives it are one object, so they sit in one
   frame side by side. Stacked, the section was the sum of both heights and the
   reader had to scroll away from the die to reach its own controls.

   The screen takes the larger share because it is the thing being watched. The
   canvas sizes itself from its client width, so narrowing the column narrows
   the drawing and nothing in the render code has to know. */

.instrument-grid {
  display: grid;
  /* Close to even. At 1.45fr the control column was narrow enough that every
     chip row wrapped, and the panel grew taller than the die it drives. */
  grid-template-columns: minmax(0, 1.12fr) minmax(0, 1fr);
  align-items: stretch;
}
.instrument-screen { display: flex; flex-direction: column; min-width: 0; }
.instrument-screen .die-wrap { flex: 1; display: flex; align-items: center; }
.instrument-screen .die-wrap canvas { width: 100%; }

.instrument-side {
  min-width: 0;
  border-left: var(--rule);
  display: flex;
  flex-direction: column;
  gap: 0;
}
/* Inside the frame the panels are already framed, so they drop their own
   borders and let the divider do the separating. */
.instrument-side > .panel,
.instrument-side > .stack > .panel {
  border: 0;
  box-shadow: none;
  background: transparent;
}
.instrument-side > .stack { gap: 0; }
.instrument-side > .stack > .panel + .panel,
.instrument-side > .panel + .panel { border-top: var(--rule); }
.instrument-side .panel-head { border-bottom: var(--rule); }

/* Below this the two halves have nothing to gain from sharing a row: the die
   would be too narrow to read and the controls too cramped to press. */
@media (max-width: 1000px) {
  .instrument-grid { grid-template-columns: minmax(0, 1fr); }
  .instrument-side { border-left: 0; border-top: var(--rule); }
}


/* The register file is a readout, so it sits with the other readouts under the
   screen rather than in the column of things you press. */
.regs-strip { border-top: var(--rule); }
.regs-strip .panel-head { border-bottom: var(--rule); }



/* Five control groups stacked in one column made the panel taller than the die
   it drives, and the grid stretched the screen to match. Two columns inside
   the panel halves it, and the row settles to the height of the picture rather
   than the height of the buttons. */
@media (min-width: 1001px) {
  .instrument-side .panel-body.stack {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 18px 20px;
    align-content: start;
  }
  .instrument-side .panel-body.stack > .controls { grid-column: 1 / -1; }
  .instrument-grid { align-items: start; }
  .instrument-screen { height: 100%; }
}

/* Sixteen registers stacked one per row is right in a full-width panel and
   wrong in half of one: it made the screen column taller than everything it
   was showing. Two columns inside the instrument, eight rows instead of
   sixteen, and the row settles on the die again. */
@media (min-width: 1001px) {
  .regs-strip .regs {
    display: grid;
    grid-template-columns: 1fr 1fr;
  }
  .regs-strip .reg:nth-child(odd) { border-right: 1px solid var(--line-soft); }
  .regs-strip .reg:nth-last-child(2):nth-child(odd) { border-bottom: 0; }
}


/* -------------------------------------------------------------- the showcase
   Between the claim and the argument: four things the processor does, each
   with a number you can check and a drawing of the thing itself. Light enough
   to scan, so the reader arrives at the console already knowing why. */

.showcase { padding-top: clamp(48px, 6vw, 84px); }
.showcase h2.wide,
.cap h3,
.cap-num b {
  font-family: var(--display-2);
  font-weight: 700;
  font-stretch: 112%;
}
.showcase h2.wide { letter-spacing: -.012em; }

.caps {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 16px;
  margin-top: 26px;
}
@media (max-width: 1060px) { .caps { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 560px) { .caps { grid-template-columns: minmax(0, 1fr); } }

.cap {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 18px 18px 20px;
  border: var(--rule);
  background: var(--panel);
  color: inherit;
  text-decoration: none;
  transition: border-color .14s var(--snap), background .14s var(--snap),
              transform .14s var(--snap);
}
.cap:hover {
  border-color: color-mix(in srgb, var(--signal) 50%, var(--line-2));
  background: var(--panel-2);
  transform: translateY(-2px);
}
.cap-fig {
  width: 100%; height: auto; display: block;
  color: var(--text-2);
  background: var(--well);
  border: 1px solid var(--line-soft);
  padding: 8px;
  box-sizing: border-box;
}
.cap h3 { margin: 2px 0 0; font-size: 19px; letter-spacing: -.005em; }
.cap p { margin: 0; font-size: 13.5px; line-height: 1.62; color: var(--text-2); }
.cap-num {
  margin-top: auto;
  padding-top: 12px;
  border-top: 1px solid var(--line-soft);
  display: flex; flex-direction: column; gap: 2px;
}
.cap-num b {
  font-size: 26px;
  color: var(--signal-ink);
  font-variant-numeric: tabular-nums;
  line-height: 1.05;
}
.cap-foot {
  margin: 22px 0 0;
  max-width: 74ch;
  font-size: 14.5px;
  line-height: 1.7;
  color: var(--text-2);
}
.cap-foot b { color: var(--text); }

/* ----------------------------------------------------------- latching in ---
   Entrance motion, in this project's idiom. A clock edge does not glide: it is
   low and then it is high. So a block does not fade in — it steps through four
   discrete states the way a signal settles through a register, and the timing
   function is the same steps() every other animation here uses.

   The `latch` class is only ever applied by reveal.js, and only to blocks that
   are below the fold at load. Nothing is hidden by the stylesheet alone, so a
   failed script leaves the page exactly as it was rather than blank. */

.latch {
  opacity: 0;
  transform: translateY(10px);
}
.latch.is-in {
  animation: latch-in 260ms steps(4, end) forwards;
}
@keyframes latch-in {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* A row of cards latches one after another rather than all at once, which is
   the difference between a row arriving and a row appearing. */
.caps .cap.is-in:nth-child(2) { animation-delay: 60ms; }
.caps .cap.is-in:nth-child(3) { animation-delay: 120ms; }
.caps .cap.is-in:nth-child(4) { animation-delay: 180ms; }
.caps .cap.latch { animation-fill-mode: both; }

@media (prefers-reduced-motion: reduce) {
  .latch, .latch.is-in { opacity: 1; transform: none; animation: none; }
}


/* -------------------------------------------------------------- the drawer -
   It was a caption with a plus sign at the far end, which is a label, not a
   control. Nobody opens a label.

   So it says what it does, and it shows what is behind it: the seven block
   names sit in the bar as a preview, so the choice is between "open this" and
   "open the thing with ALU and DECODE in it". The chevron turns rather than
   swapping glyph, because the turn is the thing that reads as a mechanism. */

.drawer {
  border: var(--rule);
  background: var(--panel);
  margin-top: 20px;
}
.drawer-head {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 14px 16px;
  cursor: pointer;
  list-style: none;
  user-select: none;
  background: var(--panel-2);
  transition: background .14s var(--snap);
  /* The action label ran past the bar on a phone. Wrapping is the fix rather
     than hiding it: the label is the reason anyone knows the drawer opens, and
     a bare chevron says least on the device with the least room. */
  flex-wrap: wrap;
}
.drawer-head::-webkit-details-marker { display: none; }
.drawer-head:hover { background: color-mix(in srgb, var(--signal) 9%, var(--panel-2)); }
.drawer-head:focus-visible { outline: 2px solid var(--signal); outline-offset: -2px; }

.drawer-mark {
  display: grid; place-items: center;
  width: 22px; height: 22px; flex: none;
  border: 1px solid var(--line-2);
  background: var(--panel);
  color: var(--signal-ink);
  transition: transform .16s var(--snap), border-color .14s var(--snap);
}
.drawer-mark svg { width: 12px; height: 12px; }
.drawer[open] .drawer-mark { transform: rotate(180deg); }
.drawer-head:hover .drawer-mark { border-color: var(--signal); }

.drawer-title {
  font-family: var(--display);
  font-size: 15px;
  font-weight: 600;
  letter-spacing: .01em;
  color: var(--text);
  flex: none;
}

/* The preview. Seeing the names is the difference between a closed box and a
   closed box you know the contents of. */
.drawer-peek { display: flex; gap: 5px; flex-wrap: wrap; min-width: 0; }
.drawer-peek i {
  font: 500 9.5px/1 var(--mono);
  letter-spacing: .13em;
  font-style: normal;
  color: var(--dim);
  border: 1px solid var(--line-soft);
  padding: 4px 6px;
  white-space: nowrap;
}
.drawer[open] .drawer-peek { opacity: .4; }

.drawer-act {
  margin-left: auto;
  flex: none;
  font: 600 10.5px/1 var(--mono);
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--signal-ink);
  border-bottom: 1px solid var(--signal);
  padding-bottom: 2px;
}
.drawer[open] .drawer-act { color: var(--dim); border-bottom-color: var(--line-2); }
.drawer[open] .drawer-act::after { content: " · close"; }
.drawer[open] .drawer-head { border-bottom: var(--rule); }
.drawer .shelf-wrap { padding-top: 4px; }

@media (max-width: 860px) {
  .drawer-peek { display: none; }
}

/* ------------------------------------------------------- the control column
   Things you press should not look like things you read. The column gets its
   own ground and its own type: values in the mono face at signal weight, and
   the labels above them dropped back so the numbers carry. */

/* A front panel is a different surface from a screen, so it gets a different
   ground: one step darker than the page, with the faint grid a control panel
   is silkscreened on. Restrained enough that the values still carry it. */
.instrument-side {
  /* Faint enough to read as a surface rather than as ruling. At full
     --line-soft the lines ran through the body copy and the paragraph became
     something you had to look past. */
  background:
    repeating-linear-gradient(90deg, color-mix(in srgb, var(--line-soft) 45%, transparent) 0 1px, transparent 1px 30px),
    repeating-linear-gradient(0deg, color-mix(in srgb, var(--line-soft) 45%, transparent) 0 1px, transparent 1px 30px),
    var(--panel-2);
}
.instrument-side .panel-head {
  background: var(--panel);
  border-bottom: var(--rule);
}
.instrument-side .panel-body { background: transparent; }

/* A rate is a measurement, so it is set like one. The live rate is filled
   rather than outlined, because at a glance an outline is not a state. */
.instrument-side .chips .chip {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: .09em;
  font-variant-numeric: tabular-nums;
}
/* The live rate is filled, not outlined. At a glance an outline is a shape,
   not a state, and this is the one control whose current value the reader has
   to be able to read without looking for it. The selector is the attribute the
   buttons actually carry, so the styling and the accessible state cannot come
   apart. */
.instrument-side .chips .chip[aria-pressed="true"] {
  background: var(--signal);
  border-color: var(--signal);
  color: var(--on-signal);
  font-weight: 600;
}

/* The uptime is the one number in the column that moves on its own, so it is
   the one that gets the readout treatment. */
.instrument-side .stack.g6 b {
  font-family: var(--mono) !important;
  font-size: 30px !important;
  line-height: 1.05;
  font-variant-numeric: tabular-nums;
  color: var(--signal-ink) !important;
  display: block;
  padding: 8px 10px;
  border: 1px solid var(--tint-line);
  background: var(--tint-soft);
}
.instrument-side .field input {
  font-family: var(--mono);
  font-variant-numeric: tabular-nums;
  font-size: 15px;
}
.instrument-side .regs-strip { border-top: var(--rule); background: var(--panel); }

/* Two columns inside the panel only while the register file is not also in
   this column; with it there the panel is already short enough. */
@media (min-width: 1001px) {
  .instrument-grid { align-items: stretch; }
  .instrument-screen .die-wrap { min-height: 0; }
}


/* A drawer holding a table has to lose the table's own top border, or the
   summary and the table draw two lines across the same seam. */
.drawer-isa .tw { border-top: 0; }

/* A drawer must never widen the page. What is inside it scrolls in its own
   container — the instruction table has a 540px minimum and the parts shelf is
   a horizontal strip — and without a clip here that content pushes the
   document sideways on a phone instead of scrolling within its own box.
   `clip` rather than `hidden` so it does not become a scroll container on the
   other axis and swallow the sticky header's context. */
/* The clip goes on the body, never on the <details>.
 *
 * Giving a <details> any overflow other than visible makes Chromium stop
 * hiding its closed content: the drawer looks shut and its contents are
 * still laid out, five hundred pixels of them, on top of whatever comes
 * next. On a phone that is the whole design step wearing the social links
 * across its face. The body can be clipped safely, and that is where the
 * clip was needed in the first place. */
.drawer-body { overflow-x: clip; }
.drawer-head { min-width: 0; }

/* --------------------------------------------------- the bar, between sizes -
   The nav collapsed at 940px but stopped fitting at about 1300, so on every
   laptop between those two it pushed the social buttons, the wallet and the
   theme switch off the right edge of the page. They did not disappear; they
   were shoved past the viewport, which is worse, because the page also grew a
   horizontal scrollbar to hold them.

   The controls are fixed-width and every one of them is a thing you press, so
   the nav is what gives way. It gives way by name rather than by position:
   these are the stops a reader is least likely to be hunting for in the bar,
   and all of them are still in the footer. A selector that matches nothing on
   the other page simply does nothing there. */

/* Deliberately NOT min-width: 0. Allowing this to shrink below its content
   made the links overflow their own box and paint on top of the buttons
   next to them, silently. It keeps its width and the rules below decide
   which stops it can afford at each size. */
.topnav { flex: none; }

@media (max-width: 1360px) { .topnav { gap: 16px; } }

/* Which stop the bar can afford to lose, at each size.
   Named by href rather than by position, so the rule says what it drops. The
   set changed when the launchpad was split and every rule here pointed at a
   link that had moved: the bar overflowed again between 960 and 1200 with
   nothing in the stylesheet admitting why. */
/* The front page is the one bar the page menu made longer rather than
   shorter: it never carried a back button to give up, and its row is all
   section anchors. One stop leaves earlier to pay for the menu. */
@media (max-width: 1500px) {
  .topnav a[href="#terms"],
  .topnav a[href="#roadmap"],
  .topnav a[href="#instrument"] { display: none; }
}
@media (max-width: 1380px) {
  .topnav a[href="/strategy.html"],
  .topnav a[href="#fab"] { display: none; }
}
@media (max-width: 1260px) {
  .topnav a[href="#path"],
  .topnav a[href="#clock"] { display: none; }
  .topnav { gap: 13px; }
}
@media (max-width: 1140px) {
  .topnav a[href="#fleet"],
  .topnav a[href="#roadmap"] { display: none; }
}
@media (max-width: 1060px) {
  .topnav a[href="#mint"],
  .topnav a[href="#machine"] { display: none; }
  /* The back button drops its label here rather than at 760: on a page that
     carries one, it is 210 pixels the nav cannot have. */
  .btn-back span { display: none; }
  .btn-back { padding: 9px 10px; }
}


/* ---------------------------------------------------------------- the fleet
   One table, two sources. Until a factory exists it shows what the shipped
   programs really reach on the netlist, and it says so above itself in those
   words; after T-0 the same markup carries a chain read.

   The sample state is drawn back a little so nobody mistakes it for live, but
   not so far that it looks broken — it is example data, not an error. */
#fleet-panel {
  display: none !important;
}

#replay-panel {
  display: none !important;
}

#fleet-panel[data-state="sample"] .fleet-table tbody { opacity: .82; }
#fleet-panel[data-state="sample"] .fleet-badge { color: var(--amber); border-color: var(--amber); }

.fleet-badge {
  border: 1px solid var(--line-2);
  padding: 3px 8px;
  color: var(--dim);
  letter-spacing: .14em;
}
.fleet-badge.is-live { color: var(--signal-ink); border-color: var(--signal); }

.fleet-note { margin: 0; border-radius: 0; border-left: 0; border-right: 0; }
.fleet-table td { font-size: 12.5px; }
.fleet-table td.num { text-align: right; font-variant-numeric: tabular-nums; }
.fleet-table td.dim { color: var(--dim); }

/* Flags read as three lamps, because that is what they are on the die. */
.flagcell { white-space: nowrap; }
.flagcell i {
  display: inline-block;
  width: 17px;
  text-align: center;
  font: 600 10px/17px var(--mono);
  font-style: normal;
  border: 1px solid var(--line-soft);
  color: var(--dim);
  margin-right: 3px;
}
.flagcell i.on {
  background: var(--signal);
  border-color: var(--signal);
  color: var(--on-signal);
}


/* ------------------------------------------------------------------ the path
   Four steps, in order, with the two that are not open yet saying so. A route
   that hides which parts are walkable is worse than no route: the reader gets
   to step three, finds a button that cannot be pressed, and concludes the page
   is broken rather than that the launch has not happened. */

.steps-sec { padding-top: clamp(40px, 5vw, 64px); }
.steps {
  list-style: none;
  margin: 26px 0 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 14px;
}
@media (max-width: 1000px) { .steps { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 560px) { .steps { grid-template-columns: minmax(0, 1fr); } }

.step a {
  display: flex;
  gap: 14px;
  height: 100%;
  padding: 16px 16px 18px;
  border: var(--rule);
  background: var(--panel);
  color: inherit;
  text-decoration: none;
  transition: border-color .14s var(--snap), background .14s var(--snap),
              transform .14s var(--snap);
}
.step a:hover {
  border-color: color-mix(in srgb, var(--signal) 50%, var(--line-2));
  background: var(--panel-2);
  transform: translateY(-2px);
}
.step-n {
  flex: none;
  width: 30px; height: 30px;
  display: grid; place-items: center;
  font: 700 15px/1 var(--display-2);
  font-stretch: 112%;
  border: 1px solid var(--line-2);
  color: var(--dim);
}
.step.is-open .step-n {
  background: var(--signal);
  border-color: var(--signal);
  color: var(--on-signal);
}
.step-body { display: flex; flex-direction: column; gap: 7px; min-width: 0; }
.step-body b {
  font-family: var(--display-2);
  font-weight: 700;
  font-stretch: 112%;
  font-size: 16px;
}
.step-body > span { font-size: 13px; line-height: 1.6; color: var(--text-2); }
.step-tag {
  margin-top: auto;
  font: 600 10px/1 var(--mono);
  font-style: normal;
  letter-spacing: .15em;
  text-transform: uppercase;
  padding-top: 10px;
}
.step.is-open .step-tag { color: var(--signal-ink); }
.step.is-locked .step-tag { color: var(--amber); }


/* ------------------------------------------------------------ the walkthrough
   A native <dialog>. The only thing worth styling is the panel and the
   backdrop; everything that makes a modal correct is already in the element. */

#howto {
  border: 0;
  padding: 0;
  background: transparent;
  max-width: min(560px, calc(100vw - 32px));
  width: 100%;
  color: var(--text);
}
#howto::backdrop { background: var(--scrim); }

.howto-panel {
  border: var(--rule);
  background: var(--panel);
  box-shadow: var(--lift-2);
}

.howto-head {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 16px;
  border-bottom: var(--rule);
  background: var(--panel-2);
}
.howto-num {
  flex: none;
  width: 30px; height: 30px;
  display: grid; place-items: center;
  font: 700 15px/1 var(--display-2);
  font-stretch: 112%;
  background: var(--signal);
  color: var(--on-signal);
}
.howto-heading { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.howto-heading h3 {
  margin: 0;
  font-family: var(--display-2);
  font-weight: 700;
  font-stretch: 112%;
  font-size: 19px;
  letter-spacing: -.005em;
}
.howto-tag {
  flex: none;
  margin-left: auto;
  font: 600 9.5px/1 var(--mono);
  letter-spacing: .15em;
  text-transform: uppercase;
  padding: 5px 7px;
  border: 1px solid currentColor;
}
.howto-tag.is-open { color: var(--signal-ink); }
.howto-tag.is-locked { color: var(--amber); }
.howto-x { flex: none; margin: -2px -4px 0 0; }

.howto-body { padding: 18px 16px; display: flex; flex-direction: column; gap: 12px; }
.howto-body p { margin: 0; font-size: 14px; line-height: 1.68; }
.howto-does {
  color: var(--text-2);
  font-size: 13px !important;
  padding: 10px 12px;
  background: var(--well);
  border-left: 2px solid var(--line-2);
}
.howto-go { align-self: flex-start; }

.howto-foot {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 14px 16px;
  border-top: var(--rule);
}
.howto-dots { display: flex; gap: 6px; margin: 0 auto; }
.howto-dot {
  width: 9px; height: 9px;
  padding: 0;
  border: 1px solid var(--line-2);
  background: transparent;
  cursor: pointer;
}
.howto-dot.on { background: var(--signal); border-color: var(--signal); }
.howto-dot:focus-visible { outline: 2px solid var(--signal); outline-offset: 2px; }

/* The rail's closing line now carries a button, so it stops being a paragraph
   and becomes a row that wraps on a phone. */
.cap-foot { display: flex; align-items: baseline; gap: 14px; flex-wrap: wrap; }
.cap-foot-line { flex: 1 1 32ch; min-width: 0; }

/* A dashed border means "not set up yet" on this site — it is how the social
   buttons say the account does not exist. Inside the walkthrough a disabled
   Next means the opposite: it is the end of a list that is complete. So it
   dims rather than dashes. */
.howto-foot .btn:disabled {
  border-style: solid;
  opacity: .4;
}


/* Below this the title and the action cannot share a line. The action takes
   the second one and keeps its full width, so the affordance survives the
   narrowest screen rather than being the first thing dropped from it. */
@media (max-width: 620px) {
  .drawer-act {
    /* Indented with padding, not margin. A 100% basis plus a 34px margin is
       34px wider than the thing it sits in, which is how the label went from
       overflowing by 117 to overflowing by 34. */
    margin-left: 0;
    padding-left: 34px;
    flex-basis: 100%;
    margin-top: -2px;
  }
  .drawer-title { flex: 1 1 auto; min-width: 0; }
}

/* ============================================================ the design step
 *
 * The controls a chip is configured with: artwork, links, and the two chip
 * groups that decide what it is paired with and what its input port reads.
 */

/* A chip that cannot be picked yet says so by being dashed and dimmed rather
   than by being hidden. Hiding it makes the page look finished when the
   thing it stands for has not been built. */
/* An author rule beats the user agent stylesheet regardless of specificity, so
   any component that sets its own display quietly un-hides itself: the artwork
   Remove button carries the hidden attribute and .btn{display:inline-flex}
   was painting it anyway, offering to remove an image nobody had chosen. */
[hidden] { display: none !important; }

.chip:disabled {
  border-style: dashed;
  color: var(--text-3, var(--text-2));
  opacity: .62;
  cursor: not-allowed;
}
.chip:disabled:hover { background: transparent; }

/* The artwork well. It is a <label> wrapping a hidden file input, so a click
   anywhere on it opens the picker and the keyboard reaches it through the
   input rather than through a handler of ours. */
.drop {
  display: flex; align-items: center; gap: 14px;
  padding: 14px 14px; cursor: pointer;
  border: 1px dashed var(--line-2); background: var(--panel-2);
  transition: border-color .14s var(--snap), background-color .14s var(--snap);
}
.drop:hover, .drop.over { border-color: var(--signal); background: var(--tint); }
.drop.has-art { border-style: solid; border-color: var(--line); }
.drop.bad { border-color: var(--warn, #c2410c); }
.drop-fig {
  flex: none; width: 38px; height: 38px; display: grid; place-items: center;
  color: var(--signal-ink); background: var(--tint); border: 1px solid var(--line-2);
}
.drop-fig svg { width: 20px; height: 20px; }
.drop-text { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.drop-text b {
  font-family: var(--mono); font-size: 12px; font-weight: 500;
  color: var(--text); overflow-wrap: anywhere;
}
.drop #lp-art-clear { margin-left: auto; flex: none; }

.links-grid { display: flex; flex-direction: column; gap: 8px; }
.linkrow {
  /* The label column was a flat 62px, which fitted the shortest of these
     labels and cut the ends off the others. A floor with room to grow
     costs nothing when the label is short. */
  display: grid; grid-template-columns: minmax(62px, max-content) 1fr;
  align-items: center; gap: 10px;
}
.linkrow .ck { margin: 0; }
.linkrow input { width: 100%; }

/* The plate's identity row grew a thumbnail, so it can no longer sit on a
   baseline: the image has none. */
.plate-id { align-items: center; }
.plate-idbox { display: flex; align-items: baseline; gap: 12px; min-width: 0; flex-wrap: wrap; }
.plate-art {
  flex: none; width: 52px; height: 52px; overflow: hidden;
  border: 1px solid var(--line-2); background: var(--panel-2);
}
.plate-art img { width: 100%; height: 100%; object-fit: cover; display: block; }
.plate-art.empty {
  background-image:
    linear-gradient(45deg, var(--line-2) 25%, transparent 25%, transparent 75%, var(--line-2) 75%),
    linear-gradient(45deg, var(--line-2) 25%, transparent 25%, transparent 75%, var(--line-2) 75%);
  background-size: 10px 10px;
  background-position: 0 0, 5px 5px;
  opacity: .5;
}

.plate-links { display: flex; flex-wrap: wrap; gap: 8px; padding-top: 12px; }
.plate-link {
  display: inline-flex; align-items: center; gap: 7px;
  font-family: var(--mono); font-size: 11px; color: var(--text-2);
  padding: 4px 9px; border: 1px solid var(--line-2); background: var(--panel-2);
  max-width: 100%; overflow-wrap: anywhere;
}
.plate-link i {
  font-style: normal; font-size: 9px; letter-spacing: .14em;
  color: var(--signal-ink);
}

@media (max-width: 520px) {
  .linkrow { grid-template-columns: 1fr; gap: 5px; }
  .drop { flex-wrap: wrap; }
  .drop #lp-art-clear { margin-left: 52px; }
}


/* The plate is a readout of the form beside it, so it follows the form rather
   than sitting at the top of a column that is half the height of the one it
   describes. Only above the breakpoint: stacked, there is nothing to follow. */
@media (min-width: 901px) {
  /* 59px of sticky header, measured, plus room to breathe. */
  #design .split > .stack:last-child { position: sticky; top: 79px; }
}

/* A sticky column only makes sense while there is a column beside it to be
   sticky against. Once the grid is one column the same rule makes the card
   and the plate ride over the form above them, which is exactly what it
   looked like on a phone. */
@media (max-width: 900px) {
  #design .split > .stack:last-child { position: static; top: auto; }
}

/* ============================================================== replay panel
 *
 * A debugger over a chip that is running on a chain. The RAM grid is the only
 * unusual piece: 256 cells that have to stay legible and stay inside their
 * panel at every width, so they wrap on a fixed track rather than a count.
 */

.rp-actions { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; }
.rp-actions .ck { min-width: 0; overflow-wrap: anywhere; }

.rp-good { color: var(--signal-ink); }
.rp-warn { color: var(--text-2); }
.rp-bad  { color: var(--warn, #c2410c); }

.rp-out { display: flex; flex-direction: column; gap: 20px; }

.rp-scrub-head { display: flex; align-items: baseline; gap: 12px; }
.rp-scrub { width: 100%; accent-color: var(--signal); cursor: pointer; }

.rp-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 20px; }
@media (max-width: 760px) { .rp-grid { grid-template-columns: 1fr; } }

/* A nested panel sits inside one that already has a border, so it drops the
   outer shadow and keeps only the line. */
.rp-sub { box-shadow: none; }

.rp-ram {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(30px, 1fr));
  gap: 3px;
}
.rp-cell {
  font-family: var(--mono); font-style: normal; font-size: 10px;
  text-align: center; padding: 4px 0;
  color: var(--dim); background: var(--panel-2); border: 1px solid transparent;
}
.rp-cell.on {
  color: var(--signal-ink); border-color: var(--line-2); background: var(--tint);
}

/* ================================================================== thesis
 *
 * One drawing carrying both halves of the argument: a gate becoming a die,
 * then the die advancing a block at a time. It scrolls rather than shrinks
 * below its natural width, because the die loses its grid when squeezed.
 */
.thesis-fig {
  margin: 0 0 28px;
  padding: 18px 0;
  border-top: var(--rule); border-bottom: var(--rule);
  overflow-x: auto;
  color: var(--signal-ink);
}
.thesis-fig svg { display: block; width: 100%; min-width: 720px; height: auto; }
.tf-head {
  font-family: var(--mono); font-size: 13px; font-weight: 500;
  letter-spacing: .22em; fill: currentColor;
}
.tf-tick {
  font-family: var(--mono); font-size: 11px; letter-spacing: .1em;
  fill: currentColor; fill-opacity: .55;
}

/* Links on this site are normally whole cards or buttons, so the global rule
   strips their decoration. An inline link inside a sentence has nothing else
   marking it, and until now there were none to notice. These are the first,
   so they get the accent that stays legible as text plus a rule under it. */
.sec p a, .cards p a {
  color: var(--signal-ink);
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-thickness: 1px;
}
.sec p a:hover { background: var(--tint); }

/* An inline link inside a note is a real tap target on a phone. Padding on an
   inline element does grow its hit box, which is what the layout audit was
   measuring when it found this one at eighteen pixels tall. */
.note a {
  color: var(--signal-ink);
  text-decoration: underline;
  text-underline-offset: 3px;
  padding: 7px 2px;
}
.note a:hover { background: var(--tint); }

/* ------------------------------------------------- the launch form, tighter
 *
 * Name and ticker share a row because they are one thought, and the socials
 * sit behind a disclosure because most launches do not have three links ready
 * and an empty triple of fields makes the form look unfinished.
 */
.lp-row2 { display: grid; grid-template-columns: minmax(0, 2fr) minmax(0, 1fr); gap: 14px; }
@media (max-width: 560px) { .lp-row2 { grid-template-columns: 1fr; } }

.field textarea {
  font-family: var(--mono); font-size: 14px; line-height: 1.5;
  background: var(--panel-2); color: var(--text);
  border: 1px solid var(--line-2); padding: 11px 13px; width: 100%;
  resize: vertical; min-height: 62px;
}
.field textarea:focus { border-color: var(--signal); outline: none; }

/* The links drawer sits inside a panel body rather than spanning one, so it
   carries its own border instead of the full-bleed rules the ISA drawer has. */
.drawer-links { border: 1px solid var(--line-2); background: var(--panel-2); }
.drawer-links .drawer-head { padding: 12px 14px; background: transparent; }
.drawer-links .drawer-body { padding: 0 14px 14px; }
.drawer-links .drawer-act { margin-left: auto; }

/* A readout whose inputs have been switched off. Dimmed rather than hidden,
   so choosing a chip with no market shows what that costs instead of quietly
   removing the evidence. */
.econ.is-off { opacity: .55; }

/* ============================================================== a chip page
 *
 * One chip, addressed by the query string. The charts size themselves to
 * their wrapper, which has to be a block with a width of its own or a canvas
 * will grow every time it is redrawn.
 */
.hero-chip { padding-bottom: 34px; }
.chip-id { display: flex; align-items: flex-start; gap: 22px; margin-bottom: 22px; }
.chip-art {
  flex: none; width: 88px; height: 88px; overflow: hidden;
  border: 1px solid var(--line-2); background: var(--panel-2);
  background-image:
    linear-gradient(45deg, var(--line-2) 25%, transparent 25%, transparent 75%, var(--line-2) 75%),
    linear-gradient(45deg, var(--line-2) 25%, transparent 25%, transparent 75%, var(--line-2) 75%);
  background-size: 14px 14px;
  background-position: 0 0, 7px 7px;
}
.chip-art img { width: 100%; height: 100%; object-fit: cover; display: block; }
.chip-id-text { min-width: 0; }
.chip-id-text h1 { margin: 6px 0 0; }
.chip-tick {
  display: inline-block; margin-top: 8px;
  font-family: var(--mono); font-size: 11px; font-weight: 600; letter-spacing: .16em;
  padding: 3px 9px; background: var(--grad); color: var(--on-signal);
}
.chip-desc { max-width: 62ch; margin: 12px 0 0; }
.chip-links { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; }

.chip-find { flex-direction: row; align-items: stretch; gap: 10px; max-width: 620px; }
.chip-find input { flex: 1 1 auto; min-width: 0; }
.chip-find .btn { flex: none; }
@media (max-width: 520px) {
  .chip-find { flex-direction: column; }
  .chip-id { flex-direction: column; gap: 16px; }
}

/* A canvas has no intrinsic width, so it is measured against this. */
.chartwrap { width: 100%; min-width: 0; }
.chartwrap canvas { display: block; width: 100%; }


/* ------------------------------------------------------------------ docs -
 *
 * A contents column that stays put and a measure that stays readable. The
 * sidebar collapses above the content on a narrow screen rather than becoming
 * a drawer: a drawer is a thing to operate, and a list of ten links is not
 * worth operating.
 */
.docs {
  max-width: var(--maxw); margin: 0 auto;
  padding: 34px var(--gutter) 90px;
  display: grid; grid-template-columns: 210px minmax(0, 1fr);
  gap: 46px; align-items: start;
}
.docs-nav { position: sticky; top: 108px; display: flex; flex-direction: column; gap: 14px; }
.docs-navhead { color: var(--dim); }
.docs-nav nav { display: flex; flex-direction: column; gap: 2px; }
.docs-nav a {
  color: var(--text-2); text-decoration: none; font-size: 14px;
  padding: 5px 10px; border-left: 2px solid var(--line-2); border-radius: 0 5px 5px 0;
}
.docs-nav a:hover { color: var(--text); border-left-color: var(--signal); background: var(--panel-2); }
.docs-navfoot { margin-top: 10px; }

.docs-body { max-width: 68ch; }
.docs-body h1 { font-size: 46px; margin: 0 0 14px; }
.docs-body h2 {
  font-size: 27px; margin: 46px 0 12px;
  padding-top: 20px; border-top: var(--rule);
  scroll-margin-top: 120px;
}
.docs-body .lede { font-size: 18px; color: var(--text); }
.docs-body p { line-height: 1.62; color: var(--text-2); }
.docs-body ul, .docs-body ol { color: var(--text-2); line-height: 1.62; padding-left: 20px; }
.docs-body li { margin: 5px 0; }
.docs-body pre {
  background: var(--well); border: var(--rule); border-radius: 8px;
  padding: 13px 15px; overflow-x: auto;
}
.docs-body pre code { font-size: 13px; color: var(--text); }
.docs-body .note { margin: 20px 0; }

.doc-table { width: 100%; border-collapse: collapse; margin: 14px 0; }
.doc-table td { padding: 8px 0; border-bottom: var(--rule); font-size: 14px; color: var(--text-2); }
.doc-table td:last-child { text-align: right; color: var(--text); }
.doc-table .mono {
  font-family: "Spline Sans Mono", monospace; font-size: 12px;
  word-break: break-all;
}
.doc-table .mono a { color: inherit; }

@media (max-width: 860px) {
  /* minmax(0, 1fr), not 1fr.

     A bare `1fr` track is `minmax(auto, 1fr)`, and `auto` as a minimum
     means min-content. One table and a few unbreakable code spans put the
     min-content of this column at 578px, so on a 390px phone the column
     was 578px wide and every paragraph in the page hung 204px past the
     edge. The two-column rule above already said minmax(0, 1fr) for this
     exact reason; collapsing to one column dropped the guard with it. */
  .docs { grid-template-columns: minmax(0, 1fr); gap: 22px; }
  .docs-nav { position: static; }
  .docs-nav nav { flex-direction: row; flex-wrap: wrap; }

  /* The global table rule sets a 540px floor so a wide data table keeps its
     columns legible and scrolls inside the wrapper it is given. A docs table
     is two columns of key and value and has no wrapper, so the floor is what
     was pushing the page sideways. Removed here, and the cells wrap instead,
     which is what a key and a value can afford to do and a data row cannot. */
  .docs-body table { min-width: 0; }
  .docs-body table td { overflow-wrap: anywhere; }
}

/* An address, a selector or a path has no spaces to break at, so it breaks
   anywhere rather than deciding how wide the page is. */
.docs-body code { overflow-wrap: anywhere; }


/* ------------------------------------------------------------ phone fixes -
 *
 * Measured at 390px, which is the width most phones report. Everything here
 * exists because something was eight pixels too wide or eighteen pixels too
 * short, and both of those are invisible in a screenshot.
 */
@media (max-width: 560px) {
  /* The bar carried a wordmark, a back button, a nav, a wallet button and a
     theme switch. At this width it keeps the two that are errands and drops
     the rest, which the page already repeats in its own sections. */
  .topbar-in { gap: 10px; height: 54px; padding: 0 12px; }
  .topbar-in .btn-back { display: none; }
  .brandmark b { font-size: 15px; }
  .markframe { width: 32px; height: 32px; }

  /* The theme switch was the eight pixels. It is an ornament next to a wallet
     button, so it goes last and it goes first. */
  .themer { display: none; }

  /* A finger is about nine millimetres. These were two and a half. */
  .tokenbar-in { padding: 9px 12px; gap: 10px; }
  .tokenbar-copy { min-height: 34px; min-width: 66px; padding: 6px 10px; }
  .tokenbar-link { min-height: 34px; display: inline-flex; align-items: center; }

  /* The row scrolls, and now it says so: the last few pixels fade, which is
     the only honest way to signal a scroll that has no scrollbar. */
  .tokenbar {
    -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 26px), transparent);
    mask-image: linear-gradient(to right, #000 calc(100% - 26px), transparent);
  }

  /* Nav links in a footer or a docs list are read, not aimed at, but these
     are aimed at. */
  .foot-col a, .docs-nav a { min-height: 34px; display: flex; align-items: center; }
}

/* Nothing on any page may make the document itself scroll sideways. If a
   figure or a table is wider than the screen it scrolls inside its own box,
   and every one of them already has such a box. */
html, body { overflow-x: hidden; }

/* ------------------------------------------------- a token, given plainly -
 *
 * The address is the payload. Somebody arriving from a post wants to paste it
 * into a wallet, so it is set large, in mono, on its own line, with the copy
 * button beside it rather than an icon they have to guess at. It wraps rather
 * than scrolls: an address cut off at a screen edge is an address nobody can
 * use, and forty-two characters fit on two lines at any width worth having.
 */
.tokenline {
  display: flex; flex-wrap: wrap; align-items: center; gap: 12px;
  padding: 14px 16px;
  background: var(--panel-2);
  border: 1px solid var(--line-2);
}
.tokenline-tag {
  font-family: var(--mono); font-size: 15px; letter-spacing: .04em;
  color: var(--signal);
}
.tokenline-ca {
  font-family: var(--mono); font-size: 15px; line-height: 1.5;
  color: var(--text);
  /* Never upper-cased: an address's letter case is its EIP-55 checksum, and
     a stylesheet that changes it destroys the only self-check it has. */
  text-transform: none;
  word-break: break-all; flex: 1 1 320px; min-width: 0;
}

/* Status, inside a sentence rather than beside it.
   A table that says what a thing does has to say what stage it is at, or a
   reader takes every row as shipped. Green for live, amber for written and
   not deployed — the same two colours the launchpad uses for the same
   distinction, so it means one thing across the site. */
.doc-table b.ok  { color: var(--g2); font-weight: 600; }
.doc-table b.wip { color: var(--amber); font-weight: 600; }
.doc-table td:last-child { text-align: left; }

/* Whose chip a row is. The label is a declaration from config.js, not a
   property of the contract, so it is styled as an annotation rather than
   as a badge of status. */
.chiptag { display: inline-block; padding: 2px 8px; border-radius: 3px;
  font-family: var(--mono); font-size: 11px; letter-spacing: .08em;
  text-transform: uppercase; color: var(--text-3); border: var(--rule); }
.chiptag.is-ours { color: var(--g2); border-color: var(--g3);
  background: rgba(0, 200, 5, .07); }


/* ------------------------------------------------------------ market board -
 * The sensor page answers "what is this processor reading". This answers the
 * question directly after it: what else could it read. A board rather than
 * cards, because the columns are the point and a reader goes down them.
 */
.mkt { display: grid; }
.mkt-row {
  /* Five tracks, all floored at zero so a long description narrows the
     column instead of adding a row. The name is the widest because some of
     these call themselves things like "BNB CRCL / USD". */
  display: grid;
  grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr) 110px 58px 86px;
  gap: 0 14px; align-items: center;
  padding: 11px 0; border-bottom: 1px solid var(--line-soft);
}
.mkt-row:last-child { border-bottom: 0; }
.mkt-head {
  color: var(--dim); font-family: var(--mono); font-size: 10px;
  letter-spacing: .18em; text-transform: uppercase; padding-bottom: 4px;
  border-bottom: 1px solid var(--line-2);
}
.mkt-row .sym {
  font-family: var(--display); font-weight: 700; font-size: 15px; color: var(--text);
}
.mkt-row .nm { font-family: var(--mono); font-size: 11.5px; color: var(--dim); }
.mkt-row .px {
  font-family: var(--mono); font-size: 14px; color: var(--text);
  text-align: right; font-variant-numeric: tabular-nums;
}
.mkt-row .ago {
  font-family: var(--mono); font-size: 11px; color: var(--dim);
  text-align: right; font-variant-numeric: tabular-nums;
}
.mkt-row.is-wired .sym { color: var(--signal-ink); }
.mkt-row.is-wired .nm { color: var(--signal); }

@media (max-width: 720px) {
  /* The scale and the age are the first two a phone can do without. */
  .mkt-row { grid-template-columns: minmax(0, 1.3fr) minmax(0, 1fr) 92px; }
  .mkt-row .ago { display: none; }
  .mkt-row .ago + .ago { display: block; }
}


/* --------------------------------------------------------- the live reading -
 * The sensor page's hero. A source, a price, an arrow and a byte: the whole
 * claim of the page in one line, and the only line on it that changes.
 */
.sn-live {
  display: flex; align-items: flex-end; flex-wrap: wrap; gap: 18px 30px;
  margin-top: 30px; padding: 22px 24px;
  border: var(--rule); background: var(--panel);
}
.sn-live-cell { display: flex; flex-direction: column; gap: 5px; min-width: 0; }
.sn-live-cell .ck { color: var(--dim); font-size: 10px; letter-spacing: .2em; }
.sn-live-cell b {
  font-family: var(--display); font-weight: 700; letter-spacing: -.02em;
  font-size: 34px; line-height: 1; color: var(--text);
  font-variant-numeric: tabular-nums;
}
.sn-live-cell.is-byte b { color: var(--signal-ink); font-size: 46px; }
.sn-live-arrow { color: var(--dim); padding-bottom: 10px; flex: none; }
.sn-live-arrow svg { width: 40px; height: 16px; display: block; }

@media (max-width: 560px) {
  .sn-live { gap: 14px 22px; padding: 18px; }
  .sn-live-cell b { font-size: 26px; }
  .sn-live-cell.is-byte b { font-size: 34px; }
  /* Pointing down the column it now reads in. */
  .sn-live-arrow { transform: rotate(90deg); padding: 0; }
}
