/* ═══ AMELIAS AGENT — ROOMS ═══════════════════════════════════════════════
   GENERATED:tokens — from tokens.json by gen-tokens.mjs. Do not hand-edit.
   Warm paper, one brand blue, and eight ROOM hues that mean PLACE: Builds,
   Chats, Projects, Library, Memory, Computers, Account and the Map each own a
   fill, a tint and an ink, reused in the rail, the page header, the Map and
   the matching marketing page. That is the wayfinding system; a beginner
   learns the product the way they learn a building.
   ── The rule ───────────────────────────────────────────────────────────────
   Colour means place. State is a dot:
       waiting on you / held  →  --kept    ochre
       live / healthy / done  →  --live    green
       failed                 →  --alarm   red
   A room hue is never a state and a state hue is never a room. Bricolage
   Grotesque carries headlines, Instrument Sans reads and operates, JetBrains
   Mono is for numbers, addresses and code — never for labels. */

:root{

  /* ── neutrals — the whole ground ───────────────────────────────────────
     A warm porcelain ground instead of pure grey: every neutral carries a hint
     Dark is TRUE black (Richy, 2026-09-06: "should be black background not almost black"),
     with the card, the well and the hairline stepped up from it.

     Every grey that carries TEXT clears 4.5:1 on --paper and on --white in both
     themes (2026-09-06). It did not: --smoke was 2.42/2.64 and --graphite 3.12/3.40,
     and an audit found the light one on 39 nodes across six screens — model-tier
     badges, cost callouts, project hints and the composer send fill. --smoke was
     documented as "placeholders, never content" and used as content anyway, so the
     rule the stylesheet could not keep is now kept by the value instead. --slate
     moved with them (4.87 → 5.41 light) to keep the ladder ordered and spaced.
     --ash is the one exception and stays where it is: disabled text and borders,
     which WCAG 1.4.3 exempts. check-a11y.mjs asserts all of this.
     */
  --paper:#F7F5F0;                   /* page canvas */
  --white:#FFFFFF;                   /* cards, inputs, the rail */
  --sunk:#EFECE6;                    /* tab wells, a surface one step off the card */
  --hairline:#E3DFD7;                /* 1px borders */
  --ash:#C9C4BA;                     /* disabled text, watermarks, the stronger border */
  --smoke:#736F66;                   /* the quietest legible grey — placeholders, tier badges — 4.59 / 4.68 */
  --graphite:#6E6A61;                /* footer micro-copy — 4.95 / 5.22 */
  --slate:#68645B;                   /* tertiary text — 5.41 / 6.73 */
  --stone:#5E5B54;                   /* helper text, secondary — 6.22 / 7.95 */
  --charcoal:#3E3C36;                /* body paragraphs — where reading weight lives */
  --obsidian:#1C1B18;                /* headings, filled buttons, the ink band */
  --carbon:#000000;                  /* the brand glyph only */

  /* ── brand — the blue, kept ────────────────────────────────────────────
     Kept from the previous system on purpose: it is the one colour customers
     already associate with us. It is the primary button, a link in prose, and
     the Studio room. It is still never a state.
     */
  --brand:#1e56e6;                   /* the fill — primary buttons, bars, the accent word */
  --brand-ink:#0F4FE0;               /* brand as TEXT on the page ground — 5.99 / 7.56 */
  --brand-on:#ffffff;                /* what goes on a --brand fill — 5.95:1 */
  --brand-inv:#8fb0ff;               /* brand as text on an --obsidian panel — 8.39 / 7.28 */

  /* ── semantic — three states, drawn small ──────────────────────────────
     A state is an eight-pixel dot, a badge or a line of text — never a card, a
     panel or a section. That is what leaves the hues free to mean place.
     */
  --kept:#8F6100;                    /* waiting on you, held, needs an answer — 4.97 / 9.25 */
  --kept-inv:#E8B04B;                /* the same, on an inverted panel */
  --live:#297A3A;                    /* running, deployed, online, healthy, done — 4.89 / 7.7 */
  --live-inv:#4ADE80;                /* the same, on an inverted panel */
  --alarm:#8B1A1A;                   /* failed, error — 8.53 / 6.0 */

  /* ── rooms — colour means place ────────────────────────────────────────
     Each room owns a fill (the tile, the active rail item), a tint (the surface
     behind it) and an ink (the same hue as text on the page ground, >= 4.5:1).
     The fill carries white in a tile; the ink is what you type with. Studio is
     the brand blue, so the Studio tokens alias it.
     */
  --studio:#1E56E6;                  /* = --brand; the Builds room */
  --studio-t:#E6EDFF;
  --studio-ink:#163FA8;
  --chats:#B53A8B;                   /* the Chats room — a conversation that runs no machine */
  --chats-t:#F8E3F0;
  --chats-ink:#8E2A6C;               /* 5.6 / 9.6 */
  --projects:#0E8A7A;
  --projects-t:#DCF2ED;
  --projects-ink:#0B6B5F;            /* 5.87 / 6.9 */
  --library:#B7791F;
  --library-t:#FBEFD6;
  --library-ink:#8A5A12;             /* 5.43 / 8.1 */
  --memory:#6B4FD8;
  --memory-t:#ECE7FB;
  --memory-ink:#4F37B0;              /* 7.69 / 8.7 */
  --computers:#1A6E8E;               /* the Computers room — a machine of your own, where a build can run for nothing */
  --computers-t:#DCEEF5;
  --computers-ink:#145A75;           /* 7.01 / 12.8 */
  --account:#5B6B7C;
  --account-t:#E7ECF1;
  --account-ink:#43515F;             /* 7.47 / 9.1 */
  --map:#D6503C;                     /* the one warm note — the compass, the Map */
  --map-t:#FCE4DF;
  --map-ink:#A83A29;                 /* 5.84 / 7.4 */

  /* ── shape and type ────────────────────────────────────────────────────
     Fourteen-pixel cards, nine-pixel small controls, pills for tabs and buttons.
     Bricolage Grotesque carries headlines, Instrument Sans reads and operates,
     JetBrains Mono is for prices, addresses, timestamps and code — never labels.
     */
  --radius:14px;                     /* cards, sheets, panels */
  --radius-sm:9px;                   /* inputs, chips, thumbnails inside a card */
  --radius-nav:10px;                 /* rail items, small controls */
  --radius-pill:9999px;              /* pill-shaped nav actions only */
  --sans:'Instrument Sans',ui-sans-serif,system-ui,-apple-system,'Segoe UI',Roboto,sans-serif;
  --mono:'JetBrains Mono',ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
  --display:'Bricolage Grotesque',var(--sans); /* headlines and room names only */

  /* ── derived surfaces ──────────────────────────────────────────────────
     Every one of these was a hand-written rgba() before. That is how a frozen
     copy of the light-mode green ended up in a box-shadow that never flipped.
     */
  --ring-card:0 0 0 1px rgba(28,27,24,.08);
  --ring-card-hover:0 0 0 1px rgba(28,27,24,.20);
  --ring-ghost:0 0 0 1px var(--hairline);
  --edge-hint:rgba(28,27,24,.24);    /* the shadow a scroll strip casts over the tabs it is hiding */
  --nav-bg:rgba(247,245,240,.85);
  --focus-halo:rgba(30,86,230,.28);
  --overlay-chip:rgba(255,255,255,.92);
  --drop-lg:0 1px 2px rgba(28,27,24,.06),0 12px 32px -12px rgba(28,27,24,.18);
  --scrim:rgba(28,27,24,.4);
  --scrim-strong:rgba(28,27,24,.86);
  --edge:rgba(28,27,24,.08);
  --alarm-halo:rgba(139,26,26,.3);
  --kept-halo:rgba(143,97,0,.35);
  --live-halo:rgba(41,122,58,.35);
  --brand-halo:rgba(30,86,230,.30);

  /* ── project identity — hashed by name, identical on the phone ────
     A project is not more urgent for being teal, so these are muted and
     matched in lightness. Never use one for a state. */
  --project-0:#1F776E;
  --project-1:#1F7387;
  --project-2:#43739D;
  --project-3:#6369A0;
  --project-4:#7D6194;
  --project-5:#905B7E;
  color-scheme:light;
}

/* ── dark ────────────────────────────────────────────────────────────────────
   The same ladder inverted rather than redrawn: the canvas becomes near-black,
   type becomes near-white, and every step keeps its role. Only the tokens
   change, so nothing below this block has to know a dark mode exists.

   Declared twice on purpose. The media query serves anyone whose OS says dark
   and who has not chosen otherwise; the attribute serves an explicit choice and
   must win in BOTH directions, which is why the media query is guarded against
   an explicit light. A single unguarded media query cannot be overridden by a
   toggle at all. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]){
    --paper:#000000;                   /* page canvas */
    --white:#141517;                   /* cards, inputs, the rail */
    --sunk:#1C1D21;                    /* tab wells, a surface one step off the card */
    --hairline:#26282C;                /* 1px borders */
    --ash:#35383D;                     /* disabled text, watermarks, the stronger border */
    --smoke:#7E8188;                   /* the quietest legible grey — placeholders, tier badges — 4.59 / 4.68 */
    --graphite:#868990;                /* footer micro-copy — 4.95 / 5.22 */
    --slate:#9A9DA4;                   /* tertiary text — 5.41 / 6.73 */
    --stone:#A8ABB2;                   /* helper text, secondary — 6.22 / 7.95 */
    --charcoal:#D2D3D6;                /* body paragraphs — where reading weight lives */
    --obsidian:#F3F2EE;                /* headings, filled buttons, the ink band */
    --carbon:#FFFFFF;                  /* the brand glyph only */
    --brand:#1e56e6;                   /* the fill — primary buttons, bars, the accent word */
    --brand-ink:#7EA6FF;               /* brand as TEXT on the page ground — 5.99 / 7.56 */
    --brand-on:#ffffff;                /* what goes on a --brand fill — 5.95:1 */
    --brand-inv:#1741d6;               /* brand as text on an --obsidian panel — 8.39 / 7.28 */
    --kept:#E8B04B;                    /* waiting on you, held, needs an answer — 4.97 / 9.25 */
    --kept-inv:#8F6100;                /* the same, on an inverted panel */
    --live:#4ADE80;                    /* running, deployed, online, healthy, done — 4.89 / 7.7 */
    --live-inv:#297A3A;                /* the same, on an inverted panel */
    --alarm:#F87171;                   /* failed, error — 8.53 / 6.0 */
    --studio:#1E56E6;                  /* = --brand; the Builds room */
    --studio-t:#1C2745;
    --studio-ink:#9DB7FF;
    --chats:#E07AC0;                   /* the Chats room — a conversation that runs no machine */
    --chats-t:#3D1F33;
    --chats-ink:#F0A6D6;               /* 5.6 / 9.6 */
    --projects:#2FB39E;
    --projects-t:#15302C;
    --projects-ink:#7ADBC9;            /* 5.87 / 6.9 */
    --library:#E0A23C;
    --library-t:#372A14;
    --library-ink:#F0C97A;             /* 5.43 / 8.1 */
    --memory:#9C86F0;
    --memory-t:#2A2445;
    --memory-ink:#C3B4FF;              /* 7.69 / 8.7 */
    --computers:#4FB3D6;               /* the Computers room — a machine of your own, where a build can run for nothing */
    --computers-t:#10303C;
    --computers-ink:#8FD4EC;           /* 7.01 / 12.8 */
    --account:#8A9BAD;
    --account-t:#262C33;
    --account-ink:#B7C4D1;             /* 7.47 / 9.1 */
    --map:#F0705B;                     /* the one warm note — the compass, the Map */
    --map-t:#3E211C;
    --map-ink:#FFA08E;                 /* 5.84 / 7.4 */
    --radius:14px;                     /* cards, sheets, panels */
    --radius-sm:9px;                   /* inputs, chips, thumbnails inside a card */
    --radius-nav:10px;                 /* rail items, small controls */
    --radius-pill:9999px;              /* pill-shaped nav actions only */
    --sans:'Instrument Sans',ui-sans-serif,system-ui,-apple-system,'Segoe UI',Roboto,sans-serif;
    --mono:'JetBrains Mono',ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
    --display:'Bricolage Grotesque',var(--sans); /* headlines and room names only */
    --ring-card:0 0 0 1px rgba(255,255,255,.10);
    --ring-card-hover:0 0 0 1px rgba(255,255,255,.26);
    --ring-ghost:0 0 0 1px var(--hairline);
    --edge-hint:rgba(255,255,255,.30); /* the shadow a scroll strip casts over the tabs it is hiding */
    --nav-bg:rgba(0,0,0,.85);
    --focus-halo:rgba(126,166,255,.35);
    --overlay-chip:rgba(20,21,23,.92);
    --drop-lg:0 1px 2px rgba(0,0,0,.4),0 12px 32px -12px rgba(0,0,0,.6);
    --scrim:rgba(0,0,0,.6);
    --scrim-strong:rgba(0,0,0,.9);
    --edge:rgba(255,255,255,.10);
    --alarm-halo:rgba(248,113,113,.3);
    --kept-halo:rgba(232,176,75,.35);
    --live-halo:rgba(74,222,128,.35);
    --brand-halo:rgba(30,86,230,.30);

    /* ── project identity — hashed by name, identical on the phone ──
       A project is not more urgent for being teal, so these are muted and
       matched in lightness. Never use one for a state. */
    --project-0:#61B6AB;
    --project-1:#60B2C7;
    --project-2:#78AAD7;
    --project-3:#98A0DA;
    --project-4:#B496CD;
    --project-5:#C990B5;
    color-scheme:dark;
  }
}

:root[data-theme="dark"]{
  --paper:#000000;                   /* page canvas */
  --white:#141517;                   /* cards, inputs, the rail */
  --sunk:#1C1D21;                    /* tab wells, a surface one step off the card */
  --hairline:#26282C;                /* 1px borders */
  --ash:#35383D;                     /* disabled text, watermarks, the stronger border */
  --smoke:#7E8188;                   /* the quietest legible grey — placeholders, tier badges — 4.59 / 4.68 */
  --graphite:#868990;                /* footer micro-copy — 4.95 / 5.22 */
  --slate:#9A9DA4;                   /* tertiary text — 5.41 / 6.73 */
  --stone:#A8ABB2;                   /* helper text, secondary — 6.22 / 7.95 */
  --charcoal:#D2D3D6;                /* body paragraphs — where reading weight lives */
  --obsidian:#F3F2EE;                /* headings, filled buttons, the ink band */
  --carbon:#FFFFFF;                  /* the brand glyph only */
  --brand:#1e56e6;                   /* the fill — primary buttons, bars, the accent word */
  --brand-ink:#7EA6FF;               /* brand as TEXT on the page ground — 5.99 / 7.56 */
  --brand-on:#ffffff;                /* what goes on a --brand fill — 5.95:1 */
  --brand-inv:#1741d6;               /* brand as text on an --obsidian panel — 8.39 / 7.28 */
  --kept:#E8B04B;                    /* waiting on you, held, needs an answer — 4.97 / 9.25 */
  --kept-inv:#8F6100;                /* the same, on an inverted panel */
  --live:#4ADE80;                    /* running, deployed, online, healthy, done — 4.89 / 7.7 */
  --live-inv:#297A3A;                /* the same, on an inverted panel */
  --alarm:#F87171;                   /* failed, error — 8.53 / 6.0 */
  --studio:#1E56E6;                  /* = --brand; the Builds room */
  --studio-t:#1C2745;
  --studio-ink:#9DB7FF;
  --chats:#E07AC0;                   /* the Chats room — a conversation that runs no machine */
  --chats-t:#3D1F33;
  --chats-ink:#F0A6D6;               /* 5.6 / 9.6 */
  --projects:#2FB39E;
  --projects-t:#15302C;
  --projects-ink:#7ADBC9;            /* 5.87 / 6.9 */
  --library:#E0A23C;
  --library-t:#372A14;
  --library-ink:#F0C97A;             /* 5.43 / 8.1 */
  --memory:#9C86F0;
  --memory-t:#2A2445;
  --memory-ink:#C3B4FF;              /* 7.69 / 8.7 */
  --computers:#4FB3D6;               /* the Computers room — a machine of your own, where a build can run for nothing */
  --computers-t:#10303C;
  --computers-ink:#8FD4EC;           /* 7.01 / 12.8 */
  --account:#8A9BAD;
  --account-t:#262C33;
  --account-ink:#B7C4D1;             /* 7.47 / 9.1 */
  --map:#F0705B;                     /* the one warm note — the compass, the Map */
  --map-t:#3E211C;
  --map-ink:#FFA08E;                 /* 5.84 / 7.4 */
  --radius:14px;                     /* cards, sheets, panels */
  --radius-sm:9px;                   /* inputs, chips, thumbnails inside a card */
  --radius-nav:10px;                 /* rail items, small controls */
  --radius-pill:9999px;              /* pill-shaped nav actions only */
  --sans:'Instrument Sans',ui-sans-serif,system-ui,-apple-system,'Segoe UI',Roboto,sans-serif;
  --mono:'JetBrains Mono',ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
  --display:'Bricolage Grotesque',var(--sans); /* headlines and room names only */
  --ring-card:0 0 0 1px rgba(255,255,255,.10);
  --ring-card-hover:0 0 0 1px rgba(255,255,255,.26);
  --ring-ghost:0 0 0 1px var(--hairline);
  --edge-hint:rgba(255,255,255,.30); /* the shadow a scroll strip casts over the tabs it is hiding */
  --nav-bg:rgba(0,0,0,.85);
  --focus-halo:rgba(126,166,255,.35);
  --overlay-chip:rgba(20,21,23,.92);
  --drop-lg:0 1px 2px rgba(0,0,0,.4),0 12px 32px -12px rgba(0,0,0,.6);
  --scrim:rgba(0,0,0,.6);
  --scrim-strong:rgba(0,0,0,.9);
  --edge:rgba(255,255,255,.10);
  --alarm-halo:rgba(248,113,113,.3);
  --kept-halo:rgba(232,176,75,.35);
  --live-halo:rgba(74,222,128,.35);
  --brand-halo:rgba(30,86,230,.30);

  /* ── project identity — hashed by name, identical on the phone ────
     A project is not more urgent for being teal, so these are muted and
     matched in lightness. Never use one for a state. */
  --project-0:#61B6AB;
  --project-1:#60B2C7;
  --project-2:#78AAD7;
  --project-3:#98A0DA;
  --project-4:#B496CD;
  --project-5:#C990B5;
  color-scheme:dark;
}
/* GENERATED:tokens end */

/* The theme control. Three states, because "follow my system" is a real answer
   and a two-way toggle cannot express it. */
.themebtn{display:inline-flex;align-items:center;justify-content:center;
  width:32px;height:32px;border:0;background:none;cursor:pointer;
  border-radius:var(--radius);color:var(--stone);font-size:13px;line-height:1;
  transition:background .15s ease,color .15s ease}
.themebtn:hover{background:var(--white);color:var(--obsidian)}
.themebtn:focus-visible{outline:2px solid var(--obsidian);outline-offset:2px}

/* Screenshots of built sites are photographs of light-coloured pages; dimming
   them slightly stops them glaring out of a dark surround. */
:root[data-theme="dark"] .gal-shot img,
:root[data-theme="dark"] .scard img,
:root[data-theme="dark"] .mkt-card img{filter:brightness(.92)}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .gal-shot img,
  :root:not([data-theme="light"]) .scard img,
  :root:not([data-theme="light"]) .mkt-card img{filter:brightness(.92)}
}

*{box-sizing:border-box}
html{-webkit-text-size-adjust:100%;scroll-behavior:smooth}
body{
  margin:0;background:var(--paper);color:var(--charcoal);
  font:16px/1.5 var(--sans);
  font-feature-settings:"calt" 0,"rlig","ss11";
  -webkit-font-smoothing:antialiased;overflow-x:hidden;
}

/* ── type ────────────────────────────────────────────────────────────────────
   Headlines are weight 450 with tight tracking and line-height 1 — dense and
   architectural. Never bold, never italic, never a serif. */
h1,h2,h3{color:var(--obsidian);font-family:var(--display);margin:0;text-wrap:balance}
h1{font-size:clamp(2.5rem,6vw,4rem);font-weight:700;line-height:1.02;letter-spacing:-.025em}
h2{font-size:clamp(1.9rem,4vw,2.75rem);font-weight:700;line-height:1.08;letter-spacing:-.02em;
  margin:0 0 1rem}
h3{font-size:1.375rem;font-weight:600;line-height:1.2;letter-spacing:-.015em}

/* The second beat of a headline, and the highest-frequency piece of colour on
   the site: it appears in every section of every page. It was --slate, which is
   grey emphasis on a grey headline on a grey page — the single biggest reason
   the old page read as one flat texture. */
h1 em,h2 em{font-style:normal;color:var(--brand-ink)}
.k{color:var(--obsidian)}          /* compute */
.m{color:var(--kept)}             /* Memory */
h2 em.m{color:var(--kept)}

/* Eyebrow: the monospace stamp. Mono owns labels and metadata exclusively. */
.cap{font-family:var(--sans);font-weight:600;font-size:11px;line-height:1.5;letter-spacing:.071em;
  text-transform:uppercase;color:var(--brand-ink);margin:0 0 12px;font-weight:600}

.sub{color:var(--charcoal);max-width:36rem;font-size:1rem;line-height:1.5}
.lede{color:var(--charcoal);font-size:1rem;line-height:1.5;max-width:38rem;margin:1rem 0 0}
.fine{font-family:var(--sans);font-weight:600;font-size:12px;line-height:1.5;color:var(--stone);
  letter-spacing:.02em}

a{color:var(--obsidian);text-decoration:none}
a:hover{text-decoration:underline;text-underline-offset:3px;text-decoration-thickness:1px}
p{margin:0 0 1rem}

/* ── layout ──────────────────────────────────────────────────────────────────
   1280px, and bands separated by space alone — no rules, no background shifts.
   The empty space is the design; a divider between every section undoes it. */
main{position:relative;z-index:1;max-width:1280px;margin:0 auto;padding:0 24px}
@media(min-width:900px){main{padding:0 48px}}
section{padding:96px 0;position:relative}
@media(min-width:900px){section{padding:128px 0}}

/* The old atmosphere layers. Kept as no-ops so a stale cached page cannot
   paint a glow over the white canvas. */
.atmos,.grain{display:none}

/* ── nav ─────────────────────────────────────────────────────────────────── */
/* The landing page's <header> has to carry main's exact geometry.
   nav breaks out of its container with a negative margin that cancels the
   container's padding — which worked while nav lived inside <main>. Moving it
   into a <header> for the landmark fix left that negative margin with nothing
   to cancel, so the bar hung 24-48px past the right edge and the whole page
   scrolled sideways. Scoped to a bare <header> so header.site on the sub-pages,
   which has its own .wrap, is untouched. */
body > header:not(.site){max-width:1280px;margin:0 auto;padding:0 24px}
@media(min-width:900px){body > header:not(.site){padding:0 48px}}


/* The price ribbon — the top line of every page. It scrolls away while nav
   sticks; an announcement that follows you around stops being an announcement.
   Full-bleed via the same negative margin nav uses. The outer segments drop on
   narrow screens; the $10 figure itself never does. */
/* Full viewport width, not the 1280 content box. The old negative-margin
   bleed only reached the centered header's padding edge, so on any screen
   wider than 1280 the bar floated in the middle with the page ground either
   side of it — which reads as "cut off", not as a banner. width:100vw plus
   the centering margin spans the real viewport; body has overflow-x:hidden,
   so the scrollbar's width cannot turn this into a sideways scroll. */
.ribbon{display:block;text-align:center;font-family:var(--sans);font-weight:600;font-size:12px;
  letter-spacing:.02em;padding:8px 24px;width:100vw;margin-left:calc(50% - 50vw);
  color:var(--brand-ink);background:var(--sunk);
  border-bottom:1px solid var(--hairline)}
.ribbon:hover{text-decoration:underline}
/* Dark mode only: the band sits just above the page black, so the standard
   brand-ink (which is tuned for text on the page ground) reads muddy on it.
   A brighter blue lifts it to a crisp ~9:1 without changing the shared token
   every other accent uses. Both theme states covered — the un-stamped
   system-dark and the explicit toggle — so it wins either way. */
@media(prefers-color-scheme:dark){:root:not([data-theme="light"]) .ribbon{color:#b8d0ff}}
:root[data-theme="dark"] .ribbon{color:#b8d0ff}
@media(max-width:680px){.ribbon .wide{display:none}}
@media(max-width:430px){.ribbon{font-size:11px}.ribbon .ribbon-side{display:none}}

/* Scoped to the header. This was a bare `nav{}`, which meant the first <nav>
   added anywhere else on the page — the footer's link columns — silently
   became a 64px-tall sticky bar with a backdrop blur and a -48px bleed on each
   side. The bleed is what showed: 96px of negative margin pushed the columns
   past the page cap and gave the site a horizontal scrollbar at exactly 1280,
   which reads as a grid bug in the new code rather than a stray selector in
   the old. Every <nav> on the built site is either in a <header> or in the
   footer; verified before narrowing this. */
header nav{display:flex;align-items:center;justify-content:space-between;height:64px;
  position:sticky;top:0;z-index:20;background:var(--nav-bg);
  backdrop-filter:blur(20px);-webkit-backdrop-filter:blur(20px);
  margin:0 -24px;padding:0 24px}
@media(min-width:900px){header nav{margin:0 -48px;padding:0 48px}}
.brand{display:flex;align-items:center;gap:.55rem;color:var(--obsidian);
  font-size:14px;font-weight:500;letter-spacing:-.01em}
.brand svg{width:18px;height:18px;color:var(--carbon)}
/* The mark is a photograph of Amelia with the call stack in her hair. Round,
   because the disc is what survives at nav and favicon sizes; see
   ~/Desktop/amelia-logo-concepts/ for the generator and the size proof. */
.brand .mark{width:24px;height:24px;display:block;flex:0 0 auto}   /* a 1px ring, not a shadow */
footer .brand .mark,header.site .brand .mark{width:18px;height:18px}

/* ── buttons ─────────────────────────────────────────────────────────────── */
.btn{display:inline-block;font-family:var(--sans);font-size:14px;font-weight:400;
  line-height:1.43;letter-spacing:normal;text-transform:none;
  padding:.6rem 1rem;border-radius:var(--radius);border:0;cursor:pointer;
  background:var(--brand);color:var(--brand-on);
  transition:background .15s ease,box-shadow .15s ease,color .15s ease,transform .15s ease}
.btn:hover{background:var(--brand);text-decoration:none;transform:translateY(-1px);
  box-shadow:0 6px 18px -8px var(--brand-halo)}
.btn:active{transform:translateY(0);box-shadow:none}
/* .blue was the legacy name from the blue era, then it was black for a while.
   It is blue again, and it is the primary action — so the selector keeps
   working and no page had to be re-marked-up. */
.btn.blue{background:var(--brand);color:var(--brand-on)}
.btn.blue:hover{background:var(--brand)}
/* The black button is still needed where a brand fill would be the second
   loudest thing next to a real primary. */
.btn.ink{background:var(--obsidian);color:var(--paper)}
.btn.ink:hover{background:var(--charcoal)}
@media(prefers-reduced-motion:reduce){.btn,.btn:hover,.btn:active{transform:none}}
.btn.ghost{background:transparent;color:var(--charcoal);box-shadow:var(--ring-ghost)}
.btn.ghost:hover{background:var(--white);color:var(--obsidian);
  box-shadow:0 0 0 1px var(--ash)}
.btn.sm{font-size:13px;padding:.45rem .8rem}
.btn:focus-visible{outline:2px solid var(--brand-ink);outline-offset:2px}
/* Pill only in the tight header cluster. */
nav .btn{border-radius:var(--radius-pill);padding:.4rem 1rem}

/* ── hero ────────────────────────────────────────────────────────────────────
   The ask box is the hero. Everything above it exists to get someone into it,
   and everything below it is the argument for people who did not type. */
.hero{padding:88px 0 96px;text-align:center}
.hero h1{max-width:20ch;margin:0 auto}
.hero .sub{margin:1.5rem auto 0;font-size:1.0625rem;color:var(--stone)}
.hero .cta{display:flex;gap:12px;justify-content:center;margin-top:2.5rem;flex-wrap:wrap}
.hero .fine{margin-top:1.25rem}

/* ── Amelia says hello ───────────────────────────────────────────────────────
   The ask box below is an empty field, and an empty field asks the visitor to
   do all the work of starting. A message that arrives on its own, a beat after
   the hero settles, turns that same field into a reply — which is the actual
   product, and exactly what the ads sell.

   It is the app's own bubble rather than a lookalike: same white surface, same
   20px with the sender's corner squared to 7px, same mark alongside. Someone
   who clicks an ad should land in the conversation they were just shown.

   The line is in the DOM from the start at opacity 0, with the dots overlaid on
   top of it, so the bubble is already its final size when the message lands.
   Swapping one element for the other would size the bubble to three dots and
   then shove the ask box down the page — a layout shift on the hero, charged
   against CLS, on the element we most want people to touch. */
/* Left, not centred, and on the composer's own width.
   Centred it read as a banner about Amelia. Flush with the left edge of the
   box underneath, with the face beside it, it reads as Amelia having said
   something — which is the whole point of putting a message above an input
   rather than a headline. Same shape the portal already uses for every reply
   she gives, so the first thing somebody sees and everything after it match. */
.hail{display:flex;align-items:flex-end;justify-content:flex-start;gap:10px;
  margin:1.5rem auto 0;max-width:44rem}
.hail-av{width:30px;height:30px;border-radius:8px;flex:0 0 auto;
  box-shadow:var(--ring-ghost)}
/* Held narrower than the row it sits in, so the line still breaks where it
   did rather than stretching into one long ribbon on a wide screen. */
.hail-b{max-width:30rem;position:relative;margin:0;background:var(--white);color:var(--obsidian);
  box-shadow:var(--ring-card);border-radius:20px;border-bottom-left-radius:7px;
  padding:13px 17px;font-size:1rem;line-height:1.45;text-align:left;
  animation:hail-in .34s cubic-bezier(.2,.7,.2,1) both}
@keyframes hail-in{from{opacity:0;transform:translateY(6px) scale(.97)}
  to{opacity:1;transform:none}}
/* Visible by DEFAULT, and hidden only once a script has proved it is running.
   It used to be opacity:0 waiting for a class, which means any failure in that
   script — or a blocked script, or a slow one — ships the opening line of the
   whole site invisible. Exactly the shape that shipped a blank hero here once
   already. */
.hail-t{opacity:1}
/* Word by word, so it reads as being said rather than pasted. Opacity only:
   every word already occupies its space, so the bubble never reflows and the
   line does not jitter as it fills. */
.hail-w{display:inline-block}
.hail-b.is-typing .hail-w{opacity:0}
.hail-b.is-typing.is-said .hail-w{opacity:1;transition:opacity .16s ease;
  transition-delay:calc(var(--i) * 45ms)}

/* Hidden until a script turns them on.
   They are a JS-driven affordance: if the script never runs there is nothing
   to wait for, so three dots sitting on top of the sentence forever is the
   worst of both. Shown by .is-typing, which only JS adds. */
.hail-dots{opacity:0;visibility:hidden}
.hail-b.is-typing .hail-dots{opacity:1;visibility:visible}
.hail-dots{position:absolute;left:17px;top:50%;transform:translateY(-50%);
  display:flex;gap:5px;transition:opacity .18s ease}
.hail-dots i{width:7px;height:7px;border-radius:var(--radius-pill);
  background:var(--smoke);animation:hail-dot 1.15s ease-in-out infinite}
.hail-dots i:nth-child(2){animation-delay:.16s}
.hail-dots i:nth-child(3){animation-delay:.32s}
.hail-b.is-said .hail-dots{opacity:0;visibility:hidden}
@keyframes hail-dot{0%,100%{opacity:.3;transform:translateY(0)}
  50%{opacity:1;transform:translateY(-3px)}}

@media(prefers-reduced-motion:reduce){
  .hail-b{animation:none}
  .hail-dots i{animation:none;opacity:.5}
}
@media(max-width:560px){
  .hail{gap:8px}
  .hail-b{font-size:.9375rem;padding:12px 15px}
}

/* ── the ask box ─────────────────────────────────────────────────────────────
   The product's own interaction, on the page: type the thing you want and it
   carries straight into the app. A plain form with action="/app" so it still
   works if the JS never loads.

   On the old dark page this needed a pulsing ring and a self-typing placeholder
   to read as a control at all — a dark rounded box on a dark ground looks like
   a panel. On white it does not have that problem: a pure white field with a
   hairline ring against #fafafa is unambiguously an input, so the ring
   animation is gone. The self-typing placeholder stays because it advertises
   what to type, which is a different job. */
/* ── the comet ───────────────────────────────────────────────────────────────
 * A light runs the border of the ask box until you touch it.
 *
 * The technique: the box's own background is the resting band, ::before is an
 * oversized conic gradient — one bright head, a short tail, transparent for the
 * rest of the sweep — rotating underneath, and ::after is an opaque surface
 * inset by the band width that masks the gradient down to a hairline edge.
 *
 * ── The part that is not borrowed ───────────────────────────────────────────
 * On a badge, that effect runs forever, because a badge is something to read.
 * This is something to TYPE IN, and an input that keeps animating while you use
 * it is competing with your own cursor. So it stops the instant you focus or
 * click a prompt, and does not come back. It is an invitation, and an invitation
 * that keeps talking after you accept is just noise.
 *
 * This page did once carry a pulsing ring here, and it was removed for a good
 * reason: it never stopped, and it fought the focus state. Both faults are the
 * ones fixed above, which is why this one earns its place.
 *
 * Colour comes free from the tokens. The core has to be the most extreme value
 * available and the direction inverts per theme — on paper the brightest thing
 * is the DARKEST ink, on a dark ground it is near-white. --obsidian is already
 * defined as exactly that in both, so the comet reads correctly in each without
 * a second rule. Getting this backwards is the one way the effect disappears.
 */
.ask{display:flex;gap:.5rem;align-items:flex-end;max-width:44rem;
  position:relative;isolation:isolate;overflow:hidden;
  margin:2rem auto 0;background:var(--hairline);border:0;border-radius:var(--radius);
  box-shadow:none;padding:8px 8px 8px 16px;text-align:left;
  transition:background .2s ease,box-shadow .15s ease}
.ask::before{content:"";position:absolute;left:50%;top:50%;
  /* Square and oversized so the head reaches every corner of a wide box. */
  width:220%;aspect-ratio:1;translate:-50% -50%;z-index:0;
  /* Tuned against the rendered pixels, not by eye.
     A conic sweep maps unevenly onto a box this wide: at 12:1 the two short
     ends take about 9 degrees each and the long edges take the rest, so a
     narrow wedge that looks generous on a square badge becomes a 14px dash on
     a 704px edge. Measured at the first attempt: a visible run of 10-63px,
     which is to say invisible. Widened until the head reads as a travelling
     light rather than a speck. */
  background:conic-gradient(from 0deg,
    transparent 0deg 150deg,
    color-mix(in oklab, var(--smoke) 30%, transparent) 250deg,
    var(--smoke) 320deg,
    var(--obsidian) 344deg 352deg,
    var(--smoke) 358deg,
    transparent 360deg);
  animation:ask-comet 5s linear infinite}
/* The opaque inner surface. This is what turns the whole spinning disc into a
   1.5px edge — without it the gradient shows through the middle of the box. */
.ask::after{content:"";position:absolute;inset:2px;z-index:1;
  /* Radius reduced by exactly the inset, so the inner curve is concentric with
     the outer one rather than very slightly wrong at each corner. */
  border-radius:calc(var(--radius) - 2px);background:var(--white)}
.ask>*{position:relative;z-index:2}
@keyframes ask-comet{to{rotate:360deg}}

/* Touched, in any way: the light stops and the box behaves like a field. */
.ask:focus-within::before,.ask.typed::before{animation:none;opacity:0}
.ask:focus-within{background:var(--obsidian);
  box-shadow:0 0 0 3px var(--focus-halo)}
/* Scoped to :not(:focus-within) deliberately. Both selectors weigh the same,
   so a bare .ask.typed sitting after the focus rule simply beat it and the
   focused field kept a hairline band — the state that matters most, cancelled
   by the rule meant to tidy up after the animation. */
.ask.typed:not(:focus-within){background:var(--hairline)}

@media(prefers-reduced-motion:reduce){
  /* Keep the emphasis, drop the travel: a still, slightly darker band. */
  .ask::before{display:none}
  .ask{background:var(--ash)}
}
.ask textarea{flex:1;min-width:0;resize:none;background:none;border:0;
  color:var(--obsidian);font-family:var(--sans);font-size:1rem;line-height:1.5;
  max-height:8rem;padding:.5rem 0}
.ask textarea:focus{outline:none}
.ask textarea::placeholder{color:var(--smoke)}
.ask .send,.send{flex:0 0 auto;width:32px;height:32px;border-radius:var(--radius);
  border:0;cursor:pointer;background:var(--obsidian);color:var(--white);
  font-size:15px;line-height:1;display:grid;place-items:center;
  transition:background .15s ease}
/* Dormant until there is something to send. It still reads as a button — the
   arrow keeps full contrast against the fill — it simply is not shouting yet. */
.ask:not(.has-text) .send{background:var(--smoke)}
.ask.has-text .send{background:var(--obsidian)}
.ask .send:hover,.send:hover{background:var(--charcoal)}
.ask .send:focus-visible,.send:focus-visible{outline:2px solid var(--obsidian);outline-offset:2px}
.ask-fine{font-family:var(--sans);font-weight:600;font-size:12px;letter-spacing:.02em;
  margin:12px 0 0;color:var(--stone);text-align:center}

/* A caret in the placeholder, so it reads as a field awaiting input. */
.ask-caret{display:inline-block;width:1px;height:1em;vertical-align:-.15em;
  background:var(--obsidian);margin-left:1px;animation:ask-blink 1.1s steps(1) infinite}
@keyframes ask-blink{50%{opacity:0}}

/* ── the big chat surface ────────────────────────────────────────────────────
   The one-row field read as a decorative panel on a dark page; shaped like a
   chat message it reads as the start of a conversation, which is what it is.
   Textarea on top, then a control row: the Plan/Build gear on the left, the
   send arrow on the right. The comet border and focus states are inherited
   from .ask untouched. */
.ask-big{flex-direction:column;align-items:stretch;gap:2px;
  max-width:46rem;padding:14px 14px 10px}
.ask-big textarea{padding:.15rem 0 .5rem;max-height:12.5rem}
/* Mode chips left; mic and send TOGETHER on the right — space-between with
   three children stranded the mic in the middle of the row, a control's
   width from the button it belongs to. The auto margin moves to whichever
   of the two right-hand controls is actually first, so a hidden mic leaves
   the send button exactly where it was. */
.ask-row{display:flex;flex-wrap:wrap;align-items:center;justify-content:flex-start;gap:10px}
.ask-row .ask-mic{margin-left:auto}
.ask-row .send{margin-left:auto}
.ask-row .ask-mic:not([hidden])~.send{margin-left:0}
/* Three pills since 2026-09-07, one word each since 2026-09-10 (Plan / Build /
   Chat), on ONE line with the mic and the send button. On a phone the
   finger-sized block further down makes the pills 44px tall and the mic and
   send 44px square. With the old two-word labels (PLAN FIRST · BUILD NOW ·
   JUST CHAT) the pills alone took 284px of a 314px row at 390, so something
   had to stack — first JUST CHAT, then the mic and send. One word each is
   ~170px of pills + 108px of buttons, which fits from 375 up. The pills never
   wrap and never shrink below their own label (no min-width:0 here, on
   purpose); only the row wraps, buttons under pills, on a screen too narrow
   for all five (320). The guard missed the first regression because a mouse
   never matches pointer:coarse — check-ask-modes measures with a finger too. */
.ask-mode{display:flex;gap:6px;flex-wrap:nowrap}
.ask-mode-b{font-family:var(--sans);font-weight:600;font-size:11px;letter-spacing:.05em;
  text-transform:uppercase;cursor:pointer;color:var(--stone);
  background:none;border:0;box-shadow:var(--ring-ghost);
  padding:6px 12px;border-radius:var(--radius-pill)}
.ask-mode-b.is-on{background:var(--brand);color:var(--brand-on);box-shadow:none}
.ask-mode-b:focus-visible{outline:2px solid var(--obsidian);outline-offset:2px}
/* After the base rule, not before it: same specificity, so an override written
   above it silently loses (the old 320px padding rule sat there, dead). */
@media(max-width:480px){
  .ask-mode{flex:1 1 auto}
  .ask-mode-b{flex:1 1 0;min-width:max-content;justify-content:center;white-space:nowrap;padding:6px 10px}
}
@media(max-width:360px){
  .ask-row{gap:6px}
  .ask-mode{gap:4px}
  .ask-mode-b{padding:6px 6px;letter-spacing:.02em}
}
/* And what the two chips actually do, said out loud under the row rather than
   left in a title attribute — a first-time visitor meets this choice before
   she knows what a turn is (beginner review, 2026-09-06). Under the row, not
   in it: .ask-row is a flex line and a paragraph inside it gets squashed. */
.ask-modewhy{margin:8px 2px 0;font-size:12.5px;line-height:1.45;color:var(--stone);max-width:56ch}
.ask-modewhy b{color:var(--obsidian);font-weight:600}

/* What can it build — the categories above the example chips. */
.cats{display:flex;flex-wrap:wrap;gap:6px;justify-content:center;
  margin:20px auto 10px;max-width:44rem}
.cats .cat{font-family:var(--sans);font-weight:600;font-size:11px;letter-spacing:.05em;
  text-transform:uppercase;cursor:pointer;color:var(--stone);
  background:none;border:0;box-shadow:var(--ring-ghost);
  padding:5px 11px;border-radius:var(--radius-pill)}
.cats .cat.is-on{background:var(--obsidian);color:var(--white);box-shadow:none}
.cats .cat:focus-visible{outline:2px solid var(--obsidian);outline-offset:2px}

/* ── prompt chips ────────────────────────────────────────────────────────────
   Clicking one fills the box rather than submitting, so the wording stays
   editable — most people want to change a word or two before they run it. */
.chips{display:flex;flex-wrap:wrap;gap:8px;justify-content:center;
  max-width:46rem;margin:16px auto 0}
.chip{font-family:var(--sans);font-size:13px;line-height:1.43;cursor:pointer;
  color:var(--stone);background:var(--white);border:0;box-shadow:var(--ring-ghost);
  border-radius:var(--radius);padding:.4rem .75rem;
  transition:color .15s ease,box-shadow .15s ease}
.chip:hover{color:var(--obsidian);box-shadow:0 0 0 1px var(--ash)}
.chip:focus-visible{outline:2px solid var(--obsidian);outline-offset:2px}

/* ── the estimator ───────────────────────────────────────────────────────────
   Deliberately quieter than the ask box. It answers "what will this cost me?",
   which stops beginners — but it is the second question, not the first. */
.est{max-width:44rem;margin:56px auto 0;background:var(--white);
  border-radius:var(--radius);box-shadow:var(--ring-card);padding:16px;text-align:left}
.est-q{font-family:var(--sans);font-weight:600;font-size:11px;letter-spacing:.071em;text-transform:uppercase;
  color:var(--obsidian);margin:0 0 12px}
.est-opts{display:grid;grid-template-columns:repeat(3,1fr);gap:8px}
@media(max-width:640px){.est-opts{grid-template-columns:1fr 1fr}}
@media(max-width:400px){.est-opts{grid-template-columns:1fr}}
.est-opt{display:flex;flex-direction:column;gap:2px;text-align:left;cursor:pointer;
  font-family:var(--sans);background:var(--paper);border:0;box-shadow:var(--ring-ghost);
  border-radius:var(--radius);padding:12px;color:var(--obsidian);
  transition:box-shadow .15s ease,background .15s ease}
.est-opt:hover{box-shadow:0 0 0 1px var(--ash)}
.est-opt:focus-visible{outline:2px solid var(--obsidian);outline-offset:2px}
/* Selection is carried by aria-selected, so the state the screen shows and the
   state assistive tech announces cannot drift apart. */
/* The brand ring, not a hairline: a 1px obsidian ring on a dark theme was
   near-invisible, and eight look-alike tiles with an invisible selection read
   as content rather than a control. */
.est-opt[aria-selected="true"]{background:var(--white);box-shadow:0 0 0 2px var(--brand)}
.est-opt-l{font-size:14px;line-height:1.43}
.est-opt-h{font-family:var(--sans);font-weight:600;font-size:11px;letter-spacing:.02em;color:var(--stone);
  line-height:1.4}
.est-out{display:flex;align-items:baseline;gap:12px;flex-wrap:wrap;
  margin:16px 0 0;padding-top:16px;border-top:1px solid var(--hairline)}
.est-price{font-family:var(--sans);font-weight:450;
  font-size:clamp(2rem,5.5vw,3rem);line-height:1;letter-spacing:-.05em;
  color:var(--obsidian);font-variant-numeric:tabular-nums}
.est-meta{font-family:var(--sans);font-weight:600;font-size:12px;color:var(--stone);letter-spacing:.02em}
.est-fine{font-family:var(--sans);font-weight:600;font-size:11px;letter-spacing:.02em;
  margin:12px 0 0;color:var(--stone);line-height:1.6}

/* ── the gallery ────────────────────────────────────────────────────────────
   Reuses .est-opt for the tab strip, so the two places on this page that ask
   "what kind of project?" look and behave identically rather than being two
   inventions of the same control. */
/* The strip sits directly under its own label now, and the section keeps a
   normal gap to the hero rather than a full band of dead air. */
#gallery{padding-top:40px}
/* Two rows that SLIDE, because a static grid of hairline boxes did not read
   as a control at all — "you can't even tell that those are clickable" was the
   verdict on the old version, on a phone. Now: a snap-scrolling rail two rows
   deep, cards with a real border and a chevron, arrows on desktop, and a
   fading edge that says there is more to the right. */
.gal-rail{position:relative;max-width:min(64rem,100%)}
.gal-opts{display:grid;grid-auto-flow:column;grid-template-rows:repeat(2,auto);
  grid-auto-columns:minmax(15rem,1fr);gap:10px;margin:0;padding:2px 2px 10px;
  overflow-x:auto;scroll-snap-type:x mandatory;scrollbar-width:thin;
  overscroll-behavior-x:contain}
.gal-opts>*{scroll-snap-align:start}
@media(max-width:560px){.gal-opts{grid-auto-columns:minmax(13rem,80%)}}
/* The edge fade only appears where there is more to see. */
.gal-rail::after{content:"";position:absolute;top:0;right:0;bottom:10px;width:44px;
  pointer-events:none;opacity:0;transition:opacity .2s ease;
  background:linear-gradient(to right,transparent,var(--paper))}
.gal-rail.has-more::after{opacity:1}
.gal-arrow{position:absolute;top:calc(50% - 26px);z-index:2;width:36px;height:36px;
  display:none;place-items:center;border-radius:50%;cursor:pointer;
  background:var(--white);border:1px solid var(--hairline);box-shadow:var(--ring-card);
  color:var(--obsidian);font-size:16px;line-height:1}
.gal-rail.has-more .gal-arrow,.gal-rail.has-back .gal-arrow{display:grid}
.gal-arrow[data-dir="back"]{left:-14px}
.gal-arrow[data-dir="fwd"]{right:-14px}
.gal-rail:not(.has-back) .gal-arrow[data-dir="back"]{display:none}
.gal-rail:not(.has-more) .gal-arrow[data-dir="fwd"]{display:none}
.gal-arrow:hover{border-color:var(--obsidian)}
@media(hover:none){.gal-arrow{display:none !important}}
/* The tile itself reads as a button now: surface, border, chevron. */
.gal-opts .est-opt{position:relative;background:var(--white);
  border:1px solid var(--hairline);border-radius:14px;padding:14px 34px 14px 15px}
.gal-opts .est-opt::after{content:"\203A";position:absolute;right:14px;top:50%;
  transform:translateY(-50%);font-size:19px;color:var(--stone)}
.gal-opts .est-opt:hover{border-color:var(--obsidian);box-shadow:var(--ring-card)}
.gal-opts .est-opt[aria-selected="true"]{border-color:var(--brand);
  box-shadow:0 0 0 2px var(--brand)}
.gal-opts .est-opt[aria-selected="true"]::after{color:var(--brand)}
/* A build with no screenshot is not a broken card — a spreadsheet job has no
   picture. Show what it actually produced: the files. */
.gal-files{margin:0;padding:14px;background:var(--sunk);border-radius:10px;
  font-family:var(--mono);font-size:11.5px;line-height:1.8;color:var(--charcoal);
  list-style:none;text-align:left}
.gal-files li::before{content:"\2014\00a0";color:var(--stone)}

.gal-prompt{max-width:min(56rem,100%);margin:20px 0 0;background:var(--white);
  border-radius:var(--radius);box-shadow:var(--ring-card);padding:16px}
.gal-prompt figcaption{font-family:var(--sans);font-weight:600;font-size:11px;letter-spacing:.071em;
  text-transform:uppercase;color:var(--stone);margin:0 0 8px}
.gal-prompt p{margin:0;font-size:15px;line-height:1.6;color:var(--charcoal)}

.gal-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:16px;
  max-width:min(80rem,100%);margin:24px 0 0}
@media(max-width:1000px){.gal-grid{grid-template-columns:1fr 1fr}}
@media(max-width:560px){.gal-grid{grid-template-columns:1fr}}

.gal-card{display:flex;flex-direction:column;background:var(--white);
  border-radius:var(--radius);box-shadow:var(--ring-card);overflow:hidden;min-width:0}
/* The frontier model gets a ring rather than a colour: it is the expensive
   option, not the recommended one, and styling it as "best" would be selling
   rather than showing. */
.gal-card.is-top{box-shadow:0 0 0 1px var(--obsidian),0 0 0 2px var(--paper)}

/* A fixed 4:3 window, so four cards line up whatever height the pages are.
   Full-page screenshots are anchored to the top — the fold is the part that
   decides whether a design is any good. */
.gal-shot{aspect-ratio:4/3;background:var(--paper);
  border-bottom:1px solid var(--hairline);overflow:hidden;
  display:flex;align-items:flex-start;justify-content:center}
.gal-shot img{width:100%;height:100%;object-fit:cover;object-position:top center;display:block}
.gal-none{margin:auto;padding:16px;text-align:center;font-family:var(--sans);font-weight:600;
  font-size:11px;line-height:1.6;color:var(--stone)}

.gal-meta{display:flex;align-items:baseline;gap:8px;flex-wrap:wrap;padding:12px 14px 0}
.gal-name{margin:0;font-size:14px;font-weight:450;color:var(--obsidian);flex:1 1 auto}
/* The card's name is the accessible way in: a real button, so it takes keyboard
   focus and announces itself, while looking exactly like the text it replaced.
   The card around it is no longer role="button" — a button role may not contain
   the "See the first attempt" button, and a screen reader would never reach it. */
.gal-open-btn{font:inherit;color:inherit;background:none;border:0;padding:0;margin:0;
  text-align:left;cursor:pointer;border-radius:2px}
.gal-open-btn:focus-visible{outline:2px solid var(--obsidian);outline-offset:2px}
.gal-cost{margin:0;font-family:var(--sans);font-size:18px;font-weight:450;
  letter-spacing:-.02em;color:var(--obsidian);font-variant-numeric:tabular-nums}
.gal-turns{margin:0;flex:0 0 100%;font-family:var(--mono);font-size:11px;
  letter-spacing:.02em;color:var(--stone)}
.gal-blurb{margin:8px 14px 0;font-size:13px;line-height:1.5;color:var(--stone)}

.gal-go{margin:12px 14px 14px;margin-top:auto;display:block;text-align:center;
  padding:10px 12px;border-radius:var(--radius);background:var(--obsidian);
  color:var(--white);text-decoration:none;font-size:13px;line-height:1.3;
  transition:opacity .15s ease}
.gal-go:hover{opacity:.85}
.gal-go:focus-visible{outline:2px solid var(--obsidian);outline-offset:2px}

.gal-start{background:var(--obsidian);color:var(--white);margin-bottom:6px}
.gal-scratch{background:var(--paper);color:var(--obsidian);box-shadow:var(--ring-ghost);margin-top:0}
.gal-scratch:hover{opacity:1;box-shadow:0 0 0 1px var(--ash)}

.gal-fine{font-family:var(--sans);font-weight:600;font-size:11px;letter-spacing:.02em;
  max-width:min(56rem,100%);margin:20px 0 0;color:var(--stone);line-height:1.6}

/* Showcase additions: the before/after toggle, the copy button, and a grid that
   holds three wide cards rather than four narrow ones — these are screenshots
   people are meant to judge on looks, so they need the room. */
.gal-grid{grid-template-columns:repeat(3,1fr);max-width:min(76rem,100%)}
@media(max-width:900px){.gal-grid{grid-template-columns:1fr 1fr}}
@media(max-width:560px){.gal-grid{grid-template-columns:1fr}}

.gal-prompt{position:relative;padding-right:84px}
.gal-copy{position:absolute;top:12px;right:12px;font-family:var(--sans);font-weight:600;font-size:11px;
  letter-spacing:.02em;padding:6px 10px;border:0;border-radius:var(--radius);
  background:var(--paper);box-shadow:var(--ring-ghost);color:var(--obsidian);cursor:pointer}
.gal-copy:hover{box-shadow:0 0 0 1px var(--ash)}
.gal-copy:focus-visible{outline:2px solid var(--obsidian);outline-offset:2px}

/* Both screenshots are stacked; the toggle swaps which one is on top. Loading
   the first attempt lazily and hiding it with opacity rather than display:none
   keeps the swap instant instead of showing a blank frame while it decodes. */
.gal-shot{position:relative}
.gal-shot img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;
  object-position:top center}
.gal-open{position:absolute;inset:0;display:block}
.gal-open:focus-visible{outline:2px solid var(--obsidian);outline-offset:-2px}
.gal-first{opacity:0;transition:opacity .18s ease}
.gal-card.show-first .gal-first{opacity:1}
.gal-toggle{position:absolute;left:8px;bottom:8px;z-index:2;font-family:var(--sans);font-weight:600;
  font-size:10px;letter-spacing:.02em;padding:5px 9px;border:0;border-radius:var(--radius);
  background:var(--overlay-chip);box-shadow:var(--ring-ghost);color:var(--obsidian);
  cursor:pointer}
.gal-toggle:hover{background:var(--white)}
.gal-toggle:focus-visible{outline:2px solid var(--obsidian);outline-offset:2px}

.ask-rec{font-family:var(--sans);font-weight:600;font-size:11px;letter-spacing:.02em;line-height:1.7;
  margin:10px 0 0;color:var(--stone);max-width:44rem}
.ask-rec strong{color:var(--obsidian);font-weight:400}
.ask-rec-why{display:block;color:var(--smoke)}

.mkt-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:16px;
  max-width:min(76rem,100%);margin:32px 0 0}
@media(max-width:900px){.mkt-grid{grid-template-columns:1fr 1fr}}
@media(max-width:560px){.mkt-grid{grid-template-columns:1fr}}
.mkt-card{display:flex;flex-direction:column;background:var(--white);
  border-radius:var(--radius);box-shadow:var(--ring-card);overflow:hidden;min-width:0}
.mkt-body{padding:14px 16px 0;flex:1 1 auto}
.mkt-name{margin:0;font-size:15px;font-weight:450;color:var(--obsidian)}
.mkt-desc{margin:6px 0 0;font-size:13px;line-height:1.55;color:var(--stone)}
.mkt-meta{margin:10px 0 0;font-family:var(--sans);font-weight:600;font-size:10.5px;letter-spacing:.02em;
  color:var(--stone)}
.mkt-card .gal-go{margin:14px 16px 16px}

/* ── site nav + mega menu ────────────────────────────────────────────────────
   Click-to-open, not hover. A hover menu cannot be operated on a touch screen
   and is hostile to keyboards, and most people arrive here on a phone. */
.navlinks{display:flex;align-items:center;gap:4px}
.navlink{display:inline-flex;align-items:center;gap:4px;font-family:var(--sans);
  font-size:13px;line-height:1.4;color:var(--charcoal);padding:.45rem .6rem;
  border:0;background:none;cursor:pointer;border-radius:var(--radius);
  transition:background .15s ease,color .15s ease}
.navlink:hover{background:var(--white);color:var(--obsidian);text-decoration:none}
.navlink[aria-current="page"]{color:var(--obsidian)}
.navtemplates{color:var(--library-ink);background:var(--library-t)}
.navcar{font-size:9px;color:var(--stone)}
.navsp{width:8px}
.navsignin{color:var(--stone)}
.navtoggle{display:none;border:0;background:none;font-size:18px;line-height:1;
  color:var(--obsidian);cursor:pointer;padding:.4rem .5rem;border-radius:var(--radius)}
/* A drawn burger rather than the "\2630" glyph. The glyph renders at whatever
   weight the system font feels like — three hairlines on a Mac, three slabs on
   Android — on the one control that has to read as a control. */
.navburger{display:block;width:20px}
.navburger i{display:block;height:2px;border-radius:2px;background:currentColor;
  transition:transform .22s ease,opacity .18s ease}
.navburger i + i{margin-top:5px}
.navtoggle[aria-expanded="true"] .navburger i:nth-child(1){transform:translateY(7px) rotate(45deg)}
.navtoggle[aria-expanded="true"] .navburger i:nth-child(2){opacity:0}
.navtoggle[aria-expanded="true"] .navburger i:nth-child(3){transform:translateY(-7px) rotate(-45deg)}

/* The three wrappers the phone sheet needs and the desktop bar must not notice.
   The theme row and the action pair are `display:contents` so their children
   stay flex items of the bar exactly as they were before the wrappers existed.
   The sheet's own brand row is not: it is `display:none`, because its children
   are a SECOND copy of the brand and a close button, and contents would have
   put both of them in the middle of the desktop nav. (It did, for one build.) */
.navtheme,.navcta{display:contents}
.navsheet-top{display:none}
.navtheme-l,.navclose,.navscrim{display:none}
/* The room tile on a top-level row. Phone only — in the bar the label is the
   control and a tile beside it would make five icons compete with the three
   dozen inside the panels. */
.nav-rt{display:none}
.navcar{display:inline-flex;align-items:center;color:var(--stone)}
.navcar svg{width:13px;height:13px;transform:rotate(90deg);transition:transform .2s ease}

.mega-wrap{position:relative}
/* Anchored to the right, not the left: the trigger sits near the right-hand end
   of the bar, so a 640px panel opening leftwards ran clean off the screen. */
/* Redesigned 2026-08-31 to the founder's standard: each row is an icon tile,
   a bold title, and a plain-sans description — the old columns of mono text
   read as a printed index ("all text, no design"). The mono voice stays for
   DATA everywhere else on the site; navigation is wayfinding, not data. */
.mega{position:absolute;top:calc(100% + 8px);right:0;z-index:40;display:grid;
  grid-template-columns:repeat(3,minmax(220px,1fr));gap:6px 18px;
  background:var(--white);border-radius:16px;box-shadow:var(--ring-card),var(--drop-lg);
  padding:22px 22px 14px;min-width:760px}
/* A two-column panel should not be stretched to the width of a three-column
   one — the sub-labels end up on one line each with a hand's width of empty
   card beside them, which reads as a rendering fault rather than as a shorter
   menu. */
.mega.cols-2{grid-template-columns:repeat(2,minmax(250px,1fr));min-width:560px}
.mega-h{font-family:var(--sans);font-size:11px;font-weight:700;letter-spacing:.08em;
  text-transform:uppercase;color:var(--stone);margin:0 0 8px}
.mega a{display:flex;align-items:flex-start;gap:11px;padding:8px 10px;margin:0 -10px;
  border-radius:12px;transition:background .15s ease}
.mega a:hover{background:var(--paper);text-decoration:none}
.mega-i{flex:0 0 auto;width:32px;height:32px;border-radius:9px;display:grid;place-items:center;
  color:var(--brand);background:color-mix(in srgb, var(--brand) 10%, transparent)}
.mega-i svg{width:17px;height:17px}
.mega a:hover .mega-i{background:color-mix(in srgb, var(--brand) 16%, transparent)}
.mega-t{min-width:0}
.mega-l{display:flex;align-items:center;gap:7px;font-size:14px;font-weight:600;
  color:var(--obsidian);line-height:1.35}
.mega-chip{font-size:10px;font-weight:700;letter-spacing:.04em;line-height:1;
  color:var(--brand-ink);background:color-mix(in srgb, var(--brand) 12%, transparent);
  border-radius:9999px;padding:3px 7px}
.mega-s{display:block;font-family:var(--sans);font-size:12.5px;
  color:var(--stone);line-height:1.45;margin-top:2px}
/* The panel closes on an invitation, the way a menu that believes in its
   product should. */
.mega-cta{grid-column:1 / -1;display:flex;align-items:center;justify-content:space-between;
  gap:12px;border-top:1px solid var(--hairline);margin-top:10px;padding:12px 0 4px}
.mega-cta-note{font-size:12.5px;color:var(--stone)}

/* ── the phone menu, as a sheet ──────────────────────────────────────────────
   The founder's verdict on what this replaces: it "wasn't redesigned like you
   redesigned everything else". It was a 10px-padded white card hanging under
   the bar holding six words in a column, a moon glyph with nothing beside it to
   say what it did, and rows about 33px tall.

   What it is now: a full-height sheet on the paper colour, slid in from the
   right, with the brand and a close button along its top, one 56px row per
   destination carrying that room's icon tile and a chevron, the three menus
   opening inline as an accordion, a theme row with the word "Theme" on it, and
   the two actions pinned to the bottom where a thumb is.

   Three things it must not do, all of which the old one did. It must not scroll
   the page behind it (`.nav-open`), it must not let Tab walk out of it into a
   page nobody can see (the trap in NAVJS), and it must not be reachable by
   keyboard while it is closed — which is why it is hidden with `visibility`
   rather than moved off screen. */
@media(max-width:1100px){
  .navtoggle{display:block}

  /* The header's backdrop-filter would otherwise become the containing block
     for the fixed sheet inside it and pin it to a 64px-tall strip. The blur is
     bought with a colour that is already 85% opaque; on a phone it is worth
     less than a menu that opens. */
  header nav{backdrop-filter:none;-webkit-backdrop-filter:none;background:var(--paper)}

  .navscrim{display:block;position:fixed;inset:0;z-index:59;
    background:rgba(0,0,0,.42);animation:navfade .2s ease}
  .navscrim[hidden]{display:none}
  @keyframes navfade{from{opacity:0}to{opacity:1}}

  /* The off-canvas drawer must not make the PAGE scroll sideways.
     ─────────────────────────────────────────────────────────────────────────
     .navlinks is parked with translateX(100%), which browsers count towards
     the document's scrollable width: at a 280px effective viewport — a phone,
     or anybody who has turned their text size up — compare, index and pricing
     all scrolled horizontally, 383px of page in 280px of screen. Found on the
     first complete run of check-layout (980 renders), which had been dying
     part way through for months.

     `clip`, not `hidden`: hidden makes html a scroll container and silently
     breaks every position:sticky inside it, which is most of the site's
     navigation. clip refuses the overflow without creating a scroll port. */
  html{overflow-x:clip}
  .navlinks{display:flex;position:fixed;top:0;right:0;bottom:0;left:auto;
    width:min(400px,100vw);z-index:60;
    flex-direction:column;align-items:stretch;gap:0;
    background:var(--paper);box-shadow:var(--drop-lg);border-radius:0;
    padding:0;margin:0;
    overflow-y:auto;overscroll-behavior:contain;-webkit-overflow-scrolling:touch;
    /* visibility is switched with a DELAY on the way out and none on the way
       in, never transitioned. Chrome interpolates a visibility transition
       discretely at the END of its duration, so `visibility .28s` left the
       sheet computing `hidden` for the entire 280ms of its own slide-in: the
       animation could not be seen, the panel popped in at the end, and
       focus() aimed at the close button was a silent no-op on an element the
       engine still considered invisible. Measured, not guessed — a probe read
       `hidden` immediately after the class went on and `visible` 400ms later.
       Delaying it out and not in is the shape that actually works: visible the
       instant it opens, still visible while it slides away. */
    visibility:hidden;transform:translateX(100%);
    transition:transform .28s cubic-bezier(.2,.7,.3,1),visibility 0s linear .28s}
  .navlinks.open{visibility:visible;transform:none;
    transition:transform .28s cubic-bezier(.2,.7,.3,1),visibility 0s}
  @media(prefers-reduced-motion:reduce){.navlinks,.navlinks.open{transition:none}.navscrim{animation:none}}

  /* A full-screen surface needs a top. Sticky, because the list under it is
     longer than a phone and the way out should not scroll away. */
  .navsheet-top{display:flex;align-items:center;justify-content:space-between;
    gap:12px;padding:14px 18px;position:sticky;top:0;z-index:2;
    background:var(--paper);border-bottom:1px solid var(--hairline)}
  .navsheet-top .brand{font-size:15px}
  .navsheet-top .brand .mark{width:26px;height:26px}
  .navclose{display:inline-flex;align-items:center;justify-content:center;
    width:40px;height:40px;margin-right:-8px;border:0;background:none;cursor:pointer;
    color:var(--stone);border-radius:var(--radius-pill)}
  .navclose svg{width:20px;height:20px}
  .navclose:hover{background:var(--sunk);color:var(--obsidian)}
  .navclose:focus-visible{outline:2px solid var(--brand-ink);outline-offset:2px}

  /* One row per destination. 56px, 16px text, sentence case — the size a thumb
     wants and the voice the rest of the site uses. */
  .navlinks > .navlink,.navlinks .mega-btn{
    display:flex;align-items:center;gap:13px;width:100%;
    min-height:56px;padding:12px 18px;margin:0;border-radius:0;
    font-family:var(--sans);font-size:16px;font-weight:500;letter-spacing:normal;
    text-transform:none;color:var(--obsidian);
    border-bottom:1px solid var(--hairline);background:none;text-align:left}
  .navlinks > .navlink:hover,.navlinks .mega-btn:hover{background:var(--sunk)}
  .navlinks .navent{font-weight:500}
  .nav-l{flex:1 1 auto;min-width:0}
  .nav-rt{display:grid;place-items:center;flex:none;width:36px;height:36px;
    border-radius:10px;background:var(--t,var(--sunk));color:var(--ink-r,var(--brand-ink))}
  .nav-rt svg{width:19px;height:19px}
  .navcar svg{transform:none}
  .mega-btn[aria-expanded="true"] .navcar svg{transform:rotate(90deg)}
  .mega-btn[aria-expanded="true"]{background:var(--sunk);border-bottom-color:transparent}

  /* The accordion body. It sits ON the tinted well the open row turns into, so
     an open section reads as one object rather than as a list that suddenly
     grew a second list. */
  .mega,.mega.cols-2{position:static;min-width:0;display:block;
    background:var(--sunk);box-shadow:none;border-radius:0;
    border-bottom:1px solid var(--hairline);
    padding:2px 18px 14px;gap:0}
  .mega-col + .mega-col{margin-top:10px}
  .mega-h{font-size:11px;margin:10px 0 4px}
  .mega a{padding:11px 10px;margin:0 -10px;min-height:48px;align-items:center}
  .mega a:hover{background:var(--white)}
  .mega-i{width:34px;height:34px;border-radius:10px}
  .mega-i svg{width:18px;height:18px}
  .mega-l{font-size:15px}
  .mega-s{font-size:13px}
  /* The sheet has its own pinned invitation at the bottom; three more of them,
     one per open accordion, is the same button four times. */
  .mega-cta{display:none}

  .navsp{display:none}

  /* The moon, with a word next to it. It had neither a label nor a row of its
     own, so on the phone it read as a stray glyph in a list of links. */
  .navtheme{display:flex;align-items:center;justify-content:space-between;
    gap:12px;min-height:56px;padding:10px 18px;
    border-bottom:1px solid var(--hairline)}
  .navtheme-l{display:block;font-family:var(--sans);font-size:16px;font-weight:500;
    color:var(--obsidian)}
  .navtheme .themebtn{width:44px;height:44px;font-size:16px;background:var(--sunk)}

  /* Pinned, so the two things somebody came to do are under a thumb whether
     they read the list or not. */
  .navcta{display:grid;gap:9px;padding:14px 18px calc(14px + env(safe-area-inset-bottom));
    margin-top:auto;position:sticky;bottom:0;z-index:2;
    background:var(--paper);border-top:1px solid var(--hairline)}
  .navcta .navsignin{display:block;text-align:center;font-family:var(--sans);
    font-size:15px;font-weight:500;padding:.8rem 1rem;border-radius:var(--radius);
    color:var(--charcoal);background:transparent;box-shadow:var(--ring-ghost)}
  .navcta .navsignin:hover{background:var(--white);color:var(--obsidian)}
  .navcta .btn.sm{display:block;text-align:center;font-size:15px;
    padding:.8rem 1rem;border-radius:var(--radius)}
}
/* The page behind an open sheet holds still. Without it the body scrolls under
   the sheet on iOS and you close the menu somewhere you never chose to be. */
html.nav-open,html.nav-open body{overflow:hidden}

/* ── the mark above the eyebrow ─────────────────────────────────────────── */
/* The hero is centred, so a left-aligned mark reads as a stray image rather
   than the top of the composition. */
.hero-mark{width:56px;height:auto;display:block;margin:0 auto 16px}
@media(max-width:900px){.hero-mark{width:40px;height:40px}}

/* ── sub-page furniture ─────────────────────────────────────────────────── */
.hero-sm{padding-top:56px;padding-bottom:0}
.hero-sm h1{font-size:clamp(2rem,5vw,3.25rem);margin:0 0 1rem}
.prose{max-width:44rem;padding:56px 0 0}
.prose h1{font-size:clamp(2rem,5vw,3rem);margin:0 0 1.25rem}
.prose h2{font-size:clamp(1.4rem,2.6vw,1.85rem);margin:2.5rem 0 .75rem}
.prose p{font-size:1rem;line-height:1.7;color:var(--charcoal);max-width:38rem}
.prose .lede{font-size:1.125rem;line-height:1.65;color:var(--obsidian)}
.prose .btn{margin-right:8px}

.tbl{margin:24px 0 0;max-width:min(44rem,100%);overflow-x:auto}
.tbl table{border-collapse:collapse;width:100%;background:var(--white);
  border-radius:var(--radius);box-shadow:var(--ring-card);overflow:hidden}
.tbl caption{text-align:left;font-family:var(--sans);font-weight:600;font-size:10px;letter-spacing:.071em;
  text-transform:uppercase;color:var(--stone);padding:0 0 10px}
.tbl th,.tbl td{text-align:left;padding:10px 14px;font-size:13.5px;
  border-bottom:1px solid var(--hairline)}
.tbl th{font-family:var(--sans);font-weight:600;font-size:10.5px;letter-spacing:.03em;
  text-transform:uppercase;color:var(--stone);font-weight:600}
.tbl tr:last-child td{border-bottom:0}
.tbl td:nth-child(2),.tbl td:nth-child(3){font-variant-numeric:tabular-nums;color:var(--obsidian)}
.tbl figcaption{font-family:var(--sans);font-weight:600;font-size:11px;letter-spacing:.02em;
  color:var(--stone);line-height:1.6;margin-top:10px}

.posts{display:grid;gap:2px;max-width:min(46rem,100%)}
.post{background:var(--white);border-radius:var(--radius);box-shadow:var(--ring-card);
  padding:22px 24px;margin-bottom:14px}
.post-date{font-family:var(--mono);font-size:10.5px;letter-spacing:.03em;
  text-transform:uppercase;color:var(--stone);margin:0 0 8px}
.post h2{font-size:1.4rem;margin:0 0 .5rem;letter-spacing:-.03em}
.post h2 a{color:var(--obsidian)}
.post .sub{margin:0;font-size:14.5px}

.cta-band{text-align:left}
.cta-band .sub{margin-bottom:1.25rem}


/* ── the demo: terminal ─ beam ─ phone ───────────────────────────────────────
   The terminal IS the marketing in this system, so it gets the full CLI-panel
   treatment: white surface, hairline ring, mono at 12-13px, ▲ for commands and
   ✓ in terminal green for confirmations. */
.stage{display:grid;grid-template-columns:minmax(0,1.25fr) auto minmax(0,.75fr);
  gap:0;align-items:center;max-width:58rem;margin:80px auto 0;text-align:left}
@media(max-width:880px){.stage{grid-template-columns:1fr;gap:24px;justify-items:center}}
.term{width:100%;background:var(--white);border-radius:var(--radius);
  box-shadow:var(--ring-card);overflow:hidden}
.term .bar{display:flex;align-items:center;gap:6px;padding:10px 14px;
  border-bottom:1px solid var(--hairline)}
.term .bar i{width:8px;height:8px;border-radius:var(--radius-pill);background:var(--hairline)}
.term .bar span{margin-left:auto;font-family:var(--mono);font-size:11px;
  letter-spacing:.071em;color:var(--stone)}
.term .scr{padding:16px;min-height:8rem;font-family:var(--mono);font-size:12px;
  line-height:1.67;color:var(--obsidian)}
.term .ps{color:var(--stone)}
.term .cur{display:inline-block;width:7px;height:13px;background:var(--obsidian);
  vertical-align:-2px;margin-left:1px;animation:blink 1.1s steps(1) infinite}
#tout div{color:var(--charcoal)}
#tout b{color:var(--obsidian);font-weight:500}   /* ▲ — work in progress */
#tout .ok{color:var(--live)}                      /* ✓ — this step finished */
#tout .mem{color:var(--kept)}                     /* what the turn kept */
@keyframes blink{50%{opacity:0}}
.tfoot{display:flex;gap:20px;padding:8px 14px;border-top:1px solid var(--hairline);
  font-family:var(--mono);font-size:11px;letter-spacing:.071em;color:var(--stone)}
.tf-b{color:var(--obsidian)}     /* compute */
.tf-a{color:var(--kept)}        /* Memory */

/* The link between the two surfaces. A hairline with a travelling tick — no
   glow, no gradient bloom. */
.beam{position:relative;width:150px;height:1px;background:var(--hairline);margin:0 -1px}
.beam::after{content:"";position:absolute;top:-1px;left:0;width:34px;height:3px;
  border-radius:3px;background:var(--obsidian);animation:beam 2.2s ease-in-out infinite}
@keyframes beam{0%{left:-10%}55%,100%{left:90%}}
@media(max-width:880px){
  .beam{width:1px;height:64px}
  .beam::after{width:3px;height:34px;left:-1px;top:0;animation:beamv 2.2s ease-in-out infinite}
  @keyframes beamv{0%{top:-10%}55%,100%{top:90%}}
}

.phone{width:210px;background:var(--obsidian);border-radius:30px;padding:8px}
.phone .notch{width:64px;height:16px;background:var(--obsidian);
  border-radius:0 0 12px 12px;margin:0 auto}
.phone .scr{background:var(--white);border-radius:22px;padding:14px 12px;min-height:13.5rem;
  font-family:var(--mono);font-size:11px;line-height:1.8;
  display:grid;align-content:start;gap:2px}
.phone .t{font-family:var(--sans);font-size:14px;font-weight:500;letter-spacing:-.02em;
  color:var(--obsidian);display:flex;align-items:center;gap:6px}
.dot{width:6px;height:6px;border-radius:var(--radius-pill);background:var(--live);
  animation:pulse 1.6s ease-in-out infinite}
@keyframes pulse{50%{opacity:.35}}
.phone .mut{color:var(--smoke)}
#mlist div{color:var(--charcoal)}
#mlist div b{color:var(--obsidian);font-weight:500}
#mlist div.mem{color:var(--kept)}
.phone .tag{justify-self:start;margin-top:auto;display:inline-block;
  font-family:var(--sans);font-weight:600;font-size:10px;letter-spacing:.071em;color:var(--stone);
  box-shadow:var(--ring-ghost);border-radius:var(--radius);padding:2px 6px}

/* ── sign-in dialog ──────────────────────────────────────────────────────── */
.signin{border:0;border-radius:var(--radius);background:var(--white);
  box-shadow:var(--ring-card);color:var(--charcoal);
  padding:32px 28px 24px;max-width:24rem;width:calc(100% - 2rem);
  text-align:center;position:relative}
.signin::backdrop{background:var(--scrim);backdrop-filter:blur(2px)}
.signin h2{font-size:1.5rem;font-weight:450;letter-spacing:-.03em;line-height:1.15;
  margin:0 0 .5rem}
.signin-what{color:var(--stone);font-size:14px;margin:0 0 1rem;line-height:1.5}
.signin-fine{font-family:var(--sans);font-weight:600;color:var(--stone);font-size:11px;
  letter-spacing:.02em;margin:1rem 0 0;line-height:1.6}
.signin .gbtn-wrap{display:flex;justify-content:center;min-height:44px}
.signin-x{position:absolute;top:8px;right:10px;background:none;border:0;
  color:var(--smoke);font-size:1.35rem;line-height:1;cursor:pointer;padding:2px 6px;
  border-radius:var(--radius)}
.signin-x:hover{color:var(--obsidian)}
.signin-err{color:var(--alarm)}

.or{display:flex;align-items:center;gap:10px;margin:16px 0 12px;
  font-family:var(--sans);font-weight:600;color:var(--stone);font-size:11px;letter-spacing:.071em;
  text-transform:uppercase}
.or::before,.or::after{content:'';flex:1;height:1px;background:var(--hairline)}
.mailform{display:flex;gap:8px;flex-wrap:wrap}
.mailform input{flex:1;min-width:0;background:var(--paper);border:0;
  box-shadow:var(--ring-ghost);border-radius:var(--radius);padding:.55rem .8rem;
  color:var(--obsidian);font-family:var(--sans);font-size:14px}
.mailform input:focus{outline:none;box-shadow:0 0 0 1px var(--obsidian)}
.mailform input::placeholder{color:var(--smoke)}
.mailform .btn{white-space:nowrap}
@media(max-width:380px){.mailform{flex-direction:column}.mailform .btn{width:100%}}

/* ── why this one ────────────────────────────────────────────────────────── */
.surfaces{display:flex;gap:8px;flex-wrap:wrap;justify-content:center;margin-top:32px}
.surface{display:flex;align-items:center;gap:8px;background:var(--white);
  box-shadow:var(--ring-ghost);border-radius:var(--radius);padding:.5rem .9rem;font-size:14px}
.surface b{font-weight:500;color:var(--obsidian)}
.surface span{color:var(--stone)}

/* A grid whose only structure is hairlines between the cells. */
.feats{display:grid;grid-template-columns:1fr 1fr;gap:0;margin-top:40px;
  border-radius:var(--radius);box-shadow:var(--ring-card);overflow:hidden;
  background:var(--white)}
@media(max-width:760px){.feats{grid-template-columns:1fr}}
.feat{padding:24px;border-right:1px solid var(--hairline);
  border-bottom:1px solid var(--hairline)}
.feat:nth-child(2n){border-right:0}
.feat:nth-last-child(-n+2){border-bottom:0}
@media(max-width:760px){
  .feat{border-right:0}
  .feat:nth-last-child(-n+2){border-bottom:1px solid var(--hairline)}
  .feat:last-child{border-bottom:0}
}
.feat h3{font-size:1.0625rem;font-weight:500;letter-spacing:-.02em;margin:0 0 .35rem}
.feat p{margin:0;color:var(--charcoal);font-size:14px;line-height:1.5}
.feat-ic{display:block;font-size:12px;margin-bottom:12px;color:var(--obsidian)}

/* ── three ways in ───────────────────────────────────────────────────────── */
.ways{display:grid;grid-template-columns:repeat(3,1fr);gap:16px;margin-top:40px}
@media(max-width:880px){.ways{grid-template-columns:1fr}}
.way{background:var(--white);border-radius:var(--radius);box-shadow:var(--ring-card);
  padding:16px}
/* The recommended path inverts rather than tinting — flipping the value scale
   is how this system says "this one", since it has no colour to say it with. */
.way.feature{background:var(--obsidian)}
.way.feature h3,.way.feature .way-tag{color:var(--white)}
.way.feature p{color:var(--ash)}
.way.feature .way-note{color:var(--graphite)}
.way h3{font-size:1.25rem;font-weight:450;letter-spacing:-.03em;margin:6px 0 8px}
.way p{color:var(--charcoal);font-size:14px;margin:0 0 12px;line-height:1.5}
.way-tag{font-family:var(--sans);font-weight:600;font-size:11px;letter-spacing:.071em;text-transform:uppercase;
  color:var(--obsidian);margin:0}
.way-note{font-family:var(--sans);font-weight:600;font-size:11px;letter-spacing:.02em;color:var(--stone);margin:0}
.way-code{background:var(--paper);box-shadow:var(--ring-ghost);border-radius:var(--radius);
  padding:12px;margin:0;overflow-x:auto;font-size:11px;line-height:1.7;color:var(--charcoal)}
.way-code code{font-family:var(--mono);white-space:pre-wrap;overflow-wrap:anywhere}

/* ── agents → memory ─────────────────────────────────────────────────────── */
.flowchart{max-width:56rem;margin:56px auto 0}
.flowchart svg{width:100%;height:auto;display:block}
.fc-chip{fill:var(--white);stroke:var(--hairline)}
.fc-lab{font:500 12px 'Geist Mono',ui-monospace,Menlo,monospace;fill:var(--obsidian)}
.fc-wire{fill:none;stroke:var(--hairline);stroke-width:1.2}
.fc-wire.lit{stroke:var(--obsidian);stroke-width:1.4;stroke-dasharray:5 90;
  animation:wire 3s linear infinite}
@keyframes wire{to{stroke-dashoffset:-95}}
/* Memory is the one green thing in the diagram, and that is the whole point. */
.fc-core{fill:var(--white);stroke:var(--kept);stroke-width:1.4}
.fc-core-lab{font:450 24px 'Geist',ui-sans-serif,system-ui,sans-serif;fill:var(--kept);
  letter-spacing:-1px}
.fc-core-sub{font:9px 'Geist Mono',ui-monospace,Menlo,monospace;fill:var(--stone);
  letter-spacing:.071em}
.fc-out{font:12px 'Geist Mono',ui-monospace,Menlo,monospace;fill:var(--stone)}
.fc-out tspan{fill:var(--kept)}

/* ── obsidian vault ──────────────────────────────────────────────────────── */
.vaultrow{display:grid;gap:32px;margin-top:48px;align-items:center}
@media(min-width:880px){.vaultrow{grid-template-columns:1.3fr 1fr}}
.vault-scr{white-space:pre;font-family:var(--mono);font-size:12px;line-height:1.9;
  overflow-x:auto;color:var(--charcoal)}
.vault-scr .d{color:var(--obsidian);font-weight:500}
.vault-scr .a{color:var(--kept)}
.vault-scr .c{color:var(--smoke)}
.vaultpts{display:grid;gap:16px;font-size:14px;color:var(--charcoal)}
.vaultpts .am{color:var(--kept);margin-right:8px}
.vaultpts .bl{color:var(--obsidian);margin-right:8px}

/* ── proof grid (asymmetric bento) ───────────────────────────────────────── */
.bento{display:grid;gap:16px;margin-top:48px;grid-template-columns:repeat(12,1fr)}
.cell{background:var(--white);border-radius:var(--radius);box-shadow:var(--ring-card);
  padding:24px;position:relative;overflow:hidden;grid-column:span 12}
@media(min-width:880px){
  .cell.a{grid-column:span 7}.cell.b{grid-column:span 5}
  .cell.c{grid-column:span 5}.cell.d{grid-column:span 7}}
.cell h3{font-size:1.375rem;font-weight:450;letter-spacing:-.03em;margin:0 0 .4rem}
.cell p{color:var(--charcoal);font-size:14px;margin:0;max-width:26rem;line-height:1.5}
.cell .art{margin-top:24px}

/* save bar: grey work, green kept */
.sbar{height:40px;border-radius:var(--radius);box-shadow:var(--ring-ghost);display:flex;
  overflow:hidden;background:var(--paper)}
.sbar .keep{background:var(--kept);animation:keep 3.2s ease-in-out infinite}
.sbar .work{background:var(--hairline)}
@keyframes keep{0%,15%{flex-basis:6%}60%,100%{flex-basis:38%}}
.slab{display:flex;justify-content:space-between;font-family:var(--sans);font-weight:600;font-size:11px;
  letter-spacing:.02em;color:var(--stone);margin-top:10px}
.slab b{color:var(--kept);font-weight:500}

/* orbit */
.orb{display:grid;place-items:center}
.orb svg{width:150px;height:150px;overflow:visible}
.orb .trk{fill:none;stroke:var(--hairline);stroke-dasharray:3 7}
.orb .pl{fill:var(--obsidian);transform-origin:75px 75px;animation:orbit 7s linear infinite}
.orb text{font:11px 'Geist Mono',ui-monospace,Menlo,monospace;fill:var(--stone)}
@keyframes orbit{to{transform:rotate(360deg)}}

/* phone shot cell */
.cell .shotwrap{display:grid;place-items:center;margin-top:24px}
.cell .shot{width:180px;border-radius:26px;border:6px solid var(--obsidian);display:block}
.shotwrap.fade{max-height:330px;overflow:hidden;
  -webkit-mask-image:linear-gradient(#000 72%,transparent);
          mask-image:linear-gradient(#000 72%,transparent)}

/* skills ticker */
.tick{margin-top:24px;overflow:hidden;
  -webkit-mask-image:linear-gradient(90deg,transparent,#000 12%,#000 88%,transparent);
          mask-image:linear-gradient(90deg,transparent,#000 12%,#000 88%,transparent)}
.tick .in{display:flex;gap:8px;width:max-content;animation:tick 22s linear infinite}
.tick span{box-shadow:var(--ring-ghost);border-radius:var(--radius);padding:6px 12px;
  font-family:var(--sans);font-weight:600;font-size:11px;color:var(--stone);white-space:nowrap;
  background:var(--paper)}
.tick span.hi{color:var(--kept);box-shadow:0 0 0 1px var(--kept-halo)}
@keyframes tick{to{transform:translateX(-50%)}}

.skillfile{margin:24px 0 0;background:var(--paper);box-shadow:var(--ring-ghost);
  border-radius:var(--radius);padding:16px;font-family:var(--mono);font-size:11px;
  line-height:1.9;color:var(--obsidian);white-space:pre-wrap}
.skillfile .c{color:var(--smoke)}
.skillfile .a{color:var(--kept)}

/* ── comparison matrix ───────────────────────────────────────────────────────
   Our column is marked by inverting its header and tinting the cells with a
   flat neutral, not by an accent — the system has no accent to spend here. */
.matrix{display:grid;grid-template-columns:minmax(11rem,1.6fr) repeat(3,1fr);
  margin-top:48px;border-radius:var(--radius);box-shadow:var(--ring-card);overflow:hidden;
  background:var(--white);font-size:13px}
.matrix>div{padding:12px 14px;border-top:1px solid var(--hairline);display:flex;
  align-items:center;justify-content:center;text-align:center;color:var(--stone)}
.matrix>div:nth-child(-n+4){border-top:0}
.mx-h{font-family:var(--sans);font-size:14px;font-weight:450;letter-spacing:-.02em;
  color:var(--slate);line-height:1.25}
.mx-h.us{color:var(--obsidian);font-weight:500}
.mx-r{justify-content:flex-start;text-align:left;color:var(--charcoal)}
.mx-c{font-family:var(--mono);font-size:13px}
.mx-c.y{color:var(--obsidian)}
.mx-c.am{color:var(--kept)}
.mx-c.us,.mx-h.us{background:var(--paper);
  box-shadow:inset 1px 0 0 var(--hairline),inset -1px 0 0 var(--hairline)}
.mx-h.us{box-shadow:inset 1px 0 0 var(--hairline),inset -1px 0 0 var(--hairline),
  inset 0 2px 0 var(--obsidian)}
@media(max-width:700px){.matrix{font-size:11px}.matrix>div{padding:8px 6px}.mx-r{min-width:0}}

/* ── pricing ─────────────────────────────────────────────────────────────── */
.price-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:16px;margin:48px 0 24px}
@media(max-width:880px){.price-grid{grid-template-columns:1fr}}
.price{background:var(--white);border-radius:var(--radius);box-shadow:var(--ring-card);
  padding:16px}
.price.feature{background:var(--obsidian)}
.price h3{font-family:var(--sans);font-weight:600;font-size:11px;letter-spacing:.071em;text-transform:uppercase;
  color:var(--obsidian);margin:0;font-weight:600}
.price.feature h3{color:var(--white)}
.price-big{font-family:var(--sans);font-size:2.25rem;font-weight:450;letter-spacing:-.05em;
  line-height:1;margin:12px 0 12px;color:var(--obsidian)}
.price.feature .price-big{color:var(--white)}
.price-big span{font-size:1.25rem;color:var(--stone)}
.price.feature .price-big span{color:var(--smoke)}
.price p:last-child{color:var(--charcoal);font-size:14px;margin:0;line-height:1.5}
.price.feature p:last-child{color:var(--ash)}

/* ── privacy ─────────────────────────────────────────────────────────────── */
.priv{text-align:center}
.priv h2{font-size:clamp(2.25rem,5.5vw,3.5rem)}
.priv .sub{margin-left:auto;margin-right:auto}
.priv .ringart{margin:48px auto 0;width:min(420px,80vw)}
.priv .ringart svg{width:100%;height:auto;overflow:visible}
.rg-ring{fill:none;stroke:var(--hairline);stroke-dasharray:4 8}
.rg-rot{transform-origin:320px 230px;animation:rgspin 80s linear infinite}
@keyframes rgspin{to{transform:rotate(360deg)}}
.rg-halo{display:none}   /* the glow was the dark theme's trick; hairline only now */
.rg-you{fill:var(--obsidian)}
.rg-lab{font:12px 'Geist Mono',ui-monospace,Menlo,monospace;fill:var(--stone)}
.rg-lab.big{font-size:13px;fill:var(--obsidian)}
.rg-chip rect{fill:var(--white);stroke:var(--hairline)}
.rg-chip text{font:12px 'Geist Mono',ui-monospace,Menlo,monospace;fill:var(--obsidian)}
.rg-chip.amber rect{stroke:var(--kept)}
.rg-chip.amber text{fill:var(--kept)}
.rg-out{font:14px 'Geist Mono',ui-monospace,Menlo,monospace;fill:var(--ash);
  letter-spacing:.071em;text-transform:uppercase}

/* ── get / beta / setup ──────────────────────────────────────────────────── */
.get{text-align:center}
.beta{background:var(--white);border-radius:var(--radius);box-shadow:var(--ring-card);
  padding:24px;margin:0 auto 32px;max-width:600px;text-align:left}
.beta .cap{color:var(--obsidian);margin:0 0 8px;text-align:left}
.beta h3{color:var(--obsidian);font-size:1.25rem;font-weight:450;letter-spacing:-.03em;
  line-height:1.2;margin:0 0 8px;text-align:left}
.beta p{color:var(--charcoal);font-size:14px;line-height:1.5;margin:0 0 16px;text-align:left}
.beta-cta{display:flex;gap:12px;flex-wrap:wrap;justify-content:flex-start}
.beta .fine{margin:16px 0 0;text-align:left}
@media(max-width:560px){.beta{padding:16px}.beta-cta .btn{flex:1 1 100%;text-align:center}}

.setup{max-width:44rem;margin:40px auto}
.setup-scr{white-space:pre-wrap;font-family:var(--mono);font-size:12px;line-height:1.9;
  color:var(--obsidian)}
.setup-scr .c{color:var(--smoke)}
.setup-scr .ok{color:var(--live)}

/* The Product Hunt badge is a third-party image and arrives in Product Hunt
   orange, which makes it the single loudest thing on an otherwise achromatic
   page. Desaturated to sit in the logo-strip convention this system uses for
   other people's marks; full colour returns on hover, so the badge is never
   misrepresented to anyone who looks at it directly. */
.ph{margin:24px 0 0;display:flex;justify-content:center}
.ph img{display:block;width:250px;height:54px;
  filter:grayscale(1) contrast(.9);opacity:.75;transition:filter .2s ease,opacity .2s ease}
.ph a:hover img,.ph a:focus-visible img{filter:none;opacity:1}

/* ── footer ──────────────────────────────────────────────────────────────────
   The rule this replaced had no container. `main` is capped at 1280px and
   padded 24px; `<footer>` is a sibling of it directly under <body>, so it
   inherited nothing and ran to the raw viewport edges — measured at 390px, the
   first link's left edge was x=0, hard against the bezel, while every pixel
   above it was inset. That is most of why it read as unfinished.

   The border stays full-bleed (it is the line that ends the page) and .foot-in
   carries the same cap and gutter as main, so the footer's first column now
   starts on the same vertical as the headline above it.

   Sans, not mono, and headed columns: 11px mono is the voice this system uses
   for machine facts — prices, model names, timings. A directory of pages is
   navigation, and setting navigation in the price typeface is what made
   eighteen links read as small print. */
footer{border-top:1px solid var(--hairline);margin-top:88px;
  color:var(--stone);font-family:var(--sans);font-size:14px;letter-spacing:normal}
.foot-in{max-width:1280px;margin:0 auto;padding:56px 24px 40px}

/* Brand block and columns. One breakpoint drops the columns under the brand,
   a second halves them — five 1fr columns at 560px would be 60px wide, which
   wraps "Claude Code on mobile" to four lines. */
/* minmax(0,...) on BOTH tracks, and min-width:0 on the nav.
   A grid item's automatic minimum is its min-content, and .foot-cols is itself
   a grid — so its min-content is the sum of five columns' longest words plus
   four gaps, which came to 980px against an 884px track. It overflowed the
   page by 24px at exactly 1280, the one width where the cap and the viewport
   meet, and nowhere else; the computed track still read 884 while the box
   measured 980, which is the tell for this and reads as a browser bug if you
   have not hit it before. */
.foot-main{display:grid;gap:40px;align-items:start;
  grid-template-columns:minmax(0,280px) minmax(0,1fr)}
.foot-cols{min-width:0;display:grid;gap:32px 24px;
  grid-template-columns:repeat(5,minmax(0,1fr))}
@media(max-width:1080px){
  .foot-main{grid-template-columns:1fr;gap:44px}
  .foot-cols{grid-template-columns:repeat(3,minmax(0,1fr))}
}
@media(max-width:560px){.foot-cols{grid-template-columns:repeat(2,minmax(0,1fr))}}

.foot-brand .brand{display:inline-flex;align-items:center;gap:8px;
  font-family:var(--sans);font-size:15px;font-weight:500;color:var(--obsidian)}
.foot-brand .brand:hover{text-decoration:none}
.foot-blurb{margin:14px 0 20px;max-width:34ch;
  font-size:13.5px;line-height:1.6;color:var(--stone)}
/* .btn paints itself brand-on; `footer a{color}` below would otherwise repaint
   the label the same grey as the ground it sits on. */
.foot-brand .btn{color:var(--brand-on)}
/* --slate, not --graphite, even though --graphite's own comment in the token
   block reads "footer micro-copy". It is 3.10:1 on paper and 4.18:1 on the
   dark ground — under AA either way, so there is no size of text it may
   legally carry. check-layout caught it across 189 pages the moment this
   footer started using it. --slate is the tertiary-text step: 4.9:1 / 6.1:1. */
.foot-fine{margin:12px 0 0;font-family:var(--sans);font-weight:600;font-size:11px;
  letter-spacing:.02em;color:var(--slate)}

.foot-h{margin:0 0 10px;font-family:var(--sans);font-size:11px;font-weight:500;
  letter-spacing:.08em;text-transform:uppercase;color:var(--obsidian)}
/* display:block so each link is its own row and its own tap target — the
   pointer:coarse rule further down sets inline-block for the old flat row and
   is out-specified here on purpose. 13.5px x 1.5 + 12px padding = 32px tall,
   clear of WCAG 2.2's 24px floor without needing the coarse-pointer branch. */
.foot-col a{display:block;padding-block:6px;font-size:13.5px;line-height:1.5;
  color:var(--stone)}
.foot-col a:hover{color:var(--brand-ink);text-decoration:none}
.foot-tag{margin-left:6px;padding:1px 6px;border-radius:999px;
  background:var(--brand);color:var(--brand-on);
  font-family:var(--sans);font-weight:600;font-size:9px;letter-spacing:.04em;vertical-align:1px}

footer a{color:var(--stone)}
footer a:hover{color:var(--obsidian)}

.foot-base{display:flex;flex-wrap:wrap;align-items:center;gap:8px 24px;
  margin-top:48px;padding-top:24px;border-top:1px solid var(--hairline);
  font-family:var(--sans);font-weight:600;font-size:11px;letter-spacing:.02em;color:var(--slate)}
.foot-legal{display:flex;flex-wrap:wrap;gap:4px 18px}
.foot-legal a{color:var(--slate);padding-block:6px}
.foot-legal a:hover{color:var(--obsidian)}
/* The sign-off. It is a pill rather than a line of text because it is the one
   thing in the footer we want read as a claim — this site is a build, not a
   brochure about builds. */
.foot-built{margin-left:auto;display:inline-flex;align-items:center;gap:8px;
  padding:7px 14px;border:1px solid var(--hairline);border-radius:999px;
  background:var(--white);color:var(--stone);
  font-family:var(--sans);font-size:12px;letter-spacing:normal;
  transition:border-color .15s ease,color .15s ease}
.foot-built:hover{border-color:var(--brand);color:var(--brand-ink);text-decoration:none}
.foot-built .mark{width:16px;height:16px}
@media(max-width:700px){.foot-built{margin-left:0;margin-top:8px}}

/* ── reveals ── retired 2026-09-05. Every section is visible at rest; a page
   that is blank until you scroll photographs blank and reads blank. ── */
.rv{opacity:1;transform:none}

/* ── guards ──────────────────────────────────────────────────────────────────
   The hidden attribute only gets display:none from the UA stylesheet, so any
   author rule that sets display beats it — .btn is inline-block and .mailform
   is flex, which has twice now left a hidden control on screen. Global,
   because the next hidden element will hit the same thing. */
[hidden]{display:none !important}

/* Grid and flex items default to min-width:auto, so a child that cannot wrap —
   the `git clone` URL inside .way-code — forces its track wider than the
   viewport and the overflow-x:auto never gets a chance to engage. */
.way,.price,.est-opt,.cell,.feat{min-width:0}
.way-code{max-width:100%}

/* ── the four steps ───────────────────────────────────────────────────────
   What the product actually does, in order. Numbered because the order is the
   point: each step only makes sense once the one before it has happened. */
.steps4{display:grid;grid-template-columns:repeat(4,1fr);gap:0;margin-top:40px;
  border-radius:var(--radius);box-shadow:var(--ring-card);overflow:hidden;
  background:var(--white)}
@media(max-width:900px){.steps4{grid-template-columns:1fr 1fr}}
@media(max-width:560px){.steps4{grid-template-columns:1fr}}
.s4{padding:24px;border-right:1px solid var(--hairline)}
.s4:last-child{border-right:0}
@media(max-width:900px){
  .s4{border-bottom:1px solid var(--hairline)}
  .s4:nth-child(2n){border-right:0}
  .s4:nth-last-child(-n+2){border-bottom:0}
}
@media(max-width:560px){
  .s4{border-right:0;border-bottom:1px solid var(--hairline)}
  .s4:last-child{border-bottom:0}
}
.s4-n{display:block;font-family:var(--mono);font-size:11px;letter-spacing:.071em;
  color:var(--stone);margin-bottom:12px}
.s4 h3{font-size:1.0625rem;font-weight:500;letter-spacing:-.02em;margin:0 0 .35rem}
.s4 p{margin:0;color:var(--charcoal);font-size:14px;line-height:1.5}

/* ═══ DIAGRAMS ══════════════════════════════════════════════════════════════
   Explaining the product with shapes instead of another paragraph.

   ── Why none of this is SVG ─────────────────────────────────────────────────
   A chart drawn into a fixed viewBox reads well at 1440 and becomes a postage
   stamp at 375: the whole drawing scales, so 12px labels become 7px. These are
   built from real elements with real type, so they reflow and the words stay
   the size words should be. The one thing SVG would buy — curved connectors —
   is not worth a diagram nobody on a phone can read.

   ── Palette discipline ──────────────────────────────────────────────────────
   Hairline rules, tonal fills, mono labels. Green appears in exactly the place
   the system already reserves it for — "kept" — which here means the repo, the
   memory and the live state. Nothing else is allowed to be a colour, so where
   colour appears it means something.
   ────────────────────────────────────────────────────────────────────────── */
.fig{margin:36px 0 0;max-width:min(46rem,100%)}
.fig figcaption{font-family:var(--sans);font-weight:600;font-size:11px;line-height:1.65;
  letter-spacing:.02em;color:var(--stone);margin-top:14px;max-width:38rem}
.fig-wide{max-width:min(58rem,100%)}

/* ── the numbered chain ──────────────────────────────────────────────────── */
.flow{display:grid;gap:10px;grid-template-columns:repeat(auto-fit,minmax(158px,1fr))}
.flow-step{background:var(--white);border-radius:var(--radius);
  box-shadow:var(--ring-card);padding:14px 15px 16px}
.flow-step.is-out{background:var(--obsidian)}
.flow-step.is-out .flow-t{color:var(--paper)}
.flow-step.is-out .flow-n,.flow-step.is-out .flow-s{color:var(--ash)}
.flow-n{display:block;font-family:var(--mono);font-size:10px;letter-spacing:.071em;
  color:var(--stone);margin-bottom:9px}
.flow-t{margin:0;font-size:13.5px;line-height:1.35;color:var(--obsidian)}
.flow-s{margin:7px 0 0;font-family:var(--sans);font-weight:600;font-size:10.5px;line-height:1.55;
  color:var(--stone)}

/* ── measured quantities ─────────────────────────────────────────────────── */
.meter{display:grid;gap:9px;background:var(--white);border-radius:var(--radius);
  box-shadow:var(--ring-card);padding:18px 18px 20px}
.meter-row{display:grid;grid-template-columns:minmax(84px,auto) 1fr minmax(58px,auto);
  align-items:center;gap:12px}
.meter-l{font-family:var(--sans);font-weight:600;font-size:11px;letter-spacing:.02em;color:var(--stone)}
.meter-track{position:relative;height:12px;background:var(--paper);
  box-shadow:inset 0 0 0 1px var(--hairline);border-radius:2px;overflow:hidden}
.meter-track i{display:block;height:100%;background:var(--obsidian);border-radius:2px 0 0 2px}
.meter-row.is-kept .meter-track i{background:var(--kept)}
.meter-row.is-ghost .meter-track i{background:var(--ash)}
.meter-v{font-family:var(--mono);font-size:11.5px;color:var(--obsidian);
  text-align:right;font-variant-numeric:tabular-nums}
.meter-note{grid-column:1/-1;font-family:var(--sans);font-weight:600;font-size:10.5px;color:var(--stone);
  margin:2px 0 0}

/* ── a spread laid out on one rule ───────────────────────────────────────── */
.scale{background:var(--white);border-radius:var(--radius);box-shadow:var(--ring-card);
  padding:26px 22px 18px}
.scale-rule{position:relative;height:1px;background:var(--hairline);margin:34px 0 0}
/* Tonal, not chromatic: the recommended point is the only one at full strength
   and the rest recede. Green is reserved for "kept" and spending it on "this is
   the one we suggest" would make the palette mean two things at once. */
.scale-pt{position:absolute;top:50%;width:8px;height:8px;margin:-4px 0 0 -4px;
  border-radius:50%;background:var(--ash);box-shadow:0 0 0 3px var(--white)}
.scale-pt.is-pick{width:12px;height:12px;margin:-6px 0 0 -6px;background:var(--obsidian);
  box-shadow:0 0 0 3px var(--white),0 0 0 4px var(--hairline)}
.scale-lbl{position:absolute;bottom:14px;transform:translateX(-50%);white-space:nowrap;
  font-family:var(--sans);font-weight:600;font-size:10px;letter-spacing:.02em;color:var(--stone)}
/* Centring a label on a dot that sits on the end of the rule puts half of it
   outside the card. The end labels align to the edge instead. */
.scale-lbl.at-start,.scale-lbl.at-end{transform:none}
.scale-lbl.lo{bottom:auto;top:14px}
.scale-ends{display:flex;justify-content:space-between;margin-top:34px;
  font-family:var(--mono);font-size:10.5px;color:var(--stone)}
@media(max-width:640px){
  /* Dot labels collide below ~640px. The rule still shows the shape of the
     spread, which is the point; the numbers are in the table underneath. */
  .scale-lbl{display:none}
  .scale-rule{margin-top:8px}
  .scale-ends{margin-top:16px}
}

/* ── a sequence of states ────────────────────────────────────────────────── */
.states{display:flex;flex-wrap:wrap;align-items:stretch;gap:8px}
.state{flex:1 1 132px;background:var(--white);border-radius:var(--radius);
  box-shadow:var(--ring-card);padding:12px 13px}
.state-n{display:block;font-family:var(--sans);font-weight:600;font-size:10px;letter-spacing:.071em;
  text-transform:uppercase;color:var(--obsidian);margin-bottom:6px}
.state.is-live .state-n{color:var(--live)}
.state.is-bad .state-n{color:var(--alarm)}
.state-d{margin:0;font-size:12.5px;line-height:1.45;color:var(--charcoal)}

/* ── what a run looks like on screen ─────────────────────────────────────── */
.termlog{background:var(--white);border-radius:var(--radius);box-shadow:var(--ring-card);
  padding:6px 0;font-family:var(--mono);font-size:11.5px;line-height:1.5}
.termlog-r{display:grid;grid-template-columns:14px 1fr auto;gap:10px;align-items:baseline;
  padding:7px 16px;border-bottom:1px solid var(--hairline)}
.termlog-r:last-child{border-bottom:0}
.termlog-r>b:first-child{font-weight:400;color:var(--stone);text-align:center}
.termlog-r span{color:var(--charcoal);overflow-wrap:anywhere}
/* Emphasis inside a log line is tonal. The marker column and the elapsed time
   are the only things allowed to go green, and only on the finishing line —
   the palette reserves that colour for confirmations, not for highlighting. */
.termlog-r span b{font-weight:400;color:var(--obsidian)}
.termlog-r em{font-style:normal;color:var(--stone);font-variant-numeric:tabular-nums}
.termlog-r.is-done>b:first-child,.termlog-r.is-done em{color:var(--live)}
.termlog-r.is-done span{color:var(--obsidian)}

/* ── nested boundaries ───────────────────────────────────────────────────── */
.zones{background:var(--white);border-radius:var(--radius);box-shadow:var(--ring-card);
  padding:16px}
.zone{border:1px dashed var(--hairline);border-radius:var(--radius);padding:14px;
  margin-top:10px}
.zone:first-child{margin-top:0}
.zone-h{display:block;font-family:var(--sans);font-weight:600;font-size:10px;letter-spacing:.071em;
  text-transform:uppercase;color:var(--stone);margin-bottom:10px}
.zone-items{display:flex;flex-wrap:wrap;gap:6px;margin:0;padding:0;list-style:none}
.zone-items li{font-family:var(--sans);font-weight:600;font-size:11px;color:var(--charcoal);
  background:var(--paper);box-shadow:inset 0 0 0 1px var(--hairline);
  border-radius:2px;padding:5px 9px}
.zone-items li.is-kept{color:var(--kept)}
.zone-note{margin:10px 0 0;font-family:var(--sans);font-weight:600;font-size:10.5px;line-height:1.55;
  color:var(--stone)}

/* ── one thing feeding many ──────────────────────────────────────────────── */
.fan{display:grid;grid-template-columns:minmax(0,1fr);gap:10px}
@media(min-width:720px){.fan{grid-template-columns:minmax(0,240px) minmax(0,1fr)}}
.fan-hub{background:var(--obsidian);border-radius:var(--radius);padding:16px 17px}
/* Both levels, because this hub has been h3 and h4 and the rule must not
   depend on which. It is h3: the diagram follows an h2, so h4 SKIPS a level —
   which is what the audit reported, and what the previous note here had exactly
   backwards while claiming to have fixed it. */
.fan-hub h3,.fan-hub h4{margin:0;font-size:13.5px;color:var(--paper);font-weight:500}
.fan-hub p{margin:7px 0 0;font-family:var(--sans);font-weight:600;font-size:10.5px;line-height:1.55;
  color:var(--ash)}
.fan-out{display:grid;gap:8px;align-content:start}
.fan-item{background:var(--white);border-radius:var(--radius);box-shadow:var(--ring-card);
  padding:11px 14px;display:flex;justify-content:space-between;gap:12px;align-items:baseline}
.fan-item b{font-weight:400;font-size:13px;color:var(--obsidian)}
.fan-item span{font-family:var(--sans);font-weight:600;font-size:10.5px;color:var(--stone);text-align:right}

/* ── this way against that way ───────────────────────────────────────────── */
.vs{display:grid;gap:10px;grid-template-columns:repeat(auto-fit,minmax(230px,1fr))}
.vs-side{background:var(--white);border-radius:var(--radius);box-shadow:var(--ring-card);
  padding:16px 17px 18px}
.vs-side.is-ours{background:var(--obsidian)}
.vs-h{display:block;font-family:var(--sans);font-weight:600;font-size:10px;letter-spacing:.071em;
  text-transform:uppercase;color:var(--stone);margin-bottom:11px}
.vs-side.is-ours .vs-h{color:var(--ash)}
.vs-side ul{margin:0;padding:0;list-style:none;display:grid;gap:8px}
.vs-side li{font-size:13px;line-height:1.45;color:var(--charcoal);
  padding-left:16px;position:relative}
.vs-side li::before{content:"";position:absolute;left:0;top:.55em;width:7px;height:1px;
  background:var(--ash)}
.vs-side.is-ours li{color:var(--paper)}
.vs-side.is-ours li::before{background:var(--stone)}
.vs-cost{display:block;margin-top:14px;font-family:var(--mono);font-size:11px;
  color:var(--stone);font-variant-numeric:tabular-nums}
/* The one cost figure that carries a hue. Cost is a value ramp everywhere
   else — budget→frontier is a legitimate choice and a traffic light would
   call frontier bad — but a ramp needs a scale, and this is two sides of a
   comparison. Marking the winning side is the whole point of the figure. */
.vs-side.is-ours .vs-cost{color:var(--live-inv)}

/* ── in one line, before the detail ──────────────────────────────────────── */
.tldr{display:flex;gap:14px;align-items:flex-start;margin:28px 0 0;
  background:var(--white);border-radius:var(--radius);box-shadow:var(--ring-card);
  padding:15px 18px;max-width:min(44rem,100%)}
.tldr b{flex:none;font-family:var(--sans);font-weight:600;font-size:10px;letter-spacing:.071em;
  text-transform:uppercase;color:var(--stone);font-weight:600;padding-top:3px}
.tldr p{margin:0;font-size:14.5px;line-height:1.5;color:var(--obsidian)}
@media(max-width:520px){.tldr{flex-direction:column;gap:8px}}

/* ═══ MOTION ════════════════════════════════════════════════════════════════
   Everything below is gated behind `.fx`, a class the shared script puts on
   <html> when it runs. That is not decoration-by-default with an off switch —
   it is the opposite, and it is deliberate.

   This site has already shipped one page where content was hidden by CSS and
   revealed by JavaScript that was not on the page: five pages were blank in
   production and measured as healthy. So nothing here may hide, shrink or
   blank anything on its own. Without `.fx` every bar is full width, every log
   line is visible and every card is in place; the class only ever takes
   something that is already correct and animates it into being correct.

   The rule for WHAT gets motion: it has to carry meaning. A bar growing from
   zero shows a quantity arriving. A log printing line by line shows a machine
   working. Cards fading in at random do neither, and the page is quieter for
   not having it.
   ────────────────────────────────────────────────────────────────────────── */

/* ── measured bars arrive ────────────────────────────────────────────────────
 * scaleX rather than width: the layout is already correct at rest, so nothing
 * reflows and a failure leaves a full, accurate bar rather than an empty one.
 * transform-origin matters — from the right it would read as a bar draining. */
/* Written as an ANIMATION off .run, not as a transition off a hidden resting
   state. The difference is what the diagram looks like when nobody has
   scrolled to it: `transform:scaleX(0)` at rest meant every bar on this site
   was an empty track in the first painted frame, in every screenshot, in
   print, and forever on any page where the observer never fired. The resting
   state is the finished, accurate bar now; the growth is a decoration layered
   on top of it when somebody is actually looking. */
@keyframes rk-grow{from{transform:scaleX(0)}to{transform:scaleX(1)}}
.fx .meter-track i{transform-origin:left}
.fx .meter.run .meter-track i{animation:rk-grow .85s cubic-bezier(.16,.84,.28,1) both}
/* Rows arrive in order so two bars being compared do not race each other. */
.fx .meter.run .meter-row:nth-child(2) .meter-track i{animation-delay:.09s}
.fx .meter.run .meter-row:nth-child(3) .meter-track i{animation-delay:.18s}
.fx .meter.run .meter-row:nth-child(4) .meter-track i{animation-delay:.27s}
.fx .meter.run .meter-row:nth-child(5) .meter-track i{animation-delay:.36s}
.fx .meter.run .meter-row:nth-child(6) .meter-track i{animation-delay:.45s}
.fx .meter.run .meter-row:nth-child(7) .meter-track i{animation-delay:.54s}

/* ── the run log prints ──────────────────────────────────────────────────────
 * The one place on the site where the animation IS the claim: this is a
 * picture of a machine working, and a machine working arrives a line at a
 * time. Shown all at once it is a table of text. */
/* Resting state is the finished log. See the note on the bars above: a
   diagram that is invisible until an observer fires is a diagram that is
   invisible in every screenshot and in the first painted frame. */
@keyframes rk-arrive{from{opacity:0;transform:translateY(3px)}to{opacity:1;transform:none}}
.fx .termlog.run .termlog-r{animation:rk-arrive .3s ease both;
  animation-delay:calc(var(--i,0) * 150ms)}

/* ── ordered things arrive in their order ──────────────────────────────────── */
@keyframes rk-rise{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}}
.fx .flow.run .flow-step,.fx .states.run .state{animation:rk-rise .4s ease both;
  animation-delay:calc(var(--i,0) * 90ms)}

/* Card grids stagger without any script: they already reveal individually, so
   this only spaces them out rather than deciding whether they appear. */
.ways .way:nth-child(2){transition-delay:.07s}
.ways .way:nth-child(3){transition-delay:.14s}

/* ── hover ───────────────────────────────────────────────────────────────────
 * One pixel and a slightly tighter ring. Enough to say "this responds",
 * not enough to make a page of cards feel like a trampoline. */
.way,.vs-side,.state,.flow-step,.fan-item{
  transition:transform .18s ease,box-shadow .18s ease}
.way:hover,.fan-item:hover{transform:translateY(-1px);
  box-shadow:0 0 0 1px rgba(0,0,0,.12),0 0 0 2px var(--paper),0 6px 16px rgba(0,0,0,.06)}
:root[data-theme="dark"] .way:hover,:root[data-theme="dark"] .fan-item:hover{
  box-shadow:0 0 0 1px rgba(255,255,255,.16),0 0 0 2px var(--paper),0 6px 16px rgba(0,0,0,.5)}
@media (prefers-color-scheme: dark){
  :root:not([data-theme="light"]) .way:hover,
  :root:not([data-theme="light"]) .fan-item:hover{
    box-shadow:0 0 0 1px rgba(255,255,255,.16),0 0 0 2px var(--paper),0 6px 16px rgba(0,0,0,.5)}
}

/* ── links underline themselves ──────────────────────────────────────────────
 * A drawn underline rather than a colour change, because the palette has no
 * colour to spend and this is a typesetter's move: the rule grows from the
 * left the way a pen would draw it. */
/* :not(.btn) matters. Without it this rule out-specifies .btn's own colour and
   every call-to-action inside prose — every blog post ends with two — rendered
   brand-ink on a brand fill at 1.10:1, which is a button with invisible text. */
.sub a:not(.btn),.prose p a:not(.btn),.way p a:not(.btn),
.zone-note a:not(.btn),figcaption a:not(.btn),.linklist a:not(.btn){color:var(--brand-ink);
  text-decoration:none;
  background-image:linear-gradient(currentColor,currentColor);
  background-size:0% 1px;background-repeat:no-repeat;background-position:0 100%;
  transition:background-size .28s cubic-bezier(.16,.84,.28,1);
  box-shadow:inset 0 -1px 0 0 var(--hairline)}
.sub a:not(.btn):hover,.prose p a:not(.btn):hover,.way p a:not(.btn):hover,
.zone-note a:not(.btn):hover,figcaption a:not(.btn):hover,.linklist a:not(.btn):hover{
  background-size:100% 1px}

/* Lists whose whole purpose is the links in them. Without this they render
   obsidian with no underline and no rule — darker than body text, but nothing a
   reader scanning the page would recognise as a link. */
.linklist{margin:14px 0 0;padding-left:20px}
.linklist li{margin:0 0 8px;font-size:15px;line-height:1.55;color:var(--stone)}

/* ── the eyebrow gets a rule ─────────────────────────────────────────────────
 * A 14px hairline that draws itself in beside the section label. Excluded from
 * the centred hero, where a rule on one side only would sit off-axis. */
section:not(.hero) > .cap::before{content:"";display:inline-block;
  width:0;height:1px;margin-right:0;vertical-align:middle;background:var(--ash);
  transition:width .5s cubic-bezier(.16,.84,.28,1) .1s,margin-right .5s ease .1s}
section:not(.hero) > .cap.in::before{width:14px;margin-right:8px}

@media(prefers-reduced-motion:reduce){
  /* Everything at its final state, no travel. Written as a block rather than
     relying on `.fx` never being added, because the class is set before this
     media query is consulted and both must agree. */
  .fx .meter-track i,.fx .meter.run .meter-track i,
  .fx .termlog-r,.fx .termlog.run .termlog-r,
  .fx .flow-step,.fx .flow.run .flow-step,
  .fx .state,.fx .states.run .state{opacity:1!important;transform:none!important;
    transition:none!important;animation:none!important}
  .way:hover,.fan-item:hover{transform:none}
  .sub a,.prose p a,.way p a,.zone-note a,figcaption a,.linklist a{background-size:100% 1px}
  section:not(.hero) > .cap::before{width:14px;margin-right:8px;transition:none}
}

/* ── Showcase detail ──────────────────────────────────────────────────────
   Opened from a gallery card. Everything a visitor wants before trusting the
   number on the card: the exact prompt, what it cost, and the page itself. */
.gd-wrap{position:fixed;inset:0;z-index:80;background:rgba(0,0,0,.45);
  display:flex;align-items:center;justify-content:center;padding:20px}
.gd{background:var(--paper);border-radius:var(--radius);width:min(1000px,100%);
  max-height:88vh;overflow-y:auto;box-shadow:0 30px 70px rgba(0,0,0,.35)}
.gd-head{display:flex;align-items:flex-start;justify-content:space-between;gap:16px;
  padding:18px 20px;border-bottom:1px solid var(--hairline);position:sticky;top:0;
  background:var(--paper);z-index:1}
.gd-title{margin:2px 0 0;font-size:1.25rem;font-weight:450;letter-spacing:-.02em;color:var(--obsidian)}
.gd-body{display:grid;grid-template-columns:minmax(0,1.55fr) minmax(0,1fr);gap:20px;padding:20px}
@media(max-width:820px){.gd-body{grid-template-columns:minmax(0,1fr)}}
.gd-shot{margin:0;border-radius:var(--radius);overflow:hidden;background:var(--white);
  box-shadow:var(--ring-card)}
.gd-shot img{display:block;width:100%;height:auto}
.gd-side{min-width:0}
.gd-side .cap{margin:0 0 6px}
.gd-side .cap:not(:first-child){margin-top:18px}
.gd-prompt{margin:0;font-size:14px;line-height:1.55;color:var(--charcoal)}
.gd-facts,.gd-files{list-style:none;margin:0;padding:0}
.gd-facts li{display:flex;justify-content:space-between;gap:12px;padding:6px 0;
  border-bottom:1px solid var(--hairline);font-size:13px;color:var(--charcoal)}
.gd-facts li b{font-weight:500;color:var(--obsidian);font-variant-numeric:tabular-nums}
.gd-files li{font-family:var(--mono);font-size:11px;color:var(--stone);padding:2px 0}
.gd-acts{display:flex;gap:10px;flex-wrap:wrap;margin-top:20px}
/* The card is the button now. */
.gal-card{cursor:pointer}
.gal-card:hover{box-shadow:0 0 0 1px var(--ash),0 0 0 3px var(--focus-halo)}



/* The showcase grid is injected after gallery.json loads, into a section that
   is `hidden` until then — so the page paints short and then grows by most of a
   screen. Measured 2026-08-25: CLS 0.60 on /showcase, where anything above 0.25
   counts as poor, and it is the largest shift on the site.

   `hidden` still marks the state; it just stops meaning display:none here, so
   the space is reserved and empty rather than absent.

   The number is measured, not guessed: the finished section is 1603px against a
   900px viewport. An earlier 78vh only took CLS to 0.17 precisely because it was
   under half the real height — reserving too little just moves the shift rather
   than removing it. Kept in vh because the grid reflows with the viewport; on a
   narrow screen the cards stack and it grows further, which is the direction
   that still leaves the reserve honest rather than a gap. */
#gallery[hidden]{display:block;visibility:hidden;min-height:175vh}


/* ── The reckoner (cut-your-software-costs) ───────────────────────────────
   Rows stack on a phone; the number stays legible at every width. */
.reckon{display:grid;grid-template-columns:minmax(0,1.4fr) minmax(0,1fr);gap:26px;
  align-items:start;margin-top:22px}
.reckon-rows{display:flex;flex-direction:column;gap:6px;min-width:0}
.reckon-row{display:flex;align-items:center;gap:12px;min-width:0;cursor:pointer;
  padding:10px 12px;border-radius:10px;background:rgba(128,128,128,.07)}
.reckon-row input[type=checkbox]{flex:none;width:17px;height:17px;accent-color:currentColor;cursor:pointer}
/* Muted by COLOUR, not by opacity.
   Fading near-black text with opacity composites to greys like #8b8b8b and
   #9b9b9b, which fail contrast at these sizes — twenty-three failures on one
   page, all of them here. --stone is the token for de-emphasised text and it
   passes, so the hierarchy survives and the text stays readable. */
.reckon-n{flex:1 1 auto;min-width:0;display:flex;flex-direction:column;font-size:.92rem;line-height:1.3}
.reckon-n small{color:var(--stone);font-size:.76rem;margin-top:2px}
.reckon-in{flex:none;display:flex;align-items:center;gap:3px;font-size:.9rem}  /* No opacity here either: it fades the CHILD too, so the small inside
     it landed at #797979 even after that child was given a passing
     colour of its own. Opacity on an ancestor is invisible in the
     child's own rule and is why this took two passes. */
.reckon-in input{width:4.4rem;font:inherit;font-size:.9rem;padding:5px 7px;border-radius:8px;
  border:1px solid rgba(128,128,128,.35);background:transparent;color:inherit}
.reckon-in small{color:var(--stone)}
.reckon-out{min-width:0;position:sticky;top:24px}
.reckon-line{display:flex;align-items:baseline;gap:8px;flex-wrap:wrap;
  padding:12px 0;border-bottom:1px solid rgba(128,128,128,.22)}
.reckon-line span{flex:1 1 8rem;min-width:0;font-size:.88rem;color:var(--stone);line-height:1.35}
.reckon-line b{font-size:1.45rem;font-variant-numeric:tabular-nums}
.reckon-line small{color:var(--stone);font-size:.75rem}
.reckon-line.is-total b{font-size:2.1rem}
.reckon-line.is-total{border-bottom:0}
.reckon-note{margin:12px 0 0;font-size:.8rem;line-height:1.55;color:var(--stone)}
@media (max-width:760px){ .reckon{grid-template-columns:minmax(0,1fr)} .reckon-out{position:static} }

/* ── Status ───────────────────────────────────────────────────────────────
   Read from /health in the visitor's browser, so it cannot say "operational"
   while something is down. */
.statuswrap{border-radius:14px;padding:22px 24px;background:rgba(128,128,128,.07);max-width:44rem}
.status-top{display:flex;align-items:center;gap:12px;margin-bottom:18px}
.status-dot{flex:none;width:12px;height:12px;border-radius:50%;background:#9aa0a6}
.status-dot.is-up{background:#3fb950}
.status-dot.is-part{background:#d29922}
.status-dot.is-down{background:#f85149}
.status-head{margin:0;font-size:1.15rem;font-weight:600}
.status-list{display:flex;flex-direction:column}
.status-row{display:flex;align-items:center;justify-content:space-between;gap:14px;
  padding:11px 0;border-bottom:1px solid rgba(128,128,128,.2);font-size:.94rem}
.status-row:last-child{border-bottom:0}
.status-state{font-size:.8rem;font-family:var(--mono);opacity:.8}
.status-state.is-up{color:#3fb950}
.status-state.is-off{color:#d29922}
.status-state.is-down{color:#f85149}
.status-foot{margin:16px 0 0;font-size:.78rem;opacity:.6;line-height:1.5}

/* The status card sits under a centred hero, so it centres too — left-aligned
   under centred copy reads as a mistake. And the shared 96px section padding
   put a dead gap between the question and its answer; on this page they are
   one thought. */
.status-hero{padding-bottom:8px}
.status-sec{padding-top:0}
.statuswrap{margin-left:auto;margin-right:auto}
.status-ask{margin:22px auto 0;text-align:center}
/* ── An article: progress, byline, author note, subscribe ─────────────────
 *
 * Several of these rules are written twice — `.byline-t,.prose .byline-t` —
 * and that is not redundancy. `.prose p` is (0,1,1) and beats a bare class, so
 * a paragraph inside an article silently takes the body-copy size no matter
 * what its own class says. The blog index has the same blocks OUTSIDE .prose,
 * so both selectors have to be there or the two pages disagree.
 */
.rdbar{position:fixed;top:0;left:0;right:0;height:3px;background:var(--hairline);
  /* Above the sticky nav (20) and below the showcase modal (80): it is chrome
     for this page, not an overlay competing with a dialog. */
  z-index:30;pointer-events:none}
.rdbar i{display:block;height:100%;width:0;background:var(--obsidian)}
/* Information, not decoration, so it still moves under reduced motion — only
   the easing goes, which is what makes it feel animated rather than live. */
@media(prefers-reduced-motion:no-preference){.rdbar i{transition:width .1s linear}}

.byline{display:flex;align-items:center;gap:12px;margin:0 0 28px}
.byline-av,.author-av{flex:none;display:grid;place-items:center;
  border-radius:var(--radius-pill);background:var(--obsidian);color:var(--paper);
  font-family:var(--mono);letter-spacing:.04em}
.byline-av{width:34px;height:34px;font-size:11px}
.author-av{width:40px;height:40px;font-size:12px}
.byline-t,.prose .byline-t{margin:0;font-size:13.5px;line-height:1.5;color:var(--charcoal)}
.byline-t b{font-weight:500;color:var(--obsidian)}
.byline-m{font-family:var(--sans);font-weight:600;font-size:10.5px;letter-spacing:.02em;color:var(--stone)}

.author{display:flex;gap:14px;align-items:flex-start;margin:56px 0 0;padding:18px;
  background:var(--white);border-radius:var(--radius);box-shadow:var(--ring-card);
  max-width:min(44rem,100%)}
.author-n,.prose .author-n{margin:0;font-size:14.5px;color:var(--obsidian)}
.author-r,.prose .author-r{margin:2px 0 8px;font-family:var(--sans);font-weight:600;font-size:10.5px;
  letter-spacing:.071em;text-transform:uppercase;color:var(--stone)}
.author-b,.prose .author-b{margin:0;font-size:13.5px;line-height:1.6;color:var(--charcoal)}

.subscribe{margin:32px 0 0;padding:22px;background:var(--white);
  border-radius:var(--radius);box-shadow:var(--ring-card);max-width:min(44rem,100%)}
.subscribe h2,.prose .subscribe h2{margin:0 0 8px;font-size:1.35rem;
  letter-spacing:-.03em;color:var(--obsidian)}
.subscribe p,.prose .subscribe p{margin:0;font-size:13.5px;line-height:1.6;
  color:var(--charcoal);max-width:34rem}
.subscribe form{display:flex;gap:8px;flex-wrap:wrap;margin:16px 0 10px}
/* Visually hidden, not display:none — the input needs a real label, and a
   placeholder is not one the moment somebody starts typing. */
.sub-lab{position:absolute;width:1px;height:1px;overflow:hidden;
  clip-path:inset(50%);white-space:nowrap}
.subscribe input{flex:1;min-width:0;background:var(--paper);border:0;
  box-shadow:var(--ring-ghost);border-radius:var(--radius);padding:.55rem .8rem;
  color:var(--obsidian);font-family:var(--sans);font-size:14px}
.subscribe input:focus{outline:none;box-shadow:0 0 0 1px var(--obsidian)}
.subscribe input::placeholder{color:var(--smoke)}
.subscribe .btn{white-space:nowrap}
.sub-note,.prose .sub-note{margin:0;font-family:var(--sans);font-weight:600;font-size:10.5px;
  line-height:1.6;color:var(--stone)}
.sub-err,.prose .sub-err{color:var(--alarm)}
@media(max-width:380px){.subscribe form{flex-direction:column}
  .subscribe .btn{width:100%}}

.cta-band{text-align:left}
.cta-band .sub{margin-bottom:1.25rem}


/* ── the demo: terminal ─ beam ─ phone ───────────────────────────────────────
   The terminal IS the marketing in this system, so it gets the full CLI-panel
   treatment: white surface, hairline ring, mono at 12-13px, ▲ for commands and
   ✓ in terminal green for confirmations. */
.stage{display:grid;grid-template-columns:minmax(0,1.25fr) auto minmax(0,.75fr);
  gap:0;align-items:center;max-width:58rem;margin:80px auto 0;text-align:left}
@media(max-width:880px){.stage{grid-template-columns:1fr;gap:24px;justify-items:center}}
.term{width:100%;background:var(--white);border-radius:var(--radius);
  box-shadow:var(--ring-card);overflow:hidden}
.term .bar{display:flex;align-items:center;gap:6px;padding:10px 14px;
  border-bottom:1px solid var(--hairline)}
.term .bar i{width:8px;height:8px;border-radius:var(--radius-pill);background:var(--hairline)}
.term .bar span{margin-left:auto;font-family:var(--mono);font-size:11px;
  letter-spacing:.071em;color:var(--stone)}
.term .scr{padding:16px;min-height:8rem;font-family:var(--mono);font-size:12px;
  line-height:1.67;color:var(--obsidian)}
.term .ps{color:var(--stone)}
.term .cur{display:inline-block;width:7px;height:13px;background:var(--obsidian);
  vertical-align:-2px;margin-left:1px;animation:blink 1.1s steps(1) infinite}
#tout div{color:var(--charcoal)}
#tout b{color:var(--obsidian);font-weight:500}   /* ▲ — work in progress */
#tout .ok{color:var(--live)}                      /* ✓ — this step finished */
#tout .mem{color:var(--kept)}                     /* what the turn kept */
@keyframes blink{50%{opacity:0}}
.tfoot{display:flex;gap:20px;padding:8px 14px;border-top:1px solid var(--hairline);
  font-family:var(--mono);font-size:11px;letter-spacing:.071em;color:var(--stone)}
.tf-b{color:var(--obsidian)}     /* compute */
.tf-a{color:var(--kept)}        /* Memory */

/* The link between the two surfaces. A hairline with a travelling tick — no
   glow, no gradient bloom. */
.beam{position:relative;width:150px;height:1px;background:var(--hairline);margin:0 -1px}
.beam::after{content:"";position:absolute;top:-1px;left:0;width:34px;height:3px;
  border-radius:3px;background:var(--obsidian);animation:beam 2.2s ease-in-out infinite}
@keyframes beam{0%{left:-10%}55%,100%{left:90%}}
@media(max-width:880px){
  .beam{width:1px;height:64px}
  .beam::after{width:3px;height:34px;left:-1px;top:0;animation:beamv 2.2s ease-in-out infinite}
  @keyframes beamv{0%{top:-10%}55%,100%{top:90%}}
}

.phone{width:210px;background:var(--obsidian);border-radius:30px;padding:8px}
.phone .notch{width:64px;height:16px;background:var(--obsidian);
  border-radius:0 0 12px 12px;margin:0 auto}
.phone .scr{background:var(--white);border-radius:22px;padding:14px 12px;min-height:13.5rem;
  font-family:var(--mono);font-size:11px;line-height:1.8;
  display:grid;align-content:start;gap:2px}
.phone .t{font-family:var(--sans);font-size:14px;font-weight:500;letter-spacing:-.02em;
  color:var(--obsidian);display:flex;align-items:center;gap:6px}
.dot{width:6px;height:6px;border-radius:var(--radius-pill);background:var(--live);
  animation:pulse 1.6s ease-in-out infinite}
@keyframes pulse{50%{opacity:.35}}
.phone .mut{color:var(--smoke)}
#mlist div{color:var(--charcoal)}
#mlist div b{color:var(--obsidian);font-weight:500}
#mlist div.mem{color:var(--kept)}
.phone .tag{justify-self:start;margin-top:auto;display:inline-block;
  font-family:var(--sans);font-weight:600;font-size:10px;letter-spacing:.071em;color:var(--stone);
  box-shadow:var(--ring-ghost);border-radius:var(--radius);padding:2px 6px}

/* ── sign-in dialog ──────────────────────────────────────────────────────── */
.signin{border:0;border-radius:var(--radius);background:var(--white);
  box-shadow:var(--ring-card);color:var(--charcoal);
  padding:32px 28px 24px;max-width:24rem;width:calc(100% - 2rem);
  text-align:center;position:relative}
.signin::backdrop{background:var(--scrim);backdrop-filter:blur(2px)}
.signin h2{font-size:1.5rem;font-weight:450;letter-spacing:-.03em;line-height:1.15;
  margin:0 0 .5rem}
.signin-what{color:var(--stone);font-size:14px;margin:0 0 1rem;line-height:1.5}
.signin-fine{font-family:var(--sans);font-weight:600;color:var(--stone);font-size:11px;
  letter-spacing:.02em;margin:1rem 0 0;line-height:1.6}
.signin .gbtn-wrap{display:flex;justify-content:center;min-height:44px}
.signin-x{position:absolute;top:8px;right:10px;background:none;border:0;
  color:var(--smoke);font-size:1.35rem;line-height:1;cursor:pointer;padding:2px 6px;
  border-radius:var(--radius)}
.signin-x:hover{color:var(--obsidian)}
.signin-err{color:var(--alarm)}

.or{display:flex;align-items:center;gap:10px;margin:16px 0 12px;
  font-family:var(--sans);font-weight:600;color:var(--stone);font-size:11px;letter-spacing:.071em;
  text-transform:uppercase}
.or::before,.or::after{content:'';flex:1;height:1px;background:var(--hairline)}
.mailform{display:flex;gap:8px;flex-wrap:wrap}
.mailform input{flex:1;min-width:0;background:var(--paper);border:0;
  box-shadow:var(--ring-ghost);border-radius:var(--radius);padding:.55rem .8rem;
  color:var(--obsidian);font-family:var(--sans);font-size:14px}
.mailform input:focus{outline:none;box-shadow:0 0 0 1px var(--obsidian)}
.mailform input::placeholder{color:var(--smoke)}
.mailform .btn{white-space:nowrap}
@media(max-width:380px){.mailform{flex-direction:column}.mailform .btn{width:100%}}

/* ═══ COLOUR, AND A LITTLE LIFE — 2026-08-28 ═══════════════════════════════
   The previous system was right about almost everything and wrong about one:
   it budgeted three hues, spent all three on states that appear a handful of
   times per page, and left the brand itself with no colour at all. So the page
   was correct, disciplined, and grey for 13,687 pixels.

   This does not undo any of it. The ground is still #fafafa, depth still comes
   from tonal value and 1px rings, Geist still reads and Geist Mono still stamps.
   What changed is that the four highest-frequency surfaces on the site now
   carry the brand hue, and things that respond to a pointer now move.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── the numbers, which are the whole argument ───────────────────────────── */
.est-price{color:var(--brand-ink)}
.meter-track i{background:var(--brand)}
.meter-row.is-kept .meter-track i{background:var(--kept)}   /* states still win */
.meter-row.is-ghost .meter-track i{background:var(--ash)}
.price-big span{color:var(--brand-ink)}
.gal-cost b,.scard-p{color:var(--brand-ink)}
.fine b,.ask-fine b{color:var(--brand-ink);font-weight:500}

/* ── the dark panels get the inverted brand ──────────────────────────────── */
.vs-side.is-ours .vs-cost{color:var(--brand-inv)}
.flow-step.is-out .flow-n{color:var(--brand-inv)}
.price.feature .price-big span{color:var(--brand-inv)}
.way.feature .way-tag{color:var(--brand-inv)}

/* ── life: movement on the pointer, not a permanent shadow ────────────────
   Deliberately NOT a resting box-shadow. "Depth comes from tonal value and 1px
   rings, never from shadow or glow" is the rule this system is built on, and a
   card that is flat until you reach for it keeps that rule while still feeling
   alive. The shadow exists only during the hover. */
.way,.post,.fan-item,.price,.mkt-card,.gal-card{
  transition:transform .18s cubic-bezier(.2,.7,.3,1),box-shadow .18s ease}
.way:hover,.post:hover,.fan-item:hover,.price:hover,.mkt-card:hover{
  transform:translateY(-2px);
  box-shadow:var(--ring-card),0 10px 24px -14px var(--scrim)}
/* The gallery card was in the transition list but never got a hover rule, so
   the whole card — which IS clickable, the entire surface opens the detail —
   sat inert under the pointer and read as a picture. */
.gal-card{cursor:pointer}
.gal-card:hover{transform:translateY(-2px);
  box-shadow:var(--ring-card),0 10px 24px -14px var(--scrim)}
.gal-card.is-top:hover{transform:translateY(-2px);
  box-shadow:0 0 0 1px var(--obsidian),0 0 0 2px var(--paper),0 10px 24px -14px var(--scrim)}
.gal-card:hover .gal-open-btn{text-decoration:underline}
@media(prefers-reduced-motion:reduce){
  .way,.post,.fan-item,.price,.mkt-card,.gal-card,
  .way:hover,.post:hover,.fan-item:hover,.price:hover,.mkt-card:hover,
  .gal-card:hover{transform:none}
}

/* ── a rule under the section eyebrow, in the brand hue ──────────────────── */
section:not(.hero) > .cap::before{background:var(--brand)}

/* ── the proof grid ─────────────────────────────────────────────────────────
   Four builds, two columns on a phone and four across on a desktop — the shape
   of the games band further down the page, which is the shape the founder asked
   for: "the website preview can be more compact like the game previews, 2 rows
   and 2 columns, and it can have quick info on cost etc like it has now."

   What it replaces: three cards, each about 560px tall, each opening with two
   clamped lines of the real prompt above a browser bar above a 4:3 screenshot.
   On a 390px phone that was 1,700 pixels of proof and you could see one card at
   a time, so the section that exists to say "look how many of these there are"
   showed you one.

   The prompt comes off the card and moves to the title attribute. It is the
   least scannable thing on a card that has to be scanned in a glance, and the
   full text was already one tap away on the showcase; what stays is what
   somebody actually compares between four builds — what it is, how many turns
   and which model, and the price.

   The deliberate rotations came off with it. Three cards fanned at an angle
   read as prints laid on a desk; four in a tidy 2x2 read as a contact sheet,
   and a contact sheet is what this is. */
.stack{max-width:1120px;margin:0 auto;position:relative;z-index:1;
  display:grid;grid-template-columns:repeat(4,1fr);gap:16px;align-items:stretch}
.scard{display:flex;flex-direction:column;background:var(--white);border-radius:var(--radius);
  overflow:hidden;text-decoration:none;box-shadow:var(--ring-card);min-width:0;
  transition:transform .18s ease,box-shadow .18s ease}
.scard:hover{transform:translateY(-3px);box-shadow:var(--ring-card),var(--drop-lg);
  text-decoration:none}
.scard-shot{position:relative;display:block;overflow:hidden;background:var(--sunk)}
/* A fixed 16:10 well, and the image covers it from the top. Every thumbnail in
   the grid is then the same shape whatever the capture was, which is the whole
   reason a contact sheet reads as a set — and 16:10 rather than the captures'
   own 4:3 because the bottom fifth of a full-page capture is never the hero.
   At 4:3 the tree-care card ended on a headline sheared mid-glyph and the
   photographer's ended on 200px of the dark band under the fold. */
.scard img{display:block;width:100%;height:100%;aspect-ratio:16/10;object-fit:cover;
  object-position:top center;background:var(--sunk)}
.scard-go{position:absolute;right:8px;bottom:8px;font-family:var(--sans);font-weight:600;
  font-size:11px;letter-spacing:.02em;color:var(--obsidian);
  background:var(--overlay-chip);padding:5px 10px;border-radius:var(--radius-pill);
  opacity:0;transform:translateY(4px);
  transition:opacity .22s ease,transform .22s cubic-bezier(.2,.7,.3,1)}
.scard:hover .scard-go,.scard:focus-visible .scard-go{opacity:1;transform:none}
/* Name, then one meta line, then the price chip on its own row. Stacked rather
   than the old title-left / price-right flex row: in a 170px column on a phone
   those two fought for the same line and the chip lost. */
.scard-m{display:flex;flex-direction:column;align-items:flex-start;gap:6px;
  padding:11px 13px 13px;min-width:0;flex:1 1 auto}
.scard-t{font-weight:600;font-size:14px;line-height:1.3;letter-spacing:-.015em;
  color:var(--obsidian);min-width:0}
.scard-meta{font-family:var(--mono);font-size:10.5px;line-height:1.4;color:var(--stone);
  letter-spacing:.02em;overflow-wrap:anywhere}
.scard-p{margin-top:auto;font-family:var(--mono);font-weight:500;font-size:13.5px;
  color:var(--brand-ink);background:color-mix(in srgb,var(--brand) 10%,transparent);
  padding:4px 10px;border-radius:var(--radius-pill);white-space:nowrap}
.stack-fine{font-family:var(--sans);font-weight:600;font-size:11.5px;color:var(--stone);text-align:center;
  margin:26px 0 0;letter-spacing:.02em}
.stack-fine a{color:var(--brand-ink);font-weight:500}
@media(prefers-reduced-motion:reduce){.scard,.scard:hover{transform:none}}

/* Two per row on a phone, all the way down — the games band's own rule, and for
   the same reason: at one per row a single card fills the screen and the set
   reads as one build rather than four. */
@media(max-width:820px){
  .stack{grid-template-columns:repeat(2,1fr);gap:12px;max-width:620px}
}
@media(max-width:440px){
  .stack{gap:10px}
  .scard-m{padding:9px 11px 11px;gap:5px}
  .scard-t{font-size:13px}
  .scard-meta{font-size:9.5px}
  .scard-p{font-size:12.5px;padding:3px 9px}
}


/* The estimator, the CTA and the Product Hunt badge used to be the tail of
   .hero and inherited its centring. They now live in their own section so the
   proof stack can sit between them and the headline, which means they have to
   be told to centre themselves — otherwise the primary button lands hard
   against the left gutter under a centred price. */
#est-sec{text-align:center;padding-top:64px}
#est-sec .cta{display:flex;gap:12px;justify-content:center;margin-top:2.5rem;flex-wrap:wrap}
#est-sec .fine{margin-top:1.25rem}
/* And the hero hands off to the stack now, so it does not need a full band of
   bottom padding as well as the stack's own margin. */
.hero{padding-bottom:40px}

/* ── touch targets ────────────────────────────────────────────────────────
   WCAG 2.2 (2.5.8) puts the minimum at 24x24 CSS px; Apple and Android both
   ask for 44. Measured on the live site at 390px these were the misses:

     the ☰ menu button      32x31   the primary way to navigate on a phone
     every footer link      ~17 tall  under the 24px minimum outright
     .btn on a phone        104x39
     .chip                  ~31 tall

   The footer ones are the actual accessibility failure; the rest are comfort.
   Scoped to `pointer:coarse` so a mouse-driven desktop is untouched — this is
   about fingers, not viewport width, and an iPad with a trackpad is neither. */
@media(pointer:coarse){
  .navtoggle{min-width:44px;min-height:44px;padding:.5rem .6rem}
  .btn{padding:.8rem 1.15rem}
  /* .sm is what the nav "Get started" and the blog "Keep me posted" use, and at
     .65rem they came out 39px — the two most-pressed buttons on a phone, three
     pixels under. */
  .btn.sm{padding:.8rem 1rem}
  .chip{padding:.6rem .85rem}
  .themebtn{width:44px;height:44px}
  footer a{display:inline-block;padding:4px 0}
  .send,.ask .send{min-width:44px;min-height:44px}
  /* The three mega-menu triggers — Solutions, Product, Explore — measured
     83x33, 74x33 and 72x33 on an iPad in landscape. Above WCAG 2.2's 24px
     floor and eleven pixels under the comfort figure every other control in
     this block was raised to, on the one nav element you have to hit before
     you can reach anything else.
     min-height rather than padding: they are inline-flex already, so the box
     grows around the label without moving it, and a mouse-driven desktop
     never sees this rule. */
  .navlink.mega-btn{min-height:44px}
  /* The two fields you actually type into.
     The hero textarea came out 266x40 and the blog's email input 228x36 — four
     and eight pixels under, on the primary call to action of the site and the
     only thing on the blog that asks for anything.
     Safe to raise where the suggestion chips below them were NOT: those are six
     wrapped rows, and 44px each pushes the proof screenshots off a phone
     screen, which is why they stay at 38. A single field grows by four pixels
     and moves nothing. Different answer for a different reason, not an
     oversight carried forward. */
  .ask textarea{min-height:44px}

  .subscribe input{min-height:44px}
}
/* The footer is a dense row of links at any pointer type, and 17px tall is
   under the minimum for everyone — so this one is not scoped to touch. */
footer a{padding-block:4px;display:inline-block}

/* The showcase's two overlay controls sat under the 24px WCAG 2.2 minimum on a
   phone — .gal-open-btn at 21px and .gal-toggle at 23px. They are absolutely
   positioned over a screenshot, so growing them costs no layout anywhere; they
   were small because they were sized to their text and never measured. */
@media(pointer:coarse){
  .gal-open-btn,.gal-toggle,.gal-copy{min-height:26px;padding-block:5px}
}

/* Room for the hero placeholder to WRAP.
   It is an animated cycle of phrases and several run to two lines on a phone:
   measured clientHeight 40 against scrollHeight 64, with .ask carrying
   overflow:hidden for its sweep animation — so the second line was cut
   horizontally through the middle of the glyphs. "business," and "co" with
   their bottom halves missing, on the first thing a visitor meets.

   Keyed to WIDTH, not to pointer type. The first attempt went inside the
   touch-input block, where it was invisible to a narrow window with a mouse —
   and to the headless browser that measures it, which is how the fix was
   caught doing nothing. */
@media(max-width:26rem){
  .ask textarea{min-height:64px}
}

/* The dictate button on the homepage composer. */
.ask-mic{flex:0 0 auto;width:32px;height:32px;border-radius:var(--radius);border:0;
  cursor:pointer;background:var(--ash);color:var(--obsidian);display:grid;place-items:center;
  transition:background .15s ease}
.ask-mic svg{width:16px;height:16px}
.ask-mic:hover{background:var(--smoke)}
.ask-mic.is-rec{background:var(--alarm);color:var(--white)}
/* Sending it up. The recorder has stopped, the words are not back yet, and a
   button that looks unchanged in that gap reads as one that did nothing. */
.ask-mic.is-busy{opacity:.5}
.ask-mic:disabled{cursor:default}
.ask-mic.is-rec svg{animation:askmicpulse 1.4s ease-in-out infinite}
@keyframes askmicpulse{0%,100%{opacity:1}50%{opacity:.45}}
@media(prefers-reduced-motion:reduce){.ask-mic.is-rec svg{animation:none}}

/* ── How Amelia chooses ─────────────────────────────────────────────────────
   The posture rows in the model sheet, and the one-line reason under the
   composer. Both exist because the product now picks the model itself: a
   choice made on somebody's behalf and not accounted for reads as erratic,
   and this is the surface whose whole argument is that the price — and now
   the reason — is on every answer. */
.tune-postures{display:flex;flex-direction:column;gap:6px;margin-bottom:18px}
.tune-posture{display:flex;flex-direction:column;gap:2px;width:100%;text-align:left;
  padding:11px 13px;border:1px solid var(--edge);border-radius:var(--radius);
  background:var(--paper);color:inherit;font:inherit;cursor:pointer}
.tune-posture:hover{background:var(--ash)}
.tune-posture.is-on{border-color:var(--brand);background:color-mix(in srgb,var(--brand) 7%,transparent)}
.tune-rec{font-size:11px;font-weight:600;letter-spacing:.04em;text-transform:uppercase;
  color:var(--brand);margin-left:6px}
.chatfine-why{display:block;margin-top:2px;opacity:.75}
@media(prefers-reduced-motion:no-preference){.tune-posture{transition:border-color .12s ease,background .12s ease}}

/* What a plan includes, under its price. Three names and three prices is
   guesswork at the moment the product has stopped working for somebody. */
.plan-inc{display:block;margin-top:4px;font-size:12px;line-height:1.45;opacity:.75}

/* The way back to Amelia, at the top of the manual model list. Where somebody
   who regrets taking the wheel is standing when they want to hand it back. */
.tune-back{border-style:dashed}
.tune-back .tune-row-n{color:var(--brand)}

/* ── Product showcase: real screenshots, framed like a window ──────────────
   The homepage was a wall of prose with no picture of the actual product.
   These are genuine captures of the app (chat, model picker, spend cap),
   shown in a light window chrome so a dark-UI shot reads on the paper ground
   in both themes. */
.showcase{max-width:1000px;margin:0 auto;padding:0 24px}
/* margin:0 is doing real work. A <figure> carries the browser's own
   `margin: 1em 40px`, which nothing here had reset, so these frames were
   throwing away EIGHTY pixels of width — 214 of an available 294 on a phone,
   384 of 464 on a desktop. The hero shot escaped it by accident (it sets
   `margin:0 auto` for centring); the two half-width shots below did not, and
   they are the ones whose content is small enough that losing 27% of the width
   makes the writing inside them unreadable. */
.shot-frame{margin:0;border-radius:10px;overflow:hidden;background:#0a0a0a;
  box-shadow:0 1px 0 var(--hairline),0 24px 60px -30px rgba(0,0,0,.55);
  border:1px solid var(--hairline)}
.shot-frame.hero-shot{max-width:920px;margin:0 auto}
.shot-bar{display:flex;align-items:center;gap:7px;padding:10px 14px;background:#151515;
  border-bottom:1px solid #262626}
.shot-bar i{width:11px;height:11px;border-radius:50%;background:#3a3a3a;flex:none}
.shot-bar i:first-child{background:#e0665b}.shot-bar i:nth-child(2){background:#e0b34e}
.shot-bar i:nth-child(3){background:#5fbf82}
.shot-bar span{margin-left:8px;font-family:var(--mono);font-size:11px;color:#8a8a8a;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.shot-frame img{display:block;width:100%;height:auto}
.shot-cap{font-family:var(--sans);font-weight:600;font-size:12px;color:var(--stone);text-align:center;
  margin:14px auto 0;max-width:44ch;line-height:1.5}
.shot-row{display:grid;grid-template-columns:1fr 1fr;gap:24px;margin-top:40px}
.shot-row .shot-cap{margin-top:12px}
@media(max-width:720px){.shot-row{grid-template-columns:1fr;gap:32px}}

/* ── What's included: a compact feature grid, icon + label + one line ────── */
.included{display:grid;grid-template-columns:repeat(4,1fr);gap:14px;margin-top:44px;
  max-width:1000px;margin-left:auto;margin-right:auto}
.inc{padding:20px 18px;border:1px solid var(--hairline);border-radius:var(--radius);
  background:var(--white)}
/* display:block, because a bare span is inline and ignores width/height —
   which let the SVG's width:100% resolve against the whole tile and fill it. */
.inc-ico{display:block;width:26px;height:26px;color:var(--brand-ink);margin-bottom:13px}
.inc-ico svg{width:26px;height:26px;display:block}
.inc h3{font-size:.95rem;margin:0 0 5px;color:var(--obsidian);letter-spacing:-.01em}
.inc p{font-size:.82rem;line-height:1.45;color:var(--charcoal);margin:0}
@media(max-width:900px){.included{grid-template-columns:repeat(2,1fr)}}
@media(max-width:460px){.included{grid-template-columns:1fr}}

/* ── The seven benefits, on the two pages that argue for a plan ───────────
   The same claims the app puts on the wall at the end of the trial, said on
   the way IN rather than only on the way out: what a plan actually buys, and
   what happens to the work if you stop paying for one. Six of the seven are
   the app's own copy, word for word — check-benefits reads CLAIMS out of app.js
   and fails if the site's wording drifts from it, so there is one place to edit
   a sentence and one place it can be wrong.

   Two columns rather than .included's four, because these are sentences and not
   labels: the longest of them is 141 characters, which is seven lines inside a
   214px .included tile and three inside one of these. The tiles themselves are
   .inc, unchanged, so this grid and the "everything's included" grid are the
   same object at two densities. */
.bens{display:grid;grid-template-columns:1fr 1fr;gap:14px;margin:36px auto 0;
  max-width:900px}
/* 640px is the app's own breakpoint for the same six claims (.claims in
   app.css). One column below it: an icon, a title and two lines of prose do
   not survive a 300px column. */
@media(max-width:640px){.bens{grid-template-columns:1fr}}
/* Seven claims into two columns leaves an orphan. Spanning the last one when
   the count is odd turns the gap into a closing line instead of a hole — and
   it is written against the COUNT rather than against "the seventh", so adding
   or dropping a claim cannot leave a half-empty row behind. */
.bens>:last-child:nth-child(odd){grid-column:1/-1}
/* Held to the grid's own measure. Left to the full 1232px column this ran as
   one 190-character line of 11px mono across the whole page. */
.bens-fine{font-family:var(--sans);font-weight:600;font-size:11.5px;color:var(--stone);text-align:center;
  margin:20px auto 0;max-width:900px;letter-spacing:.02em}
.bens-fine a{color:var(--brand-ink);font-weight:500}

/* ── Games band: playable arcade, each opens in a new tab ──────────────────
   The showcase proves it builds real, playable things — so let people play
   them. Thumbnails are real captures; the cards link out to the live game. */
.games{display:grid;grid-template-columns:repeat(4,1fr);gap:18px;max-width:1000px;
  margin:44px auto 0;padding:0 24px}
.game-card{display:block;border:1px solid var(--hairline);border-radius:10px;overflow:hidden;
  background:var(--white);text-decoration:none;color:inherit;transition:transform .12s ease,box-shadow .12s ease}
.game-card:hover{transform:translateY(-3px);box-shadow:0 14px 30px -18px rgba(0,0,0,.5)}
/* display:block, for the same reason .inc-ico above needs it: a bare span is
   inline, and an inline box IGNORES aspect-ratio. So this never got the square
   it asks for — it took whatever height the grid row happened to be (168x276
   on a 430px phone), and object-fit:cover then sliced the left and right
   columns off a 4x4 board. Two of the four games showed three columns of a
   grid whose whole point is that it is four.
   contain, not cover: the thumbnail exists so somebody can tell WHICH GAME it
   is before tapping. Cover fills the box by discarding the edges of the board;
   contain scales the whole board down to fit and letterboxes the remainder
   against #0f1115, which is the ground the games are drawn on anyway, so the
   letterbox does not read as a gap. Breakout is 880x1076 and is the one that
   actually letterboxes; the other three are square captures. */
.game-thumb{display:block;position:relative;aspect-ratio:1;overflow:hidden;background:#0f1115}
.game-thumb img{width:100%;height:100%;object-fit:contain;object-position:center;display:block}
.game-play{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;
  background:rgba(15,17,21,.42);opacity:0;transition:opacity .12s ease;
  font-family:var(--sans);font-weight:600;font-size:13px;color:#fff;font-weight:600;letter-spacing:.03em}
.game-card:hover .game-play,.game-card:focus-visible .game-play{opacity:1}
.game-meta{display:block;padding:12px 14px 14px}
.game-meta b{display:block;font-size:.95rem;font-weight:650;color:var(--obsidian)}
/* What it is, in one line — a title and an arrow told nobody what they were
   about to open. */
.game-blurb{display:block;margin-top:3px;font-size:12.5px;line-height:1.45;color:var(--stone)}
/* The claim that separates these from every other AI-made game: the score
   goes to a database we host, shared across everybody. Verified per game —
   all four post to /v1/leaderboard. */
.game-saves{display:inline-flex;align-items:center;gap:5px;margin-top:9px;
  font-family:var(--sans);font-weight:600;font-size:10.5px;letter-spacing:.02em;color:var(--brand-ink);
  background:color-mix(in srgb, var(--brand) 10%, transparent);
  border-radius:9999px;padding:3px 9px}
.game-saves::before{content:"";width:6px;height:6px;border-radius:50%;background:var(--live)}
/* Two per row all the way down: at one-per-row a single card filled a phone
   screen, so the set read as one enormous game rather than four small ones. */
@media(max-width:820px){.games{grid-template-columns:repeat(2,1fr);gap:12px;padding:0 16px}}
@media(max-width:440px){.games{grid-template-columns:repeat(2,1fr);gap:10px}
  .game-meta{padding:10px 11px 12px}
  .game-meta b{font-size:.88rem}
  .game-blurb{font-size:11.5px}
  .game-saves{font-size:9.5px;padding:2px 7px}}

/* ── Full-size screenshot viewer (showcase) ────────────────────────────────
   A lightbox for a gallery capture, so "see it full size" shows the image in
   a real viewer instead of opening a naked .jpg that reads as a broken page. */
.shotbox{position:fixed;inset:0;z-index:120;background:rgba(0,0,0,.86);
  display:none;align-items:center;justify-content:center;padding:24px;overflow:auto}
.shotbox[hidden]{display:none}
.shotbox.open{display:flex}
.shotbox img{max-width:100%;max-height:calc(100vh - 48px);border-radius:8px;
  box-shadow:0 20px 60px -20px rgba(0,0,0,.6);background:#fff}
.shotbox-x{position:fixed;top:16px;right:20px;width:40px;height:40px;border-radius:999px;
  border:0;background:rgba(255,255,255,.14);color:#fff;font-size:22px;line-height:1;cursor:pointer}
.shotbox-x:hover{background:rgba(255,255,255,.26)}

/* ═══ ROOMS KIT (2026-09-05) ═════════════════════════════════════════════════
   Phase 3 of Rooms. Eight reusable marketing blocks and the three pages that
   sell — home, pricing, enterprise — plus the five-item navigation.

   The complaint this answers, verbatim: "the marketing pages look more like
   dictionaries with just text and no visuals", "on the pricing page I can
   barely find enterprise", "the enterprise page is not very good to understand
   or to visualize. Everything is just plain. It looks like text."

   Two rules run through all of it. Colour means PLACE: a section declares its
   room once, as --c (the fill), --t (the tinted surface) and --ink-r (the same
   hue as readable text), and every child reads those instead of naming a hue.
   And mono is for FACTS: prices, durations, addresses and code — never a label,
   never a heading, never a sentence.
   ─────────────────────────────────────────────────────────────────────────── */

/* Room defaults, so a block dropped on a page with no room still paints. */
.rk{--c:var(--brand);--t:var(--studio-t);--ink-r:var(--studio-ink)}
.rk{padding:64px 0;position:relative}
@media(min-width:900px){.rk{padding:88px 0}}
.rk-narrow{max-width:44rem}

/* ── the eyebrow ─────────────────────────────────────────────────────────────
   A square dot in the room's hue, then the label. It is the smallest possible
   piece of wayfinding and it is on every section: by the third page you know
   teal means the thing you end up owning without having read that anywhere. */
.eyebrow{display:inline-flex;align-items:center;gap:9px;margin:0 0 14px;
  font-family:var(--sans);font-size:12px;font-weight:600;letter-spacing:.07em;
  text-transform:uppercase;color:var(--ink-r,var(--brand-ink))}
.eyebrow i{width:10px;height:10px;border-radius:3px;background:var(--c,var(--brand));
  display:inline-block;flex:none}

/* ── the icon tile ───────────────────────────────────────────────────────────
   28 / 40 / 56, filled with the room's tint and drawn in the room's ink. The
   filled variant (the room's own colour, white glyph) is for a room's own
   header, where the tile IS the room rather than a pointer to it. */
.rk-tile{width:40px;height:40px;border-radius:11px;display:inline-grid;place-items:center;
  background:var(--t,var(--sunk));color:var(--ink-r,var(--brand-ink));flex:none}
.rk-tile svg{width:20px;height:20px;stroke:currentColor;fill:none;stroke-width:1.8;
  stroke-linecap:round;stroke-linejoin:round}
.rk-tile.lg{width:56px;height:56px;border-radius:15px}
.rk-tile.lg svg{width:26px;height:26px}
.rk-tile.sm{width:28px;height:28px;border-radius:8px}
.rk-tile.sm svg{width:15px;height:15px}
.rk-tile.fill{background:var(--c,var(--brand));color:var(--brand-on)}

/* State is a dot, never a card. */
.rk-dot{display:inline-block;width:8px;height:8px;border-radius:50%;background:var(--live);
  margin-right:7px;vertical-align:middle}

.rk-lede{font-size:1.0625rem;line-height:1.55;color:var(--charcoal);max-width:38rem;margin:0}
.rk-note{margin:14px 0 0;font-size:13px;color:var(--stone)}
.rk-acts{display:flex;flex-wrap:wrap;gap:10px;align-items:center;margin-top:26px}
.rk h2{margin-bottom:14px}

/* ── 1 · hero with a frame ───────────────────────────────────────────────────
   Centred, and stacked, since 2026-09-06. The founder's instruction, verbatim:
   "Top text on pages should be centered title and description."

   It used to be two columns — words left, picture right — which is the shape
   every SaaS front page has, and it has two costs. On a phone the columns stack
   anyway, so the left-aligned text was a desktop-only decision paying for
   itself on the surface nobody was looking at. And the eye enters a two-column
   hero at the top left and leaves at the top right, which puts the picture and
   the headline in competition; stacked, the words say what it is and the frame
   answers, in that order, at every width.

   The frame is capped at 880px rather than allowed to fill the 1232px main
   column: a browser chrome eleven hundred pixels wide stops reading as a
   picture of a screen and starts reading as the page's own furniture. */
.rk-hero{padding-top:44px}
.rk-hero-in{display:block;text-align:center}
.rk-hero-say{max-width:56rem;margin-inline:auto}
.rk-hero h1{margin:0 0 18px;font-size:clamp(2.15rem,4.6vw,3.4rem)}
/* A measure, which is the whole reason the lede is not simply centred inside
   the 56rem the headline gets: a centred line you have to sweep ninety
   characters to read is harder than the left-aligned one it replaced, not
   easier. 48ch and not 60: `ch` is the width of a ZERO, which is wider than
   average prose, so 58ch measured out at about 86 real characters on the
   /features lede — the exact thing the cap exists to prevent. This lands at
   about seventy. */
.rk-hero .rk-lede{max-width:48ch;margin-inline:auto}
.rk-hero .rk-acts{justify-content:center}
.rk-hero .rk-note{max-width:52ch;margin-inline:auto}
/* Centred text, left-aligned picture. Everything inside the frame — an address
   bar, a typed sentence, a file list — is content, and content does not centre
   just because the block introducing it does. */
.rk-hero-see{min-width:0;max-width:880px;margin:38px auto 0;text-align:left}
@media(max-width:900px){.rk-hero-see{margin-top:28px}}
.rk-hero-see:empty{display:none;margin:0}
/* The hero's `extra` slot — twenty-one pages put a one-line summary card there.
   It sat flush left under a left-aligned headline; under a centred one it read
   as a card that had come loose. Centred as a block, left-aligned inside,
   because it is a sentence and not a caption. */
.rk-hero > .tldr{margin-left:auto;margin-right:auto;text-align:left}

/* ── the product frame ───────────────────────────────────────────────────────
   Drawn, not screenshotted. A screenshot of a chat is unreadable at 390px and
   stale the day the UI moves; this is the SHAPE of the thing — a sentence, a
   site, and the two numbers — which is what the picture is for. */
.pf{background:var(--white);border:1px solid var(--hairline);border-radius:var(--radius);
  overflow:hidden;box-shadow:var(--drop-lg)}
.pf-bar{display:flex;align-items:center;gap:6px;padding:10px 13px;background:var(--sunk);
  border-bottom:1px solid var(--hairline);margin:0}
.pf-bar i{width:9px;height:9px;border-radius:50%;background:var(--ash);display:inline-block;flex:none}
.pf-bar span{margin-left:8px;font-size:11.5px;color:var(--stone);overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap}
.pf-b{padding:16px;display:flex;flex-direction:column;gap:12px}
/* Always Studio blue, never the section's room. The bubble is a picture of the
   composer, and the composer lives in Studio — painting it coral on a Map page
   made the customer's own sentence look like an error. Colour means place, and
   the place this bubble is in is not the place the section is about. */
.pf-ask{align-self:flex-end;max-width:92%;margin:0;background:var(--brand);
  color:var(--brand-on);padding:10px 14px;border-radius:13px 13px 4px 13px;
  font-size:14px;line-height:1.45}
.pf-site{border:1px solid var(--hairline);border-radius:var(--radius-sm);padding:12px;
  background:var(--paper);display:block}
/* Brand into Projects, on every page. Like .pf-ask above, this is a picture of
   the product rather than a piece of the section it sits in — a coral-to-teal
   masthead on the Map page read as a warning stripe. */
.pf-site .pf-h{display:block;height:10px;width:46%;border-radius:4px;
  background:linear-gradient(90deg,var(--brand),var(--projects))}
.pf-site .pf-l{display:block;height:6px;border-radius:3px;background:var(--ash);margin-top:9px}
.pf-site .pf-l.s{width:58%}
.pf-site .pf-g{display:grid;grid-template-columns:repeat(3,1fr);gap:7px;margin-top:11px}
.pf-site .pf-g i{height:28px;border-radius:5px;background:var(--sunk);border:1px solid var(--hairline)}
.pf-r{display:flex;justify-content:space-between;gap:12px;flex-wrap:wrap;margin:0;
  font-size:11.5px;color:var(--stone)}
.pf-r b{color:var(--live);font-weight:500;white-space:nowrap}
/* A real screenshot in the same chrome, so a drawn frame and a captured one
   read as the same object rather than as two different design languages. */
.pf-shot{margin:0}
.pf-shot img{display:block;width:100%;height:auto}

/* ── 2 · three tiles ─────────────────────────────────────────────────────── */
.rk-tiles{display:grid;gap:16px;margin-top:26px}
.rk-tiles.c2{grid-template-columns:repeat(2,1fr)}
.rk-tiles.c3{grid-template-columns:repeat(3,1fr)}
.rk-tiles.c4{grid-template-columns:repeat(4,1fr)}
@media(max-width:900px){.rk-tiles.c3,.rk-tiles.c4{grid-template-columns:repeat(2,1fr)}}
@media(max-width:620px){.rk-tiles.c2,.rk-tiles.c3,.rk-tiles.c4{grid-template-columns:1fr}}
.rk-card{background:var(--white);border:1px solid var(--hairline);border-radius:var(--radius);
  padding:20px}
.rk-card h3{font-size:1.0625rem;margin:13px 0 5px;letter-spacing:-.01em}
.rk-card p{margin:0;font-size:14px;line-height:1.55;color:var(--stone)}

/* ── 3 · the number strip ────────────────────────────────────────────────────
   Prices from gallery.json, in mono. The one place on a marketing page where a
   figure gets to be the biggest thing in its box. */
/* auto-fit, and the rules are drawn by the GAP.
   Two things were wrong with `repeat(4,1fr)` plus nth-child borders.

   Phase 4 put a strip on about thirty pages and most have three real figures
   rather than four, because three is how many that page has actually measured.
   A fixed four-column grid gave every one of those an EMPTY fourth cell — a
   296px white box with a rule down its left edge, which reads as a figure that
   failed to load rather than as one that was never claimed.

   And the separators were nth-child rules written for one exact layout, so the
   moment a strip wrapped to two rows anywhere the viewport media query did not
   predict — a four-figure strip inside a show-and-tell's half column, say —
   the row it wrapped onto had no rule above it and stray rules down its side.
   A 1px gap over a hairline ground draws every separator that exists and none
   that does not, at any count, at any width. */
.rk-strip{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(190px,100%),1fr));
  gap:1px;margin-top:30px;
  border:1px solid var(--hairline);border-radius:var(--radius);overflow:hidden;
  background:var(--hairline)}
.rk-strip div{padding:18px 20px;min-width:0;background:var(--white)}
.rk-strip b{display:block;font-size:1.3rem;font-weight:500;color:var(--obsidian);
  letter-spacing:-.02em;line-height:1.1}
.rk-strip span{display:block;margin-top:5px;font-size:13px;color:var(--stone);line-height:1.4}

/* ── 4 · show and tell ───────────────────────────────────────────────────── */
.rk-st{display:grid;grid-template-columns:1fr 1fr;gap:44px;align-items:center}
@media(max-width:900px){.rk-st{grid-template-columns:1fr;gap:28px}}
.rk-st.flip .rk-st-say{order:2}
@media(max-width:900px){.rk-st.flip .rk-st-say{order:0}}
.rk-st-say p{margin:0 0 12px;color:var(--charcoal);max-width:36rem}
.rk-st-say p:last-child{margin-bottom:0}
.rk-st-see{min-width:0}

/* ── 5 · steps ───────────────────────────────────────────────────────────── */
.rk-steps{counter-reset:rkstep;list-style:none;margin:26px 0 0;padding:0;
  display:grid;gap:2px}
/* The number in column one, the heading and the sentence BOTH in column two.
   Naming the column explicitly is load-bearing: a three-child grid on two
   columns puts the third child back in column one, and the first version of
   this shipped every step's sentence set one word per line in a 38px gutter. */
.rk-steps li{counter-increment:rkstep;display:grid;grid-template-columns:30px 1fr;
  gap:2px 14px;padding:16px 0;border-top:1px solid var(--hairline)}
.rk-steps li:first-child{border-top:0}
.rk-steps li::before{content:counter(rkstep);font-family:var(--mono);font-size:13px;
  color:var(--ink-r,var(--brand-ink));background:var(--t,var(--sunk));
  width:30px;height:30px;border-radius:9px;display:grid;place-items:center;
  grid-column:1;grid-row:1 / span 2;align-self:start}
.rk-steps b{grid-column:2;display:block;font-family:var(--display);font-size:1.0625rem;
  font-weight:600;color:var(--obsidian)}
.rk-steps span{grid-column:2;color:var(--stone);font-size:14.5px;line-height:1.55}

/* ── 6 · proof ───────────────────────────────────────────────────────────── */
.rk-proof-card{display:grid;grid-template-columns:1.15fr 1fr;gap:0;background:var(--white);
  border:1px solid var(--hairline);border-radius:var(--radius);overflow:hidden;
  color:inherit;box-shadow:var(--drop-lg)}
@media(max-width:760px){.rk-proof-card{grid-template-columns:1fr}}
.rk-proof-card:hover{text-decoration:none;border-color:var(--ash)}
.rk-proof-shot{display:block;background:var(--sunk);min-width:0}
.rk-proof-shot img{display:block;width:100%;height:100%;object-fit:cover;object-position:top center}
.rk-proof-in{display:flex;flex-direction:column;gap:9px;padding:24px}
.rk-proof-h{font-family:var(--display);font-size:1.375rem;font-weight:600;color:var(--obsidian);
  line-height:1.2;letter-spacing:-.015em}
.rk-proof-ask{color:var(--charcoal);font-size:14.5px;line-height:1.55}
.rk-proof-facts{display:flex;flex-wrap:wrap;gap:6px}
.rk-proof-facts span{font-size:11.5px;color:var(--stone);background:var(--sunk);
  border-radius:var(--radius-pill);padding:4px 10px}
.rk-proof-price{font-size:1.75rem;font-weight:500;color:var(--obsidian);letter-spacing:-.02em}
.rk-proof-go{margin-top:auto;font-size:14px;font-weight:600;color:var(--ink-r,var(--brand-ink))}

/* ── 7 · asked, answered ─────────────────────────────────────────────────── */
.rk-faq-list{margin-top:8px}
#faq details{border-bottom:1px solid var(--hairline)}
#faq summary{cursor:pointer;font-family:var(--display);font-weight:600;font-size:1.0625rem;
  color:var(--obsidian);padding:16px 0;list-style:none;display:flex;
  justify-content:space-between;align-items:center;gap:14px}
#faq summary::-webkit-details-marker{display:none}
#faq summary::after{content:"+";color:var(--ink-r,var(--brand-ink));font-size:1.35rem;
  line-height:1;flex:none;font-family:var(--sans)}
#faq details[open] summary::after{content:"\2013"}
#faq details>p{margin:0 0 18px;color:var(--stone);line-height:1.6;max-width:44rem}

/* ── 8 · the door ────────────────────────────────────────────────────────────
   The one colourless block. Every hue above it means a place; the exit is not
   a place, so it is ink and paper and one button. */
.rk-door{padding:0}
.rk-door-in{background:var(--obsidian);border-radius:var(--radius);padding:52px 32px;
  text-align:center}
@media(min-width:900px){.rk-door-in{padding:64px 40px}}
/* Scoped to .rk-door-in, NOT to .rk-door.
   The home page's door shares its <section> with the TestFlight card and the
   self-host terminal, both of which sit on a WHITE surface. Written as
   `.rk-door .btn.ghost` these inverted colours reached down into them and
   painted paper-on-white text at 1.09:1 — ten findings, on the one control
   that says "talk to a human". A rule that inverts colour has to name the
   inverted surface, not the section that happens to contain one. */
.rk-door-in h2{color:var(--paper);margin:0 0 12px}
.rk-door-in h2 em{color:var(--brand-inv);font-style:normal}
.rk-door-in>p{margin:0 auto;max-width:40rem;
  color:color-mix(in srgb,var(--paper) 78%,var(--obsidian))}
.rk-door-in .rk-acts{justify-content:center}
.rk-door-in .btn{background:var(--paper);color:var(--obsidian)}
.rk-door-in .btn:hover{background:var(--white)}
.rk-door-in .btn.ghost{background:transparent;color:var(--paper);
  box-shadow:0 0 0 1px color-mix(in srgb,var(--paper) 40%,var(--obsidian))}
.rk-door-in .btn.ghost:hover{background:color-mix(in srgb,var(--paper) 12%,var(--obsidian));
  color:var(--paper)}
.rk-door-in .rk-door-fine{margin:18px auto 0;font-size:13px;
  color:color-mix(in srgb,var(--paper) 70%,var(--obsidian))}

/* ── the plan switch and the plan cards ──────────────────────────────────────
   "On the pricing page I can barely find enterprise... have to look a long
   time to find business." Both were rows in a wide table. The switch names the
   two audiences out loud and puts the company's two cards one click away, at
   the top of the page, in the same shape as the personal three. */
.seg{display:inline-flex;gap:4px;padding:4px;background:var(--sunk);
  border-radius:var(--radius-pill);margin:6px 0 26px;max-width:100%;
  border:1px solid var(--hairline)}
.seg button{border:0;background:none;padding:10px 20px;border-radius:var(--radius-pill);
  font:600 15px var(--sans);color:var(--stone);cursor:pointer;white-space:nowrap}
.seg button[aria-selected="true"]{background:var(--white);color:var(--obsidian);
  box-shadow:var(--ring-card)}
.seg button:focus-visible{outline:2px solid var(--brand-ink);outline-offset:2px}
@media(max-width:400px){.seg button{padding:10px 12px;font-size:14px}}

.plans{display:grid;grid-template-columns:repeat(3,1fr);gap:18px;align-items:stretch}
.plans.two{grid-template-columns:repeat(2,1fr);max-width:860px}
@media(max-width:900px){.plans,.plans.two{grid-template-columns:1fr}}
.plan{background:var(--white);border:1px solid var(--hairline);border-radius:var(--radius);
  padding:24px;display:flex;flex-direction:column;gap:14px;position:relative}
.plan.hi{border-color:var(--brand);box-shadow:0 0 0 3px var(--focus-halo)}
.plan .tag{position:absolute;top:-12px;left:20px;background:var(--brand);color:var(--brand-on);
  font-size:12px;font-weight:600;padding:4px 11px;border-radius:var(--radius-pill)}
.plan h3{font-size:1.375rem}
.plan .plan-who{margin:0;font-size:13.5px;color:var(--stone)}
.plan .plan-price{margin:0;font-family:var(--display);font-size:2.5rem;font-weight:700;
  color:var(--obsidian);line-height:1;letter-spacing:-.03em}
.plan .plan-price small{font-family:var(--sans);font-size:13.5px;font-weight:500;
  color:var(--stone);letter-spacing:0;display:block;margin-top:8px}
.plan .plan-inc-list{list-style:none;margin:0;padding:0;font-size:14.5px;line-height:1.5;
  display:flex;flex-direction:column;gap:9px;color:var(--charcoal)}
.plan .plan-inc-list li{display:flex;gap:10px;align-items:flex-start}
.plan .plan-inc-list li::before{content:"";width:17px;height:17px;border-radius:50%;flex:none;
  margin-top:2px;background:var(--projects-t);
  /* A tick drawn with currentColor is not possible in a pseudo-element mask
     without a mask image, so the tick is a border trick: a rotated corner. */
  box-shadow:inset 0 0 0 1px var(--hairline)}
.plan .plan-inc-list li.usage{font-family:var(--mono);font-size:12.5px;color:var(--stone)}
.plan .plan-inc-list li.usage::before{display:none}
.plan .btn{margin-top:auto;text-align:center}
/* The dark card. It is the only inverted surface on the page, which is the
   whole point: "custom" has no number to make it stand out, so the surface
   does the standing out. */
.plan.ent{background:var(--obsidian);border-color:var(--obsidian)}
.plan.ent h3,.plan.ent .plan-price{color:var(--paper)}
.plan.ent .plan-who,.plan.ent .plan-inc-list li.usage,.plan.ent .plan-price small{
  color:color-mix(in srgb,var(--paper) 74%,var(--obsidian))}
.plan.ent .plan-inc-list{color:color-mix(in srgb,var(--paper) 88%,var(--obsidian))}
.plan.ent .plan-inc-list li::before{background:color-mix(in srgb,var(--paper) 22%,var(--obsidian));
  box-shadow:none}
.plan.ent .btn{background:var(--paper);color:var(--obsidian)}
.plan.ent .btn:hover{background:var(--white)}
.plans-fine{margin:18px 0 0;font-size:13px;color:var(--stone)}

/* ── the spending limit, drawn ───────────────────────────────────────────── */
.cap-fig{display:grid;grid-template-columns:1fr 1fr;gap:32px;align-items:start;
  background:var(--white);border:1px solid var(--hairline);border-radius:var(--radius);
  padding:26px;margin-top:26px}
@media(max-width:820px){.cap-fig{grid-template-columns:1fr;gap:22px}}
.cap-bar{height:16px;border-radius:8px;background:var(--sunk);overflow:hidden;display:flex;
  border:1px solid var(--hairline)}
.cap-bar i{display:block;height:100%;background:var(--brand)}
.cap-row{display:flex;justify-content:space-between;gap:12px;font-size:12.5px;
  color:var(--stone);margin-top:10px}
.cap-row b{color:var(--obsidian);font-weight:500}
.cap-stop{margin-top:20px;display:flex;gap:12px;align-items:flex-start;
  background:var(--sunk);border-radius:var(--radius-sm);padding:12px 14px}
.cap-stop p{margin:0;font-size:13.5px;color:var(--charcoal)}

/* ── the worked bill ─────────────────────────────────────────────────────────
   The enterprise page opened with eight headlines and eight paragraphs. It
   opens with arithmetic now: what twenty people actually pay, line by line,
   against what twenty seats cost whether or not anybody signs in. */
.bill{background:var(--white);border:1px solid var(--hairline);border-radius:var(--radius);
  padding:22px;box-shadow:var(--drop-lg)}
.bill-l{margin:0 0 8px;font-size:11.5px;font-weight:600;letter-spacing:.07em;
  text-transform:uppercase;color:var(--stone)}
.bill-row{display:grid;grid-template-columns:1fr auto;gap:12px;padding:11px 0;
  border-top:1px solid var(--hairline);font-size:14px;align-items:baseline;
  color:var(--charcoal)}
.bill-row:first-of-type{border-top:0}
.bill-row .mono{color:var(--obsidian)}
.bill-row.tot{border-top:2px solid var(--obsidian);font-weight:600;color:var(--obsidian);
  font-size:15px;margin-top:2px}
.bill-row.tot .mono{font-size:1.25rem}
.bill-vs{margin-top:22px;padding-top:18px;border-top:1px solid var(--hairline)}
.pbar{height:12px;border-radius:6px;background:var(--sunk);overflow:hidden;display:flex;
  border:1px solid var(--hairline)}
.pbar i{display:block;height:100%}
.pbar i.ours{background:var(--projects)}
.pbar i.theirs{background:var(--alarm)}
.bill-note{margin:9px 0 0;font-size:13px;color:var(--stone)}
.bill-note .mono{color:var(--obsidian)}

/* ── a room section on /features ─────────────────────────────────────────────
   The Map, for a visitor. Six sections, one per room, each opening with the
   room's own tile and hue so the page that lists everything is also the page
   that teaches where everything lives. */
.room-head{display:flex;align-items:center;gap:16px;margin-bottom:6px}
/* On a phone the 56px tile beside a headline that wraps to four lines leaves
   the heading a 250px column. The tile goes above instead — it is a label for
   the room, not a bullet for the sentence. */
@media(max-width:600px){
  .room-head{display:block}
  .room-head .rk-tile{margin-bottom:14px}
}
.room-head h2{margin:0}
.room-head .room-where{margin:5px 0 0;font-size:13px;color:var(--stone);font-family:var(--mono)}
.rk-band{background:var(--t,var(--sunk));border-radius:var(--radius);padding:26px;
  border:1px solid var(--hairline)}

/* ── nav: five items, and Enterprise among them ──────────────────────────── */
.navent{font-weight:600}
.mega .mega-i{color:var(--ink-r,var(--brand-ink));background:var(--t,var(--sunk))}
.foot-h{display:flex;align-items:center;gap:8px}
.foot-dot{width:9px;height:9px;border-radius:3px;background:var(--c,var(--brand));
  display:inline-block;flex:none}

/* ── the home page's hero, centred ───────────────────────────────
   "Top text on pages should be centered title and description." The kit's hero
   was rewritten to that shape and this one follows it, because a home page that
   composes differently from every page it links to is exactly the drift the
   generator exists to prevent.

   The ask box is still the hero and nothing inside it moved: every id the
   composer's JavaScript reaches for is untouched, the textarea still types
   left to right, and only the BOX is centred, never its contents. What changed
   is that the headline, the lede, the hail bubble, the chips and the fine print
   now sit on the page's centre line, and the drawn product frame follows the
   composer instead of standing beside it.

   Everything here is an override of the centred base rules rather than a second
   copy of them, so the two cannot drift. */
.hero.rooms{text-align:center;padding:40px 0 72px}
.hero.rooms .rk-hero-in{display:block}
.hero.rooms .rk-hero-say{max-width:none;margin:0}
.hero.rooms h1{max-width:18ch;margin:0 auto}
.hero.rooms .hero-mark{margin:0 auto 14px}
.hero.rooms .sub{margin:1.1rem auto 0;max-width:54ch}
/* The bubble is a line of speech, so it is centred as a BLOCK and keeps its own
   left-aligned words — centring the text inside a chat bubble makes the one
   piece of the page meant to look like a person look like a poster. */
.hero.rooms .hail{justify-content:center;margin-left:auto;margin-right:auto}
.hero.rooms .hail-b{text-align:left}
.hero.rooms .ask,.hero.rooms .ask-big{margin-left:auto;margin-right:auto;text-align:left}
.hero.rooms .cats,.hero.rooms .chips{justify-content:center;margin-left:auto;margin-right:auto}
.hero.rooms .ask-fine,.hero.rooms .ask-rec{text-align:center}
/* Below the composer, capped at 880px, left-aligned inside its own chrome — the
   same rule the kit hero uses, for the same reason. */
.hero.rooms .rk-hero-see{min-width:0;max-width:880px;margin:38px auto 0;text-align:left}
@media(max-width:1000px){.hero.rooms .rk-hero-see{margin-top:28px}}

/* The home page's door shares its section with the TestFlight card and the
   self-host terminal below it, so it keeps the section padding that the shared
   .rk-door drops. */
#get.rk-door{padding:72px 0}
@media(min-width:900px){#get.rk-door{padding:96px 0}}
#get .beta,#get .setup{text-align:left}
#get .beta{max-width:44rem;margin-left:auto;margin-right:auto}

/* The tick inside a plan's bullet. Drawn with two borders on a rotated box
   rather than an inline SVG data URI, because the URI would have to carry a
   literal hex for the stroke and a literal hex is a colour that cannot flip
   with the theme — the exact bug check-tokens exists to catch. */
.plan .plan-inc-list li{position:relative}
.plan .plan-inc-list li::after{content:"";position:absolute;left:6px;top:5px;
  width:4px;height:8px;border:solid var(--projects-ink);border-width:0 2px 2px 0;
  transform:rotate(45deg)}
.plan .plan-inc-list li.usage::after{display:none}
.plan.ent .plan-inc-list li::after{border-color:var(--paper)}
/* Hover keeps the room's hue too. Without this the base rule above is beaten
   by the older, more specific hover rule and every tile snapped back to brand
   blue the moment the pointer touched it — which is the wayfinding failing at
   exactly the moment somebody is using it. */
.mega a:hover .mega-i{background:color-mix(in srgb,var(--c,var(--brand)) 22%,transparent)}

/* ── wide content gets its own scroller ──────────────────────────────────────
   Phase 4. check-layout found the same finding on three pages at 280px and at
   390@1.3: "code reaches 444px" — a domain like
   `www.yourbusiness.com.yourbusiness.com` or a curl line is one unbreakable
   word, and one unbreakable word wider than the viewport makes the WHOLE PAGE
   scroll sideways. Every other column then sits half off screen, on exactly
   the two settings an older person's phone is most likely to be on.

   Two shapes, because they want opposite things. Inline code in a sentence is
   prose and should WRAP — a hostname broken across two lines is still a
   hostname. A command is not prose: breaking `curl -fsSL … | bash` mid-flag
   invites somebody to copy half of it, so a command SCROLLS inside its own box
   and the page stays still. */
code,kbd,samp{font-family:var(--mono);font-size:.92em;overflow-wrap:anywhere;
  word-break:break-word}
/* A command line, or anything else that must not be broken. Block, its own
   horizontal scroller, and never wider than its column. */
.codeline,pre{display:block;max-width:100%;overflow-x:auto;overflow-y:hidden;
  -webkit-overflow-scrolling:touch}
.codeline{background:var(--sunk);border:1px solid var(--hairline);
  border-radius:var(--radius-sm);padding:11px 13px;font-family:var(--mono);
  font-size:13px;color:var(--obsidian);white-space:pre;overflow-wrap:normal;
  word-break:normal;line-height:1.5}
.codeline code,pre code{white-space:pre;overflow-wrap:normal;word-break:normal;
  background:none;padding:0;font-size:inherit}
/* The termlog is a picture of a shell, and a shell line does not reflow. It
   already wraps `overflow-wrap:anywhere` on its text column, but the grid it
   sits in has to be allowed to be narrower than its content. */
.termlog{max-width:100%;overflow-x:auto}

/* ── PHASE 4: the home page's middle, and the shapes it needed ───────────────
   Twelve of the home page's sections were the old text-only pattern — a label,
   a headline, a paragraph, a grid of cards — and three of them said what three
   others had already said in different words. Merging those pairs needed four
   small shapes that the kit did not have, because the kit's blocks are all
   whole sections and these are rows INSIDE one. */

/* A second show-and-tell row under the first, inside one section. Used by the
   three product captures (which were three <section>s and 352px of padding to
   separate three pictures of the same product) and by the repo section. */
.rk .rk-st + .rk-st,.rk-st-2{margin-top:38px}
@media(min-width:900px){.rk .rk-st + .rk-st,.rk-st-2{margin-top:52px}}
.rk > .rk-st:first-child{margin-top:0}

/* A heading for a second movement inside one section. Smaller than the h2 that
   opened the section and larger than a card's h3, so the eye reads it as "still
   the same subject, next part of it" rather than as a new chapter. */
.rk-sub-h{font-family:var(--display);font-size:1.5rem;font-weight:600;
  letter-spacing:-.015em;color:var(--obsidian);margin:44px 0 12px}
.rk-sub-h + .rk-lede{margin-bottom:4px}

/* The receipts, directly under the three cards they describe rather than in a
   section of their own. No top padding: it is a caption, not a chapter. */
.stack-strip{margin:26px 0 0}
.stack-strip .rk-strip{margin-top:0}

/* The two other ways in, side by side under the door. Stacked, they were 1046px
   — the tallest block on the page after the hero — for a TestFlight link and a
   two-line shell command. */
.get-two{display:grid;grid-template-columns:1fr 1fr;gap:20px;align-items:stretch;
  margin-top:2.5rem}
@media(max-width:860px){.get-two{grid-template-columns:1fr}}
/* margin:0, not just margin-top:0. `.setup` carries `margin:40px auto` from
   when it was a centred block on a wide page, and an AUTO INLINE MARGIN on a
   grid item switches the item off `stretch` and sizes it to fit-content — so
   in a 232px track it laid itself out at 284px and pushed the page sideways at
   280px. The finding read as a terminal that would not wrap; it was a margin. */
.get-two > *{margin:0;min-width:0;max-width:100%}
.get-two .setup,.get-two .term{height:100%}

/* A card in a tile grid may carry a link in its heading and a measured figure
   under its sentence. Both were inline styles on the old .way markup. */
.rk-card h3 a{color:inherit;text-decoration:none}
.rk-card h3 a:hover{text-decoration:underline;text-underline-offset:3px}
.rk-card p small{display:block;margin-top:8px;font-size:12.5px;color:var(--stone)}

/* A drawn terminal is a picture of a shell, and a shell line does not reflow.
   Two separate things made this drag the whole page sideways at 280px, and
   fixing one of them alone changed nothing:

     the SCREEN — `white-space:pre-wrap` breaks at spaces, so a clone URL, one
     unbreakable 40-character token, simply stuck out of the box;

     the TITLE BAR — a flex row whose label is `white-space:nowrap` by way of
     being a single unbreakable phrase. A flex item does not shrink below its
     min-content width unless it is told it may, so the bar had a 284px floor
     and handed it up to the terminal, the block around it, and the page.

   The screen scrolls; the label ellipsises. */
.term,.term .bar,.term .scr{min-width:0;max-width:100%}
.term .scr{overflow-x:auto;-webkit-overflow-scrolling:touch}
.term .bar span{min-width:0;overflow-wrap:anywhere}

/* ── mono, actually ──────────────────────────────────────────────────────────
   "Mono is for numbers, addresses, timestamps and code — never a label" is the
   first rule at the top of this file, and `class="mono"` is how the kit says
   it: build-pages.mjs puts it on every product-frame address, every figure in a
   number strip, every price on a proof card. There was no rule for it. The
   class did nothing at all except where a component happened to declare
   var(--mono) for itself, so the rule the whole system is built on was being
   applied by hand in five places and silently ignored everywhere else. */
.mono{font-family:var(--mono)}

/* ── the hand-written pages' measure ────────────────────────────────────────
   support, privacy, terms, 404, claude-code-mobile and self-hosted-ai-agent all
   wrap their prose in .wrap.narrow — a class that has never existed in this
   stylesheet. Every one of them has been setting body copy at the full 1232px
   of <main>, which is about 170 characters a line: roughly twice the width
   anybody can read without losing their place on the return sweep. */
.wrap{max-width:1180px;margin-inline:auto}
/* Left, not centred: the kit hero above it is left-aligned at 44rem, and a
   centred column under a left-aligned headline reads as two pages stapled
   together. */
.wrap.narrow{max-width:46rem;margin-inline:0}

/* A kit hero handing off to a plain prose section: the hero already pays 64-88px
   below itself and `section` pays 96-128 above, so the two together left a
   200px band of nothing between a headline and the first thing it introduces. */
.rk-hero + section:not(.rk){padding-top:0}

/* The product frame's status line may wrap on a narrow screen.
   `.pf-r b` was `white-space:nowrap`, which was survivable while `class="mono"`
   did nothing; in an actual monospace face the widest of these lines —
   "quoted first, then charged · $0.0092" — is 263px, and nowrap made it push a
   280px page sideways. It has spaces in it, so it can simply wrap; nowrap was
   never buying anything a normal line box does not already give it when there
   is room. */
.pf-r b{white-space:normal}

/* ── grid items must be allowed to be narrower than their content ───────────
   Two lanes of phase 4 hit the same finding independently and both worked
   around it in markup rather than here: a CSS grid item's automatic minimum is
   its MIN-CONTENT width, so one unbreakable token — a doubled hostname in a
   tile, a `white-space:pre` shell command in a show-and-tell's text column —
   refuses to shrink and takes the whole page sideways with it at 280px and at
   390px with the text scaled up.

   `.rk-st-see` already had this and `.rk-st-say` did not, which is exactly the
   kind of asymmetry nobody can predict from the outside. Every box in the kit
   that holds author-supplied content gets it, so the next person putting a
   command in a card does not have to discover this again. */
.rk-st-say,.rk-st-see,.rk-card,.rk-strip div,.rk-hero-say,.rk-hero-see,
.rk-proof-in,.rk-steps li{min-width:0}
.rk-card>*{max-width:100%}

/* ── the desktop mega menu, matched to the sheet ─────────────────────────────
   R2. The phone menu became a designed object; a bar menu that then used a
   different tile size, a different radius and an unmarked column heading would
   be the same drift in the other direction — two menus for one product.

   So: the panel takes the kit's card radius, the icon tiles take the sheet's
   34px, and every column heading gets the same square room dot the eyebrow and
   the footer heading already carry. The dot is drawn in CSS rather than added
   to the markup because `.mega-h` is generated in three places and a pseudo
   cannot be forgotten by one of them. */
@media(min-width:901px){
  .mega{border-radius:var(--radius);padding:20px 20px 12px;gap:4px 20px}
  .mega-i{width:34px;height:34px;border-radius:10px}
  .mega-i svg{width:18px;height:18px}
  .mega-h{display:flex;align-items:center;gap:8px;margin:0 0 6px}
  .mega-h::before{content:"";width:8px;height:8px;border-radius:3px;flex:none;
    background:var(--c,var(--brand))}
  .mega a{padding:9px 10px;border-radius:11px}
  .mega-cta{margin-top:8px;padding:12px 0 2px}
}

/* The plans come first and read centred: eyebrow, heading, lede, the switch. */
.rk-center>.eyebrow,.rk-center>h2,.rk-center>.rk-lede{text-align:center;margin-left:auto;margin-right:auto}
.rk-center>.eyebrow{justify-content:center;display:flex}
.rk-center .seg{display:flex;width:max-content;margin-left:auto;margin-right:auto}
/* The switch wraps rather than pushing the page sideways at 280px, and the
   plans follow the hero without a second hero's worth of air. */
.rk-center .seg{width:auto;max-width:100%;flex-wrap:wrap;justify-content:center}
@media(max-width:420px){.seg button{padding:8px 12px;font-size:13.5px}}
.rk-hero + #plans{padding-top:8px}
/* A card with no capture: the room's tile in the same 16:10 well. A card that
   asks, not links, fills the box above it. */
.scard-tile{display:grid;place-items:center;aspect-ratio:16/10;width:100%;background:var(--t,var(--sunk))}
.scard-tile i{width:56px;height:56px;border-radius:16px;display:grid;place-items:center;background:var(--white);color:var(--c,var(--obsidian));box-shadow:var(--ring-ghost)}
.scard-tile svg{width:26px;height:26px}
.scard-tile[data-room="library"]{--c:var(--library);--t:var(--library-t)}
.scard-tile[data-room="studio"]{--c:var(--studio);--t:var(--studio-t)}
button.scard{appearance:none;border:0;font:inherit;text-align:left;cursor:pointer;padding:0}

/* ═══ THE COMPARISON KIT ══════════════════════════════════════════════════════
   Three tabs, one range input each, a horizontal bar per product and a grid of
   ticks under it. Everything here is tokens — a chart drawn in literal hexes is
   a chart that stops being readable the moment somebody switches theme, and a
   price comparison nobody can read in dark mode is a price comparison nobody
   reads.

   The bar is a grid rather than a flex row so the three columns line up ACROSS
   rows: a product name, a track, and the number. Under 700px the row becomes
   two lines — name and number on one, the full-width track under it — because a
   track squeezed into a 90px column is a decoration, not a measurement. */

/* Was missing entirely. /compare has shipped `<span class="sr-only">Feature</span>`
   in its table header since it was written, and with no rule for the class it
   has been rendering the literal word "Feature" in the corner of the table. */
.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;
  clip:rect(0,0,0,0);white-space:nowrap;border:0}

.ck{margin:26px 0 0;max-width:100%}
.ck-tabs{display:inline-flex;gap:4px;padding:4px;background:var(--sunk);
  border-radius:var(--radius-pill);max-width:100%;flex-wrap:wrap}
.ck-tab{border:0;background:none;font:inherit;font-family:var(--sans);font-weight:600;
  font-size:13.5px;color:var(--stone);padding:9px 18px;border-radius:var(--radius-pill);
  cursor:pointer;white-space:nowrap}
.ck-tab[aria-selected="true"]{background:var(--white);color:var(--obsidian);
  box-shadow:var(--ring-card)}
.ck-tab:focus-visible{outline:2px solid var(--brand-ink);outline-offset:2px}

.ck-panel{margin:14px 0 0;padding:18px;background:var(--white);border-radius:var(--radius);
  box-shadow:var(--ring-card);min-width:0}

.ck-ctl{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:6px 14px;align-items:center;
  padding-bottom:14px;border-bottom:1px solid var(--hairline)}
.ck-ctl label{grid-column:1/-1;font-family:var(--sans);font-weight:600;font-size:13px;
  color:var(--charcoal)}
.ck-ctl input[type=range]{width:100%;min-width:0;accent-color:var(--c,var(--brand));height:22px}
.ck-ctl input[type=range]:focus-visible{outline:2px solid var(--brand-ink);outline-offset:3px}
.ck-ctl output{font-size:12.5px;color:var(--slate);white-space:nowrap}

.ck-bars{display:grid;gap:14px;padding:16px 0 4px}
.ck-bar{display:grid;grid-template-columns:minmax(0,10.5rem) minmax(0,1fr) auto;
  gap:2px 12px;align-items:center;min-width:0}
.ck-name{font-family:var(--sans);font-weight:600;font-size:13px;color:var(--charcoal);min-width:0}
.ck-track{display:block;height:16px;border-radius:var(--radius-sm);background:var(--sunk);
  overflow:hidden;min-width:0}
.ck-track>i{display:block;height:100%;border-radius:var(--radius-sm);background:var(--ash);
  transition:width .22s ease}
.ck-cost{font-size:13px;color:var(--charcoal);white-space:nowrap}
.ck-note{grid-column:2/-1;font-size:11.5px;color:var(--slate);line-height:1.45;min-width:0}

/* Ours is BRAND, not the room. The room hue says where you are standing —
   slate on /pricing, amber on /compare — and using it for our own bar meant
   the one thing the chart exists to point at changed colour depending on which
   page you read it on, and on /compare shared its hue with the section
   heading. One bar in one colour, the same on all three placements, matched to
   the tint behind the "ours" column of the grid underneath it. */
.ck-bar.is-ours .ck-name{color:var(--obsidian)}
.ck-bar.is-ours .ck-track>i{background:var(--brand)}
.ck-bar.is-ours .ck-cost{color:var(--obsidian);font-weight:600}
/* The worst case is a sentence with a live number in it, not a bar. As a bar it
   broke the only axis the chart has — at 3,000 answers it is $153 against a $20
   rival — so it either squashed everything else or was clamped to the same
   length as a $20 subscription, which is a lie about a length. */
.ck-worst{margin:2px 0 0;padding:10px 0 0;border-top:1px dashed var(--hairline);
  font-size:12px;color:var(--slate)}
.ck-worst b{color:var(--charcoal)}
/* An empty track with the word in the number column: a price we could not get
   is not a price of zero, and nothing here should let it look like one. */
.ck-bar.is-unknown .ck-track{background:transparent;
  box-shadow:inset 0 0 0 1px var(--hairline)}
.ck-bar.is-unknown .ck-name,.ck-bar.is-unknown .ck-cost{color:var(--smoke)}
.ck-bar.is-unknown .ck-cost{font-size:11.5px;letter-spacing:.02em;
  white-space:normal;text-align:right}

/* The wrapper is the scroller, so it has to be narrower than its column: in a
   grid cell it grew to the table's width and pushed the whole page sideways at
   280px (check-layout, 2026-09-06). */
.ck,.ck>*,.ck-pane{min-width:0}
.ck-ben-wrap{margin:18px 0 0;overflow-x:auto;min-width:0;max-width:100%}
.ck-ben{border-collapse:collapse;width:100%;font-size:13px}
.ck-ben th,.ck-ben td{border-bottom:1px solid var(--hairline);padding:8px 10px;
  text-align:center;vertical-align:middle}
.ck-ben thead th{font-family:var(--sans);font-weight:600;font-size:10.5px;
  letter-spacing:.03em;text-transform:uppercase;color:var(--slate);
  border-bottom:1px solid var(--ash)}
.ck-ben tbody th{text-align:left;font-family:var(--sans);font-weight:500;font-size:12.5px;
  color:var(--charcoal);width:42%}
.ck-ben th.is-ours,.ck-ben td.is-ours{background:var(--studio-t)}
.ck-ben thead th.is-ours{color:var(--brand-ink)}
.ck-ben .ck-y{color:var(--live);font-weight:700}
.ck-ben .ck-n{color:var(--ash)}
.ck-ben .ck-u{color:var(--smoke)}
.ck-ben tr:last-child th,.ck-ben tr:last-child td{border-bottom:0}

.ck-panel figcaption{margin:16px 0 0;font-size:11.5px;line-height:1.6;color:var(--slate)}
.ck-panel figcaption a{color:var(--brand-ink)}

@media(max-width:700px){
  .ck-panel{padding:14px}
  .ck-bar{grid-template-columns:minmax(0,1fr) auto}
  .ck-track{grid-column:1/-1;height:14px;margin-top:4px}
  .ck-note{grid-column:1/-1}
  .ck-ben{font-size:12px}
  /* Three column counts exist, so three rules, rather than one custom property
     markup sets — check-tokens.mjs keeps the list of properties the markup may
     set and a new one has to be argued for there, not smuggled in here. */
  .ck-ben[data-cols="4"]{min-width:23.4rem}
  .ck-ben[data-cols="5"]{min-width:27rem}
  .ck-ben[data-cols="6"]{min-width:30.6rem}
  .ck-ben th,.ck-ben td{padding:7px 5px}
  .ck-ben tbody th{width:9rem;font-size:11.5px}
  .ck-ben thead th{font-size:9.5px;letter-spacing:0}
}
@media(prefers-reduced-motion:reduce){.ck-track>i{transition:none}}

/* ── four rungs, not three ───────────────────────────────────────────────────
   Chat AI joined the bottom of the individual ladder on 2026-09-06. Four cards
   at a quarter each are too narrow to hold a plan's list much above a phone, so
   they go two-by-two well before they go one-by-one — and the price drops a
   step in size, because a 2.5rem number in a 250px card is the thing that
   pushes the card sideways. */
.plans.four{grid-template-columns:repeat(4,1fr);gap:14px}
.plans.four .plan{padding:20px;gap:12px}
.plans.four .plan .plan-price{font-size:2.15rem}
.plans.four .plan .plan-inc-list{font-size:13.5px}
@media(max-width:1080px){.plans.four{grid-template-columns:repeat(2,1fr);gap:18px}}
@media(max-width:640px){.plans.four{grid-template-columns:1fr}}


/* ═══ THE PHONE, MEASURED (2026-09-06) ═══════════════════════════════════════
   The marketing half of the same sweep. Kept in one block at the end for the
   same reason as app.css's: each is a small override of a rule written
   elsewhere, and scattering them would have made every one a merge conflict. */

/* ── a link in a sentence is not a colour ───────────────────────────────────
   axe: link-in-text-block, serious, 13 nodes across /, /pricing and
   /for-business. Inside a paragraph the only thing separating these from the
   words around them was --brand-ink, which is invisible to about one man in
   twelve. Nav links, buttons and cards are untouched: they are not in a
   sentence, and the rule only applies where a link sits inside running text. */
.rk-note a,.plans-fine a,p>a:not([class]),li>a:not([class]){
  text-decoration:underline;text-underline-offset:.18em;text-decoration-thickness:from-font}

/* The prose-link system several hundred lines up DOES carry a non-colour cue —
   an inset 1px box-shadow that the hover gradient then draws over. Two problems
   with it: the resting rule is --hairline, which is 1.11:1 on --paper and so
   invisible to anybody, and a box-shadow is not a text decoration, so it is
   invisible to axe as well. The flourish is kept; the resting state becomes a
   real underline in the link's own colour. Same specificity as the rule it is
   correcting, or it would silently lose. */
.sub a:not(.btn),.prose p a:not(.btn),.way p a:not(.btn),
.zone-note a:not(.btn),figcaption a:not(.btn),.linklist a:not(.btn){
  text-decoration:underline;text-underline-offset:.18em;text-decoration-thickness:from-font;
  box-shadow:none}

/* ── a sideways scroll a keyboard can reach ─────────────────────────────────
   axe: scrollable-region-focusable, serious. Both wrappers scroll on overflow
   and carried no tabindex, so the model comparison and the plan table were
   unreachable without a mouse or a finger. The attribute is on the markup; the
   ring is here, because a focusable box with no visible focus is the next bug. */
.ck-ben-wrap:focus-visible,.tbl:focus-visible{outline:2px solid var(--obsidian);outline-offset:2px}

/* ── the finger, on the homepage composer ───────────────────────────────────
   The PLAN FIRST / BUILD NOW toggle measured 92x26 and the mic 32x32, on the
   first control a visitor meets. Same coarse-pointer scoping as the block
   several hundred lines up; a mouse-driven desktop is untouched. */
@media(pointer:coarse){
  .ask-mode-b{min-height:44px;display:inline-flex;align-items:center}
  .ask-mic{width:44px;height:44px}
}

/* ── --brand is a fill; --brand-ink is the same blue as TEXT ────────────────
   #1E56E6 reads 5.60:1 on the light ground and 3.53:1 on the dark one, so
   every `color:var(--brand)` in the stylesheet was a contrast failure that
   only appeared in dark mode. tokens.json has carried --brand-ink for exactly
   this ("brand as TEXT on the page ground") since the palette was written —
   these six just never used it. 5.99 / 8.79. */
.gal-opts .est-opt[aria-selected="true"]::after{color:var(--brand-ink)}
.tune-rec{color:var(--brand-ink)}
.tune-back .tune-row-n{color:var(--brand-ink)}
