/* KUUP studio — GRAPHITE V1
   Paper, graphite, Hanken Grotesk, and useful motion only. */

:root {
  --paper: oklch(96.5% 0.002 90);
  /* The paper's grain, as a token: body paints it, and so does any section
     that paints its own paper (the /business-brain hero band). Same tile,
     same origin, so the two are one surface. */
  --grain: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='240' height='240'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.76' numOctaves='2'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' opacity='.018' filter='url(%23n)'/%3E%3C/svg%3E");
  --ink: oklch(22% 0.005 270);
  --graphite: oklch(49% 0.006 270);
  --graphite-light: oklch(70% 0.004 270);
  --line: color-mix(in oklab, var(--ink) 16%, transparent);
  --line-strong: color-mix(in oklab, var(--ink) 38%, transparent);
  --wash: color-mix(in oklab, var(--ink) 5.5%, transparent);
  --err: oklch(52% 0.19 29);
  --sans: "Hanken Grotesk", sans-serif;
  --ease-out: cubic-bezier(.19, 1, .22, 1);
  --ease-soft: cubic-bezier(.25, .8, .25, 1);
  --pad: clamp(1.15rem, 4vw, 4.5rem);
  --header-h: 4.25rem;
  --content: 92rem;
  /* tokens carried over from the KUUP design-system handoff */
  --ink-2: var(--graphite);
  /* Demo metadata is real explanatory text, not decorative chrome. Keep it at
     the normal secondary-text contrast even when the responsive demo makes it
     small. */
  --cube-grey: var(--graphite);
  --font-mono: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  --face: color-mix(in oklab, var(--paper) 93%, var(--ink));
  --r-sm: 10px;
  --r-card: 18px;
  --shadow-1: 0 1px 2px oklch(20% 0.01 265 / .05), 0 12px 32px -14px oklch(20% 0.01 265 / .16);
  --shadow-2: 0 2px 4px oklch(20% 0.01 265 / .06), 0 22px 48px -16px oklch(20% 0.01 265 / .22);

  /* ---------- TYPE SCALE ------------------------------------------------
     THE RULE: a heading's size, weight and tracking come from a token —
     never from a number written at the point of use. Ported from
     compactliving-web/src/styles/global.css (@theme), but filled with
     KUUP's own values: the three main clusters (90 / 51 / 46px at 1280px)
     already existed, they were just written out fourteen separate times.

     Deviation from the reference, on purpose: compactliving also ships
     Tailwind Preflight's `h1..h6 { font-size: inherit }` reset, so every
     heading there MUST carry a class. That reset is a Tailwind artifact,
     not a design idea — a hand-coded site sizes headings through its own
     semantic classes just as well, with no risk of an unclassed heading
     silently collapsing to body size. So: tokens yes, reset no. */
  --t-display:   clamp(3.3rem, 7vw, 7.8rem);      /* page + section titles */
  --t-statement: clamp(2rem, 4vw, 4.4rem);        /* hero / editorial line */
  --t-h2:        clamp(1.9rem, 3.6vw, 3.2rem);    /* case + sub-heads      */
  --t-h3:        clamp(1.2rem, 1.9vw, 1.5rem);    /* cards, steps, FAQ     */
  --t-lead:      clamp(1.08rem, .5vw + .95rem, 1.3rem);

  /* Four weights. There were eight. */
  --wt-title:  650;   /* display                          */
  --wt-state:  550;   /* statement                        */
  --wt-head:   620;   /* h2 / h3 / labels                 */
  --wt-accent: 430;   /* the italic <em> clause in a title */

  /* Tracking travels with its step — that is the whole point of the token. */
  --tr-display: -.04em;
  --tr-state:   -.035em;
  --tr-h2:      -.03em;
  --tr-h3:      -.02em;
}

.band {
  --paper: oklch(22% 0.005 270);
  --ink: oklch(96.5% 0.002 90);
  --graphite: oklch(74% 0.004 90);
  --graphite-light: oklch(52% 0.004 90);
  --line: color-mix(in oklab, var(--ink) 17%, transparent);
  --line-strong: color-mix(in oklab, var(--ink) 42%, transparent);
  --wash: color-mix(in oklab, var(--ink) 7%, transparent);
  background: var(--paper);
  color: var(--ink);
}

*, *::before, *::after { box-sizing: border-box; }
html { scroll-behavior: smooth; }
/* The paper is on <html> as well as <body>, on purpose (2026-09-02). With
   only body painting it, the browser propagates that background to the
   canvas — OUTSIDE body's own stacking context. Body is a stacking context
   for the 150ms of its fade-in, so during those 150ms every mix-blend-mode
   on the page blended against transparency: the /business-brain laptop's
   white ground flashed as a white box on load, then snapped to paper. With
   html painted, body's background stays inside body, and the blend meets
   paper from the first frame. Same colour, no grain here: body's grain is
   painted over it everywhere the page reaches. */
html { background: var(--paper); }
body {
  margin: 0;
  overflow-x: hidden;
  background: var(--paper);
  background-image: var(--grain);
  color: var(--ink);
  font-family: var(--sans);
  font-size: clamp(1rem, .35vw + .94rem, 1.125rem);
  line-height: 1.6;
  /* `backwards`, not `both` (changed 2026-09-02). Chromium keeps a FILLING
     opacity animation as a stacking context after it has finished, and the
     body's background is propagated to the canvas, outside that context — so
     with `both`, every mix-blend-mode inside <body> blended against
     transparency instead of paper. The /business-brain laptop's white ground
     came through as a white box; it was the site's first blend. `backwards`
     still covers the first frame, and the end state is the natural opacity
     anyway, so nothing visible changed except the blend. */
  animation: bodyIn 150ms ease-out backwards;
}
@keyframes bodyIn { from { opacity: 0; } }

[hidden] { display: none !important; }
::selection { background: var(--ink); color: var(--paper); }
:focus-visible { outline: 2px solid currentColor; outline-offset: 4px; }
a { color: inherit; }
:target { scroll-margin-top: calc(var(--header-h) + 1.5rem); }
img, svg { max-width: 100%; }
h1, h2, h3, h4, p, ul, ol, figure, blockquote, fieldset, legend { margin: 0; padding: 0; }
ul, ol { list-style: none; }
button, input, textarea { font: inherit; }
button { color: inherit; }

.skip-link {
  position: fixed;
  z-index: 1000;
  top: -5rem;
  left: var(--pad);
  background: var(--ink);
  color: var(--paper);
  padding: .55rem .9rem;
  font-size: .78rem;
  text-decoration: none;
}
.skip-link:focus { top: .5rem; }

.micro,
.nav-list,
.work-code,
.work-meta,
.fact-strip,
.tip,
.table-note,
.site-foot,
.field label,
.form-note,
.field-err,
.availability,
.package-label {
  font-size: .7rem;
  font-weight: 620;
  letter-spacing: .08em;
  line-height: 1.35;
  text-transform: uppercase;
}

.shell { width: min(100%, var(--content)); margin-inline: auto; padding-inline: var(--pad); }
.muted { color: var(--graphite); }
.rule {
  height: 1px;
  background: var(--line-strong);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 1.25s var(--ease-out);
}
.rule.in, .no-js .rule { transform: scaleX(1); }
.section {
  padding-block: clamp(6rem, 15vh, 11rem);
}
.section-head {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(14rem, 32rem);
  gap: 2rem;
  align-items: end;
  margin: clamp(2.5rem, 7vh, 5rem) 0 clamp(3rem, 8vh, 6rem);
}
.section-title,
.page-title,
.t-display {
  max-width: 13ch;
  font-size: var(--t-display);
  font-weight: var(--wt-title);
  letter-spacing: var(--tr-display);
  line-height: .93;
}
.section-title em,
.page-title em,
.t-display em,
.t-statement em,
.t-h2 em {
  color: var(--graphite);
  font-weight: var(--wt-accent);
  font-style: italic;
}
.section-copy { color: var(--graphite); max-width: 42ch; }
.t-h3 { font-size: var(--t-h3); font-weight: var(--wt-head); letter-spacing: var(--tr-h3); line-height: 1.2; }
.t-lead { max-width: 46ch; color: var(--graphite); font-size: var(--t-lead); line-height: 1.5; }
.text-link {
  text-underline-offset: .28em;
  text-decoration-thickness: 1px;
  text-decoration-color: var(--line-strong);
}
.text-link:hover { text-decoration-color: currentColor; }

/* Shared header and navigation */
.site-head {
  position: fixed;
  z-index: 100;
  top: 0;
  left: 0;
  right: 0;
  height: var(--header-h);
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: 2rem;
  padding-inline: var(--pad);
  border-bottom: 1px solid transparent;
  transition: background-color .3s, border-color .3s;
}
.site-head.scrolled,
.site-head.menu-open {
  background: color-mix(in oklab, var(--paper) 95%, transparent);
  border-bottom-color: var(--line);
}
.brand { text-decoration: none; line-height: 1; white-space: nowrap; }
.brand-word { font-size: 1.1rem; font-weight: 790; letter-spacing: -.025em; }
.brand-word small {
  margin-left: .45em;
  color: #8e9099; /* brand constant — matches the logo lockup, not themed */
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-size: .52em;
  font-weight: 400;
  letter-spacing: .18em;
  text-transform: lowercase;
}
.site-nav { justify-self: center; }
.nav-list { display: flex; gap: clamp(1rem, 2.5vw, 2.5rem); }
.nav-list a {
  position: relative;
  color: var(--graphite);
  text-decoration: none;
  transition: color .25s;
}
.nav-list a::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: -.35rem;
  height: 1px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: right;
  transition: transform .35s var(--ease-out);
}
.nav-list a:hover,
.nav-list a[aria-current="page"] { color: var(--ink); }
.nav-list a:hover::after,
.nav-list a[aria-current="page"]::after { transform: scaleX(1); transform-origin: left; }
.head-end { display: flex; align-items: center; gap: .8rem; grid-column: 3; justify-self: end; }
.head-cta {
  display: inline-flex;
  align-items: center;
  min-height: 2.3rem;
  padding: .5rem 1.1rem;
  border-radius: 999px;
  background: var(--ink);
  color: var(--paper);
  font-size: .82rem;
  font-weight: 640;
  text-decoration: none;
  white-space: nowrap;
  transition: background-color .25s, color .25s, box-shadow .25s;
}
.head-cta:hover,
.head-cta:focus-visible { background: var(--paper); color: var(--ink); box-shadow: inset 0 0 0 1px var(--line-strong); }
.menu-toggle {
  display: none;
  width: 2.75rem;
  height: 2.75rem;
  padding: 0;
  border: 0;
  background: none;
  color: var(--ink);
  cursor: pointer;
}
.menu-lines { display: block; position: relative; width: 1rem; height: .75rem; margin: auto; }
.menu-lines::before,
.menu-lines::after {
  content: "";
  position: absolute;
  left: 0;
  width: 100%;
  height: 1px;
  background: currentColor;
  transition: transform .3s var(--ease-out), top .3s var(--ease-out);
}
.menu-lines::before { top: .15rem; }
.menu-lines::after { top: .55rem; }
.menu-toggle[aria-expanded="true"] .menu-lines::before { top: .36rem; transform: rotate(45deg); }
.menu-toggle[aria-expanded="true"] .menu-lines::after { top: .36rem; transform: rotate(-45deg); }

/* The menu card's footer row. Hidden by default: above 56rem the nav is the
   horizontal list in the header, and this would sit in the middle of it. */
.nav-foot { display: none; }

/* Dimmed backdrop behind the slide-in menu */
.nav-scrim {
  position: fixed;
  inset: 0;
  z-index: 40;
  background: rgba(18, 18, 20, .42);
  opacity: 0;
  visibility: hidden;
  transition: opacity .45s var(--ease-out), visibility 0s .45s;
}
.nav-scrim.is-open { opacity: 1; visibility: visible; transition: opacity .45s var(--ease-out), visibility 0s; }

/* Shared buttons */
.btn {
  display: inline-flex;
  min-height: 2.8rem;
  align-items: center;
  justify-content: center;
  padding: .72rem 1.25rem;
  /* A soft hairline, not an outline — --line (16%) rather than
     --line-strong (38%). The fill on hover does the work instead. */
  border: 1px solid var(--line);
  border-radius: 999px;
  background: transparent;
  color: var(--ink);
  font-size: .9rem;
  font-weight: 620;
  text-decoration: none;
  cursor: pointer;
  transition: color .25s, background-color .25s, border-color .25s, transform .3s var(--ease-soft);
  will-change: transform;
}
.btn:hover,
.btn:focus-visible { border-color: var(--line-strong); background: var(--wash); }
.btn-solid { background: var(--ink); color: var(--paper); border-color: var(--ink); }
.btn-solid:hover,
.btn-solid:focus-visible { background: color-mix(in oklab, var(--ink) 82%, var(--paper)); color: var(--paper); border-color: transparent; }
/* The global ring is `2px solid currentColor`, and on a solid button
   currentColor IS `--paper` — so the keyboard focus ring is drawn in the page's
   own background colour and vanishes at 1.00:1. Both surfaces need the ink
   version: inside `.band` the tokens flip, and `--ink` follows them. */
.btn-solid:focus-visible { outline-color: var(--ink); }

/* v4 reveal system */
.js .reveal { opacity: 0; transform: translateY(1.4rem); }
/* The first screen must paint as content, not wait behind an opacity animation:
   hiding the hero made its transition end become the LCP timestamp. Load
   reveals keep the small rise but are fully readable from first paint. */
.js .reveal-load { transform: translateY(.55rem); }
.reveal.in {
  opacity: 1;
  transform: none;
  transition: opacity .85s var(--ease-out), transform .85s var(--ease-out);
  transition-delay: calc(var(--i, 0) * 95ms);
}
.reveal-load.in {
  transform: none;
  transition: transform .55s var(--ease-out);
  transition-delay: calc(var(--d, 0) * 90ms);
}

/* Shared footer. Rebuilt 2026-09-02 on Rico's "do a better footer —
   sales@kuupstudio.com is said too many times": every page's contact band
   used to carry the address in large type directly above the footer's
   larger one. The address now appears once per page, here, under a small
   label; the bands keep their heading, their line and their button. The
   markets moved into the bottom row, beside the copyright. */
/* Tightened the same day — "the footer is too tall, and empty". The five link
   rows at a 44px tap height stretched the top row to ~260px with the address
   bottom-pinned under a void, and the band above it had 117px of padding
   each side of two lines. Now: the links sit in two short columns so the
   right side is about as tall as the address block, the paddings are cut,
   and a hairline marks where the footer begins in the dark. */
.site-foot { padding: clamp(2.5rem, 5vh, 3.5rem) var(--pad) 1.5rem; border-top: 1px solid var(--line); }
/* One section, not two (Rico, 2026-09-02: "try to make not 2 sections in
   there"). The band and the footer are separate elements — the band is
   page-specific, the footer is byte-identical everywhere and the checker
   holds it to that — but where a band ends the page, the footer follows it
   as a second row of the same block: no hairline, no top padding, and the
   band closes its own bottom padding up. Pages without a band (contact,
   404, the legal pages) keep the footer's own top edge. */
main:has(.contact-band) + .site-foot { padding-top: 0; border-top: 0; }
.footer-contact { display: grid; justify-items: start; gap: 1rem; }
.footer-contact .micro { color: var(--graphite); }
.footer-top {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: end;
  gap: 1.5rem 3rem;
  padding-bottom: clamp(2rem, 4vh, 3rem);
}
.footer-mail {
  justify-self: start;
  /* Was clamp(1.6rem, 6vw, 4.25rem) — a 68px display line under a band
     heading, so the close read as two headlines. 40px at 1440 (Rico,
     2026-09-02: "make the write us email a bit smaller"). */
  font-size: clamp(1.5rem, 2.8vw, 2.6rem);
  font-weight: 560;
  letter-spacing: -.035em;
  line-height: 1.02;
  text-transform: lowercase;
  color: var(--ink);
  text-decoration: none;
}
.footer-mail::after {
  content: "\2197";
  display: inline-block;
  margin-left: .1em;
  font-size: .6em;
  vertical-align: .2em;
  transition: transform .3s var(--ease-out);
}
.footer-mail:hover { text-decoration: underline; text-decoration-thickness: 2px; text-underline-offset: .14em; }
.footer-mail:hover::after { transform: translate(.16em, -.16em); }
.footer-meta { display: grid; align-content: end; justify-items: start; gap: 1.25rem; color: var(--graphite); }
.availability { display: flex; align-items: center; gap: .55rem; }
.availability-dot {
  width: .45rem;
  height: .45rem;
  flex: 0 0 auto;
  border-radius: 50%;
  background: currentColor;
  animation: availabilityPulse 2.4s ease-in-out infinite;
}
@keyframes availabilityPulse { 50% { opacity: .25; } }
.footer-bottom {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 1rem;
  padding-top: 1.3rem;
  border-top: 1px solid var(--line);
  color: var(--graphite);
}

/* Home hero */
.home-hero {
  min-height: min(80svh, 48rem);
  display: grid;
  place-items: center;
  padding: calc(var(--header-h) + 3.5rem) var(--pad) 3rem;
}
.hero-copy { align-self: center; justify-self: center; text-align: center; }
.tagline,
.t-statement {
  font-size: var(--t-statement);
  font-weight: var(--wt-state);
  letter-spacing: var(--tr-state);
  line-height: 1.14;
}
/* Each line is pinned (in JS) to its exact final width and centred, so the
   text grows outward from the middle of a box that never changes size — the
   line looks the same half-typed as it does finished. */
.tl-line { display: block; margin-inline: auto; min-height: 1.14em; }
.tl-em { color: var(--graphite); font-style: italic; }
.caret {
  display: inline-block;
  width: .045em;
  min-width: 2px;
  height: .88em;
  margin-left: .07em;
  margin-right: calc(-.045em - .07em - 2px);
  vertical-align: -.04em;
  background: currentColor;
  animation: caretBlink 1.05s steps(1) infinite;
}
.js .home-hero .caret { animation-play-state: paused; }
.js .home-hero.is-active .caret { animation-play-state: running; }
@keyframes caretBlink { 50% { opacity: 0; } }
.hero-actions { display: flex; justify-content: center; align-items: center; flex-wrap: wrap; gap: 1.25rem; margin-top: 2.75rem; }

/* What we build — animated pillar stage (KUUP design-system handoff, ported) */
.micro { font-family: var(--sans); font-size: .7rem; letter-spacing: .14em; text-transform: uppercase; color: var(--ink-2); }
.k-card {
  position: relative;
  background: var(--face);
  border: 1px solid var(--line); border-radius: var(--r-card);
  padding: 1.4rem 1.5rem;
  box-shadow: var(--shadow-1);
  transition: border-color .3s var(--ease-soft), transform .35s var(--ease-soft), box-shadow .4s var(--ease-soft);
}
/* viewfinder crop marks — the drafting "tell" on hover */
.k-marks::before, .k-marks::after {
  content: ""; position: absolute; width: 12px; height: 12px;
  border: 1.5px solid currentColor; opacity: 0;
  transition: opacity .4s var(--ease-soft), transform .4s var(--ease-out);
}
.k-marks::before { top: 7px; left: 7px; border-width: 1.5px 0 0 1.5px; transform: translate(3px, 3px); }
.k-marks::after { bottom: 7px; right: 7px; border-width: 0 1.5px 1.5px 0; transform: translate(-3px, -3px); }
.k-marks:hover::before, .k-marks:hover::after { opacity: .45; transform: none; }
.k-term {
  font-family: var(--font-mono); font-size: .72rem; line-height: 1.9;
  border: 1px solid var(--line); border-radius: var(--r-sm); padding: .8em 1em;
  color: var(--ink-2); display: grid;
  background: color-mix(in oklab, var(--paper) 88%, var(--ink) 2%);
  box-shadow: var(--shadow-1);
}
.k-term b, .k-term .ok { color: var(--ink); font-weight: 600; }
.k-term__dots i { font-style: normal; animation: kBlink 1.4s infinite; }
.k-term__dots i:nth-child(2) { animation-delay: .2s; }
.k-term__dots i:nth-child(3) { animation-delay: .4s; }
@keyframes kBlink { 0%, 100% { opacity: .15; } 50% { opacity: 1; } }
@keyframes kroom { 0%, 18%, 100% { opacity: 0; } 6%, 12% { opacity: .3; } }
@media (prefers-reduced-motion: reduce) { .k-term__dots i { animation: none; } }
.js [data-stage] [data-room],
.js [data-stage] circle,
.js [data-stage] .k-term__dots i,
.js .case .brain-live i,
.js .case .k-term__dots i { animation-play-state: paused !important; }
.js [data-stage].is-active [data-room],
.js [data-stage].is-active circle,
.js [data-stage].is-active .k-term__dots i,
.js .case.is-active .brain-live i,
.js .case.is-active .k-term__dots i { animation-play-state: running !important; }
@media (prefers-reduced-motion: reduce) {
  [data-stage] [data-room],
  [data-stage] circle,
  .case .brain-live i { animation: none !important; }
}
.terminal b, .terminal .ok { color: var(--ink); }
@keyframes terminalBlink { 50% { opacity: .15; } }
.featured-project figcaption,
.shot-strip figcaption,
.case-figure-caption {
  display: flex;
  justify-content: space-between;
  gap: .75rem 1.5rem;
  margin-top: .7rem;
  color: var(--graphite);
  font-size: .66rem;
  font-weight: 620;
  letter-spacing: .065em;
  line-height: 1.45;
  text-transform: uppercase;
}

/* ---- Selected work: feature cases ---- */
.case {
  display: grid;
  grid-template-columns: minmax(0, .82fr) minmax(0, 1fr);
  gap: clamp(2rem, 5vw, 4.5rem);
  align-items: center;
  margin-top: clamp(3.5rem, 8vh, 6rem);
}
.case--flip { grid-template-columns: minmax(0, 1fr) minmax(0, .82fr); }
.case--flip .case-media { order: -1; }
.case-copy { align-self: center; }
.case-title,
.t-h2 { font-size: var(--t-h2); font-weight: var(--wt-head); letter-spacing: var(--tr-h2); line-height: 1.04; }
.case-title { margin: .55rem 0 0; }
.case-title em { font-style: italic; font-weight: var(--wt-accent); color: var(--graphite); }
.case-text { max-width: 44ch; margin-top: 1.1rem; color: var(--graphite); line-height: 1.58; }
.case-link { display: inline-block; margin-top: 1.5rem; font-weight: 620; color: var(--ink); text-decoration: underline; text-decoration-thickness: 1.5px; text-underline-offset: .28em; }
.case-link:hover { text-decoration-thickness: 2px; }

/* device duo — desktop browser + overlapping phone */
.device-duo { position: relative; padding: .5rem 2.2rem 2.6rem .5rem; }
.device-browser {
  border: 1px solid var(--ink); border-radius: 11px; overflow: hidden;
  background: var(--paper); box-shadow: var(--shadow-2);
}
.device-bar {
  display: flex; align-items: center; gap: .45rem;
  height: 1.95rem; padding: 0 .85rem;
  border-bottom: 1px solid var(--line);
  background: color-mix(in oklab, var(--paper) 90%, var(--ink));
}
.device-bar i { width: .5rem; height: .5rem; border-radius: 50%; background: color-mix(in oklab, var(--ink) 26%, transparent); }
.device-bar span { margin-left: .8rem; font-family: var(--font-mono); font-size: .62rem; letter-spacing: .03em; color: var(--ink-2); }
.device-screen { display: block; }
.device-screen img { display: block; width: 100%; height: auto; }
.device-phone {
  position: absolute; right: 0; bottom: 0;
  width: 26%; min-width: 6rem; max-width: 9.25rem;
  padding: .3rem; border-radius: 15px;
  border: 1px solid var(--ink); background: var(--paper);
  box-shadow: var(--shadow-2);
}
.device-phone img { display: block; width: 100%; height: auto; border-radius: 9px; }

/* the brain — dashboard mockup */
.brain-dash { display: grid; gap: 1rem; padding: 1.3rem 1.4rem; }
.brain-dash-head { display: flex; align-items: center; justify-content: space-between; }
.brain-live { display: inline-flex; align-items: center; gap: .45rem; font-size: .68rem; font-weight: 620; letter-spacing: .08em; text-transform: uppercase; color: var(--ink-2); }
.brain-live i { width: .5rem; height: .5rem; border-radius: 50%; background: var(--ink); animation: kBlink 1.8s infinite; }
.brain-stats { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem; padding: .3rem 0 .1rem; border-top: 1px solid var(--line); border-bottom: 1px solid var(--line); }
.brain-stats div { padding: .7rem 0; }
.brain-stats b { display: block; font-size: clamp(1.3rem, 2.2vw, 1.75rem); font-weight: 680; letter-spacing: -.02em; color: var(--ink); line-height: 1; }
.brain-stats span { display: block; margin-top: .35rem; font-size: .62rem; font-weight: 620; letter-spacing: .07em; text-transform: uppercase; color: var(--ink-2); }
.brain-term { gap: .1rem; }
.brain-draft { border: 1px solid var(--line); border-radius: var(--r-sm); padding: .85rem 1rem; background: var(--paper); }
.brain-draft p { margin: .5rem 0 0; font-size: .82rem; line-height: 1.5; color: var(--ink); }

/* ---- Other projects (compact) + more ---- */
.more-work { margin-top: clamp(3.5rem, 8vh, 6rem); }
.mini-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: clamp(1rem, 2.5vw, 1.75rem); }
/* Six-up variant for the projects index — the archive rows below only reveal
   their thumbnail on hover, so the page needs somewhere the work is just visible. */
/* 22rem, not 17rem: at 17rem the six projects lay out 4 + 2 and the last row
   is an orphan. 22rem gives 3 × 2 on desktop, 2 across on tablet, 1 on phone. */
.mini-grid--wide { grid-template-columns: repeat(auto-fit, minmax(min(100%, 22rem), 1fr)); }
.mini-project { display: block; text-decoration: none; color: var(--ink); }
.mini-thumb { display: block; overflow: hidden; border: 1px solid var(--line); border-radius: 8px; background: var(--wash); }
.mini-thumb img { display: block; width: 100%; height: auto; aspect-ratio: 16 / 8; object-fit: cover; object-position: top; transition: transform .7s var(--ease-out); }
.mini-project:hover .mini-thumb img { transform: scale(1.03); }
.mini-cap { display: flex; justify-content: space-between; gap: 1rem; margin-top: .6rem; font-size: .7rem; font-weight: 620; letter-spacing: .05em; text-transform: uppercase; color: var(--ink-2); }
.mini-cap a, .mini-project:hover .mini-name { color: var(--ink); }
.more-row { display: flex; align-items: baseline; justify-content: space-between; gap: 1.5rem; flex-wrap: wrap; margin-top: clamp(1.75rem, 4vh, 2.75rem); }
.more-row p { color: var(--graphite); max-width: 40ch; }

/* Condensed contact band. Padding cut 2026-09-02 — two lines and a button
   were sitting in 117px of air each side, above a footer in the same dark. */
.contact-band { padding-block: clamp(3.5rem, 8vh, 5.5rem) clamp(2rem, 4vh, 2.75rem); }   /* bottom closes up: the footer row follows in the same block */
/* Full width on the page padding, not the rail (2026-09-02): the footer that
   now follows every band as the second row of one block runs full width, and
   a rail-capped band sat 128px to its right on any screen wider than the
   rail — 1472px on most pages, 1184 on /business-brain. Same left edge for
   both rows at every width. */
.contact-band-grid { display: grid; grid-template-columns: minmax(0, 1.2fr) minmax(14rem, .8fr); gap: 3rem; align-items: end; width: 100%; }
.contact-band h2 {
  max-width: 12ch;
  font-size: var(--t-display);
  font-weight: var(--wt-state);
  letter-spacing: var(--tr-display);
  line-height: .92;
}
.contact-band h2 em { color: var(--graphite); font-weight: var(--wt-accent); }
.contact-band-side { display: grid; justify-items: start; gap: 1.4rem; }
/* /business-brain's band, per Rico's mark-up (2026-09-02): the heading on ONE
   line with its copy underneath on the left, the button alone on the right,
   flush with the rail. The 12ch heading cap that wraps the other pages'
   longer bands comes off here; the phone stacks and the button goes back to
   the left edge with the copy. Other pages' bands are untouched. */
.contact-band-copy { display: grid; gap: clamp(1rem, 2.5vh, 1.5rem); }
.contact-band-copy p { max-width: 52ch; }
.page-brain .contact-band h2 { max-width: none; }
.page-brain .contact-band-side { justify-self: end; }
/* "the button needs to be centered with the text and text more left" (Rico,
   2026-09-02). Centred: the row aligns to its middle, not its baseline.
   Left: this page narrows --content to 74rem, so its band started 128px
   right of the footer's "write to us" below it; the band now runs the full
   width on the page padding, like the footer and like every other page's
   band already does at 1440, so the two rows of the closing block share one
   left edge. */
.page-brain .contact-band-grid { align-items: center; }   /* width: 100% moved to the base rule — every band now matches the footer */
/* .contact-band-mail is gone (2026-09-02): the band no longer repeats the
   address the footer below it carries. */

/* Interior page hero */
.page-hero {
  min-height: min(82svh, 52rem);
  display: grid;
  align-content: end;
  padding-top: calc(var(--header-h) + 6rem);
  padding-bottom: clamp(4rem, 10vh, 7rem);
}
.page-kicker { margin-bottom: 1.7rem; color: var(--graphite); }
.page-intro {
  max-width: 42ch;
  margin-top: clamp(2rem, 6vh, 4rem);
  color: var(--graphite);
  font-size: clamp(1.2rem, 2vw, 1.75rem);
  line-height: 1.4;
}
.filter-btn:hover,
.filter-btn.active { color: var(--ink); text-decoration: underline; text-underline-offset: .35em; }
.archive { padding-block: clamp(4rem, 9vh, 7rem) clamp(7rem, 16vh, 12rem); }
.archive-divider { margin: clamp(4rem, 9vh, 7rem) 0 1rem; padding-top: 1.25rem; border-top: 1px solid var(--line-strong); color: var(--graphite); }
.archive-row {
  position: relative;
  display: grid;
  grid-template-columns: 5.5rem minmax(14rem, 1.2fr) minmax(13rem, 1fr) 5rem minmax(8rem, .6fr);
  gap: 1.5rem;
  align-items: center;
  min-height: 7.75rem;
  border-top: 1px solid var(--line);
  color: inherit;
  text-decoration: none;
}
.archive-row:last-child { border-bottom: 1px solid var(--line); }
.archive-row[hidden] { display: none; }
.archive-name { font-size: clamp(1.55rem, 3vw, 3.2rem); font-weight: var(--wt-state); letter-spacing: var(--tr-state); line-height: 1; }
.archive-tagline { color: var(--graphite); font-size: .95rem; line-height: 1.45; }
.archive-year, .archive-tags { color: var(--graphite); }
.archive-tags { font-size: .75rem; text-transform: uppercase; letter-spacing: .07em; }
.archive-thumb {
  position: absolute;
  z-index: 3;
  right: 12%;
  top: 50%;
  width: min(18rem, 22vw);
  overflow: hidden;
  border: 1px solid var(--ink);
  background: var(--paper);
  opacity: 0;
  pointer-events: none;
  transform: translateY(-44%);
  transition: opacity .25s, transform .55s var(--ease-out);
}
.archive-thumb img { display: block; width: 100%; height: auto; }
.archive-row:hover .archive-thumb,
.archive-row:focus-visible .archive-thumb { opacity: 1; transform: translateY(-50%); }

/* Case study */
.case-hero { min-height: auto; display: block; padding-bottom: clamp(5rem, 11vh, 8rem); }
.case-client {
  font-size: clamp(5rem, 15vw, 15rem);
  font-weight: 760;
  letter-spacing: -.055em;
  line-height: .72;
}
.case-tagline { max-width: 18ch; margin-top: clamp(2.5rem, 6vh, 5rem); font-size: clamp(1.8rem, 4vw, 4.2rem); font-weight: 480; letter-spacing: -.035em; line-height: 1.02; }
.case-tagline em { color: var(--graphite); }
.case-hero-shot { width: min(100%, 80rem); margin-top: clamp(3rem, 8vh, 6rem); }
.case-hero-shot img,
.mobile-shot img { display: block; width: 100%; height: auto; border: 1px solid var(--ink); }
.case-figure-caption a { color: var(--ink); text-underline-offset: .25em; white-space: nowrap; }
.fact-strip {
  display: grid;
  grid-template-columns: .6fr 1fr 1.5fr auto;
  gap: 1rem 2rem;
  padding-block: 1.35rem;
  border-top: 1px solid var(--line-strong);
  border-bottom: 1px solid var(--line-strong);
  color: var(--graphite);
}
.fact-strip a { color: var(--ink); text-underline-offset: .3em; }
.case-problem { padding-block: clamp(7rem, 18vh, 13rem); }
.case-problem p {
  max-width: 34ch;
  margin-left: auto;
  font-size: clamp(1.85rem, 4vw, 4rem);
  letter-spacing: -.035em;
  line-height: 1.13;
}
.site-scroll-figure { width: min(100%, 80rem); }
.site-scroll-frame {
  --site-scroll-height: clamp(30rem, 62vw, 48rem);
  height: var(--site-scroll-height);
  overflow: hidden;
  border: 1px solid var(--ink);
  background: var(--wash);
}
.site-scroll-frame img {
  display: block;
  width: 100%;
  height: auto;
  transform: translateY(0);
  transition: transform 11s linear;
}
.site-scroll-frame:hover img,
.site-scroll-frame:focus-visible img { transform: translateY(calc(-100% + var(--site-scroll-height))); }
.annotation-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1px; margin-top: 1px; background: var(--line); }
.annotation { min-height: 12rem; padding: 1.4rem; background: var(--paper); }
.annotation .micro { display: block; margin-bottom: 3rem; color: var(--graphite); }
.annotation p { max-width: 32ch; }
.responsive-proof {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(15rem, 24.375rem);
  gap: clamp(3rem, 9vw, 10rem);
  align-items: center;
}
.responsive-copy { max-width: 42rem; }
.responsive-copy h2 { margin-top: 1.25rem; font-size: clamp(2.8rem, 6vw, 6.5rem); font-weight: var(--wt-head); letter-spacing: var(--tr-display); line-height: .95; }
.responsive-copy h2 em { color: var(--graphite); font-weight: 430; }
.responsive-copy > p:last-child { max-width: 42ch; margin-top: 2rem; color: var(--graphite); }
.mobile-shot { width: min(100%, 24.375rem); justify-self: end; }
.plan-demo-wrap {
  display: grid;
  grid-template-columns: minmax(0, 1.35fr) minmax(16rem, .65fr);
  min-height: 34rem;
  border: 1px solid var(--ink);
}
.house-plan {
  display: grid;
  grid-template-columns: 1.5fr 1fr 1fr;
  grid-template-rows: 1fr .72fr;
  min-height: 34rem;
}
.house-room {
  position: relative;
  border: 0;
  border-right: 1px solid var(--ink);
  border-bottom: 1px solid var(--ink);
  background: transparent;
  text-align: left;
  cursor: pointer;
  transition: background-color .25s;
}
.house-room.living { grid-row: 1 / 3; }
.house-room.kitchen { grid-column: 2 / 4; }
.house-room.bed { grid-column: 2; grid-row: 2; }
.house-room.sauna { grid-column: 3; grid-row: 2; border-right: 0; }
.house-room:nth-last-child(-n+2) { border-bottom: 0; }
.house-room:hover,
.house-room.active { background: var(--wash); }
.house-room span { position: absolute; top: .8rem; left: .8rem; font-size: .72rem; font-weight: 650; letter-spacing: .08em; text-transform: uppercase; }
.house-room::after {
  content: "+";
  position: absolute;
  right: .75rem;
  bottom: .55rem;
  color: var(--graphite);
  font-size: 1.35rem;
}
.plan-info { display: grid; align-content: end; padding: clamp(1.5rem, 4vw, 3.5rem); }
.plan-info .micro { margin-bottom: 1rem; color: var(--graphite); }
.plan-info h3 { font-size: clamp(2.4rem, 4.5vw, 5rem); font-weight: var(--wt-state); letter-spacing: -.04em; line-height: .95; }
.plan-info .sqm { margin-top: .75rem; color: var(--graphite); font-size: 1.2rem; }
.plan-info .room-copy { max-width: 30ch; margin-top: 2rem; }
.next-project { display: grid; grid-template-columns: 1fr auto; gap: 2rem; align-items: end; padding-block: clamp(5rem, 12vh, 9rem); border-top: 1px solid var(--line); text-decoration: none; }
.next-project .next-name { font-size: clamp(3rem, 8vw, 9rem); font-weight: var(--wt-state); letter-spacing: var(--tr-display); line-height: .9; }
.mini-room:hover, .mini-room.active { background: var(--wash); }
.mini-status { margin-top: 1rem; color: var(--graphite); font-size: .82rem; text-align: center; }
.faq-list { border-top: 1px solid var(--line); }
.faq-item { border-bottom: 1px solid var(--line); }
.faq-item summary {
  display: flex;
  justify-content: space-between;
  gap: 2rem;
  padding: 1.4rem 0;
  font-size: clamp(1.25rem, 2.1vw, 1.8rem);
  font-weight: var(--wt-state);
  list-style: none;
  cursor: pointer;
}
.faq-item summary::-webkit-details-marker { display: none; }
.faq-item summary::after { content: "+"; color: var(--graphite); font-size: 1.5rem; font-weight: 400; }
.faq-item[open] summary::after { content: "−"; }
.faq-answer { max-width: 65ch; padding: .25rem 0 2rem; color: var(--graphite); }
.principles-list { border-top: 1px solid var(--line); }
.principles-list li { padding: 1.15rem 0; border-bottom: 1px solid var(--line); font-size: clamp(1.25rem, 2.3vw, 2rem); letter-spacing: -.02em; }

/* ---- Project writeup ------------------------------------------------
   Rico's rule (2026-08-12): a post lives under the project it came from —
   there is no separate blog section and no /journal/ route. So this is an
   article block inside a case page, not a new page type. It adds no type
   sizes: every value below is a token or a measure in ch. */
.writeup { padding-block: clamp(4rem, 10vh, 7rem) clamp(2rem, 5vh, 4rem); }
.writeup-meta {
  display: flex;
  flex-wrap: wrap;
  gap: .5rem 1.5rem;
  margin-bottom: 1.4rem;
  color: var(--graphite);
}
.writeup-title { max-width: 17ch; font-size: var(--t-h2); font-weight: var(--wt-head); letter-spacing: var(--tr-h2); line-height: 1.04; }
.writeup-title em { color: var(--graphite); font-weight: var(--wt-accent); }
.writeup-lead { max-width: 52ch; margin-top: 1.6rem; font-size: var(--t-lead); line-height: 1.5; }
.writeup-body {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 20rem), 1fr));
  gap: clamp(1.75rem, 3.5vw, 2.75rem) clamp(2rem, 5vw, 4.5rem);
  margin-top: clamp(2.5rem, 6vh, 4rem);
}
.writeup-part { padding-top: 1.15rem; border-top: 1px solid var(--line); }
.writeup-part h3 { font-size: var(--t-h3); font-weight: var(--wt-head); letter-spacing: var(--tr-h3); line-height: 1.15; }
.writeup-part h3 em { color: var(--graphite); font-weight: var(--wt-accent); }
.writeup-part p { max-width: 46ch; margin-top: .7rem; color: var(--graphite); }
.writeup-part p + p { margin-top: .7rem; }
.mono { font-family: var(--font-mono); font-size: .9em; color: var(--ink); }

/* Draft blank. Every one of these is a fact only Rico can supply — what the
   studio actually did on a project. They are listed in FABRICATED.md and the
   page does not ship while any remain. Deliberately not --err: that token is
   reserved for form validation, and a dashed hairline is loud enough. */
.todo {
  display: inline-block;
  padding: .12em .55em;
  border: 1px dashed var(--line-strong);
  background: var(--wash);
  color: var(--graphite);
  font-size: .78em;
  font-weight: var(--wt-head);
  letter-spacing: .04em;
  text-transform: uppercase;
}

/* Contact and v4 intent form */
.contact-hero { min-height: auto; padding-bottom: clamp(4rem, 8vh, 6rem); }
.contact-email {
  display: inline-block;
  max-width: 100%;
  margin-top: clamp(3rem, 8vh, 6rem);
  font-size: clamp(2rem, 6.5vw, 7.5rem);
  font-weight: var(--wt-state);
  letter-spacing: -.05em;
  line-height: .9;
  overflow-wrap: anywhere;
  text-decoration-thickness: 2px;
  text-underline-offset: .12em;
}
.human-note { margin-top: 1.2rem; color: var(--graphite); }
.contact-meta { display: flex; gap: 2rem; flex-wrap: wrap; margin-top: 2.5rem; color: var(--graphite); }
.form-section { padding-block: clamp(5rem, 12vh, 9rem); }
/* The enquiry form sits on a surface of its own. It was the only thing on the
   page floating bare on the paper while the "direct lines" aside next to it was
   already bordered — so the secondary content read as the object and the primary
   action read as background. Same card vocabulary as .k-card: face fill, one
   hairline, one soft shadow. No new tokens. */
.form-wrap {
  max-width: 72rem;
  background: var(--face);
  border: 1px solid var(--line);
  border-radius: var(--r-card);
  padding: clamp(1.35rem, 3vw, 2.5rem);
  box-shadow: var(--shadow-1);
}
.intents > legend { margin-bottom: 1.4rem; font-size: clamp(1.3rem, 2vw, 1.8rem); font-weight: var(--wt-head); }
.intent-grid { display: grid; grid-template-columns: repeat(4, 1fr); border-top: 1px solid var(--line); border-left: 1px solid var(--line); }
.intent-grid input,
.chip-row input { position: absolute; opacity: 0; pointer-events: none; }
.intent-card {
  min-height: 10rem;
  display: grid;
  align-content: space-between;
  padding: 1rem;
  border-right: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  cursor: pointer;
  transition: background-color .25s, color .25s;
}
.intent-card svg { width: 1.8rem; fill: none; stroke: currentColor; stroke-width: 1.35; }
.intent-copy { display: grid; gap: .35rem; }
.ic-title { font-weight: 620; }
.ic-sub { color: var(--graphite); font-size: .8rem; line-height: 1.4; }
.intent-grid input:checked + .intent-card { background: var(--ink); color: var(--paper); --graphite: color-mix(in oklab, var(--paper) 72%, transparent); }
.intent-grid input:focus-visible + .intent-card,
.chip-row input:focus-visible + .chip { outline: 2px solid var(--ink); outline-offset: -4px; }
.conditional { display: grid; grid-template-rows: 0fr; opacity: 0; transition: grid-template-rows .5s var(--ease-out), opacity .35s; }
.conditional.open { grid-template-rows: 1fr; opacity: 1; }
.conditional-inner { display: grid; gap: 1.75rem; overflow: hidden; }
.conditional.open .conditional-inner { padding-top: 2.25rem; }
.chips legend,
.field label { display: block; margin-bottom: .6rem; color: var(--graphite); }
.chips legend em,
.field label em { font-style: normal; text-transform: none; letter-spacing: 0; }
.chip-row { display: flex; flex-wrap: wrap; gap: .55rem; }
.chip {
  min-height: 2.6rem;
  display: inline-flex;
  align-items: center;
  padding: .45rem 1rem;
  border: 1px solid var(--line-strong);
  border-radius: 999px;
  font-size: .86rem;
  cursor: pointer;
}
.chip-row input:checked + .chip { background: var(--ink); color: var(--paper); border-color: var(--ink); }
.fields { display: grid; grid-template-columns: repeat(2, 1fr); gap: 2rem 3rem; margin-top: 3rem; }
.field label.req::after { content: " *"; color: var(--err); }
.field input,
.field select,
.field textarea {
  width: 100%;
  border: 0;
  border-bottom: 1px solid var(--line-strong);
  border-radius: 0;
  padding: .65rem 0;
  background: transparent;
  color: var(--ink);
  font-size: 1.05rem;
}
.field textarea { min-height: 10rem; padding: .9rem; border: 1px solid var(--line-strong); resize: vertical; line-height: 1.5; }
.field input:focus-visible,
.field select:focus-visible,
.field textarea:focus-visible {
  border-color: var(--ink);
  outline: 2px solid var(--ink);
  outline-offset: 3px;
}
.field-msg { margin-top: 2.5rem; }
.field.invalid input,
.field.invalid select,
.field.invalid textarea { border-color: var(--err); }
.field.invalid select { outline: 2px solid var(--err); outline-offset: 3px; }
.field-err { display: block; margin-top: .5rem; color: var(--err); }
.hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }
.form-foot { display: flex; align-items: center; gap: 1.5rem; flex-wrap: wrap; margin-top: 2.5rem; }
.form-note { max-width: 48ch; color: var(--graphite); }
.no-js-contact { display: none; max-width: 48ch; padding: 1rem; border: 1px solid var(--line-strong); border-radius: var(--r-sm); }
.no-js .no-js-contact { display: block; }
.no-js #leadForm { display: none; }
.sent-panel { min-height: 24rem; padding-top: 2rem; }
.sent-panel h2 { font-size: var(--t-display); font-weight: var(--wt-state); letter-spacing: var(--tr-display); }
.sent-panel p { max-width: 50ch; margin-top: 1rem; color: var(--graphite); }
.sent-panel .btn { margin-top: 2rem; }

/* Responsive */
@media (max-width: 70rem) {
  .archive-row { grid-template-columns: 4.5rem minmax(12rem, 1.1fr) minmax(10rem, .9fr) 4rem; }
  .archive-tags { display: none; }
}

@media (max-width: 56rem) {
  /* The menu is a card that sits on top of the header, not a full-height
     drawer. Modelled on samara.com's mobile navigation, measured rather than
     eyeballed: a rounded translucent panel inset from the edges, sized to its
     content (theirs is 366×467 in a 390×844 screen), floating over a hidden
     header, with the close control kept in the same corner the opener was in.
     Their transition is `transform .24s cubic-bezier(.16, 1, .3, 1)` plus a
     .2s opacity fade, and the card travels a few px from the side its button
     is on — 8px for them. Theirs comes from the top-left because that is where
     their hamburger lives; ours is at the top-right, so ours comes in from the
     right. Same move, mirrored to our own corner. */
  .site-nav {
    position: fixed;
    inset: .55rem .55rem auto auto;
    z-index: 60;
    width: min(24rem, calc(100vw - 1.1rem));
    padding: 3.35rem 1.25rem 1.25rem;
    border: 1px solid var(--line);
    border-radius: var(--r-card);
    /* samara run theirs at 90% because what shows through is a photograph.
       Ours sits over black text on paper, and at 90% the hero's CTA pill read
       straight through the card and fought the word "projects". 97% keeps the
       frosted edge without the page competing with the menu. */
    /* No backdrop-filter. It was here for the frosted look, but at 97% paper
       it contributes nothing — captures with it on and off came back
       byte-identical — while costing a compositing layer that exists from
       page load, on an element that is meant to be invisible until tapped.
       iOS composites backdrop-filter layers ahead of applying opacity, which
       is how a hidden card gets to flash on reload. Nothing to composite,
       nothing to flash. */
    background: color-mix(in oklab, var(--paper) 97%, transparent);
    box-shadow: var(--shadow-2);
    color: var(--ink);
    opacity: 0;
    transform: translate(10px, -6px);
    visibility: hidden;
    transition: transform .24s cubic-bezier(.16, 1, .3, 1), opacity .2s linear, visibility 0s .24s;
  }
  .site-nav.open {
    opacity: 1;
    transform: none;
    visibility: visible;
    transition: transform .24s cubic-bezier(.16, 1, .3, 1), opacity .2s linear, visibility 0s;
  }
  /* The class swap in <head> means this is visible only when JavaScript did
     not run. Primary navigation remains a normal in-flow list instead of a
     permanently hidden drawer with an inert menu button. */
  .no-js .site-head { position: static; height: auto; min-height: var(--header-h); }
  .no-js .menu-toggle { display: none; }
  .no-js .site-nav {
    position: static;
    grid-column: 1 / -1;
    width: 100%;
    margin-top: .5rem;
    padding: .5rem 0 0;
    border: 0;
    border-top: 1px solid var(--line);
    border-radius: 0;
    background: transparent;
    box-shadow: none;
    opacity: 1;
    transform: none;
    visibility: visible;
  }
  .no-js .nav-foot { display: none; }
  /* Left-aligned rows, top-anchored. The old panel centred three links in a
     full-height column, which left them floating in the middle of nothing. */
  .nav-list { display: block; }
  .nav-list li + li { border-top: 1px solid var(--line); }
  .nav-list a {
    display: flex;
    align-items: center;
    justify-content: flex-start;
    gap: .5rem;
    padding: 1rem .15rem;
    border-radius: var(--r-sm);
    color: var(--ink);
    font-size: 1.15rem;
    font-weight: 560;
    letter-spacing: -.02em;
    line-height: 1.35;
    text-align: left;
    text-transform: lowercase;
    transition: background-color .2s linear;
    /* iOS paints its own grey block on every link tap. Tint it to ours so the
       feedback is guaranteed even where :active is unreliable — Safari only
       applies :active to a link once something is listening for touch. */
    -webkit-tap-highlight-color: color-mix(in oklab, var(--ink) 8%, transparent);
  }
  /* Enough of a tell that these are tappable, and no more: the arrow the rest
     of the site already uses ("case study →", "more projects →"), sitting
     quietly at the end of the row, and a wash under the finger. */
  .nav-list a:active { background: var(--wash); }
  /* The desktop rule paints hover and the current page in `--ink` and wins on
     specificity ((0,2,1) against this block's (0,1,1)), so both states have to
     be re-stated here at matching specificity and let source order decide.
     This is the same trap the old dark panel hit in reverse — there it was ink
     on ink, and the link for the page you were on vanished. */
  .nav-list a:hover,
  .nav-list a[aria-current="page"] { color: var(--ink); }
  /* The underline indicator is anchored to the text width on desktop; in a
     full-width row it would run the whole card. Mark the current page with
     weight and a graphite caret instead. */
  /* The desktop ::after is an absolutely-positioned 1px underline that grows
     on hover; in a full-width row it would run the whole card. Reuse the slot
     for the arrow instead — same selector, same specificity, later in the
     file, so every one of its properties has to be unset by hand. */
  .nav-list a::after {
    /* The `/ ""` is the alt text for generated content, and it is load-bearing:
       without it the accessibility tree hands out `link "projects →"` and a
       screen reader reads the arrow aloud on every row. It is decoration.
       Declared twice on purpose — the alt-text syntax is Safari 17.4+, and a
       browser that does not parse it drops the WHOLE declaration, arrow and
       all. The plain one first means those get the arrow, just narrated. */
    content: "→";
    content: "→" / "";
    position: static;
    inset: auto;
    width: auto;
    height: auto;
    background: none;
    color: var(--graphite);
    font-size: 1rem;
    line-height: 1;
    opacity: .45;
    transform: none;
    transition: transform .25s var(--ease-out), opacity .25s linear;
    margin-left: auto;
  }
  .nav-list a[aria-current="page"] { font-weight: 700; }
  .nav-list a[aria-current="page"]::before { content: "· "; color: var(--graphite); }
  /* (0,2,2), so it beats the rule above and has to re-state `transform`, which
     the desktop version sets to scaleX(1) for the current page. */
  .nav-list a[aria-current="page"]::after { transform: none; opacity: .7; }
  .nav-list a:active::after { transform: translateX(3px); opacity: 1; }
  /* The global ring is `outline: 2px solid currentColor; outline-offset: 4px`,
     which on a full-width row draws a black rectangle wider than the card's
     own padding. Tuck it inside the row instead. Only keyboard users ever see
     it — a real tap does not match :focus-visible, checked with a dispatched
     touch rather than a scripted click. */
  .nav-list a:focus-visible { outline-offset: -3px; border-radius: var(--r-sm); }
  /* Footer row: the wordmark comes back here, because the header's copy of it
     is hidden while the card is open — samara do the same, their mark sits at
     the foot of the card. The language mark sits opposite it. */
  .nav-foot {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    margin-top: .9rem;
    padding-top: 1.15rem;
    border-top: 1px solid var(--line);
  }
  .nav-foot-brand {
    color: var(--ink);
    line-height: 1;
    text-decoration: none;
    white-space: nowrap;
  }
  .lang { position: relative; }
  .lang-pick {
    display: inline-flex;
    align-items: center;
    gap: .42rem;
    padding: .35rem .15rem;
    border: 0;
    border-radius: var(--r-sm);
    background: none;
    color: var(--graphite);
    cursor: pointer;
    -webkit-tap-highlight-color: color-mix(in oklab, var(--ink) 8%, transparent);
    transition: color .2s linear;
  }
  .lang-pick[aria-expanded="true"] { color: var(--ink); }
  .lang-pick svg { display: block; width: 1.15rem; height: 1.15rem; }
  .lang-code { font-size: .8rem; font-weight: 640; letter-spacing: .1em; }
  /* Opens upward: the picker sits on the card's bottom edge, so there is
     nothing below it. Same motion as the card itself — .24s on the same
     curve — so the two feel like one piece of furniture. */
  .lang-menu {
    position: absolute;
    right: 0;
    bottom: calc(100% + .5rem);
    z-index: 2;
    min-width: 13rem;
    margin: 0;
    padding: .35rem;
    list-style: none;
    border: 1px solid var(--line);
    border-radius: var(--r-card);
    background: color-mix(in oklab, var(--paper) 97%, transparent);
    /* Same reasoning as the card above — no hidden composited layer. */
    box-shadow: var(--shadow-2);
    opacity: 0;
    visibility: hidden;
    transform: translateY(6px);
    transition: transform .24s cubic-bezier(.16, 1, .3, 1), opacity .2s linear, visibility 0s .24s;
  }
  .lang-menu.open {
    opacity: 1;
    visibility: visible;
    transform: none;
    transition: transform .24s cubic-bezier(.16, 1, .3, 1), opacity .2s linear, visibility 0s;
  }
  .lang-menu li {
    display: flex;
    align-items: center;
    gap: .5rem;
    padding: .55rem .6rem;
    border-radius: var(--r-sm);
    font-size: .92rem;
    line-height: 1;
  }
  .lang-menu li[aria-current] { background: var(--wash); color: var(--ink); font-weight: 620; }
  /* The three that do not exist yet: grey, and labelled. */
  .lang-menu li.is-soon { color: var(--graphite-light); }
  .lang-name { flex: 1; text-transform: lowercase; }
  .lang-tag { font-size: .68rem; font-weight: 640; letter-spacing: .1em; }
  .lang-soon {
    padding: .16rem .42rem;
    border-radius: 999px;
    background: var(--wash);
    color: var(--graphite);
    font-size: .6rem;
    font-weight: 640;
    letter-spacing: .12em;
    text-transform: uppercase;
  }
  /* The opener stays on top of the card and becomes the ✕ — same corner, same
     tap target, so the finger does not have to move. */
  .menu-toggle { display: block; position: relative; z-index: 70; }
  /* samara puts nothing behind their card, so neither do we: the scrim stays
     as the tap-outside-to-close catcher, but it no longer dims the page. */
  .nav-scrim { background: transparent; }
  /* With the menu open the header itself gets out of the way — samara hides
     their whole bar. Ours has to: the card is translucent, so a wordmark and a
     black CTA pill sitting underneath it would smear through the blur. The
     `.head-end` box keeps its width with the CTA only hidden, so the ✕ does
     not shift under the finger that just tapped it. */
  .site-head.menu-open { background: transparent; border-bottom-color: transparent; }
  .site-head.menu-open .brand,
  .site-head.menu-open .head-cta { opacity: 0; visibility: hidden; }
  .brand, .head-cta { transition: opacity .18s linear, visibility 0s; }
  .section-head,
  .service-section-head { grid-template-columns: 1fr; }
  .section-copy,
  .service-section-head p { margin-left: auto; }
  .footer-top { grid-template-columns: 1fr; }
  .footer-meta { justify-self: start; }
  .case, .case--flip { grid-template-columns: 1fr; gap: 1.75rem; }
  .case--flip .case-media { order: 0; }
  .case-text { max-width: none; }
  .contact-band-grid { grid-template-columns: 1fr; }
  .page-brain .contact-band-side { justify-self: start; }
  .archive-row { grid-template-columns: 4.25rem minmax(0, 1fr) 4rem; gap: 1rem; }
  .archive-tagline { grid-column: 2; padding-bottom: 1.2rem; }
  .archive-tags { display: none; }
  .archive-year { grid-column: 3; grid-row: 1; }
  .archive-thumb {
    position: static;
    grid-column: 2 / 4;
    width: min(100%, 28rem);
    margin-bottom: 1.25rem;
    opacity: 1;
    pointer-events: auto;
    transform: none;
  }
  .archive-row:hover .archive-thumb,
  .archive-row:focus-visible .archive-thumb { transform: none; }
  .fact-strip { grid-template-columns: 1fr 1fr; }
  .plan-demo-wrap { grid-template-columns: 1fr; }
  .plan-info { min-height: 20rem; border-top: 1px solid var(--ink); }
  .responsive-proof { grid-template-columns: 1fr; }
  .mobile-shot { justify-self: start; }
  .intent-grid { grid-template-columns: repeat(2, 1fr); }
}

@media (max-width: 22rem) {
  .head-cta { padding-inline: .72rem; font-size: .75rem; }
}

@media (max-width: 42rem) {
  :root { --header-h: 3.9rem; }
  .section { padding-block: 5.5rem; }
  /* Phone titles run their own ramp rather than --t-display, because 7vw of a
     375px screen is unreadably small. Kept, but behind a token so a page can
     step the whole scale down — otherwise a page-level override silently stops
     working below 42rem, which is exactly what happened to .page-brain. The
     fallback is the original value, so every other page is untouched. */
  .section-title, .page-title { font-size: var(--t-display-sm, clamp(3rem, 15vw, 5rem)); }
  .footer-bottom { grid-template-columns: 1fr; }
  .footer-bottom span:last-child { justify-self: start; }
  /* At 62svh the "what we build" heading was fully readable on arrival, so the
     phone opened on two sections at once and neither one landed. 92svh, not
     100: section 2 opens with 5.5rem of its own padding, so at 92 the first
     thing in it still clears the fold on every common phone (checked 320×568
     up to 430×932) while a strip of paper stays visible — the page reads as
     scrollable rather than as a slide. `svh` is the small viewport, the one
     with the browser chrome showing, which is the state the page loads in, so
     this fills the first screen without ever overflowing it. */
  .home-hero { min-height: 92svh; padding-top: calc(var(--header-h) + 3rem); }

  /* The section head does not fade-and-rise on a phone. There is one animation
     in this section and it is the cube; making the heading slide up first only
     delays it, and on a small screen the two land close enough together to
     read as one restless block. The `.in` class still gets added, it just has
     nothing left to do. `#build .reveal` is (1,1,0) against `.js .reveal`'s
     (0,2,0), so this wins without !important. Desktop keeps the reveal. */
  #build .reveal { opacity: 1; transform: none; transition: none; }
  .featured-project figcaption,
  .shot-strip figcaption,
  .case-figure-caption { align-items: flex-start; flex-direction: column; }
  .archive-row { grid-template-columns: 3.65rem minmax(0, 1fr); padding-block: 1rem; }
  .archive-year { grid-column: 1; grid-row: 2; }
  .archive-tagline { grid-column: 2; }
  .archive-thumb { grid-column: 2; }
  .case-client { font-size: 20vw; }
  .fact-strip { grid-template-columns: 1fr; }
  .annotation-grid { grid-template-columns: 1fr; }
  .annotation { min-height: 10rem; }
  .house-plan { min-height: 25rem; }
  .next-project { grid-template-columns: 1fr; }
  .intent-grid { grid-template-columns: 1fr; }
  .intent-card { min-height: 8rem; }
  .fields { grid-template-columns: 1fr; }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { animation-duration: .01ms !important; animation-iteration-count: 1 !important; transition-duration: .01ms !important; }
  body { animation: none; }
  .js .reveal,
  .js .reveal-load { opacity: 1; transform: none; }
  .rule { transform: scaleX(1); }
  .caret { display: none; }
  .availability-dot { animation: none; }
  .featured-image:hover img,
  .featured-image:focus-visible img,
  .shot-strip figure > a:hover img,
  .shot-strip figure > a:focus-visible img,
  .site-scroll-frame:hover img,
  .site-scroll-frame:focus-visible img { transform: none; }
}

/* ---- Business brain: page scale ------------------------------------
   This page carries several times more information than any other on the
   site, and at the shared display scale a single section title took most of
   a screen before one fact arrived. The type scale is stepped down by
   REDEFINING THE TOKENS for this page rather than overriding heading by
   heading — so the rule still holds that a heading's size comes from a token,
   and the .section-title / .page-title classes are untouched. Nothing else on
   the site moves. */
.page-brain {
  /* Rico, 2026-08-30: "on desktop its too big, i think images and text should
     be smaller, hard to read otherwise." Measured before changing anything, and
     it was three separate causes, not one:

     1. The display ramp topped out at 86px on a 1728 screen (5vw with a 5.4rem
        cap). Pulled back about a fifth.
     2. THE ACTUAL "HARD TO READ": `.shell` is 92rem, so a full-width row ran
        1325px and the .brief summary column reached **89 characters a line**.
        This file's own rule is 42–46ch for body copy. A narrower rail fixes the
        cause; the per-element caps below fix the rest.
     3. A 21:9 frame on a 1325px rail is 568px tall — more than half a laptop
        screen, for a box that is still empty.

     Scoped to .page-brain deliberately. The same tokens dress /websites,
     /ai-backends, /hosting and /widgets, and Rico was looking at this page.
     If he wants the site to follow, move --content and the ramps to :root. */
  /* Second step down, 2026-08-30: "make everything a bit smaller aswell".
     Original → first pass → now, all at 1440:
       display  72 → 55 → 46px      (and 86 → 66 → 56 at 1728)
       h2       38 → 30 → 26px
       h3       21 → 17 → 16px
       rail   1472 → 1248 → 1184px  (Composio runs 1240, Goliath 1184–1312) */
  --content:      74rem;
  --t-display:    clamp(2.1rem, 3.2vw, 3.5rem);
  --t-display-sm: clamp(2.1rem, 10.5vw, 3.4rem);   /* the phone ramp — untouched, he said desktop */
  --t-h2:         clamp(1.28rem, 1.8vw, 1.6rem);
  --t-h3:         clamp(.95rem, 1.1vw, 1.1rem);
}

/* Vertical rhythm. Cut with the type on the first pass, then opened back up
   on 2026-08-30 — "i like that you made text smaller, but space them out more
   now a bit aswell". Small type needs MORE air around it, not less: the
   original 135px was right for 72px headings and wrong for 46px ones. */
.page-brain .section { padding-block: clamp(4.5rem, 11vh, 8rem); }
/* The section under the hero used to open tighter than the rest, so its
   hairline showed on the first screen. Since 2026-09-02 the hero IS the
   first screen and the hairline is gone (Rico), so §2 keeps the page's
   normal rhythm top and bottom. */
.page-brain .section-head { margin: clamp(2rem, 5vh, 3.5rem) 0 clamp(2.25rem, 6vh, 4rem); }
.page-brain .briefs { margin-top: clamp(2rem, 5vh, 3rem); }
/* No gap: the sphere is cropped by this list's top hairline. */
.page-brain .walls { margin-top: 0; }
.page-brain .stat-row { margin-top: clamp(.5rem, 2vh, 1.25rem); }

/* The hero lost its paragraph on 2026-08-30 ("as this is the first section, it
   should have less text"), and on 2026-09-02 it gained Rico's MacBook-and-hand
   figure under the text (see .mac below). The box is no longer the lever:
   the laptop is what fills the first screen, so the hero is as tall as its
   content and starts at the top instead of bottom-pinning a block in an
   87svh box. The old rule is in git if the picture ever comes off again. */
.page-brain .page-hero {
  /* Rico, 2026-09-02, in order: "lower the make your agents do more line",
     "a bit more down", then "the mac and the hand need to be fixed to one
     section when you open the page". So: the hook sits 15vh under the
     header (54 → 99 → 135px at 900 tall), and the box is ONE SCREEN — the
     laptop is pinned to its bottom edge, so the wrist always runs off the
     fold and nothing of the hand is below it. Capped at 62rem so a 27"
     display does not stretch the air; on a short screen the laptop shrinks
     instead (--mac-w, below), so it never drops out of the first screen.
     The phone keeps its content-sized box — see the 56rem block. */
  --hero-top: calc(var(--header-h) + clamp(7rem, 15vh, 9.5rem));
  --mac-air: clamp(3rem, 8vh, 6rem);
  /* The laptop's width: the rail, capped at the artboard's 727. The desktop
     block below adds the height budget. Used by .mac-stage and, through the
     perspective, by .mac-body — so both always agree. */
  --mac-w: min(100vw - 2 * var(--pad), 727px);
  min-height: min(100svh, 62rem);
  align-content: space-between;
  padding-top: var(--hero-top);
  /* "one tiny shade darker, until the hand cutoff" (Rico, 2026-09-02). The
     band runs edge to edge — .shell would have stopped it at the rail — with
     the rail rebuilt as padding, and it ends at the hero's bottom edge,
     which is exactly where the wrist is cropped. 2.5% ink in the paper is
     about two points of lightness; the grain is the body's own tile at the
     same origin, so the band is a tone, not a different surface. */
  width: 100%;
  padding-inline: max(var(--pad), (100% - var(--content)) / 2);
  background: color-mix(in oklab, var(--paper) 97.5%, var(--ink)) var(--grain);
  /* No bottom padding and a clip: the hand runs off the bottom edge and the
     hero's edge crops the wrist, exactly as the artboard's frame did. §2's
     hairline then reads as the ground the arm disappears under. */
  padding-bottom: 0;
  overflow: hidden;
}
@media (min-width: 56.0625rem) {
  /* The height budget. The laptop and hand stand .676 of their width tall,
     so what fits under the hook is (screen − top block − minimum air −
     hook) / .676. At 1440×900 that is 800px, so the 727 cap wins and the
     air is what is left (~120px); at 800 tall the laptop drops to ~690
     wide and still ends on the fold. 20rem is the floor. */
  .page-brain .page-hero {
    --mac-w: min(100vw - 2 * var(--pad), 727px,
                 max(20rem, (100svh - var(--hero-top) - var(--mac-air) - 5.5rem) / .676));
  }
}

/* THE ASK demo gets its own rail — "the claude chat section with the demo can
   be a bit bigger" (Rico, 2026-08-30). Done here rather than in the markup on
   purpose: that section is carried byte-identical from the 2026-08-30 build,
   and selecting it by its aria-labelledby keeps it that way. The demo is the
   one thing on the page that earns full width — everything else is prose, and
   prose gets narrower to stay readable. */
.page-brain [aria-labelledby="showcase-title"] { --content: 92rem; }
.page-brain .bb { font-size: 1.05rem; }

/* Body copy steps down with the headings. Scoped, like everything above. */
.page-brain .section-copy,
.page-brain .case-text,
.page-brain .pipe p,
.page-brain .faq-answer,
.page-brain .more-body p { font-size: .93rem; }
.page-brain .pipe-note { font-size: .78rem; }

/* §2's rows: "this should be smaller, taking less space, and more keywords
   rather than sentences" (Rico, 2026-08-30). The copy became keyword strings
   separated by middots; this makes the row match. */
.page-brain .wall { padding-block: 1.25rem; }
.page-brain .wall span { margin-top: .25rem; font-size: .85rem; line-height: 1.45; max-width: 72ch; }

/* ---- Business brain: flow steps, use cases, walls ---- */
.flow-steps { border-top: 1px solid var(--line); }
.flow-step {
  display: grid;
  grid-template-columns: 4.5rem minmax(0, 1fr);
  gap: 1.5rem;
  padding-block: 1.5rem;
  border-bottom: 1px solid var(--line);
  align-items: start;
}
.flow-step .step-n {
  color: var(--graphite);
  font-variant-numeric: tabular-nums;
  font-size: .7rem;
  font-weight: var(--wt-head);
  letter-spacing: .08em;
  text-transform: uppercase;
  padding-top: .35rem;
}
.flow-step h3 { margin-bottom: .35rem; }
.flow-step p { color: var(--graphite); max-width: 62ch; }

.use-case { border-top: 1px solid var(--line); padding-block: 1.75rem; }
.use-case:last-child { border-bottom: 1px solid var(--line); }
.use-case q {
  display: block;
  font-size: var(--t-h2);
  font-weight: var(--wt-head);
  letter-spacing: var(--tr-h2);
  line-height: 1.1;
  max-width: 26ch;
  quotes: "\201C" "\201D";
}
.use-case p { margin-top: .9rem; color: var(--graphite); max-width: 64ch; }
.use-case .src {
  display: inline-block;
  margin-top: .7rem;
  font-family: var(--font-mono);
  font-size: .68rem;
  color: var(--graphite);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  padding: .2rem .5rem;
}

.walls { border-top: 1px solid var(--line-strong); }
.wall {
  padding-block: 1.35rem;
  border-bottom: 1px solid var(--line);
  max-width: none;
}
.wall b {
  display: block;
  font-size: var(--t-h3);
  font-weight: var(--wt-head);
  letter-spacing: var(--tr-h3);
}
.wall span { display: block; margin-top: .4rem; color: var(--graphite); max-width: 64ch; }

@media (max-width: 42rem) {
  .flow-step { grid-template-columns: 2.75rem minmax(0, 1fr); gap: 1rem; }
}

/* ============================================================
   SCANNABLE: stats, pipeline diagram, points, expanders
   Added 2026-08-12 — the brain page had grown to 1,387 words of prose.
   The rule now: a section leads with something you can SEE or SCAN,
   and the long-form version lives behind an expander.
   ============================================================ */

/* --- big numerals ------------------------------------------------ */
.stat-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}
.stat { padding: 1.5rem 1.25rem 1.5rem 0; }
.stat + .stat { border-left: 1px solid var(--line); padding-left: 1.25rem; }
.stat b {
  display: block;
  font-size: clamp(2rem, 4vw, 3.4rem);
  font-weight: var(--wt-state);
  letter-spacing: var(--tr-state);
  line-height: 1;
  font-variant-numeric: tabular-nums;
}
.stat span {
  display: block;
  margin-top: .5rem;
  color: var(--graphite);
  font-size: .68rem;
  font-weight: var(--wt-head);
  letter-spacing: .08em;
  text-transform: uppercase;
  line-height: 1.35;
}

/* --- the pipeline diagram ---------------------------------------- */
.pipeline {
  display: grid;
  /* Explicit counts, not auto-fit: there are six boxes, and auto-fit left a
     five-wide row with one orphan underneath — the empty cells showed as
     bare grid background. 1 / 2 / 3 columns always divide six exactly. */
  grid-template-columns: 1fr;
  gap: 1px;
  background: var(--line);
  border: 1px solid var(--line);
  border-radius: var(--r-card);
  overflow: hidden;
}
.pipe {
  position: relative;
  background: var(--paper);
  padding: 1.35rem 1.15rem;
  display: grid;
  align-content: start;
  gap: .45rem;
  min-height: 10rem;
}
.pipe .micro { color: var(--graphite); }
.pipe b {
  font-size: var(--t-h3);
  font-weight: var(--wt-head);
  letter-spacing: var(--tr-h3);
  line-height: 1.2;
}
.pipe p { font-size: .88rem; color: var(--graphite); line-height: 1.5; margin: 0; max-width: none; }
/* the human approval gate — the one box that is not automatic */
.pipe--gate { background: var(--wash); }
.pipe--gate b::after {
  content: "";
  display: block;
  width: 2rem;
  height: 2px;
  margin-top: .5rem;
  background: var(--ink);
}
.pipe-note {
  margin-top: 1rem;
  color: var(--graphite);
  font-size: .8rem;
}

/* --- scannable points -------------------------------------------- */
.points { display: grid; gap: 0; }
.point {
  display: grid;
  grid-template-columns: 1.5rem minmax(0, 1fr);
  gap: .9rem;
  padding-block: .85rem;
  border-bottom: 1px solid var(--line);
  max-width: none;
  align-items: baseline;
}
.point:first-child { border-top: 1px solid var(--line); }
.point::before {
  content: "";
  width: .5rem;
  height: .5rem;
  border: 1.5px solid var(--line-strong);
  transform: translateY(-.1em);
}
.point b { font-weight: var(--wt-head); }
.point span { color: var(--graphite); }

/* --- the expander: long-form behind one click -------------------- */
.more {
  border-top: 1px solid var(--line);
  margin-top: 1.5rem;
}
.more > summary {
  display: flex;
  align-items: center;
  gap: .6rem;
  padding-block: 1rem;
  list-style: none;
  cursor: pointer;
  color: var(--graphite);
  font-size: .78rem;
  font-weight: var(--wt-head);
  letter-spacing: .08em;
  text-transform: uppercase;
  transition: color .25s;
}
.more > summary::-webkit-details-marker { display: none; }
.more > summary:hover { color: var(--ink); }
.more > summary::after {
  content: "+";
  margin-left: auto;
  font-size: 1.4rem;
  font-weight: 400;
  line-height: 1;
  transition: transform .3s var(--ease-out);
}
.more[open] > summary::after { content: "−"; }
.more-body { padding-bottom: 1.75rem; }
.more-body p { color: var(--graphite); max-width: 66ch; }
.more-body p + p { margin-top: 1rem; }
.more:focus-within > summary { color: var(--ink); }

@media (min-width: 42rem) { .pipeline { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 64rem) { .pipeline { grid-template-columns: repeat(3, 1fr); } }

@media (max-width: 42rem) {
  .stat + .stat { border-left: 0; border-top: 1px solid var(--line); padding-left: 0; }
  .stat { padding-right: 0; }
}

@media (prefers-reduced-motion: reduce) {
  .more > summary::after { transition: none; }
}

/* The five-layer stack diagram, the archive-field canvas, the corpus mix bar
   and the scale slider were deleted 2026-08-30 with the sections that used
   them. All four lived on /business-brain and nowhere else, and three of them
   existed to frame that client's 2026-08-03 corpus figures, which are
   superseded. Their JS went with them (see main.js).

   `.field-fallback` survives them: widgets.html uses it for its own
   JS-is-off message. It is kept here, and only it. */
.field-fallback { display: none; color: var(--graphite); font-size: .9rem; }
.no-js .field-fallback { display: block; }

/* --- the pipeline pulse: data actually moving through the stages --- */
.pipe { overflow: hidden; }

/* ============================================================
   CONTACT — two columns, escape hatches, expectations.
   Ported from compactliving-web/src/pages/contact.astro: the form is
   the default path, not the only one, and the page says what happens
   after you press send.
   ============================================================ */
.ct-grid {
  display: grid;
  grid-template-columns: minmax(0, 1.05fr) minmax(0, .95fr);
  gap: clamp(2rem, 5vw, 4rem);
  align-items: start;
}
.ct-aside { display: grid; gap: 2.25rem; align-content: start; }

.ct-card { border: 1px solid var(--line); border-radius: var(--r-card); padding: 1.5rem; }
.ct-card > .micro { display: block; color: var(--graphite); margin-bottom: 1rem; }
.ct-detail { padding-block: .9rem; border-top: 1px solid var(--line); }
.ct-detail:first-of-type { border-top: 0; padding-top: 0; }
.ct-detail dt {
  color: var(--graphite);
  font-size: .68rem;
  font-weight: var(--wt-head);
  letter-spacing: .08em;
  text-transform: uppercase;
}
.ct-detail dd { margin-top: .3rem; font-size: 1.02rem; }
.ct-detail dd a { text-underline-offset: .25em; }
.ct-detail small { display: block; margin-top: .25rem; color: var(--graphite); font-size: .82rem; }

/* what happens next — numbered, because it is a sequence */
.ct-next { counter-reset: step; }
.ct-next li {
  display: grid;
  grid-template-columns: 2rem minmax(0, 1fr);
  gap: .9rem;
  padding-block: .9rem;
  border-top: 1px solid var(--line);
  max-width: none;
}
.ct-next li::before {
  counter-increment: step;
  content: "0" counter(step);
  color: var(--graphite);
  font-variant-numeric: tabular-nums;
  font-size: .7rem;
  font-weight: var(--wt-head);
  letter-spacing: .08em;
  padding-top: .2rem;
}
.ct-next b { display: block; font-weight: var(--wt-head); }
.ct-next span { color: var(--graphite); font-size: .92rem; }

/* a send failure must never be a dead end */
.form-banner {
  margin-top: 1.5rem;
  padding: 1rem 1.15rem;
  border: 1px solid var(--err);
  border-radius: var(--r-sm);
  color: var(--ink);
  font-size: .95rem;
}
.form-banner a { color: var(--ink); text-underline-offset: .25em; }

.sent-panel .sent-mark {
  display: grid;
  place-items: center;
  width: 2.75rem;
  height: 2.75rem;
  border-radius: 50%;
  background: var(--ink);
  color: var(--paper);
  margin-bottom: 1.25rem;
  font-size: 1.1rem;
}

@media (max-width: 56rem) {
  .ct-grid { grid-template-columns: 1fr; }
  .ct-aside { order: 2; }
}
/* A select with appearance:none loses the native arrow, and the underline
   treatment the text inputs use then makes it read as something you type
   into. So the selects get their own affordance: a real bordered control
   with a chevron, so it is obvious it opens.

   The chevron is two linear-gradients rather than an SVG data URI — it
   uses currentColor, so it follows the text colour instead of hardcoding
   a hex that would drift from --ink. No icon set, no extra request. */
.field select {
  appearance: none;
  cursor: pointer;
  /* A soft fill, not an outline. The filled surface plus the chevron is
     enough to say "this opens" — the heavy border was doing the same job
     twice and shouting about it. */
  border: 1px solid transparent;
  border-radius: var(--r-sm);
  padding: .85rem 3rem .85rem 1rem;
  background-color: var(--wash);
  background-image:
    linear-gradient(45deg, transparent 50%, currentColor 50%),
    linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position: calc(100% - 1.5rem) calc(50% - 2px), calc(100% - 1.15rem) calc(50% - 2px);
  background-size: 6px 6px;
  background-repeat: no-repeat;
  transition: border-color .25s, background-color .25s;
}
.field select:hover { background-color: color-mix(in oklab, var(--ink) 9%, transparent); }
.field select:focus { border-color: var(--line-strong); background-color: var(--wash); }
/* the placeholder option should read as a prompt, not as an answer */
.field select:has(option[value=""]:checked) { color: var(--graphite); }
/* The form now lives in a ~half-width column, so four intent cards across
   squeeze the labels onto four lines. Two-by-two fits the measure. */
.ct-grid .intent-grid { grid-template-columns: repeat(2, 1fr); }
@media (max-width: 42rem) { .ct-grid .intent-grid { grid-template-columns: 1fr; } }
.field-intent { margin-bottom: 2rem; }
/* the first question is the one that routes everything — give it presence */
.field-intent select {
  font-size: var(--t-h3);
  font-weight: var(--wt-head);
  padding-block: 1rem;
  background-position: calc(100% - 1.7rem) calc(50% - 3px), calc(100% - 1.25rem) calc(50% - 3px);
  background-size: 8px 8px;
}

/* --- file picker: a real <input> under a dashed label ---------------
   The label IS the drop zone, the input is sr-only — so drag-and-drop
   works for a mouse, and keyboard and screen readers get a normal file
   input. Pattern from compactliving-web's ContactForm. */
/* `.field label` is (0,1,1) and outranks a bare `.drop` (0,1,0), so the
   opt-out has to be scoped: `.field .drop` is (0,2,0) and wins. */
.field .drop {
  text-transform: none;
  letter-spacing: normal;
  font-size: .95rem;
  font-weight: 400;
  margin-bottom: 0;
  color: var(--graphite);
}
.drop {
  display: block;
  border: 1px dashed var(--line-strong);
  border-radius: var(--r-sm);
  padding: 1.5rem 1.25rem;
  text-align: center;
  cursor: pointer;
  color: var(--graphite);
  transition: border-color .25s, background-color .25s;
}
.drop:hover, .drop.is-over { border-color: var(--ink); background: var(--wash); }
.drop b { display: block; color: var(--ink); font-weight: var(--wt-head); margin-bottom: .3rem; }
.drop small { font-size: .82rem; }
.drop input[type="file"] {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden; clip-path: inset(50%);
  white-space: nowrap;
}
.drop:focus-within { border-color: var(--ink); outline: 2px solid var(--ink); outline-offset: 3px; }

.chips-files { display: grid; gap: .5rem; margin-top: .85rem; }
.file-chip {
  display: flex;
  align-items: center;
  gap: .75rem;
  padding: .6rem .8rem;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  font-size: .88rem;
}
.file-chip .fname { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.file-chip .fsize { margin-left: auto; color: var(--graphite); font-size: .78rem; font-variant-numeric: tabular-nums; white-space: nowrap; }
.file-chip button {
  border: 0; background: none; cursor: pointer;
  color: var(--graphite); font-size: 1.1rem; line-height: 1;
  padding: .25rem .35rem; border-radius: 4px;
  min-width: 2rem; min-height: 2rem;
}
.file-chip button:hover { color: var(--ink); }
.file-chip.is-over-limit { border-color: var(--err); }
.file-chip.is-over-limit .fsize { color: var(--err); }
.file-note { margin-top: .6rem; color: var(--graphite); font-size: .82rem; }
.file-note a { color: var(--ink); text-underline-offset: .25em; }

/* ============================================================
   FORM RHYTHM — one gap between every field, instead of the
   ad-hoc margin-top on whichever field happened to need air.
   ============================================================ */
:root { --form-gap: 2.5rem; }

.form-wrap .field { margin-bottom: var(--form-gap); }
/* inside the two-column row the grid gap already does the spacing */
.fields { gap: var(--form-gap) 3rem; margin-top: 0; margin-bottom: var(--form-gap); }
.fields .field { margin-bottom: 0; }

.field label,
.chips legend { margin-bottom: .75rem; }

.field input,
.field textarea { padding-block: 1rem; }
.field textarea { min-height: 11rem; padding: 1rem; border: 1px solid var(--line); border-radius: var(--r-sm); }
.field textarea:focus { border-color: var(--ink); }

/* these two carried bespoke margins that now double up */
.field-msg { margin-top: 0; }
.field-intent { margin-bottom: var(--form-gap); }

.drop { padding: 2.25rem 1.5rem; }
.form-foot { margin-top: .5rem; gap: 1.25rem 2rem; }
.form-banner { margin-top: 0; margin-bottom: var(--form-gap); }

/* the aside needs the same generosity or the two columns fight */
.ct-card { padding: 1.75rem; }
.ct-detail { padding-block: 1.15rem; }
.ct-aside { gap: 2.75rem; }
.ct-next li { padding-block: 1.15rem; }
.ct-next span { line-height: 1.5; }

/* and the page itself */
.form-section { padding-block: clamp(3rem, 7vh, 5rem) clamp(5rem, 12vh, 9rem); }
.ct-grid { gap: clamp(2.5rem, 6vw, 5rem); }

@media (max-width: 42rem) {
  :root { --form-gap: 2rem; }
}

/* the hero's only job is to point down at the form */
.scroll-cue {
  display: inline-flex;
  align-items: center;
  gap: .5rem;
  margin-top: clamp(2rem, 5vh, 3rem);
  font-size: .8rem;
  font-weight: var(--wt-head);
  letter-spacing: .08em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--graphite);
  transition: color .25s;
}
.scroll-cue span { transition: transform .3s var(--ease-out); }
.scroll-cue:hover { color: var(--ink); }
.scroll-cue:hover span { transform: translateY(.25rem); }
.contact-hero { padding-bottom: clamp(3rem, 7vh, 5rem); }

/* Selects: small and clean. They were at --t-h3 / 620 weight, which made a
   form control look like a heading. Body size, normal weight, compact. */
.field select,
.field-intent select {
  font-size: 1rem;
  font-weight: 400;
  padding: .8rem 2.75rem .8rem .9rem;
  min-height: 2.75rem;
  background-position: calc(100% - 1.35rem) calc(50% - 2px), calc(100% - 1rem) calc(50% - 2px);
  background-size: 6px 6px;
}
.field-intent { margin-bottom: var(--form-gap); }

@media (prefers-reduced-motion: reduce) { .scroll-cue:hover span { transform: none; } }

/* ============================================================
   LEADING BY ROLE
   Body leading on heading-sized text is the single thing that
   makes a page read loose. Measured on /contact: the FAQ question
   was 27px at 1.60. Leading tightens as type grows — that is the
   whole rule.
   ============================================================ */
.faq-item summary { line-height: 1.22; padding-block: 1.5rem; }
.faq-answer { line-height: 1.6; max-width: 62ch; padding-bottom: 2.25rem; }
.faq-item { padding-inline: 0; }

/* The submit note is a sentence, not a label — it was set in the
   uppercase micro group, which is unreadable for two lines of prose. */
.form-note {
  font-size: .88rem;
  font-weight: 400;
  letter-spacing: 0;
  line-height: 1.55;
  text-transform: none;
  color: var(--graphite);
  max-width: 34ch;
}

/* Aside: give the rows air and let the step titles lead. */
.ct-detail { padding-block: 1.35rem; }
.ct-detail dd { margin-top: .45rem; line-height: 1.45; }
.ct-detail small { margin-top: .5rem; line-height: 1.55; }
.ct-next li { padding-block: 1.35rem; gap: 1.1rem; }
.ct-next b { font-size: 1rem; line-height: 1.35; margin-bottom: .3rem; }
.ct-next span { line-height: 1.6; }
.ct-card > .micro { margin-bottom: 1.35rem; }

/* Hero: the note and the cue were sitting close under a 90px headline. */
.human-note { margin-top: 1.75rem; line-height: 1.6; max-width: 46ch; }
.page-kicker { margin-bottom: 2rem; }

/* Safety net: nothing heading-sized should inherit body leading. */
.wall b, .point b, .pipe b, .flow-step h3, .use-case q { line-height: 1.25; }

/* The last two carrying body leading at heading size. The pillar-card h3s
   set font-size inline but not line-height, so they fell through to the
   body's 1.6; a CSS rule still wins for the property the inline style
   never mentions. */
[data-card] h3 { line-height: 1.25; }
.sent-panel h2 { line-height: 1.02; }

/* The opening hairline used to be followed by a section-head, which
   supplied the gap. That head is gone, so the rule was sitting flush on
   the first label — 0px. Give it the same space a section-head would have. */
#form .rule { margin-bottom: clamp(2.5rem, 6vh, 4rem); }

/* --- model portability: three assistants, one folder ---------------- */
.models { display: grid; gap: 0; }
.model-row {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--gap-grid, 1.25rem);
}
.model-chip {
  border: 1px solid var(--line);
  border-radius: var(--r-card);
  padding: 1.15rem 1rem;
  text-align: center;
}
.model-chip b { display: block; font-size: 1.05rem; font-weight: var(--wt-head); line-height: 1.3; }
.model-chip span { display: block; margin-top: .35rem; color: var(--graphite); font-size: .78rem; line-height: 1.4; }

/* the three feed one thing — draw that as a bracket, not one stem.
   A single centred line under three chips reads as "the middle one connects". */
.model-join {
  height: 3rem;
  position: relative;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--gap-grid, 1.25rem);
  --col-centre: calc((100% - 2 * var(--gap-grid, 1.25rem)) / 6);
}
.model-join i { position: relative; }
/* a stem down from each chip to the rail */
.model-join i::before {
  content: "";
  position: absolute;
  left: 50%; top: 0; height: 50%;
  width: 1px;
  background: var(--line-strong);
}
/* the rail, spanning centre-of-first to centre-of-last */
.model-join::before {
  content: "";
  position: absolute;
  top: 50%; height: 1px;
  left: var(--col-centre); right: var(--col-centre);
  background: var(--line-strong);
}
/* one line down from the rail into the folder */
.model-join i:nth-child(2)::after {
  content: "";
  position: absolute;
  left: 50%; top: 50%; bottom: 0;
  width: 1px;
  background: var(--line-strong);
}
.model-join::after {
  content: "";
  position: absolute;
  left: 50%; bottom: 0;
  width: 7px; height: 7px;
  border-right: 1px solid var(--line-strong);
  border-bottom: 1px solid var(--line-strong);
  transform: translate(-50%, 2px) rotate(45deg);
}
.model-vault {
  border: 1px solid var(--ink);
  border-radius: var(--r-card);
  padding: 1.5rem;
  text-align: center;
  background: var(--wash);
}
.model-vault b { display: block; font-size: var(--t-h3); font-weight: var(--wt-head); line-height: 1.25; letter-spacing: var(--tr-h3); }
.model-vault span { display: block; margin-top: .5rem; color: var(--graphite); max-width: 46ch; margin-inline: auto; line-height: 1.55; }

@media (max-width: 42rem) {
  .model-row { grid-template-columns: 1fr; gap: .75rem; }
  /* stacked chips — a bracket has nothing to bracket, so just the one stem.
     The i's collapse to zero-size inlines here, so the stem moves onto ::before. */
  .model-join { height: 1.75rem; display: block; }
  .model-join i { display: none; }
  .model-join::before {
    left: 50%; right: auto; top: 0; bottom: 0;
    width: 1px; height: auto;
  }
}


/* The case-study centrepiece — the slow top-to-bottom pan of the client's real
   homepage — was driven only by :hover/:focus-visible, so on a phone it was a
   static crop and the caption still said "hover to scroll". Phones are most of
   this site's traffic and these are the pages that sell the work.
   `.is-touch` is set by main.js on a coarse pointer; there it plays on its own. */
.is-touch .site-scroll-frame.is-pan-active img { animation: kuup-pan 14s ease-in-out infinite alternate; }
@keyframes kuup-pan { from { transform: translateY(0); } to { transform: translateY(calc(-100% + 100cqh)); } }
@media (prefers-reduced-motion: reduce) {
  .is-touch .site-scroll-frame.is-pan-active img { animation: none; }
}

/* ═══════════════════════════════════════════════════════ redesign-v3 · chrome
   2026-08-22 — the integration pass.

   Everything below arrived from somewhere else: the five page agents each left
   their CSS in a page-level <style> block and listed it for folding, the vendor
   marks came from assets/logos/SNIPPET.md, and the last two groups are what the
   new footer column and the now-clickable cube cards need. Existing tokens only
   — no new colours, and still no accent.

   The page-level <style> blocks are deliberately left in place on the four
   pages this pass does not own: the declarations are identical, the page block
   loads after this file, so it wins its own ties and nothing moves. They are
   duplicates, not conflicts, and they can be deleted whenever those files are
   next opened.
   ──────────────────────────────────────────────────────────────────────────── */

/* ---- the deck ------------------------------------------------------------
   The <h2> that sits directly under a page title, carrying the phrase a buyer
   actually types. It is the SEO-load-bearing line on five pages, so it gets one
   class and one treatment. Three pages first shipped it as .wdg-deck / .ab-deck
   / .bb-deck and two more as h2.page-intro — which is a <p> class, so the
   browser's own bold won and the deck out-weighted the title above it. Five
   pages, two different renderings of the same structural element. Unified
   2026-08-22: everything is .page-deck. Do not reintroduce a per-page name. */
.page-deck {
  max-width: 42ch;
  margin-top: 1.5rem;
  font-size: var(--t-h3);
  font-weight: var(--wt-state);
  letter-spacing: var(--tr-h3);
  line-height: 1.3;
}
.page-deck em {
  color: var(--graphite);
  font-weight: var(--wt-accent);
  font-style: italic;
}


/* .hero-actions is centred because the HOME hero is centred. Every .page-hero
   is left-aligned, so its buttons follow it. Four pages carried this rule. */
.page-hero .hero-actions { justify-content: flex-start; margin-top: clamp(2rem, 5vh, 2.75rem); }

/* Measure on the qualifier line that follows a price. Two names, one job. */
.websites-note { max-width: 64ch; }
.hosting-note { max-width: 62ch; }

/* .pipeline is built for six boxes — 1 / 2 / 3 columns divide six exactly. A
   three-box pipeline leaves an empty third cell between 42rem and 64rem, and
   an empty cell in this grid paints as bare --line. */
@media (min-width: 42rem) and (max-width: 63.999rem) {
  .pipeline--trio .pipe:last-child { grid-column: 1 / -1; }
}

/* /hosting — the three cross-link cards */
.hosting-cards { margin-top: clamp(1.5rem, 4vh, 2.5rem); }
.hosting-card { display: flex; flex-direction: column; gap: .55rem; height: 100%; }
.hosting-card p { color: var(--graphite); max-width: 40ch; }
.hosting-card .text-link { margin-top: .5rem; align-self: start; font-weight: 620; }

/* ── vendor marks ───────────────────────────────────────────────── 2026-08-22
   Claude / OpenAI / Gemini logomarks, shown to say which assistant a build
   runs on. Each mark is one currentColor path, so it takes --graphite on
   paper and inside .band without a second rule.

   --mark is the row's optical size. The per-vendor --f after it is measured,
   not eyeballed: each viewBox is cropped to its own bounding box, then scaled
   by the geometric mean of height-normalisation and ink-area-normalisation
   against the OpenAI mark. The Gemini sparkle covers 21% of its box against
   OpenAI's 37%, which is why it needs to run larger to read the same weight. */
.vendor-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .8rem clamp(1.4rem, 4vw, 2.8rem);
  margin-block-start: .9rem;
  --mark: 1.75rem;
}
.vendor-row > li { display: flex; align-items: center; gap: .6rem; }

.vendor-mark { display: block; color: var(--graphite); --f: 1; }
.vendor-mark svg { display: block; width: auto; height: calc(var(--mark) * var(--f)); }
.vendor-mark--claude { --f: .99; }
.vendor-mark--openai { --f: 1; }
.vendor-mark--gemini { --f: 1.15; }

/* .micro uppercases, and a trademark is spelled the way its owner spells it. */
.vendor-name { text-transform: none; letter-spacing: .01em; }

/* The two pages that stack a mark inside the existing .model-chip rather than
   laying them out in a .vendor-row. .model-chip is text-align:center, which
   does not centre a display:block replaced element, and Gemini runs 15% taller
   by design — in a stack that would push its name below the other two, so the
   wrapper keeps a fixed height and the taller spark overflows it symmetrically.
   Unified 2026-08-22: /business-brain first shipped these under a second name;
   there is one name now, and the page-level copies are gone. */
.model-chip .vendor-mark {
  --mark: 1.5rem;
  display: flex;
  align-items: center;
  justify-content: center;
  height: var(--mark);
  margin: 0 0 .9rem;
}

/* ---- footer link column --------------------------------------------------
   /widgets, /ai-backends and /hosting are not in the four-item nav, so this
   column is how they are reachable from every page. It replaces .footer-meta
   in the right-hand slot of .footer-top and inherits the footer's own .micro
   type, so it needs no size of its own — only spacing, colour and a tap
   target. 2.75rem is 44px: the rows are the smallest links on the site and
   phones are most of the traffic. */
.footer-links { display: grid; align-content: end; justify-items: start; gap: .55rem; }
.footer-links .micro { color: var(--graphite); }
/* Two columns, three rows: five links stand about as tall as the address
   block beside them instead of five times a tap target. */
.footer-links ul { display: grid; grid-template-columns: repeat(2, auto); column-gap: 2.5rem; }
.footer-links li a {
  display: flex;
  align-items: center;
  min-height: 2.25rem;
  color: var(--ink);
  text-decoration: none;
  text-underline-offset: .28em;
  text-decoration-thickness: 1px;
  text-decoration-color: var(--line-strong);
}
.footer-links li a:hover { text-decoration: underline; text-decoration-color: currentColor; }

/* ---- the cube cards are links -------------------------------------------
   Each pillar card now navigates to its page. Rico's constraint on that
   section is that nothing about it moves, and main.js writes
   element.style.transform straight onto [data-card] for the flight out of the
   cube — so the anchor goes INSIDE the existing <h3> and stretches over the
   card with an ::after. No wrapper, no new element in the flow, no second
   class on [data-card]: the animation cannot tell the difference.

   .k-card already sets position:relative, which is what the ::after needs. */
.card-link { color: inherit; text-decoration: none; }
.card-link::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: var(--r-card);
}
.card-arrow { display: inline-block; transition: transform .3s var(--ease-out); }
/* The ::after is a child of the anchor, so pointing anywhere on the card is a
   hover on the link — the underline and the nudge are the whole-card feedback,
   in the same underline vocabulary as .text-link. */
.card-link:hover { text-decoration: underline; text-decoration-color: var(--line-strong); }
.card-link:hover .card-arrow { transform: translateX(.18em); }
.k-card:has(.card-link:hover) { border-color: var(--line-strong); }
/* Focus has to be drawn on the ::after, not on the anchor: the anchor's own box
   is just the words inside the h3, and a ring around three words on a card the
   whole of which is clickable points at the wrong thing. One ring, card-sized. */
.card-link:focus-visible { outline: none; }
.card-link:focus-visible::after {
  outline: 2px solid currentColor;
  outline-offset: 3px;
  border-radius: var(--r-card);
}
.k-card:has(.card-link:focus-visible) { border-color: var(--line-strong); }

@media (prefers-reduced-motion: reduce) {
  .card-arrow { transition: none; }
}

/* .footer-top drops to one column at 56rem and .footer-meta gets a
   justify-self there; .footer-links stands in the same slot and needs it too. */
@media (max-width: 56rem) {
  .footer-links { justify-self: start; }
  /* Phones are most of the traffic: the rows go back to a 44px tap height. */
  .footer-links li a { min-height: 2.75rem; }
}

/* ── 2026-08-22 · contact is mobile-only in the nav ────────────────────────
   The "get in touch" pill already sits at the right of the header on desktop,
   so a second contact link three items to its left is the same destination
   twice. It stays in the markup because the mobile card IS the navigation
   there — and the pill is hidden while that card is open, so removing the
   item outright would leave a phone with the menu open no route to contact. */
@media (min-width: 56.0625rem) {
  .nav-list .nav-contact { display: none; }
}

/* ============================================================
   BRIEF ROWS — the how/why grammar on /business-brain.
   Added 2026-08-30. A row is an icon, a claim, a one-line
   summary, and the long version behind the same click, so a
   section can be read in fifteen seconds or in three minutes
   without being written twice.

   A THIRD <details> variant on purpose. `.more` is a
   section-level fold used on five pages and `.faq-item` is a
   question used on six — neither should grow a four-column grid
   for one page's sake. `.more-body` IS reused verbatim: it is a
   bare class with no ancestor selector, so it drops into this
   parent unchanged.

   Shape borrowed from goliathdata.com's icon rows, rendered in
   this site's hairline rhythm instead of as cards — the whole
   compression there comes from short line length, not from
   boxes.
   ============================================================ */
.briefs { border-top: 1px solid var(--line); }
.brief { border-bottom: 1px solid var(--line); }

.brief > summary {
  display: grid;
  grid-template-columns: 1.5rem minmax(0, 1fr) minmax(0, 1fr) 1.1rem;
  gap: 1.2rem;
  align-items: start;
  padding-block: 1.4rem;
  list-style: none;
  cursor: pointer;
  transition: color .25s;
}
.brief > summary::-webkit-details-marker { display: none; }
.brief > summary:hover .brief-t,
.brief:focus-within > summary .brief-t {
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: .28em;
  text-decoration-color: var(--line-strong);
}

/* Same icon spec as .intent-card svg — the site has exactly one. */
.brief svg {
  width: 1.5rem;
  height: 1.5rem;
  margin-top: .05rem;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.35;
  stroke-linecap: round;
  stroke-linejoin: round;
  color: var(--graphite);
}
.brief[open] svg { color: var(--ink); }

.brief-t {
  font-size: var(--t-h3);
  font-weight: var(--wt-head);
  letter-spacing: var(--tr-h3);
  line-height: 1.24;
}
.brief-s {
  padding-top: .15em;
  /* 89 characters a line before this cap existed. The rule at the top of this
     file is 42–46ch for body copy; 56 is the ceiling for a secondary line. */
  max-width: 56ch;
  color: var(--graphite);
  font-size: .85rem;
  line-height: 1.55;
}

.brief > summary::after {
  content: "+";
  justify-self: end;
  font-size: 1.35rem;
  font-weight: 400;
  line-height: 1;
  color: var(--graphite);
}
.brief[open] > summary::after { content: "\2212"; }

/* Indented to the text column, so the long version reads as the
   same row rather than as a new one. */
.brief .more-body { padding: .2rem 0 1.75rem calc(1.5rem + 1.2rem); }
.brief .more-body p { max-width: 62ch; }
.brief .more-body > :first-child { margin-top: 0; }
.brief .more-body .k-term,
.brief .more-body .models,
.brief .more-body .pipe-note { margin-top: 1.25rem; }

@media (max-width: 56rem) {
  .brief > summary {
    grid-template-columns: 1.5rem minmax(0, 1fr) 1.1rem;
    gap: .4rem 1rem;
    padding-block: 1.15rem;
  }
  .brief svg { grid-column: 1; grid-row: 1; }
  .brief-t { grid-column: 2; grid-row: 1; }
  .brief > summary::after { grid-column: 3; grid-row: 1; }
  .brief-s { grid-column: 2 / 4; grid-row: 2; padding-top: 0; font-size: .9rem; }
  .brief .more-body { padding-left: calc(1.5rem + 1rem); }
}
@media (max-width: 34rem) {
  .brief .more-body { padding-left: 0; }
}

/* ============================================================
   THE AGENT ROTATOR — the statement band under the hero.
   Ported 2026-08-30 from Rico's "Agent Rotator Widget" artboard.

   NOT placed in the hero's empty right half, which is where he
   pointed. It is a 68px full-width statement line, and putting it
   beside the h1 would have stood two headlines side by side with the
   deck underneath — three headline-weight things in one viewport.
   As a band directly under the hero it runs at its own scale, fills
   the space the hero was dead-ending into, and turns "a database"
   into "the AI you already use does more" exactly where the argument
   needs that turn.

   Sized off --t-display so it can never drift away from the h1 it
   sits under. Marks are the site's own; the artboard's stock
   ChatGPT .webp was not needed.
   ============================================================ */
/* Centred and sized up on Rico's call (2026-08-30): "make it centered, its
   the main hook". It leads the hero, so the whole hero centres with it —
   a centred line above left-aligned type reads as a mistake, and the home
   hero is already centred, so this is in the vocabulary rather than new. */
.rot-head { margin: 0; }
.rot-line {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: nowrap;
  gap: .3em;
  /* "just a tiny bit bigger" on desktop (Rico, 2026-09-02): 2.5vw → 2.7vw,
     cap 2.3rem → 2.5rem — 36 → 39px at 1440. The floor is unchanged, so
     phones, which sit on it, do not move. */
  font-size: clamp(1.45rem, 2.7vw, 2.5rem);
  font-weight: var(--wt-head);
  letter-spacing: var(--tr-h2);
  line-height: 1.12;
}
/* The hook's second line, in the graphite italic the site uses for the
   turn in every heading. */
.rot-with {
  display: block;
  margin-top: .15em;
  margin-bottom: 0;   /* was 5–15rem while a row was bottom-pinned under it; the laptop's own margin sets the gap now */
  font-size: clamp(1.15rem, 2.05vw, 1.9rem);   /* stepped up with .rot-line, same ratio */
  font-weight: var(--wt-head);
  letter-spacing: var(--tr-h2);
  line-height: 1.12;
  text-align: center;
}
.rot-with em { font-style: italic; font-weight: var(--wt-accent); color: var(--graphite); }

/* There is no second row any more. Until 2026-09-02 the hook had a
   two-column row under it — the deck left, the buttons right — then the deck
   came off, then the buttons, both on Rico's instruction that day. The hero
   is his artboard now: hook, air, laptop. The row's rules went with it. */

/* MOBILE HERO. The box is content-sized on every width since 2026-09-02, so
   the phone only tightens the top: the hook starts near the top of the fold
   and the laptop follows it. The row below stacks and centres, as before. */
@media (max-width: 56rem) {
  /* The phone hero is one screen as well (Rico, 2026-09-02: "the hand should
     always be at the bottom of screen one, almost at the bottom, a couple
     pixels up, to account for Safari's and Chrome's domain bar"). 100svh is
     the screen WITH the browser's bars showing, so the bar never covers the
     wrist; the 6px is his margin under it. The laptop and hand are pinned to
     the bottom edge, nothing of the hand is cropped here — its image ends at
     the wrist — and on a short screen the laptop shrinks with the same
     height budget the desktop uses, so it never drops below the fold. */
  .page-brain .page-hero {
    --hero-top: calc(var(--header-h) + 1.5rem);
    --mac-air: clamp(2.5rem, 6vh, 3.5rem);
    /* Bigger than the rail on purpose (Rico, from his phone, 2026-09-02:
       "make the hand and mac larger"). The photo carries empty margins: the
       deck spans 82% of it, so a stage of 118vw puts the deck at ~97% of the
       screen with a sliver of air each side, and the hand scales with it.
       The stage is centred with negative margins below, and the hero's
       overflow clip keeps the page from scrolling sideways. The height
       budget stays in svh so the laptop's size does not change as the
       browser bar collapses. */
    --mac-w: min(118vw,
                 max(14rem, (100lvh - var(--hero-top) - var(--mac-air) - 4.5rem - var(--hand-clear)) / .71));
    /* The room under the wrist. svh already stops at the browser's bars.
       Rico's margin went 6px → 24px + the safe-area inset → 12px, no inset
       (from his phone, 2026-09-02: "a bit more down, on phone its too
       high"): iOS Safari can report a 34px inset on top of a unit that has
       already stopped at the bar, which put the hand ~58px up. */
    --hand-clear: .75rem;
    /* THE BAR, third pass (Rico, from his iPhone, 2026-09-02). iOS Safari
       draws the page UNDER its translucent bottom bar: the layout viewport
       stops at the bar's top edge, the pixels continue beneath it.
         · svh hero: ended at the bar, section 2's paper showed through the
           glass ("I see 2nd a bit").
         · lvh hero with the hand pinned to svh: band under the bar, hand
           12px above the bar's edge — "the hand starts too high".
       So the hero is the LARGE viewport and the hand is pinned to ITS
       bottom, --hand-clear up: on load the wrist runs under the glass, the
       way the Apple page this recreates runs its hand off the screen, and
       once the bar collapses on the first scroll the whole hand sits 12px
       off the bottom. Where the bar is at the top (Chrome, Safari's
       single-tab layout) the fold crops the wrist on load instead — the
       desktop look — and reveals it on scroll. No dynamic unit, nothing
       jumps. Desktop-class browsers have no bar: lvh is the screen. */
    min-height: 100vh;
    min-height: 100lvh;
    padding-top: var(--hero-top);
    padding-bottom: var(--hand-clear);
    /* Fourth pass ("good, but a bit too much, a bit up"): halfway into the
       bar region on load rather than fully under it — half of (large minus
       small viewport) on top of the 12px. No bar, no difference. */
    padding-bottom: calc(var(--hand-clear) + (100lvh - 100svh) / 2);
    /* The hook sits in the MIDDLE of the air above the laptop (Rico,
       2026-09-02: "lower the phrase as well in the middle"): two flexible
       rows either side of it split whatever the screen leaves, so it is
       centred between the header and the laptop on every phone height. */
    /* 1.15 / .85, not 1 / 1: "the agents text, just a tiny bit down" (Rico,
       from his phone) — the hook sits a little below the midpoint of the
       air, about 20px on an iPhone, and the bias scales with the screen. */
    grid-template-rows: 1.15fr auto .85fr auto;
    align-content: stretch;
    /* minmax(0, 1fr), not the implicit auto column: an auto column grows to
       the widest item, and the stage is wider than the screen (see --mac-w)
       — so the column became 443px wide on a 375px phone and everything in
       the hero, hook included, was centred on that instead of the screen.
       That was the "not centered" on Rico's iPhone. A 0-based column stays
       the screen's width and the stage overflows it evenly. */
    grid-template-columns: minmax(0, 1fr);
  }
  .page-brain .page-hero .rot-head { grid-row: 2; }
  /* Two classes deep on purpose: the base .mac rule sits later in the file.
     A grid, so the stage — wider than this box, see --mac-w — is centred by
     the grid and overflows both sides equally, in every engine. Negative
     side margins did the same job in Chromium and came out off-centre on
     Rico's iPhone. */
  .page-brain .page-hero .mac { grid-row: 4; margin-top: var(--mac-air); display: grid; grid-template-columns: minmax(0, 1fr); justify-items: center; }
  /* Uncropped on the phone: the wrist sits --hand-clear above the screen's
     bottom. The stage is then .71 of its width tall, which the budget above
     uses. */
  .page-brain .page-hero .mac-hand { margin-bottom: 0; }
  /* The stage may be wider than the rail here (see --mac-w); the grid above
     centres it, so the base rule's width cap and auto margins come off. */
  .page-brain .page-hero .mac-stage { width: var(--mac-w); margin-inline: 0; }
  /* With §2's hairline gone (2026-09-02) the hand was followed by ~130px of
     empty paper on a phone. Only the top, only here: the rest of the page's
     rhythm is untouched, and on desktop §2 starts below the fold anyway. */
  .page-brain > .page-hero + .section { padding-top: clamp(2.5rem, 6vh, 3.5rem); }
}


/* The hook centres itself — .rot-line through justify-content, .rot-with
   through text-align — so the hero stays left-aligned like every other
   .page-hero and needs no override. Rico, 2026-08-30: "dont put other text
   in the center, leave it to the corner below" — that text was the deck, and
   it came off on 2026-09-02; the button pair that remains follows the hook
   (see .hero-row above). */

/* The three labels are stacked in one grid cell and the cell's width
   is animated to whichever is showing — that is what makes the two
   fixed halves slide apart rather than jump. main.js measures each
   label and writes --w. */
.rot {
  display: inline-grid;
  grid-template-columns: minmax(0, 100%);
  justify-items: center;
  align-items: center;
  width: var(--w, auto);
  transition: width .34s var(--ease-out);
}
.rot-item {
  grid-area: 1 / 1;
  width: max-content;
  justify-self: center;
  white-space: nowrap;
  opacity: 0;
  visibility: hidden;
  transform: translateY(8px) scale(.97);
  filter: blur(3px);
  pointer-events: none;
  transition: opacity .17s cubic-bezier(.5, 0, .74, .32),
              transform .17s cubic-bezier(.5, 0, .74, .32),
              filter .17s ease,
              visibility 0s linear .17s;
}
.rot-item.is-on {
  opacity: 1;
  visibility: visible;
  transform: none;
  filter: blur(0);
  transition: opacity .3s var(--ease-soft),
              transform .38s var(--ease-out),
              filter .3s ease,
              visibility 0s;
}
.rot-item--pill {
  display: inline-flex;
  align-items: center;
  gap: .3em;
  padding: .12em .4em .14em;
  border: 1px solid var(--line);
  border-radius: .22em;
  background: var(--face);
  box-shadow: var(--shadow-1);
}
.rot-item--pill img { width: .78em; height: .78em; object-fit: contain; display: block; }
.rot-item--pill b { font-size: .8em; font-weight: var(--wt-title); letter-spacing: .05em; text-transform: uppercase; }

/* No JS, no cycling — the first label is simply the one that shows,
   and the line still reads as a complete sentence. */
.no-js .rot-item:first-child { opacity: 1; visibility: visible; transform: none; filter: blur(0); }

@media (max-width: 42rem) {
  .rot-line { flex-wrap: wrap; gap: .15em .3em; }
  .rot-with { margin-bottom: 1rem; }
}

/* Nothing cycles, nothing blurs. main.js also skips starting the
   timer, so the line is static rather than mid-transition. */
@media (prefers-reduced-motion: reduce) {
  .rot, .rot-item { transition: none; }
}

/* ============================================================
   THE MACBOOK — §1's picture. Ported 2026-09-02 from Rico's Claude
   Design canvas "MacBook hero screenshot recreation" (artboard
   "MacBook Neo Brain"): his recreation of Apple's MacBook Neo hero,
   the laptop held up on one hand with Claude's empty state on the
   screen.

   His build, kept exactly. One 661×360 photo of an open MacBook is
   used twice: the lid (top 283 rows) squashed to 94% from its
   bottom edge so the screen reads as seen from slightly above, and
   the keyboard deck (bottom 77 rows) tipped back 58° inside a
   1400px perspective so it foreshortens into a thin base. Multiply
   blend drops the photo's white ground onto the paper — there is no
   cut-out to maintain. The Claude window is drawn at 1440×854 and
   scaled onto the lid: the canvas scaled it by a fixed .28125, here
   tan(atan2()) divides the lid's real width by 1440 so the window
   fits whatever the lid measures. (Not container-query units on the
   text itself: Chrome bumps any relative font-size under 6px up to
   6px, which at 4px-per-label would have burst the input box. A
   transform is the one scaling Chrome leaves alone — same as the
   artboard.)

   Fluid where the canvas was a fixed 661px: every dimension is an
   aspect-ratio or a percentage of the stage, so the laptop is the
   same shape at 320 as at 1440. The 727px cap is the artboard's
   661 × its zoom of 1.1.

   The hand is not positioned, on purpose. The blend group is a
   stacking context and paints above in-flow content, so the
   fingertips vanish behind the deck — that is the hold, and it is
   the same paint order the artboard had.
   ============================================================ */
/* The MINIMUM air between the hook and the laptop. The hero is one screen
   with the laptop pinned to its bottom edge (align-content: space-between),
   so the real air is whatever the screen leaves — ~120px at 1440×900. This
   margin only bites when the screen is short, and then --mac-w shrinks the
   laptop to keep it. */
.mac { width: 100%; margin-top: var(--mac-air, 3rem); }
.mac-stage {
  position: relative;
  width: min(100%, var(--mac-w, 727px));
  margin-inline: auto;
  /* Neither a container nor a background here, and both were tried.
     Multiply blends against the backdrop of the nearest stacking context.
     container-type on the stage (for a cqw-based perspective) made the
     stage one, so the blend met transparency and the photo's white ground
     came through as a white box. A paper fill on the stage fixed that and
     showed instead as a lighter rectangle, because the body's paper has
     grain and a flat fill does not. So: nothing between here and <body>
     makes a stacking context, and the white meets the real page. */
}
.mac-body {
  position: relative;
  /* 1400 / 661 of the stage, from the same --mac-w the stage is sized by,
     so the two cannot drift — and without a container query (see
     .mac-stage). The scrollbar's few pixels inside 100vw move it well
     under 1%. */
  perspective: calc(2.118 * var(--mac-w, 727px));
  mix-blend-mode: multiply;
}
.mac-lid,
.mac-base {
  display: block;
  width: 100%;
  height: auto;
  object-fit: cover;
}
.mac-lid {
  aspect-ratio: 661 / 283;
  object-position: 0 0;
  transform: scaleY(.94);
  transform-origin: 50% 100%;
}
.mac-base {
  aspect-ratio: 661 / 77;
  object-position: 50% 100%;
  transform: rotateX(-58deg);
  transform-origin: 50% 0;
}
/* Same transform as the lid, so the screen sits where the glass is. */
/* height:0 + padding-bottom, not aspect-ratio: this box and the screen
   inside it are absolutely positioned, and iOS Safari rendered the screen
   blank (Rico, from his phone, 2026-09-02) — an aspect-ratio height on an
   absolutely positioned box is exactly where engines still disagree. A
   percentage padding resolves against the containing block's WIDTH in
   every engine, so the ratio is the same and the height is never in doubt. */
.mac-screen-wrap {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 0;
  padding-bottom: 42.813%;          /* 283 / 661 */
  transform: scaleY(.94);
  transform-origin: 50% 100%;
}
/* 124,33 · 405×240 of the 661×283 lid. The top offset is a margin because a
   percentage margin resolves against the WIDTH, which is definite everywhere;
   a percentage `top` would need the aspect-ratio height to be. */
.mac-screen {
  position: absolute;
  top: 0;
  left: 18.759%;
  width: 61.271%;
  margin-top: 4.992%;
  height: 0;
  padding-bottom: 36.309%;          /* 240 / 661 — against the wrap's width, like the width above */
  overflow: hidden;
  background: #faf9f5;
  container-type: inline-size;
}
.mac-ui {
  position: absolute;
  top: 0;
  left: 0;
  width: 1440px;
  height: 854px;
  transform-origin: 0 0;
  transform: scale(.309);                          /* 445 / 1440 — the lid at the 727px cap, for engines without the line below */
  transform: scale(tan(atan2(100cqw, 1440px)));    /* the lid's width / 1440, wherever the lid is */
  /* main.js also measures the screen and writes the same scale inline (a
     ResizeObserver), so an engine that drops the trig line still fits the
     window to the lid instead of showing a desktop-sized corner of it. */
  font-family: var(--sans);
  font-size: 15px;
  font-weight: 450;
  line-height: 1.2;
  color: #1c1a17;
}
.mac-ui-nav { position: absolute; top: 20px; left: 20px; display: flex; align-items: center; gap: 22px; }
.mac-ui-nav svg { display: block; width: 22px; height: 22px; }
.mac-ui-side { position: relative; display: flex; }
.mac-ui-side i { position: absolute; top: -4px; right: -4px; width: 7px; height: 7px; border-radius: 999px; background: #2f7bf6; }
.mac-ui-ghost { position: absolute; top: 18px; right: 22px; width: 26px; height: 26px; }
.mac-ui-main { position: absolute; top: 268px; left: 0; right: 0; display: flex; flex-direction: column; align-items: center; }
.mac-ui-greet {
  display: flex;
  align-items: center;
  gap: 14px;
  margin: 0;
  max-width: none;
  font-family: Georgia, "Times New Roman", serif;
  font-size: 44px;
  font-weight: 400;
  letter-spacing: -.01em;
  line-height: 1.15;
  color: #1c1a17;
  white-space: nowrap;
}
.mac-ui-greet img { display: block; width: 42px; height: 42px; }
.mac-ui-box {
  box-sizing: border-box;
  width: 628px;
  margin-top: 58px;
  padding: 18px 20px 14px;
  border: 1px solid rgba(28, 26, 23, .10);
  border-radius: 14px;
  background: #fff;
  box-shadow: 0 1px 2px rgba(28, 26, 23, .04);
  text-align: left;
}
.mac-ui-box > p { margin: 0 0 26px; max-width: none; font-size: 17px; line-height: 1.2; color: #a7a29a; }
.mac-ui-row { display: flex; align-items: center; justify-content: space-between; }
.mac-ui-row > span { display: flex; align-items: center; gap: 12px; }
.mac-ui-row > span + span { gap: 10px; }
.mac-ui-row svg { display: block; }
.mac-ui-plus { width: 20px; height: 20px; }
.mac-ui-mic { width: 18px; height: 18px; }
.mac-ui-chev { width: 14px; height: 14px; }
.mac-ui-toggle { display: flex; align-items: center; gap: 2px; padding: 2px; border-radius: 8px; background: #f0eee8; }
.mac-ui-toggle b { padding: 4px 10px; border: 1px solid rgba(28, 26, 23, .08); border-radius: 6px; background: #fff; font-weight: inherit; }
.mac-ui-toggle i { padding: 4px 10px; }
.mac-ui-toggle i,
.mac-ui-muted { font-style: normal; color: #a7a29a; }
.mac-ui-home { position: absolute; bottom: 16px; left: 50%; width: 58px; height: 6px; border-radius: 999px; background: #b5b1a8; transform: translateX(-50%); }
/* 500px box at 661 held 575px of hand out of a 1078px image → 40.35% of the
   stage; the box was centred, and the hand sat 2px off its centre — ignored.
   The artboard lifted it -62/661 into the deck; this is 6px less at the
   727px cap (Rico, 2026-09-02: "the fingers seem to be inside the mac, mac
   needs to rise a bit"). The deck's light front lip is translucent under
   the multiply blend — light grey × skin shows the skin — so a nail that
   sits behind the lip shows THROUGH it. With the tips just under the lip's
   edge, nothing does. The bottom margin pulls the wrist past the hero's
   edge, where overflow:hidden crops it like the artboard's frame. */
.mac-hand {
  display: block;
  width: 40.35%;
  height: auto;
  margin: -8.55% auto -3.5%;
}

/* ============================================================
   THE VAULT SPHERE — §2's picture.
   Ported 2026-08-30 from Rico's Claude Design artboard
   "Vault Sphere Hero.dc.html". Three things were deliberately NOT
   carried across:

   1. The `_ds` bundle it shipped with — a second copy of Hanken
      Grotesk, a token set and a 41KB JS bundle, all of which this
      stylesheet already has. The site is zero-dependency and stays
      that way; the artboard's var() names map onto ours already
      (--ink-2 IS --graphite here).
   2. The supplied blue raster folder asset. Its provenance was unclear,
      so the served file was removed. The folder is an original
      inline stroke SVG on the site's one icon spec instead, so it takes
      --ink like every other glyph and costs no request.
   3. The card's 760px height. It was built as a full-bleed hero;
      here it is one section among nine.

   What IS his: the Fibonacci dot sphere, the depth fade, the chips
   emerging from the centre, and the positions they settle into.
   ============================================================ */
/* No card. Rico, 2026-08-30: "take off the grey backround from the sphere."
   The --face panel, its hairline and its shadow are gone, and so is the
   radial glow that sat behind the dots — both were grey on paper. The sphere
   now sits directly on the page, cropped by an invisible boundary, which is
   what the composio reference does. `overflow: hidden` stays: it is the only
   thing still doing the cropping. */
.sphere-block {
  display: grid;
  grid-template-columns: minmax(0, 36fr) minmax(0, 64fr);
  /* Top-aligned, not centred: "push it more up". Centring hung the copy off
     the dome's midpoint, which put it level with the sphere's widest part
     instead of level with its top. */
  align-items: start;
  gap: clamp(1.5rem, 4vw, 3.5rem);
  position: relative;
  /* Bottom padding is zero and the row list below has its top margin
     removed, so the dome's cut edge sits exactly on the hairline above the
     rows — the sphere is cut by a line that is visibly there, which is what
     the composio reference does. The clipping itself happens on
     .sphere-stage, not here.

     Top padding opened up 2026-08-30 — "make the section a bit longer, not
     so tight". The block's height is driven by the dome, and a 54%-tall
     dome is a short box; the copy beside it was being squeezed into it. */
  padding-block: clamp(2.5rem, 5vw, 4.5rem) 0;
}
.sphere-copy { position: relative; z-index: 2; }
.sphere-copy .micro { display: flex; align-items: center; gap: .5rem; color: var(--graphite); }
.sphere-copy .micro svg { width: .95rem; height: .95rem; fill: none; stroke: currentColor; stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; }
.sphere-copy .section-title { margin-top: 1.15rem; }
.sphere-copy .section-copy { margin-top: 1.1rem; }

/* THE DOME. The stage is a window 54% as tall as it is wide; the sphere
   inside it is a full square, top-aligned, so the bottom 46% is cut.

   This was a negative margin first and that was a bug: percentage margins
   resolve against the CONTAINING BLOCK's width, and once the layout stacks
   the container is the full section rather than the sphere — so the same
   -46% ate 80% of the sphere at 768px and 46% at 1440px. Sizing the window
   off its own width instead is width-independent by construction. */
.sphere-stage {
  position: relative;
  justify-self: end;
  align-self: end;
  width: min(100%, 48rem);
  aspect-ratio: 1 / .54;
  margin-top: clamp(1rem, 3vw, 2.5rem);
  overflow: hidden;
}
.sphere-inner {
  position: absolute;
  inset: 0 0 auto 0;
  aspect-ratio: 1;
}
.sphere-dots {
  position: absolute;
  inset: 0;
  color: var(--graphite);
  pointer-events: none;
  /* No-JS fallback: a quiet dotted disk holds the composition before the
     progressively enhanced canvas exists. */
  border-radius: 50%;
  background-image: radial-gradient(circle, currentColor 1px, transparent 1.4px);
  background-size: 17px 17px;
  opacity: .38;
  -webkit-mask-image: radial-gradient(circle, #000 0 67%, transparent 68%);
  mask-image: radial-gradient(circle, #000 0 67%, transparent 68%);
}
.js .sphere-dots { background-image: none; opacity: 1; -webkit-mask-image: none; mask-image: none; }
.sphere-dots canvas { display: block; width: 100%; height: 100%; }

.sphere-chips { position: absolute; inset: 0; pointer-events: none; }
.sphere-chips li {
  position: absolute;
  top: 50%;
  left: 50%;
  display: flex;
  align-items: center;
  gap: .55rem;
  padding: .5rem .85rem .5rem .5rem;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: var(--paper);
  box-shadow: var(--shadow-1);
  white-space: nowrap;
  font-family: var(--font-mono);
  font-size: .72rem;
  color: var(--ink);
  animation: chipSlot 12s ease-in-out infinite;
  --x: 50%;
  --y: 50%;
}
.js .sphere-chips li { animation-play-state: paused; }
.js .sphere-block.is-active .sphere-chips li { animation-play-state: running; }
.sphere-chips img {
  flex: none;
  width: 1.15rem;
  height: 1.15rem;
  object-fit: contain;
  display: block;
}

/* One keyframe, six destinations — each chip carries its own --x/--y.
   Rico's artboard wrote six near-identical @keyframes blocks; custom
   properties collapse that to one. The hold is wider than his (8→50%
   rather than 11→33%) so two or three chips are up at once, which is
   what the reference he sent actually looks like. */
@keyframes chipSlot {
  0%, 58%, 100% { top: 50%; left: 50%; opacity: 0; transform: translate(-50%, -50%) scale(.25); }
  8%, 50%       { top: var(--y); left: var(--x); opacity: 1; transform: translate(-50%, -50%) scale(1); }
}
.sphere-chips li:nth-child(1) { --y: 3%;  --x: 55%; animation-delay: 0s; }
.sphere-chips li:nth-child(2) { --y: 16%; --x: 81%; animation-delay: -2s; }
.sphere-chips li:nth-child(3) { --y: 31%; --x: 28%; animation-delay: -4s; }
.sphere-chips li:nth-child(4) { --y: 40%; --x: 74%; animation-delay: -6s; }
.sphere-chips li:nth-child(5) { --y: 46%; --x: 47%; animation-delay: -8s; }
.sphere-chips li:nth-child(6) { --y: 24%; --x: 68%; animation-delay: -10s; }

@media (max-width: 62rem) {
  .sphere-block { grid-template-columns: minmax(0, 1fr); }
  .sphere-stage {
    justify-self: center;
    width: min(100%, 26rem);
    margin: clamp(1rem, 4vw, 2rem) auto 0;
  }
  .sphere-chips li { font-size: .64rem; padding: .4rem .65rem .4rem .4rem; }
}
@media (max-width: 34rem) {
  .sphere-chips li:nth-child(2),
  .sphere-chips li:nth-child(4) { display: none; }
}

/* Everything stops. The sphere holds one painted frame (main.js draws it
   once and does not start the loop) and the chips sit where they land. */
@media (prefers-reduced-motion: reduce) {
  .sphere-chips li {
    animation: none;
    top: var(--y);
    left: var(--x);
    opacity: 1;
    transform: translate(-50%, -50%);
  }
}

/* ============================================================
   THE FLOW — §4's picture: everything you already have, converging
   down into one vault you own.
   Ported 2026-08-30 from Rico's "Integrations Diagram Sideways"
   artboard. Same three rules as the sphere: no `_ds` bundle, no
   `transform: scale()` on a fixed artboard, and the marks come
   from this site's own `assets/marks/` set rather than the
   artboard's stock downloads — seven of the eight sources plus
   Claude were already shipping in the demo above.

   The artboard fit itself by scaling a 1600x1060 board, which is
   the exact trick DESIGN-SYSTEM.md forbids. Here the lines are one
   SVG with a viewBox (so they scale exactly) and the nodes are HTML
   positioned in percentages of the same coordinate space, so the
   two stay registered at every width without a single fixed pixel.

   The sweep is CSS `stroke-dashoffset` on paths carrying
   `pathLength="100"`, not the artboard's SMIL gradients: normalised
   path length means one keyframe drives curves of different real
   lengths, and unlike SMIL it stops for prefers-reduced-motion.
   ============================================================ */
.flow-wrap { margin-block: clamp(1.5rem, 4vw, 3rem); }
.flow {
  position: relative;
  width: 100%;
  aspect-ratio: 1600 / 520;
}
.flow-svg { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; }
.flow-svg--tall { display: none; }

.flow-line { fill: none; stroke: var(--graphite); stroke-opacity: .32; stroke-width: 1.4; }
.flow-line--main { stroke-opacity: .45; stroke-width: 1.6; }

/* THE TRAVELLING LIGHT.
   A long, blurred segment sliding along each line — not a dot. The
   blur is what does the work: it feathers both ends of the dash into
   nothing, so what you see is a glow moving rather than a dash with
   two edges. Kept deliberately faint (.34 against the hairline's .32)
   because the brief was "very subtle and lightly", and anything
   brighter here competes with the demo panel two sections above.

   This started as an SVG luminance mask sliding a gradient band over
   the paths, which is the more elegant version and is what the
   original artboard did with SMIL. It was removed because it could
   not be verified in this environment — a control render with an
   all-black mask (which must hide everything) still drew 91% of the
   ink, so the mask was not resolving reliably. Blur plus a long dash
   is the version whose behaviour is fully readable from computed
   style, and it degrades to nothing on `prefers-reduced-motion`. */
.flow-pulse {
  fill: none;
  stroke: var(--ink);
  stroke-opacity: .34;
  stroke-width: 2.2;
  stroke-linecap: round;
  /* A 30-unit light and a 170-unit gap: the pattern repeats every 200, and
     the path is 100, so only ONE light can ever be on a line. The old
     `30 100` repeated every 130 — while a light was leaving into the cube,
     the next repeat was already sliding in at the icon, then fading out
     sitting there. That was the flicker Rico saw (2026-09-02). */
  stroke-dasharray: 30 170;
  filter: blur(3px);
  opacity: 0;
}
/* Eased, not linear: a light that leaves the icon gently and settles into
   the cube reads as a signal, a constant-speed dash reads as a loading bar. */
.js .flow-wrap.is-active .flow-pulse { animation: flowLight 5.2s cubic-bezier(.42, 0, .58, 1) var(--flow-delay, 0s) infinite; }
/* Staggered, so six lights never leave in lockstep. The pulses are paths 7–12
   of each board (after the six hairlines), hence nth-of-type from 7. The
   offsets are deliberately uneven — an even ladder reads as a sequence.
   Carried as a custom property because the shorthand above would otherwise
   reset animation-delay to 0s by specificity. */
.flow-pulse:nth-of-type(7)  { --flow-delay: 0s; }
.flow-pulse:nth-of-type(8)  { --flow-delay: -2.1s; }
.flow-pulse:nth-of-type(9)  { --flow-delay: -3.7s; }
.flow-pulse:nth-of-type(10) { --flow-delay: -.9s; }
.flow-pulse:nth-of-type(11) { --flow-delay: -2.9s; }
.flow-pulse:nth-of-type(12) { --flow-delay: -1.5s; }

/* pathLength="100" normalises every curve, so one keyframe drives six
   paths of different real lengths at the same apparent speed. Offset 200
   puts the light's tail at the icon; 70 puts its tail past the end — the
   whole light is inside the cube before the pattern could repeat. It fades
   in as it leaves the icon and out as it enters the cube, then the line
   rests for the last quarter of the cycle. */
@keyframes flowLight {
  0%        { stroke-dashoffset: 200; opacity: 0; }
  10%       { opacity: 1; }
  62%       { opacity: 1; }
  74%       { stroke-dashoffset: 70; opacity: 0; }
  100%      { stroke-dashoffset: 70; opacity: 0; }
}

/* Positions live here, not inline on the nodes — an inline --x/--y would
   win over the phone media query and the two rows would never happen. */
.flow-node:nth-of-type(1) { --x: 8.75%;  --y: 12.69%; }
.flow-node:nth-of-type(2) { --x: 25.25%; --y: 12.69%; }
.flow-node:nth-of-type(3) { --x: 41.75%; --y: 12.69%; }
.flow-node:nth-of-type(4) { --x: 58.25%; --y: 12.69%; }
.flow-node:nth-of-type(5) { --x: 74.75%; --y: 12.69%; }
.flow-node:nth-of-type(6) { --x: 91.25%; --y: 12.69%; }
.flow-node--key           { --x: 50%;    --y: 82.69%; }

/* The nodes are anchors only. They used to be paper cards — border, shadow,
   the logo at 68% inside — and Rico had them off on 2026-09-02 ("remove the
   white squares behind the logos"). The logo now fills the anchor, so the
   diagram keeps its mass; the lines still start 4 units under it. */
.flow-node {
  position: absolute;
  left: var(--x);
  top: var(--y);
  width: 4.75%;
  aspect-ratio: 1;
  transform: translate(-50%, -50%);
  display: grid;
  place-items: center;
}
.flow-node img { width: 90%; height: 90%; object-fit: contain; border-radius: 22%; }
/* No per-mark sizing. logo-word.png used to draw half the size of its
   neighbours (Rico: "make word logo as big as others"): the file carried
   transparent padding on every side, ~48×44 of artwork in a 96×64 canvas,
   where the other marks are trimmed to their pixels. The FILE was trimmed
   on 2026-09-02 — it fixes the demo panel's copy of it too — so it sizes
   like the rest. It is a 48px source, so it is the one mark that is soft
   on a retina screen; a larger Word mark would sharpen it. */
.flow-node--key { width: 6.5%; }
.flow-node--key img { border-radius: 0; }
.flow-cube { width: 60%; height: auto; }
.flow-cube polygon:nth-child(1) { fill: var(--ink); }
.flow-cube polygon:nth-child(2) { fill: var(--cube-grey); }
.flow-cube polygon:nth-child(3) { fill: var(--paper); stroke: var(--ink); stroke-width: 2.4; stroke-linejoin: round; }


.flow-caption { margin-top: 1rem; color: var(--graphite); font-size: .8rem; }

/* PHONE BOARD. The wide one scrolled sideways inside its own box — it
   overhung a 338px rail by 334px and rendered 32px icons, which is a diagram
   nobody reads. Below the breakpoint it reflows instead: the same six
   sources in two rows of three, converging into the cube underneath, on a
   720x620 board rather than 1600x520. Icons land at ~47px and the whole
   thing fits the rail with no scrolling. Desktop is untouched.

   Both boards ship; only one is ever displayed. They are 12 hairlines and
   12 lit copies of pure path data — no images, no script, nothing to load. */
@media (max-width: 56rem) {
  .flow-wrap { overflow: visible; }
  /* Capped and centred: at 768 the phone board ran the full 707px rail and
     rendered 98px icons, which is a diagram shouting. */
  .flow { width: min(100%, 27rem); margin-inline: auto; aspect-ratio: 720 / 620; }
  .flow-svg--wide { display: none; }
  .flow-svg--tall { display: block; }

  .flow-node { width: 13.89%; }
  .flow-node--key { width: 18.06%; }

  /* two rows of three, in the phone board's coordinate space */
  .flow-node:nth-of-type(1) { --x: 18.06%; --y: 11.29%; }
  .flow-node:nth-of-type(2) { --x: 50%;    --y: 11.29%; }
  .flow-node:nth-of-type(3) { --x: 81.94%; --y: 11.29%; }
  .flow-node:nth-of-type(4) { --x: 18.06%; --y: 32.26%; }
  .flow-node:nth-of-type(5) { --x: 50%;    --y: 32.26%; }
  .flow-node:nth-of-type(6) { --x: 81.94%; --y: 32.26%; }
  .flow-node--key           { --x: 50%;    --y: 77.42%; }
}

/* Nothing moves, and the lit copies stay hidden — only the static
   hairlines remain, which is the whole diagram minus the motion. */
@media (prefers-reduced-motion: reduce) {
  .js .flow-pulse { animation: none; opacity: 0; }
}

/* ══════════════════════════════════════════════════════════════════════════
   THE ASK — showcase panel on /business-brain
   Ported 2026-08-30 from the Claude Design canvas "Business Brain on Claude".

   Two deliberate departures from the canvas:

   1. NO transform:scale(). The canvas is a 1720×1008 artboard that fits
      itself by scaling. That renders 10px mono type at 2px on a phone. Here
      the thing reflows instead — three columns → one, each at native size.

   2. The resolved state is the DEFAULT. Every "in progress" look is scoped
      under .bb.is-live, which only JS adds. No JS, no fixed width, no
      motion budget → the reader still gets the finished answer.

   Colours come from the page tokens; the canvas hex values map as
   #1f2025 → --ink · #64666e → --graphite · #8e9099 → --cube-grey
   #a3a4ab → --graphite-light · #c93c2a → --err · #f2f1ee → --face
   ══════════════════════════════════════════════════════════════════════════ */

.bb {
  /* Host card + bubble. JS swaps these three as the host cycles
     Claude → ChatGPT → Cursor; they are the only per-host paint. */
  --bb-card: #fdfaf4;
  --bb-bubble: #f4f0e6;
  --bb-dim: color-mix(in oklab, var(--ink) 78%, var(--paper));
  --bb-gap: .75rem;
  --bb-r: 18px;
  --bb-lift: 0 3px 8px oklch(20% .01 265 / .09), 0 40px 70px -22px oklch(20% .01 265 / .38);

  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--bb-gap);
  font-size: 1rem;
}

/* ── the frame ─────────────────────────────────────────────────────────── */
/* Phone and tablet: the conversation, and nothing else (Rico, 2026-08-30).
   The four side panels are the desktop frame around it; stacked into one
   narrow column they turn into a long scroll of chrome the reader has to get
   past to reach the thing they came for. display:none, not a visual hide, so
   they leave the accessibility tree with the layout. */
@media (max-width: 74.99rem) {
  /* Scoped through .bb on purpose: `.bb-panel { display: flex }` is declared
     further down, and a bare `.bb-src` ties it on specificity and loses on
     source order. */
  .bb .bb-src,
  .bb .bb-tools,
  .bb .bb-gates,
  .bb .bb-vault { display: none; }

  /* On its own the chat would stretch the full shell — an answer set 1100px
     wide is ~130 characters a line. Capped and centred it keeps a readable
     measure and still reads as a real chat window. */
  .bb-chat { max-width: 44rem; margin-inline: auto; width: 100%; }

  /* The run plays here too, so the frame is pinned exactly as the desktop one
     is: a growing answer must never push the page around under the reader.
     Pinned, the conversation scrolls inside its own box the way a real chat
     does, and the section stops being a 1,250px wall of resolved text. */
  .bb.is-live { height: clamp(28rem, 82vh, 42rem); }
}

@media (min-width: 75rem) {
  .bb {
    /* At rest: min-height, so the resolved answer — which is longer than the
       frame — pushes the box taller instead of being cut off. While playing:
       a definite height (below), because the chat can only scroll its content
       under the mask if it has one. */
    min-height: clamp(40rem, 86vh, 60rem);
    grid-template-columns: 19rem minmax(0, 1fr) 19rem;
    grid-template-rows: minmax(0, 1fr) auto auto;
    grid-template-areas:
      "src chat tools"
      "src chat gates"
      "src chat vault";
  }
  .bb.is-live { height: clamp(40rem, 86vh, 60rem); }
  .bb-src   { grid-area: src; }
  .bb-chat  { grid-area: chat; }
  .bb-tools { grid-area: tools; }
  .bb-gates { grid-area: gates; }
  .bb-vault { grid-area: vault; }
}

/* A section head with no deck beside it. The two-column head exists to pair a
   title with its copy; with the copy gone the title should sit in the middle
   of the page rather than hang off the left edge of an empty grid. Used by the
   showcase, whose block is centred too. */
.section-head--mid {
  grid-template-columns: minmax(0, 1fr);
  justify-items: center;
  text-align: center;
}
/* Sized down from the page's display step and given a wider measure, so the
   line lands in two rather than three. At 13ch (the default) or 20ch it broke
   three ways and read as a wall. */
.section-head--mid .section-title {
  max-width: 24ch;
  font-size: clamp(2.3rem, 4.4vw, 3.9rem);
}

/* ── the cables ────────────────────────────────────────────────────────────
   From the canvas: a two-rail ribbon with a pulse running along it, sitting
   in the column gutter and lighting only for the panel the run is touching
   right now. That "which card is in use" signal was the one part of the
   canvas the first port dropped. Desktop only — there is no gutter to draw
   in on a phone, where the panels do not render at all. */
.bb-cable { display: none; }

/* ── side panels ───────────────────────────────────────────────────────── */
.bb-panel {
  min-height: 0;
  padding: .95rem 1.15rem;
  border: 1px solid var(--line);
  border-radius: var(--bb-r);
  background: var(--face);
  box-shadow: var(--shadow-1);
  display: flex;
  flex-direction: column;
  transition:
    border-color .4s var(--ease-soft), box-shadow .55s var(--ease-soft),
    transform .55s var(--ease-soft), background .55s var(--ease-soft),
    filter .55s var(--ease-soft);
}
/* Dimmed until the run touches them — but only once JS is driving. */
.bb.is-live .bb-panel { filter: grayscale(.6) opacity(.8); }
.bb.is-live .bb-panel.is-lit {
  filter: none;
  background: color-mix(in oklab, var(--paper) 55%, #fff);
  border-color: var(--line-strong);
  box-shadow: var(--bb-lift);
  transform: scale(1.025);
}

.bb-panel__head { display: flex; align-items: baseline; justify-content: space-between; gap: .6rem; }
.bb-panel__head h3 {
  margin: 0;
  font-family: var(--font-mono);
  font-size: .655rem;
  font-weight: 600;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: var(--ink);
}
.bb-panel__note {
  font-family: var(--font-mono);
  font-size: .625rem;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--cube-grey);
  white-space: nowrap;
  transition: color .35s;
}
.bb-panel__lede { margin: .5rem 0 0; font-size: .81rem; line-height: 1.5; color: var(--bb-dim); }
.bb-panel__lede b { font-weight: 660; color: var(--ink); }

/* Corner ticks — the canvas' "this one is active" marker. */
.bb-panel { position: relative; }
.bb-panel::before,
.bb-panel::after {
  content: "";
  position: absolute;
  width: 14px; height: 14px;
  opacity: 0;
  transition: opacity .35s;
  pointer-events: none;
}
.bb-panel::before { left: -7px; top: -7px; border-left: 1.5px solid currentColor; border-top: 1.5px solid currentColor; }
.bb-panel::after { right: -7px; bottom: -7px; border-right: 1.5px solid currentColor; border-bottom: 1.5px solid currentColor; }
.bb.is-live .bb-panel.is-lit::before,
.bb.is-live .bb-panel.is-lit::after { opacity: 1; }
.bb-gates { color: var(--err); }
.bb-gates .bb-panel__head h3 { color: var(--ink); }

/* ── left · what it is reading ─────────────────────────────────────────── */
.bb-srcs { list-style: none; margin: .7rem 0 0; padding: 0; display: flex; flex-direction: column; gap: .375rem; min-height: 0; }
.bb-srcs > li { border: 1px solid var(--line); background: var(--paper); border-radius: 14px; padding: .6rem .9rem; }
.bb-srcs__top { display: flex; align-items: center; gap: .6rem; margin: 0; }
.bb-srcs__top svg { flex: none; width: 15px; height: 15px; fill: none; stroke: var(--ink); stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; opacity: .7; }
.bb-srcs__top b { flex: 1; font-size: .875rem; font-weight: 660; letter-spacing: -.014em; }
.bb-srcs__top i { font-family: var(--font-mono); font-style: normal; font-size: .655rem; color: var(--graphite); font-variant-numeric: tabular-nums; white-space: nowrap; }
.bb-srcs__sub { margin: .3rem 0 0; font-size: .75rem; line-height: 1.5; color: var(--graphite); }
.bb-srcs__logos {
  display: flex; align-items: center; gap: .55rem;
  margin: .5rem 0 0; padding-top: .5rem;
  border-top: 1px solid color-mix(in oklab, var(--ink) 10%, transparent);
  min-height: 21px;
  transition: opacity .5s var(--ease-out), transform .5s var(--ease-out);
}
.bb-srcs__logos img { height: 19px; width: auto; display: block; }
.bb-srcs__logos .bb-db { width: 19px; height: 19px; fill: none; stroke: var(--ink); stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; opacity: .8; }
.bb.is-live .bb-srcs__logos { opacity: 0; transform: translateY(5px) scale(.9); }
.bb.is-live .bb-srcs__logos.is-on { opacity: 1; transform: none; }

/* ── right · the tools ─────────────────────────────────────────────────── */
.bb-tools__view {
  margin-top: .7rem;
  flex: 1;
  min-height: 0;
  overflow: hidden;
  -webkit-mask-image: linear-gradient(180deg, transparent 0, #000 16px, #000 calc(100% - 20px), transparent 100%);
  mask-image: linear-gradient(180deg, transparent 0, #000 16px, #000 calc(100% - 20px), transparent 100%);
}
.bb-tools__list { list-style: none; margin: 0; padding: .25rem 0 .6rem; display: flex; flex-direction: column; gap: .5rem; }

.bb-tools__list > li {
  display: flex; align-items: center; gap: .6rem;
  border: 1px solid color-mix(in oklab, var(--ink) 10%, transparent);
  border-radius: 12px;
  padding: .62rem .8rem;
  color: var(--graphite);
  transition: border-color .35s var(--ease-soft), background .35s var(--ease-soft), color .35s var(--ease-soft);
}
.bb-tools__list svg { flex: none; width: 14px; height: 14px; fill: none; stroke: currentColor; stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; opacity: .75; }
.bb-tools__list b { flex: 1; font-size: .845rem; font-weight: 620; letter-spacing: -.012em; color: var(--ink); }
.bb-tools__list i { font-family: var(--font-mono); font-style: normal; font-size: .625rem; color: var(--cube-grey); white-space: nowrap; }
/* The five the run actually used, once it has searched. */
.bb.is-live.is-searched .bb-tools__list > li.is-hit {
  border-color: var(--line-strong);
  background: var(--face);
  color: var(--ink);
}
.bb.is-live.is-searched .bb-tools__list > li.is-hit i { color: var(--graphite); }
/* Slow pan so all ten are readable in a 4-item window. Paused off-screen. */
@media (min-width: 75rem) {
  .bb.is-live:not(.is-paused) .bb-tools__list { animation: bbPan 34s var(--ease-soft) infinite alternate; }
  .bb.is-live.is-paused .bb-tools__list { animation-play-state: paused; }
}
@keyframes bbPan { 0%, 12% { transform: translateY(0); } 88%, 100% { transform: translateY(var(--bb-pan, -14rem)); } }

/* ── right · the walls ─────────────────────────────────────────────────── */
.bb-nevers { list-style: none; display: flex; flex-wrap: wrap; gap: .375rem; margin: .8rem 0 0; padding: 0; }
.bb-nevers > li {
  font-family: var(--font-mono);
  font-size: .69rem;
  border: 1px solid color-mix(in oklab, var(--ink) 14%, transparent);
  color: var(--graphite);
  border-radius: 999px;
  padding: .25rem .7rem;
  transition: all .35s var(--ease-soft);
}
/* At rest the resolved state is "the gate already fired", so the chip is
   marked to match the note under it. While playing, it lights on cue. */
.bb:not(.is-live) .bb-never--live,
.bb.is-priced .bb-never--live {
  border-color: color-mix(in oklab, var(--err) 50%, transparent);
  background: color-mix(in oklab, var(--err) 10%, transparent);
  color: var(--err);
  font-weight: 600;
}
.bb-fired {
  display: flex; align-items: flex-start; gap: .55rem;
  margin-top: .8rem; padding-top: .7rem;
  border-top: 1px solid color-mix(in oklab, var(--ink) 14%, transparent);
  transition: opacity .5s var(--ease-out);
}
.bb.is-live .bb-fired { opacity: .16; }
.bb.is-live.is-priced .bb-fired { opacity: 1; }
.bb-fired__mark { font-family: var(--font-mono); font-size: .78rem; color: var(--err); line-height: 1.3; }
.bb-fired__t { margin: 0; font-family: var(--font-mono); font-size: .72rem; color: var(--err); line-height: 1.45; }
.bb-fired__s { margin: .18rem 0 0; font-size: .78rem; color: var(--bb-dim); line-height: 1.5; }

/* ── right · what it leaves behind ─────────────────────────────────────── */
.bb-kept { list-style: none; margin: .7rem 0 0; padding: 0; display: flex; flex-direction: column; gap: .44rem; transition: opacity .6s var(--ease-out); }
.bb.is-live .bb-kept { opacity: .13; }
.bb.is-live.is-filed .bb-kept { opacity: 1; }
.bb-kept > li {
  display: flex; align-items: center; gap: .6rem;
  border: 1px solid color-mix(in oklab, var(--ink) 14%, transparent);
  border-radius: 11px; background: var(--paper); padding: .55rem .75rem;
}
.bb-kept svg { flex: none; width: 15px; height: 15px; fill: none; stroke: var(--ink); stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; opacity: .7; }
.bb-kept b { flex: 1; font-size: .78rem; font-weight: 400; line-height: 1.4; }
.bb-kept b em { font-style: normal; font-weight: 660; }
.bb-kept i { font-family: var(--font-mono); font-style: normal; font-size: .625rem; color: var(--cube-grey); white-space: nowrap; }

/* ── centre · the conversation ─────────────────────────────────────────── */
.bb-chat {
  min-height: 0;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  border: 1px solid var(--line-strong);
  border-radius: 26px;
  background: var(--bb-card);
  box-shadow: 0 1px 2px oklch(20% .01 265 / .05), 0 22px 50px -26px oklch(20% .01 265 / .34);
  transition: background .9s var(--ease-soft);
}
.bb-chat__head { display: flex; align-items: center; justify-content: center; flex-wrap: wrap; gap: .5rem 1rem; padding: 1.15rem 1.25rem .25rem; flex: none; }
.bb-host { display: flex; align-items: center; gap: .55rem; }
.bb-host b { font-size: clamp(.95rem, 2.6vw, 1.32rem); font-weight: 700; letter-spacing: -.022em; }
.bb-host i { font-size: clamp(.95rem, 2.6vw, 1.32rem); font-style: normal; font-weight: 400; color: var(--cube-grey); letter-spacing: -.015em; }
.bb-host__mark { width: 25px; height: 25px; object-fit: contain; display: block; flex: none; }
.bb-x { font-size: 1.05rem; color: var(--graphite-light); }
.bb-cube { width: 27px; height: 27px; display: block; flex: none; }
.bb-cube polygon { fill: var(--ink); }
.bb-cube .bb-cube__mid { fill: var(--cube-grey); }
.bb-cube .bb-cube__top { fill: var(--paper); stroke: var(--ink); stroke-width: 2.4; stroke-linejoin: round; }
.bb-cube--lg { width: 36px; height: 36px; }

.bb-chat__body {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  gap: 1.1rem;
  padding: 1.35rem 1.25rem .5rem;
  overflow: visible;
}
/* Pinned and clipped ONLY while playing: the turn scrolls up off the top as
   the answer grows, the way a real chat does. At rest nothing is ever cut. */
.bb.is-live .bb-chat__body { overflow: hidden; overflow-anchor: none; }
.bb:not(.is-live) .bb-chat__body { justify-content: flex-start; }

.bb-empty { display: none; flex: 1; flex-direction: column; align-items: center; justify-content: center; gap: .55rem; text-align: center; padding: 0 2rem; }
.bb-empty__marks { display: flex; align-items: center; gap: .7rem; }
.bb-empty__marks img { width: 34px; height: 34px; object-fit: contain; display: block; }
.bb-empty__line { margin: .2rem 0 0; font-size: clamp(1.05rem, 1.5vw, 1.32rem); font-weight: 640; letter-spacing: -.02em; line-height: 1.3; }
.bb-empty__line em { font-style: italic; font-weight: 420; color: var(--graphite); }
.bb-empty__sub { margin: 0; font-family: var(--font-mono); font-size: .69rem; letter-spacing: .1em; text-transform: uppercase; color: var(--cube-grey); }

.bb-turn { display: flex; justify-content: flex-end; transition: opacity .5s var(--ease-out), transform .6s var(--ease-out); }
.bb-bubble {
  margin: 0;
  max-width: 80%;
  background: var(--bb-bubble);
  border: 1px solid color-mix(in oklab, var(--ink) 7%, transparent);
  border-radius: 20px 20px 7px 20px;
  padding: .95rem 1.15rem;
  font-size: clamp(1rem, 1.15vw, 1.19rem);
  line-height: 1.5;
  transition: background .9s var(--ease-soft);
}
.bb-bot { transition: opacity .5s var(--ease-out), transform .6s var(--ease-out); }
.bb-bot__label { display: flex; align-items: center; gap: .5rem; margin: 0; }
.bb-bot__label img { width: 22px; height: 22px; object-fit: contain; display: block; flex: none; }
.bb-bot__label span { font-family: var(--font-mono); font-size: .625rem; font-weight: 600; letter-spacing: .18em; text-transform: uppercase; color: var(--cube-grey); }

/* The three working states stack in ONE grid cell — the box is always as
   tall as the tallest, so swapping them never moves the answer below. */
.bb-trio { display: grid; margin-top: .85rem; }
.bb-trio > * { grid-area: 1 / 1; align-self: start; transition: opacity .4s var(--ease-out); }
.bb-think, .bb-work { opacity: 0; visibility: hidden; }
/* At rest only the summary exists, so the trio collapses to its height and
   leaves no dead gap. While playing all three stay in the grid — that is what
   keeps the box from resizing as they swap. */
.bb:not(.is-live) .bb-think,
.bb:not(.is-live) .bb-work { display: none; }
/* Once the answer starts arriving no call is in flight, so the work card and
   the thinking dots have nothing left to hold space for. Collapsing them at
   that boundary hands ~56px back to the answer — two lines of it on a phone.
   The trio still holds full height through the call phases, which is what
   stops it jumping while the calls swap. */
.bb.is-live.is-answered .bb-think,
.bb.is-live.is-answered .bb-work { display: none; }
.bb-summary { opacity: 1; }
.bb.is-live[data-phase="sent"] .bb-think { opacity: 1; visibility: visible; }
.bb.is-live.is-calling .bb-work { opacity: 1; visibility: visible; }
.bb.is-live:not(.is-answered) .bb-summary { opacity: 0; }

.bb-think { display: flex; align-items: center; gap: .38rem; }
.bb-think i { width: 5px; height: 5px; border-radius: 50%; background: var(--cube-grey); }
.bb.is-live:not(.is-paused) .bb-think i { animation: bbDot 1.1s ease-in-out infinite; }
.bb-think i:nth-child(2) { animation-delay: .16s; }
.bb-think i:nth-child(3) { animation-delay: .32s; }
.bb-think span { font-size: .81rem; color: var(--graphite); margin-left: .38rem; }
@keyframes bbDot { 0%, 100% { opacity: .25; transform: translateY(0); } 40% { opacity: 1; transform: translateY(-2px); } }

.bb-work {
  border: 1px solid var(--line);
  border-radius: 14px;
  background: var(--wash);
  padding: .8rem .95rem;
  transition: opacity .4s var(--ease-out), border-color .35s;
}
.bb.is-refusing .bb-work { border-color: color-mix(in oklab, var(--err) 40%, transparent); }
.bb-work__top { display: flex; align-items: center; justify-content: space-between; gap: .75rem; }
.bb-work__name { display: flex; align-items: center; gap: .55rem; min-width: 0; }
.bb-work__name svg { flex: none; width: 14px; height: 14px; fill: none; stroke: var(--ink); stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }
.bb-work__name b { font-family: var(--font-mono); font-size: .875rem; font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.bb-work__tag { font-family: var(--font-mono); font-size: .655rem; white-space: nowrap; color: var(--ink); }
.bb.is-refusing .bb-work__name svg { stroke: var(--err); }
.bb.is-refusing .bb-work__name b,
.bb.is-refusing .bb-work__tag { color: var(--err); }
.bb-work__detail { margin: .38rem 0 0; font-family: var(--font-mono); font-size: .78rem; line-height: 1.5; color: var(--graphite); }
.bb-work__bar { height: 2px; border-radius: 2px; background: color-mix(in oklab, var(--ink) 10%, transparent); margin-top: .7rem; overflow: hidden; }
.bb-work__bar i { display: block; width: 30%; height: 2px; border-radius: 2px; background: var(--ink); }
.bb.is-live:not(.is-paused) .bb-work__bar i { animation: bbScan 1.1s var(--ease-soft) infinite; }
.bb.is-refusing .bb-work__bar i { background: var(--err); }
@keyframes bbScan { from { transform: translateX(-100%); } to { transform: translateX(320%); } }

.bb-summary {
  display: flex; align-items: center; justify-content: space-between;
  gap: .4rem .75rem;
  flex-wrap: wrap;          /* the run summary is long; on a phone it wraps
                               rather than running out of its own pill */
  margin: 0;
  border: 1px solid var(--line);
  border-radius: 18px;
  background: var(--wash);
  padding: .5rem .95rem;
  font-family: var(--font-mono);
  font-size: clamp(.66rem, 2.1vw, .78rem);
  line-height: 1.45;
  color: var(--bb-dim);
}
@media (min-width: 30rem) { .bb-summary { border-radius: 999px; flex-wrap: nowrap; } }
.bb-summary__t { font-size: .655rem; color: var(--cube-grey); }

/* A row that takes no space until its content is due. grid-template-rows
   0fr→1fr animates height with nothing measured in JS — the canvas needed a
   ResizeObserver and two pixel values for the same effect. */
.bb-grow { display: grid; grid-template-rows: 1fr; transition: grid-template-rows .6s var(--ease-out); }
.bb-grow > * { overflow: hidden; min-height: 0; }
.bb.is-live .bb-grow { grid-template-rows: 0fr; }
.bb.is-live .bb-grow--ans.is-on,
.bb.is-live.is-filed .bb-grow--filed,
.bb.is-live.is-replied .bb-grow--reply { grid-template-rows: 1fr; }

.bb-answer { display: flex; flex-direction: column; margin-top: .35rem; }
.bb-ans {
  margin: 0;
  padding-top: .62rem;
  font-size: clamp(.95rem, 1.05vw, 1.03rem);
  line-height: 1.6;
  text-wrap: pretty;
  transition: opacity .5s var(--ease-out), transform .55s var(--ease-out);
}
.bb-ans b { font-weight: 660; }
.bb.is-live .bb-grow--ans > .bb-ans { opacity: 0; transform: translateY(7px); }
.bb.is-live .bb-grow--ans.is-on > .bb-ans { opacity: 1; transform: none; }
/* Redaction bars — this is a real archive, so the specifics are covered. */
.bb-redact { display: inline-block; height: .7em; width: var(--w, 4em); border-radius: 3px; background: currentColor; opacity: .22; }
.bb-cite { font-family: var(--font-mono); font-size: .69rem; background: var(--wash); border-radius: 5px; padding: .1rem .38rem; white-space: nowrap; }
.bb-flag { font-family: var(--font-mono); font-size: .75rem; color: var(--err); background: color-mix(in oklab, var(--err) 10%, transparent); border-radius: 5px; padding: .1rem .44rem; white-space: nowrap; }

.bb-filed { display: flex; align-items: center; gap: .5rem; margin: 0; padding-top: .75rem; font-family: var(--font-mono); font-size: .69rem; color: var(--graphite); transition: opacity .55s var(--ease-out), transform .6s var(--ease-out); }
.bb.is-live .bb-filed { opacity: 0; transform: translateY(6px); }
.bb.is-live.is-filed .bb-filed { opacity: 1; transform: none; }

/* grid-template-rows 0fr→1fr: height animation with nothing measured. */
.bb-verify { display: grid; grid-template-rows: 1fr; transition: grid-template-rows .75s var(--ease-out); }
.bb.is-live .bb-verify { grid-template-rows: 0fr; }
.bb.is-live.is-verifying .bb-verify,
.bb.is-live.is-verified .bb-verify { grid-template-rows: 1fr; }
.bb-verify__inner { overflow: hidden; min-height: 0; }
.bb-verify__card { margin-top: .95rem; border: 1px solid color-mix(in oklab, var(--ink) 16%, transparent); border-radius: 14px; background: var(--wash); padding: .68rem .88rem; }
.bb-verify__top { display: flex; align-items: center; justify-content: space-between; gap: .75rem; }
.bb-verify__name { display: flex; align-items: center; gap: .55rem; }
.bb-verify__name svg { flex: none; width: 14px; height: 14px; fill: none; stroke: var(--ink); stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }
.bb-verify__name b { font-family: var(--font-mono); font-size: .81rem; font-weight: 500; }
.bb-verify__eta { font-family: var(--font-mono); font-size: .655rem; white-space: nowrap; color: var(--ink); }
.bb-verify__run { display: none; }
.bb.is-live.is-verifying .bb-verify__run { display: block; }
.bb-verify__row { display: flex; align-items: baseline; gap: .55rem; margin: .5rem 0 0; }
.bb-verify__row span { font-family: var(--font-mono); font-size: .72rem; color: var(--cube-grey); }
.bb-verify__row b { flex: 1; font-family: var(--font-mono); font-size: .72rem; font-weight: 400; color: var(--ink); }
.bb-verify__row i { font-family: var(--font-mono); font-style: normal; font-size: .655rem; color: var(--cube-grey); white-space: nowrap; }
.bb-verify__meter { display: flex; align-items: center; gap: .55rem; margin-top: .55rem; }
.bb-dots { display: flex; gap: .25rem; }
.bb-dots i { width: 5px; height: 5px; border-radius: 50%; background: color-mix(in oklab, var(--ink) 18%, transparent); transition: background .3s; }
.bb-dots i.is-on { background: var(--ink); }
.bb-bar { flex: 1; height: 2px; border-radius: 2px; background: color-mix(in oklab, var(--ink) 10%, transparent); overflow: hidden; }
.bb-bar i { display: block; width: var(--bb-v, 100%); height: 2px; border-radius: 2px; background: var(--ink); transition: width .6s var(--ease-out); }
.bb-verify__done { display: flex; align-items: center; justify-content: space-between; gap: .75rem; margin: .5rem 0 0; font-family: var(--font-mono); font-size: .72rem; }
.bb.is-live.is-verifying .bb-verify__done { display: none; }

.bb-reply { margin: 0; padding-top: .95rem; font-size: clamp(.95rem, 1.05vw, 1.03rem); line-height: 1.6; text-wrap: pretty; transition: opacity .55s var(--ease-out), transform .6s var(--ease-out); }
.bb.is-live .bb-reply { opacity: 0; transform: translateY(8px); }
.bb.is-live.is-replied .bb-reply { opacity: 1; transform: none; }

/* ── the composer ──────────────────────────────────────────────────────── */
.bb-input { flex: none; padding: .7rem 1.15rem 1.05rem; }
.bb-input > * { border: 1px solid var(--line); border-radius: 22px; background: var(--wash); }
.bb-input__line { margin: 0; border: 1px solid var(--line); border-radius: 22px 22px 0 0; border-bottom: 0; background: var(--wash); padding: 1rem 1.1rem .2rem; min-height: 26px; font-size: clamp(.95rem, 1.1vw, 1.13rem); line-height: 1.45; }
.bb-input__foot { display: flex; align-items: center; justify-content: space-between; gap: .75rem; border-radius: 0 0 22px 22px; border-top: 0; padding: .5rem 1.1rem .8rem; }
.bb.is-typing .bb-input__line,
.bb.is-typing .bb-input__foot { border-color: color-mix(in oklab, var(--ink) 32%, transparent); }
.bb-ph { color: var(--graphite-light); }
.bb.is-live.is-typing .bb-ph { display: none; }
.bb-caret { display: inline-block; width: 1.5px; height: 18px; background: var(--ink); vertical-align: -3px; opacity: 0; }
.bb.is-live:not(.is-paused).is-typing .bb-caret { opacity: 1; animation: bbCaret 1.1s step-end infinite; }
@keyframes bbCaret { 0%, 49% { opacity: 1; } 50%, 100% { opacity: 0; } }
.bb-input__left { display: flex; align-items: center; gap: .6rem; }
.bb-input__left svg { width: 19px; height: 19px; fill: none; stroke: var(--cube-grey); stroke-width: 1.7; stroke-linecap: round; }
.bb-chip { font-family: var(--font-mono); font-size: .625rem; letter-spacing: .1em; text-transform: uppercase; color: var(--cube-grey); border: 1px solid var(--line); border-radius: 999px; padding: .22rem .62rem; }
.bb-input__right { display: flex; align-items: center; gap: .85rem; }
.bb-model { font-size: .93rem; color: var(--graphite); }
.bb-send { width: 38px; height: 38px; border-radius: 50%; background: color-mix(in oklab, var(--ink) 10%, transparent); display: flex; align-items: center; justify-content: center; transition: background .3s; }
.bb-send svg { width: 16px; height: 16px; fill: none; stroke: var(--cube-grey); stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; transition: stroke .3s; }
.bb.is-armed .bb-send { background: var(--ink); }
.bb.is-armed .bb-send svg { stroke: var(--paper); }

.bb-foot { display: block; margin: 1.15rem 0 0; text-align: center; color: var(--cube-grey); }

/* ── while the question is being typed, the turn is not there yet ──────── */
.bb.is-live.is-typing .bb-empty { display: flex; }
.bb.is-live.is-typing .bb-turn,
.bb.is-live.is-typing .bb-bot { display: none; }
.bb.is-live:not(.is-typing) .bb-turn { opacity: 1; transform: none; }

/* ── motion off ────────────────────────────────────────────────────────── */
@media (prefers-reduced-motion: reduce) {
  .bb *,
  .bb *::before,
  .bb *::after { animation: none !important; transition: none !important; }
}


/* ── desktop scale (Rico, 2026-08-30 — third pass, "a lot smaller") ───────
   The rule here is a HIERARCHY, not a uniform shrink. Rico: "a lot smaller,
   more space, easier to read, but middle card is most important."

   Smaller and easier to read only reconcile if they are applied to different
   things. So the four side panels drop hard — they are reference, read in a
   glance — while the conversation, which is the thing anyone actually reads
   word by word, gives up very little. Shrinking the surroundings is what
   makes the middle read as the subject.

   The tracks are capped rather than filling the shell, and the grid centres:
   the leftover becomes margin, which is the "more space". Tracks stay tight
   against each other so the cables still meet both edges of their gutter.

   Desktop only — phone and tablet show the conversation alone. */
@media (min-width: 75rem) {
  .bb {
    --bb-col: 4rem;                 /* the cable is drawn exactly this long */
    column-gap: var(--bb-col);
    row-gap: 1.25rem;
    justify-content: center;        /* leftover becomes air, not wider cards */
    /* Tracks 70rem -> 76rem and the gutter 48px -> 64px: the three parts sit
       further apart and the whole block is wider, without any of them growing
       enough to undo the third scale pass. */
    grid-template-columns:
      minmax(13rem, 17rem)
      minmax(0, 34rem)
      minmax(13rem, 17rem);
    /* Floor measured at the worst case (1280 wide, where the rail is
       narrowest and its content wraps tallest). Tools is the grid's only
       flexible row and absorbs all of that variance — below the floor it
       renders as a header with nothing under it. */
    min-height: clamp(38rem, 74vh, 48rem);
    grid-template-rows: minmax(0, 1fr) auto auto;
    grid-template-areas:
      "src chat tools"
      "src chat gates"
      "src chat vault";
  }
  .bb.is-live { height: clamp(38rem, 74vh, 48rem); }
  .bb-src   { grid-area: src; }
  .bb-chat  { grid-area: chat; }
  .bb-tools { grid-area: tools; }
  .bb-gates { grid-area: gates; }
  .bb-vault { grid-area: vault; }

  /* ── the four panels: reference, not reading ── */
  .bb-panel { padding: .85rem .95rem; border-radius: 14px; }
  .bb-panel__head h3 { font-size: .535rem; letter-spacing: .16em; }
  .bb-panel__note { font-size: .51rem; }
  .bb-panel__lede { font-size: .655rem; line-height: 1.4; margin-top: .3rem; }

  .bb-srcs { gap: .45rem; margin-top: .6rem; flex: 1; justify-content: space-between; }
  .bb-srcs { overflow: hidden; }  /* a short frame crops the list, not the cable */
  .bb-srcs > li { padding: .45rem .62rem; border-radius: 10px; }
  .bb-srcs__top { gap: .4rem; }
  .bb-srcs__top b { font-size: .665rem; }
  .bb-srcs__top i { font-size: .535rem; }
  .bb-srcs__top svg { width: 12px; height: 12px; }
  .bb-srcs__sub { font-size: .615rem; margin-top: .16rem; }
  .bb-srcs__logos { margin-top: .3rem; padding-top: .3rem; min-height: 15px; gap: .38rem; }
  .bb-srcs__logos img { height: 14px; }
  .bb-srcs__logos .bb-db { width: 14px; height: 14px; }

  .bb-tools__view { margin-top: .45rem; }
  .bb-tools__list { gap: .4rem; }
  .bb-tools__list > li { padding: .4rem .55rem; border-radius: 9px; gap: .45rem; }
  .bb-tools__list b { font-size: .665rem; }
  .bb-tools__list i { font-size: .52rem; }
  .bb-tools__list svg { width: 12px; height: 12px; }

  /* Tight enough that the six chips land in three rows, not four, in the
     narrow rail — the fourth row was coming straight out of the tools panel
     above, which is the one thing in this column that needs the height. */
  .bb-nevers { gap: .26rem; margin-top: .45rem; }
  .bb-nevers > li { font-size: .555rem; padding: .14rem .38rem; letter-spacing: -.005em; }
  .bb-fired { margin-top: .45rem; padding-top: .4rem; gap: .36rem; }
  .bb-fired__t { font-size: .59rem; line-height: 1.35; }
  .bb-fired__s { font-size: .625rem; line-height: 1.4; margin-top: .12rem; }

  .bb-kept { gap: .36rem; margin-top: .45rem; }
  .bb-kept > li { padding: .38rem .52rem; gap: .45rem; }
  .bb-kept b { font-size: .625rem; }
  .bb-kept i { font-size: .52rem; }
  .bb-kept svg { width: 12px; height: 12px; }

  /* ── the conversation: this is the one you read ── */
  .bb-chat { border-radius: 20px; }
  .bb-chat__head { padding: .85rem 1rem .1rem; gap: .35rem .75rem; }
  .bb-host { gap: .4rem; }
  .bb-host b,
  .bb-host i { font-size: .95rem; }
  .bb-host__mark { width: 18px; height: 18px; }
  .bb-cube { width: 19px; height: 19px; }
  .bb-x { font-size: .85rem; }
  .bb-chat__body { gap: .8rem; padding: .95rem 1.25rem .35rem; }
  .bb-bubble { font-size: .9rem; padding: .66rem .85rem; border-radius: 16px 16px 6px 16px; }
  .bb-bot__label img { width: 16px; height: 16px; }
  .bb-bot__label span { font-size: .545rem; }
  .bb-trio { margin-top: .55rem; }
  .bb-work { padding: .55rem .7rem; border-radius: 11px; }
  .bb-work__name b { font-size: .705rem; }
  .bb-work__tag { font-size: .545rem; }
  .bb-work__detail { font-size: .645rem; margin-top: .26rem; }
  .bb-summary { font-size: .635rem; padding: .34rem .7rem; }
  .bb-answer { margin-top: .15rem; }
  .bb-ans { font-size: .845rem; padding-top: .46rem; }   /* held: this is the read */
  .bb-cite { font-size: .575rem; }
  .bb-flag { font-size: .62rem; }
  .bb-filed { font-size: .575rem; padding-top: .5rem; }
  .bb-verify__card { margin-top: .6rem; padding: .48rem .62rem; border-radius: 11px; }
  .bb-verify__name b { font-size: .665rem; }
  .bb-verify__eta { font-size: .545rem; }
  .bb-verify__row b,
  .bb-verify__row span,
  .bb-verify__row i,
  .bb-verify__done { font-size: .6rem; }
  .bb-reply { font-size: .845rem; padding-top: .6rem; }  /* held, with the answer */

  .bb-input { padding: .42rem .85rem .75rem; }
  .bb-input__line { font-size: .88rem; padding: .66rem .8rem .15rem; border-radius: 17px 17px 0 0; }
  .bb-input__foot { padding: .34rem .8rem .55rem; border-radius: 0 0 17px 17px; }
  .bb-chip { font-size: .53rem; padding: .16rem .48rem; }
  .bb-input__left svg { width: 15px; height: 15px; }
  .bb-model { font-size: .74rem; }
  .bb-send { width: 28px; height: 28px; }
  .bb-send svg { width: 13px; height: 13px; }

  /* the cables */
  .bb-cable {
    display: block;
    position: absolute;
    top: 50%;
    width: var(--bb-col);
    height: 7px;
    margin-top: -3.5px;
    opacity: 0;
    pointer-events: none;
    transition: opacity .55s var(--ease-out);
  }
  .bb-cable::before,
  .bb-cable::after {
    content: "";
    position: absolute;
    left: 0; right: 0;
    height: 1px;
    background: color-mix(in oklab, var(--ink) 22%, transparent);
  }
  .bb-cable::before { top: 0; }
  .bb-cable::after { bottom: 0; }
  .bb-cable > i {
    position: absolute;
    top: 1px; left: 0;
    width: 5px; height: 5px;
    border-radius: 50%;
    background: var(--ink);
  }
  /* +1px clears the panel's own border — the containing block is its padding box. */
  .bb-src .bb-cable { left: calc(100% + 1px); }
  .bb-tools .bb-cable,
  .bb-gates .bb-cable,
  .bb-vault .bb-cable { right: calc(100% + 1px); }
  .bb-gates .bb-cable > i { background: var(--err); }

  /* Lit for exactly as long as its panel is — same signal, same cue. */
  .bb.is-live .bb-panel.is-lit .bb-cable { opacity: .85; }
  .bb.is-live:not(.is-paused) .bb-panel.is-lit .bb-cable > i { animation: bbTravel 1.5s linear infinite; }
  .bb.is-live.is-paused .bb-cable > i { animation-play-state: paused; }
}
@keyframes bbTravel {
  0%   { transform: translateX(0); opacity: 0; }
  18%  { opacity: 1; }
  82%  { opacity: 1; }
  100% { transform: translateX(calc(var(--bb-col) - 5px)); opacity: 0; }
}

/* ── short desktop viewports ───────────────────────────────────────────────
   A laptop at 1280×700 caps the frame at 80vh = 560px, and six source cards
   carrying a description line each need ~746px. Something has to give. The
   description is the least load-bearing part of a card — the name, the count
   and the tools it covers all stay — so that line goes, rather than cropping
   the sixth source off the bottom of the column. */
@media (min-width: 75rem) and (max-height: 51rem) {
  .bb-srcs__sub { display: none; }
  .bb-srcs > li { padding: .55rem .85rem; }
  .bb-srcs__logos { margin-top: .38rem; padding-top: .38rem; }
}


/* ── phone and tablet: the conversation, scaled ───────────────────────────
   Deliberately at the END of this block. These are single-class selectors
   competing with the base rules above them, and a media query adds no
   specificity — placed earlier they simply lose on source order, which is
   exactly how the first attempt failed silently. */
@media (max-width: 74.99rem) {
  /* Everything in the conversation drops a step so the exchange fits the
     pinned frame instead of running off the top of it. */
  .bb-chat__head { padding: .9rem 1rem .1rem; gap: .35rem .7rem; }
  .bb-host b,
  .bb-host i { font-size: .9rem; }
  .bb-host__mark { width: 17px; height: 17px; }
  .bb-cube { width: 18px; height: 18px; }
  .bb-x { font-size: .8rem; }
  .bb-chat__body { gap: .6rem; padding: .8rem 1rem .3rem; }
  .bb-bubble { font-size: .82rem; padding: .55rem .75rem; line-height: 1.45; border-radius: 15px 15px 5px 15px; }
  .bb-bot__label img { width: 15px; height: 15px; }
  .bb-bot__label span { font-size: .53rem; }
  .bb-trio { margin-top: .5rem; }
  .bb-work { padding: .5rem .65rem; border-radius: 10px; }
  .bb-work__name b { font-size: .715rem; }
  .bb-work__tag { font-size: .53rem; }
  .bb-work__detail { font-size: .645rem; margin-top: .22rem; line-height: 1.45; }
  .bb-summary { font-size: .62rem; padding: .3rem .62rem; }
  .bb-answer { margin-top: .1rem; }
  .bb-ans { font-size: .775rem; padding-top: .38rem; line-height: 1.5; }
  .bb-cite { font-size: .585rem; }
  .bb-flag { font-size: .625rem; }
  .bb-filed { font-size: .575rem; padding-top: .45rem; }
  .bb-verify__card { margin-top: .45rem; padding: .4rem .55rem; border-radius: 10px; }
  .bb-verify__name b { font-size: .655rem; }
  .bb-verify__eta { font-size: .53rem; }
  .bb-verify__row b,
  .bb-verify__row span,
  .bb-verify__row i,
  .bb-verify__done { font-size: .585rem; }
  .bb-reply { font-size: .775rem; padding-top: .5rem; line-height: 1.5; }
  .bb-empty__line { font-size: .95rem; }
  .bb-empty__sub { font-size: .58rem; letter-spacing: .08em; }

  .bb-input { padding: .4rem .8rem .7rem; }
  .bb-input__line { font-size: .845rem; padding: .6rem .75rem .12rem; border-radius: 16px 16px 0 0; }
  .bb-input__foot { padding: .3rem .75rem .5rem; border-radius: 0 0 16px 16px; }
  .bb-chip { font-size: .5rem; padding: .14rem .42rem; }
  .bb-input__left svg { width: 14px; height: 14px; }
  .bb-model { font-size: .72rem; }
  .bb-send { width: 26px; height: 26px; }
  .bb-send svg { width: 12px; height: 12px; }
}
