/* ─────────────────────────────────────────────────────────────────────────
   Scenes. Every one of them is a full-bleed picture with a sentence next to
   it — no three-up feature grid anywhere on this page, on purpose. The thing
   being sold is a board, so the board is what you look at.
   ───────────────────────────────────────────────────────────────────────── */

/* ── hero ────────────────────────────────────────────────────────────────── */

.hero { padding: clamp(104px, 14vh, 152px) 0 clamp(30px, 5vh, 60px); position: relative; overflow: clip; }

/* The bloom. One warm light behind the board, sitting in the copper, wide and
   very low in opacity — it is the only thing on the page that is not either
   ink, hairline or photograph. */
.hero::before {
  content: '';
  position: absolute;
  top: -18vh; left: 50%;
  width: min(1500px, 135vw); height: min(1100px, 105vw);
  transform: translateX(-50%);
  background: radial-gradient(closest-side, var(--cu-glow), transparent 72%);
  pointer-events: none;
  z-index: 0;
}
.hero .wrap { position: relative; z-index: 1; }

/* The headline and the ask stack over the app window, which runs the full
   measure. The product is the first thing on the page after the sentence that
   says what it is for — that ordering is the whole point of the fold, and the
   old version put a still of the board in a corner instead. */
.hero-say { max-width: 62ch; }
.hero-grid {
  display: grid;
  grid-template-columns: minmax(0, 0.82fr) minmax(0, 1fr);
  gap: clamp(24px, 4vw, 64px);
  align-items: center;
  margin-top: clamp(26px, 3vw, 40px);
}
.hero-board {
  position: relative;
  margin-right: calc(var(--gut) * -1 - 7vw);
  margin-top: clamp(-90px, -6vh, -20px);
}
@media (max-width: 940px) {
  .hero-grid { grid-template-columns: 1fr; }
  .hero-board { margin: 16px calc(var(--gut) * -1) 0; }
}

/* The captures carry the viewer's own backdrop — a vertical gradient from
   #17191f down to #08080a, which is the page colour by the time it reaches
   the bottom. So only the top and the sides need softening; feathering those
   makes the rectangle disappear and leaves the render sitting on the page
   rather than inside a box. Same mask on the hero still and on the scrubbed
   canvas, so they read as one surface. */
.hero-board img { width: 100%; height: auto; }
.hero-board img, .board-figure canvas, .board-figure img {
  -webkit-mask-image:
    linear-gradient(180deg, transparent 0%, #000 17%),
    linear-gradient(90deg, transparent 0%, #000 11%, #000 89%, transparent 100%);
  -webkit-mask-composite: source-in;
  mask-image:
    linear-gradient(180deg, transparent 0%, #000 17%),
    linear-gradient(90deg, transparent 0%, #000 11%, #000 89%, transparent 100%);
  mask-composite: intersect;
}
/* The hero still is cropped tighter than the scrubbed frames, so its fade has
   to be shallower or it eats the barrel jack off the top of the board. */
.hero-board img {
  -webkit-mask-image:
    linear-gradient(180deg, transparent 0%, #000 9%),
    linear-gradient(90deg, transparent 0%, #000 6%, #000 94%, transparent 100%);
  mask-image:
    linear-gradient(180deg, transparent 0%, #000 9%),
    linear-gradient(90deg, transparent 0%, #000 6%, #000 94%, transparent 100%);
}

/* Two lines, never three. The whole job of the fold is to get the sentence and
   the app window on screen together; a third line of display type costs about
   90px and buys nothing. 24ch is where "Say what the board should do." breaks
   after "the". */
.hero h1 { margin: 0; max-width: 24ch; font-size: clamp(2.6rem, 5vw, 4.6rem); }
/* No two-tone headline. Half a sentence in grey reads as a sentence that ran
   out of ink, and it made the *verb* — the thing being promised — the quiet
   half. One weight, one colour; the accent is spent on the button instead. */
.hero h1 em { font-style: normal; color: inherit; }
.hero .lede { margin-top: 20px; max-width: 62ch; }
.hero .acts { margin-top: 26px; }
.hero .under { margin-top: 14px; max-width: 62ch; }
.hero { padding-top: clamp(88px, 11vh, 122px); }

/* ── the scrub: a sticky stage the board turns inside ─────────────────────── */

/* The stage is one screen tall and sticks; the section around it is what
   gives the scroll something to spend. 420vh puts roughly 3.2 screens of
   scroll behind 36 frames — fast enough not to feel like a tax, slow enough
   that a frame is not skipped on a trackpad flick. */
.turn { position: relative; height: 420vh; }
.turn .stage {
  position: sticky;
  top: 0;
  height: 100vh;
  display: grid;
  place-items: center;
  overflow: clip;
}
.board-figure {
  position: relative;
  margin-bottom: clamp(40px, 9vh, 110px);
  width: min(1420px, 104vw);
  aspect-ratio: 2570 / 1620;
}
.board-figure > * {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
}
.board-figure img { object-fit: contain; }

/* The captions ride along the bottom of the stage, one at a time. */
.beats {
  position: absolute;
  left: 0; right: 0; bottom: clamp(28px, 7vh, 78px);
  display: grid;
  place-items: center;
  pointer-events: none;
}
/* The board is bigger than the captions want it to be, so the captions bring
   their own ground: a scrim that fades the render out under the text instead
   of moving the board somewhere it reads smaller. */
.beats::before {
  content: '';
  position: absolute;
  left: -50vw; right: -50vw; bottom: calc(-1 * clamp(28px, 7vh, 78px) - 40px);
  top: -18vh;
  background: linear-gradient(180deg, transparent, rgb(var(--stage-fade) / .55) 38%, rgb(var(--stage-fade) / .94) 66%);
  pointer-events: none;
}
.beat {
  grid-area: 1 / 1;
  position: relative;
  text-align: center;
  opacity: 0;
  transform: translateY(10px);
  transition: opacity .45s var(--ease), transform .45s var(--ease);
  padding: 0 var(--gut);
}
.beat.on { opacity: 1; transform: none; }
.beat h3 {
  font-variation-settings: 'wdth' 116;
  font-weight: 630; letter-spacing: -.03em;
  font-size: clamp(1.4rem, 3.1vw, 2.5rem);
  margin: 0; line-height: 1.05;
}
.beat p { margin: 10px auto 0; max-width: 46ch; color: var(--fg-2); font-size: 15px; }

/* A thin progress rail down the left of the stage — the one bit of chrome
   that tells you the scene has a length. */
.rail {
  position: absolute;
  left: var(--gut); top: 32vh; bottom: 32vh;
  width: 2px;
  background: var(--line);
  border-radius: 2px;
  overflow: hidden;
}
.rail i { display: block; width: 100%; background: var(--cu); height: 0; }
@media (max-width: 860px) { .rail { display: none; } }
/* On a phone the frame is mostly empty around a small board, so the figure is
   allowed to run well past the viewport and let the mask cut it off. */
@media (max-width: 860px) { .board-figure { width: 168vw; margin-bottom: 6vh; } }

/* ── split scenes: a sentence beside a picture ───────────────────────────── */

.split {
  display: grid;
  grid-template-columns: minmax(0, 1.06fr) minmax(0, 1fr);
  gap: clamp(34px, 5vw, 84px);
  align-items: center;
}
.split.flip > *:first-child { order: 2; }
@media (max-width: 940px) { .split { grid-template-columns: 1fr; } .split.flip > *:first-child { order: 0; } }

.say h2 { margin: 16px 0 0; }
.say .body { margin-top: 20px; font-size: 1.02rem; line-height: 1.62; }
.say .body + .body { margin-top: 16px; }

/* A picture is mounted, not floated: hairline, a dark plate, a hard shadow
   under it. Same treatment everywhere so the page reads as one system. */
.plate {
  position: relative;
  border: 1px solid var(--line);
  border-radius: 5px;
  background: var(--sub);
  overflow: hidden;
  box-shadow: 0 40px 90px -50px rgb(var(--shadow-rgb) / var(--plate-shadow-a, .95)),
              0 2px 0 rgb(var(--sheen-rgb) / .03) inset;
}
.plate img { width: 100%; height: auto; }
.plate figcaption {
  border-top: 1px solid var(--line);
  padding: 11px 15px;
  font-size: 12.5px;
  color: var(--fg-3);
  background: rgb(var(--sheen-rgb) / var(--sheen-a));
}
.plate figcaption b { color: var(--fg-2); font-weight: 600; }

/* ── full-bleed picture band ─────────────────────────────────────────────── */

.band { padding: clamp(48px, 6vw, 88px) var(--gut); }
.band .plate { max-width: 1560px; margin: 0 auto; }

/* ── numbers ─────────────────────────────────────────────────────────────── */

.counts {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  border-top: 1px solid var(--line);
}
.counts div {
  padding: 26px 24px 26px 0;
  border-right: 1px solid var(--line);
}
.counts div:last-child { border-right: 0; }
.counts strong {
  display: block;
  font-variation-settings: 'wdth' 112;
  font-weight: 640;
  font-size: clamp(1.9rem, 3.4vw, 2.9rem);
  letter-spacing: -.04em;
  line-height: 1;
}
.counts span { display: block; margin-top: 9px; font-size: 13.5px; color: var(--fg-3); max-width: 26ch; }
@media (max-width: 700px) { .counts div { border-right: 0; border-bottom: 1px solid var(--line); padding-right: 0; } }

/* ── the fabrication gallery ─────────────────────────────────────────────── */

.gallery {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 14px;
}
.gallery figure { margin: 0; }
.gallery .plate img { aspect-ratio: 16 / 9.4; object-fit: cover; }
.gallery figcaption .data { color: var(--fg-3); }
@media (min-width: 1100px) {
  .gallery figure:nth-child(1), .gallery figure:nth-child(6) { grid-column: span 2; }
  .gallery figure:nth-child(1) .plate img, .gallery figure:nth-child(6) .plate img { aspect-ratio: 32 / 10.4; }
}

/* ── the transcript ──────────────────────────────────────────────────────── */

.turnbox {
  border: 1px solid var(--line);
  border-radius: 5px;
  background: linear-gradient(180deg, var(--card), var(--sub));
  overflow: hidden;
}
.turnbox .said {
  padding: 22px 26px;
  border-bottom: 1px solid var(--line);
  font-size: 1.05rem;
  letter-spacing: -.015em;
}
.turnbox .said::before {
  content: '';
  display: inline-block;
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--cu);
  margin-right: 11px; vertical-align: 2px;
}
.turnbox .did { padding: 6px 0; }
.turnbox .did li {
  display: flex; gap: 14px; align-items: baseline;
  padding: 9px 26px;
  font-size: 14.5px;
  color: var(--fg-2);
  border-bottom: 1px solid rgb(var(--line-rgb) / .55);
}
.turnbox .did li:last-child { border-bottom: 0; }
.turnbox .did { list-style: none; margin: 0; }
.turnbox .did .t { color: var(--cu); font-family: var(--mono); font-size: 12px; white-space: nowrap; min-width: 132px; }
.turnbox .foot {
  border-top: 1px solid var(--line);
  padding: 14px 26px;
  font-size: 13px; color: var(--fg-3);
  display: flex; flex-wrap: wrap; gap: 8px 20px; justify-content: space-between;
}
.turnbox .foot b { color: var(--ok); font-weight: 600; }

/* ── pricing ─────────────────────────────────────────────────────────────── */

.tiers { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 14px; }
.tier {
  border: 1px solid var(--line);
  border-radius: 5px;
  background: var(--sub);
  padding: 26px 24px 24px;
  display: flex; flex-direction: column; gap: 14px;
}
.tier.lead { border-color: var(--cu-deep); background: linear-gradient(180deg, rgb(var(--accent-rgb) / .07), transparent 55%), var(--sub); }
.tier h3 { margin: 0; font-size: 17px; font-weight: 640; letter-spacing: -.02em; }
.tier .price {
  font-variation-settings: 'wdth' 112;
  font-weight: 640; font-size: 2.5rem; letter-spacing: -.04em; line-height: 1;
}
.tier .price sub { font-size: .95rem; font-weight: 500; color: var(--fg-3); vertical-align: baseline; letter-spacing: -.01em; }
.tier ul { margin: 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: 9px; font-size: 14px; color: var(--fg-2); }
.tier ul li { display: flex; gap: 9px; }
.tier ul li::before { content: '—'; color: var(--fg-3); }
.tier .cta, .tier .ghost { margin-top: auto; width: 100%; }
.tier .note { font-size: 12.5px; color: var(--fg-3); margin: 0; }

/* ── the line: what Rivet PCB is the first of ────────────────────────────── */

.line-list { display: flex; flex-direction: column; border-top: 1px solid var(--line); margin-top: 40px; }
.line-list div {
  display: flex; align-items: baseline; gap: 20px;
  padding: 20px 0;
  border-bottom: 1px solid var(--line);
}
.line-list .n { font-family: var(--mono); font-size: 12px; color: var(--fg-3); min-width: 34px; }
.line-list .w { font-variation-settings: 'wdth' 112; font-weight: 620; font-size: clamp(1.2rem,2.2vw,1.7rem); letter-spacing: -.03em; }
.line-list .s { margin-left: auto; font-size: 13px; color: var(--fg-3); text-align: right; }
.line-list div.now .w { color: var(--cu); }

/* ── close ───────────────────────────────────────────────────────────────── */

.close { text-align: center; padding: clamp(80px, 10vw, 136px) 0 clamp(90px, 12vw, 150px); position: relative; overflow: clip; }
.close::before {
  content: '';
  position: absolute; bottom: -40%; left: 50%;
  width: min(1200px, 130vw); height: min(700px, 80vw);
  transform: translateX(-50%);
  background: radial-gradient(closest-side, rgb(var(--accent-rgb) / var(--glow-a)), transparent 70%);
  pointer-events: none;
}
.close .wrap { position: relative; }
.close .acts { justify-content: center; margin-top: 30px; }
.close .under { margin: 18px auto 0; max-width: 46ch; }

/* ── faq ─────────────────────────────────────────────────────────────────── */

.faq { border-top: 1px solid var(--line); }
.faq details { border-bottom: 1px solid var(--line); }
.faq summary {
  list-style: none; cursor: pointer;
  padding: 22px 0;
  display: flex; gap: 18px; align-items: baseline;
  font-weight: 600; font-size: 1.02rem; letter-spacing: -.015em;
  transition: color .2s var(--ease);
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after {
  content: '+'; margin-left: auto; color: var(--fg-3); font-weight: 400; font-size: 1.3rem; line-height: 1;
  transition: transform .3s var(--ease), color .2s var(--ease);
}
.faq details[open] summary::after { transform: rotate(45deg); color: var(--cu); }
.faq summary:hover { color: var(--cu-hi); }
.faq .a { padding: 0 0 24px 0; color: var(--fg-2); max-width: 68ch; font-size: 15.5px; }
.faq .a p { margin: 0 0 12px; }
.faq .a p:last-child { margin-bottom: 0; }

/* ── the index ────────────────────────────────────────────────────────────
   Deliberately a LIST and not a three-up card grid. A grid of cards is the
   house's named tell for a page that was assembled rather than designed, and
   it also flattens five things of unequal weight into five equal boxes. Rows
   let the description run to a readable measure and give the whole block one
   left edge to read down. */

.index { display: flex; flex-direction: column; border-top: 1px solid var(--line); margin-top: 44px; }
.index a {
  display: grid;
  grid-template-columns: 46px minmax(150px, 0.42fr) minmax(0, 1fr) 28px;
  align-items: baseline;
  gap: 10px 20px;
  padding: 24px 0 22px;
  border-bottom: 1px solid var(--line);
  position: relative;
  transition: color .22s var(--ease);
}
/* The hover wash is a pseudo-element bled into the gutters, so the row lights
   up edge to edge instead of stopping at the text column. */
.index a::before {
  content: '';
  position: absolute;
  inset: 0 calc(var(--gut) * -0.5);
  background: rgb(var(--fg-rgb) / .035);
  opacity: 0;
  transition: opacity .22s var(--ease);
  pointer-events: none;
}
.index a:hover::before { opacity: 1; }
.index .n { font-family: var(--mono); font-size: 12px; color: var(--fg-3); }
.index .w {
  font-variation-settings: 'wdth' 112;
  font-weight: 620;
  font-size: clamp(1.15rem, 2vw, 1.55rem);
  letter-spacing: -.03em;
  line-height: 1.05;
}
.index .d { font-size: 14.5px; color: var(--fg-2); line-height: 1.55; max-width: 54ch; }
.index .arw { color: var(--fg-3); justify-self: end; transition: transform .28s var(--ease), color .22s var(--ease); }
.index a:hover .arw { transform: translateX(4px); color: var(--cu); }
@media (max-width: 780px) {
  .index a { grid-template-columns: 34px minmax(0, 1fr) 24px; }
  .index .d { grid-column: 2 / 4; }
}

/* ── the undo stack ───────────────────────────────────────────────────────
   Eight things the turn did, stacked, that collapse into one press. It runs
   once on arrival and then holds at the collapsed state — a loop would make a
   claim about undo being reversible-forever that the app does not make. */

.undo-viz { position: relative; display: flex; flex-direction: column; align-items: center; gap: 26px; }
.uv-stack { position: relative; width: 100%; max-width: 320px; height: 250px; perspective: 900px; }
.uv-stack i {
  position: absolute;
  left: 0; right: 0;
  top: calc(var(--i) * 27px);
  display: flex;
  align-items: baseline;
  gap: 10px;
  padding: 9px 14px;
  border-radius: 8px;
  border: 1px solid var(--line);
  background: var(--card);
  box-shadow: 0 4px 14px -8px rgb(var(--shadow-rgb) / .3);
  font-style: normal;
  transform-origin: 50% 100%;
  z-index: calc(10 - var(--i));
}
.uv-stack i span { font-family: var(--mono); font-size: 11px; color: var(--cu-deep); }
.uv-stack i b { margin-left: auto; font-size: 12px; font-weight: 550; color: var(--fg-3); }
/* The rows collapse UPWARD into the topmost one rather than fading out where
   they stand — the point is that eight things become one thing, not that eight
   things vanish. And what is left behind is a sentence, so somebody who scrolls
   back to this section later finds a finished state instead of an empty box. */
.undo-viz.in .uv-stack i {
  animation: uvfall .46s var(--ease) forwards;
  animation-delay: calc(1.15s + (7 - var(--i)) * .05s);
}
@keyframes uvfall {
  to { opacity: 0; transform: translateY(calc(var(--i) * -27px)) scale(.97); }
}
.uv-done {
  position: absolute;
  top: 96px; left: 0; right: 0;
  margin: 0;
  text-align: center;
  font-size: 14.5px;
  color: var(--fg-2);
  opacity: 0;
}
.uv-done b { color: var(--fg); font-weight: 600; }
.undo-viz.in .uv-done { animation: uvdone .5s var(--ease) 1.6s forwards; }
@keyframes uvdone { to { opacity: 1; } }
/* With Reduce Motion on, nothing animates — so the stack stays put and the
   sentence is simply there from the start. Both states have to read. */
@media (prefers-reduced-motion: reduce) {
  .uv-done { position: static; opacity: 1; margin-top: 14px; }
}
.uv-key { display: flex; gap: 6px; }
.uv-key kbd {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 34px; height: 34px; padding: 0 9px;
  border-radius: 7px;
  border: 1px solid var(--line-2);
  border-bottom-width: 2px;
  background: var(--card);
  font-family: var(--sans); font-size: 14px; font-weight: 600; color: var(--fg-2);
}
.undo-viz.in .uv-key kbd { animation: uvpress .34s var(--ease) 1s; }
@keyframes uvpress {
  40% { transform: translateY(2px); border-bottom-width: 1px; background: var(--cu); color: var(--cta-ink); border-color: var(--cu); }
}
@media (prefers-reduced-motion: reduce) {
  .undo-viz.in .uv-stack i, .undo-viz.in .uv-key kbd { animation: none; }
}

/* ── what leaves the machine ─────────────────────────────────────────────── */

.wires { display: flex; flex-direction: column; border-top: 1px solid var(--line); }
.wire-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 76px minmax(0, .82fr);
  align-items: center;
  gap: 14px;
  padding: 17px 0;
  border-bottom: 1px solid var(--line);
  font-size: 14px;
}
.wr-l { color: var(--fg); font-weight: 550; letter-spacing: -.012em; }
.wr-r { color: var(--fg-2); }
.wire-row.muted .wr-l, .wire-row.muted .wr-r { color: var(--fg-3); }
.wr-bar { position: relative; height: 12px; display: block; }
.wr-bar i { position: absolute; inset: 5px 0 auto; height: 2px; border-radius: 2px; display: block; }
/* A closed loop: it goes nowhere, so it is drawn as a bracket that turns back. */
.wr-bar i.stay { background: var(--fg-3); opacity: .5; }
.wr-bar i.stay::before, .wr-bar i.stay::after {
  content: ''; position: absolute; width: 2px; height: 7px; background: inherit; top: -5px;
}
.wr-bar i.stay::before { left: 0; }
.wr-bar i.stay::after { right: 0; }
/* An arrow out, in the accent, because it is the only thing on this list that
   actually crosses the machine's edge. */
.wr-bar i.out { background: var(--cu); }
.wr-bar i.out::after {
  content: ''; position: absolute; right: -1px; top: -3px;
  border: 4px solid transparent; border-left-color: var(--cu); border-right: 0;
}
.wr-bar i.none { background: repeating-linear-gradient(90deg, var(--line-2) 0 4px, transparent 4px 8px); }
@media (max-width: 560px) {
  .wire-row { grid-template-columns: minmax(0, 1fr) 44px minmax(0, .9fr); font-size: 13px; }
}

/* ── feature sections ─────────────────────────────────────────────────────
   The product surface is the section; the words are a caption on it.

   This replaced a row list — five headings with a sentence each and no
   picture anywhere. It read as a table of contents for a page that had
   already ended, and the owner said so: "frankly what I see here is boring."
   The correction is the one the whole atlas agrees on and that Cursor,
   Linear, Vercel and Stripe all make on their own front pages: show the
   running product at size, and cut the prose down to a headline, one
   sentence, and a link for the person who wants the detail.

   So the visual takes ~62% and bleeds past the gutter; the words sit in a
   narrow column beside it and never grow a third paragraph. Sections
   alternate sides so the eye has somewhere to go. */

.feat {
  display: grid;
  grid-template-columns: minmax(0, 1.62fr) minmax(0, 1fr);
  gap: clamp(28px, 4.5vw, 76px);
  align-items: center;
  padding: clamp(46px, 6vw, 88px) 0;
  border-top: 1px solid var(--line);
}
.feat:first-of-type { border-top: 0; }
.feat.flip > .feat-visual { order: 2; }
@media (max-width: 940px) {
  .feat { grid-template-columns: 1fr; gap: 24px; }
  .feat.flip > .feat-visual { order: 0; }
}

/* The picture is allowed to leave the container on its outer side. A visual
   that respects the gutter reads as an illustration; one that runs off the
   edge reads as a thing that continues. */
.feat-visual { position: relative; margin-left: calc(var(--gut) * -1 - 2vw); }
.feat.flip .feat-visual { margin-left: 0; margin-right: calc(var(--gut) * -1 - 2vw); }
@media (max-width: 940px) {
  .feat-visual, .feat.flip .feat-visual { margin: 0 calc(var(--gut) * -1); }
}

.feat-frame {
  position: relative;
  border-radius: 12px;
  overflow: hidden;
  background: var(--stage-bot);
  border: 1px solid var(--line);
  box-shadow: 0 40px 90px -46px rgb(var(--shadow-rgb) / var(--plate-shadow-a, .5)),
              0 2px 10px -6px rgb(var(--shadow-rgb) / .2);
}
.feat-frame img { width: 100%; height: auto; display: block; }
/* A caption strip is a fact about the picture, not a title for the section —
   it names what you are looking at inside the frame. */
.feat-frame figcaption {
  border-top: 1px solid rgb(var(--sheen-rgb) / .07);
  padding: 10px 14px;
  font-size: 12.5px;
  color: #97a1b2;
  background: rgb(0 0 0 / .35);
}
.feat-frame figcaption b { color: #dfe5ee; font-weight: 600; }

.feat-say { max-width: 42ch; }
.feat-say h2 {
  font-variation-settings: 'wdth' 114;
  font-weight: 630;
  letter-spacing: -.032em;
  line-height: 1.04;
  font-size: clamp(1.5rem, 2.5vw, 2.1rem);
  margin: 0;
  text-wrap: balance;
}
.feat-say p { margin: 14px 0 0; color: var(--fg-2); font-size: 15.5px; line-height: 1.6; }
.feat-say p b { color: var(--fg); font-weight: 600; }
/* The one accent on the section, and it is the way out of it. */
.feat-link {
  display: inline-flex; align-items: center; gap: 7px;
  margin-top: 20px;
  color: var(--cu-deep);
  font-size: 15px; font-weight: 580; letter-spacing: -.012em;
}
.feat-link .arw { transition: transform .28s var(--ease); }
.feat-link:hover .arw { transform: translateX(4px); }

/* A run of small facts under a feature, where the fact IS the interesting
   part — used sparingly, and never as a substitute for a picture. */
.feat-facts { display: flex; flex-wrap: wrap; gap: 6px 8px; margin-top: 18px; }
.feat-facts span {
  padding: 4px 10px;
  border: 1px solid var(--line);
  border-radius: 999px;
  font-size: 12.5px;
  color: var(--fg-2);
  background: var(--sub);
}
.feat-facts span b { color: var(--fg); font-weight: 600; }

/* ── the panel, redrawn ───────────────────────────────────────────────────
   Not a screenshot, and it says so in its caption and in README.md under the
   same declaration the hero demo carries: tool names, arguments and replies
   are transcribed from the recorded session; the layout is the app's own.

   It is redrawn rather than captured for one honest reason — a screenshot of
   this panel taken on a machine with no symbol library shows every call
   failing, which is a fact about the capture rig and not about the product.
   A picture that misrepresents the product in ITS OWN favour would be the
   thing to refuse; this one misrepresents it against, so it is the capture
   that is wrong. */

/* REDRAWN 2026-08-08 TO THE PANEL THAT SHIPPED. What was here was the panel as
   it looked before the rebuild — tabs across the top of a single column, one
   boxed row per call, the call names set in monospace. All three are wrong
   now, and the last one was always wrong: a phrase like "Placing parts" is
   prose, and monospace is reserved for text that is literally machine data.
   The approved design is three rules deep:

     a RAIL marks a change of place, a TEXT SWITCH marks a change of mode;
     the rail selection wraps the ICON only, label unboxed below it;
     the work WRAPS to one line the moment the final answer starts, and the
     ask, the answer, the touched chips and the undo stay outside it. */

.panelshot {
  background: #0b0d11;
  color: #e8ecf2;
  font-size: 13px;
  line-height: 1.5;
  display: grid;
  grid-template-columns: 54px minmax(0, 1fr);
}

.ps-rail {
  border-right: 1px solid #1b202a;
  background: #10131a;
  padding: 8px 0;
  display: flex; flex-direction: column; gap: 2px;
}
.ps-rail span {
  display: grid; justify-items: center; gap: 3px;
  padding: 6px 0 5px;
  font-size: 9px; color: #68728a; letter-spacing: .01em;
}
/* The selection is a square behind the icon and nothing else — a pill round
   icon-and-label makes every rail button a different width. */
.ps-rail span i {
  width: 26px; height: 23px; border-radius: 6px;
  border: 1.4px solid #39414f;
  display: block;
}
.ps-rail span.on { color: #e8ecf2; }
.ps-rail span.on i { border-color: transparent; background: rgb(var(--accent-rgb) / .17); }

.ps-col { min-width: 0; }
.ps-head {
  display: flex; align-items: center; gap: 6px;
  padding: 9px 13px;
  border-bottom: 1px solid #1b202a;
  font-size: 11px; letter-spacing: .05em; text-transform: uppercase;
  font-weight: 640; color: #68728a;
}
.ps-sess { margin-left: auto; text-transform: none; letter-spacing: 0; font-weight: 500; font-size: 11.5px; }

.ps-body { padding: 14px 13px 15px; display: flex; flex-direction: column; gap: 10px; }
.ps-ask {
  margin: 0; padding: 1px 0 1px 11px;
  border-left: 2px solid rgb(var(--accent-rgb));
  color: #97a1b2; font-size: 12.5px;
}

/* The turn, collapsed. One line, a disclosure arrow, and it is the only thing
   standing where twenty-four rows used to be. */
.ps-wrap {
  display: flex; align-items: center; gap: 8px;
  padding: 7px 11px;
  background: #12151b;
  border: 1px solid #1e2430;
  border-radius: 8px;
  font-size: 12.5px; color: #97a1b2;
  width: fit-content;
}
.ps-wrap i { width: 6px; height: 6px; border-radius: 50%; background: #5fc98f; flex: none; }
.ps-wrap em { font-style: normal; color: #68728a; margin-left: 2px; }

.ps-said { margin: 0; color: #e8ecf2; font-size: 13px; line-height: 1.6; }

/* What the turn touched. Refdes and net names ARE machine data, so these are
   the one thing in the panel that is set in monospace. */
.ps-chips { display: flex; flex-wrap: wrap; gap: 5px; }
.ps-chips span {
  font-family: var(--mono); font-size: 10.5px; color: #97a1b2;
  border: 1px solid #232a37; border-radius: 999px; padding: 2px 8px;
}

.ps-undo {
  align-self: flex-start;
  padding: 4px 11px;
  border: 1px solid #2c3444; border-radius: 999px;
  font-size: 11.5px; color: #97a1b2;
}
.ps-undo b { font-family: var(--mono); font-size: 10.5px; font-weight: 500; color: #68728a; margin-left: 5px; }

@media (max-width: 520px) { .panelshot { grid-template-columns: 1fr; } .ps-rail { display: none; } }

/* The footprints frame sits over the bare board and fades first, so the scrub
   reads footprints → copper → parts rather than starting mid-story. It takes
   the same mask as its neighbours or its edges would not match theirs. */
.board-figure .fp { z-index: 1; }
/* The canvas starts invisible and rivet.js brings it in on the same ramp that
   takes `.bare` out. Painted opaque from the start it showed the orbit's first
   frame under everything else, which is what kept two boards on screen at once
   even after the fade order was fixed. */
.board-figure canvas { opacity: 0; }

/* ── REGISTRATION ──────────────────────────────────────────────────────────
   The four states of the scrub are two kinds of picture: a 2D editor canvas
   (editor-2d.webp) and 3D renders of the same board (bare-top.webp and the
   36 orbit frames). Contained at the same width, the BOARD inside them is not
   the same size — measured in a browser by drawing both at the figure's own
   contain geometry and blending at 50%, the board is 1.46x bigger in the 2D
   capture. So the dissolve was four photographs cross-fading rather than one
   object changing, which is exactly what the owner saw.

   The 1.46 is SPLIT rather than spent on one layer, and the reason is the
   resolution gate. Both captures are 3550 px wide into a 1420 px figure —
   2.5x. Scaling the 3D layers up by the whole 1.46 would put them at 1.71x,
   under the 2x floor, and tools/check-image-resolution.js would NOT catch it:
   it measures the canvas ELEMENT against the natural size and cannot see a
   scale applied inside the draw call. A gate that cannot see the thing it is
   guarding is the failure mode to design out, not to rely on.

     3D layers  x 1.20 → 1704 px displayed of 3550 natural = 2.08x
     2D layer   x 0.822 →  1167 px displayed of 3550 natural = 3.04x
     ratio      1.20 / 0.822 = 1.46 ✓

   The canvas half of this lives in assets/js/rivet.js, in draw(). The two
   numbers have to keep their ratio; change one and change the other. */
.board-figure .fp   { transform: scale(0.822); }
.board-figure .bare { transform: scale(1.20); }
