/*
 * Counter marketing site.
 *
 * One stylesheet, no inline styles anywhere: the Content-Security-Policy in `_headers` is
 * `style-src 'self'`, so a `style=` attribute would be dropped, and every font is served from
 * this origin too. The one thing JavaScript writes is the two custom properties that size a
 * character grid's cell, and it writes them through the CSSOM rather than as an attribute.
 *
 * Everything below is built out of the tokens in the first block: a ten-step space scale, one
 * type scale, and a colour set that is designed twice rather than inverted once -- gold, teal
 * and violet that read on near-black are far too light on paper-white. Dark is the default
 * regardless of the OS preference; `data-theme="light"` on <html> is what the footer toggle
 * writes for a visitor who wants the light set.
 */

/* Self-hosted so the site makes no third-party request at all and the CSP can stay
   first-party. These are the variable woff2 subsets Google Fonts serves, fetched once and
   committed under src/fonts with the SIL Open Font License they ship under.

   The symbols file is not optional and its range is narrowed to the one thing it alone
   carries: U+2500-259F, the block and box-drawing characters the hero and the turnstile are
   drawn with. Google's own subset also claims some punctuation that latin already covers;
   dropping that overlap means the two faces never compete and neither has to be declared
   first. Without this file every block glyph falls back to whatever monospace the system has,
   the advances stop matching and the grid comes apart. */
@font-face {
  font-family: "Geist Mono";
  font-style: normal;
  font-weight: 300 600;
  font-display: swap;
  src: url("/fonts/geist-mono-symbols.woff2") format("woff2");
  unicode-range: U+2500-259F;
}

@font-face {
  font-family: "Geist Mono";
  font-style: normal;
  font-weight: 300 600;
  font-display: swap;
  src: url("/fonts/geist-mono-latin.woff2") format("woff2");
  unicode-range:
    U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
    U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215,
    U+FEFF, U+FFFD;
}

@font-face {
  font-family: "Geist";
  font-style: normal;
  font-weight: 400 600;
  font-display: swap;
  src: url("/fonts/geist-latin.woff2") format("woff2");
  unicode-range:
    U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
    U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215,
    U+FEFF, U+FFFD;
}

/* ------------------------------------------------------------------ tokens */

:root {
  color-scheme: dark;

  /* Space. Every margin, gap and padding on the site is one of these ten steps. */
  --s1: 4px;
  --s2: 8px;
  --s3: 12px;
  --s4: 16px;
  --s5: 24px;
  --s6: 32px;
  --s7: 48px;
  --s8: 64px;
  --s9: 96px;
  --s10: 128px;

  --container: 1200px;
  --gutter: var(--s5);
  --section: var(--s9);
  --header: 64px;
  --radius: 12px;
  --radius-sm: 8px;
  --plate: 40px;

  /* Dark is the site's default, whatever the visitor's OS prefers: the hero is drawn for a
     near-black ground, and the light set below is an opt-in the footer toggle writes to
     `data-theme`. The ground is never #000 -- a pure black ground makes every hairline on it
     look like a defect -- and the surfaces are three distinct near-blacks. */
  --ground: #0e1013;
  --surface: #15181c;
  --surface-2: #1c2026;
  --hairline: #262b32;
  --hairline-lit: #606a79;
  --ink: #e8e6df;
  --muted: #9aa0a8;
  --claude: #c9a45c;
  --chatgpt: #5fb3a3;
  --muse: #9f97d4;
  /* The art's quietest tier: the ambient lattice behind the flow. */
  --field: #454e5c;

  --font-sans:
    "Geist", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica,
    Arial, sans-serif;
  --font-mono:
    "Geist Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}

/* Light. Not the dark set inverted: the accents are pulled down so gold, teal and violet
   still read on paper-white, and surfaces step up from the ground rather than away from it,
   so a card reads as paper on paper and needs only a hairline to separate it. Applied only
   when the visitor chooses it. */
:root[data-theme="light"] {
  color-scheme: light;
  --ground: #f4f3ef;
  --surface: #ffffff;
  --surface-2: #f0eee8;
  --hairline: #dcd8cf;
  --hairline-lit: #8f8a7c;
  --ink: #15171a;
  --muted: #5d6470;
  --claude: #7d5f1d;
  --chatgpt: #1c6053;
  --muse: #4f478f;
  --field: #bdb7a7;
}

@media (max-width: 719px) {
  :root {
    --gutter: var(--s4);
    --section: var(--s8);
  }
}

/* -------------------------------------------------------------------- base */

*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  font-synthesis: none;
}

body {
  margin: 0;
  background: var(--ground);
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: 17px;
  line-height: 1.6;
  -webkit-text-size-adjust: 100%;
}

/* -------------------------------------------------------------------- type */

h1,
h2,
h3,
h4 {
  margin: 0 0 var(--s4);
  text-wrap: balance;
  font-weight: 600;
}

h1 {
  font-size: clamp(40px, 4.6vw, 68px);
  line-height: 1.02;
  letter-spacing: -0.02em;
  margin-bottom: var(--s5);
}

h2 {
  font-size: clamp(28px, 3vw, 40px);
  line-height: 1.08;
  letter-spacing: -0.02em;
}

h3 {
  font-size: clamp(19px, 1.4vw, 22px);
  line-height: 1.25;
  letter-spacing: -0.01em;
}

h4 {
  font-size: 17px;
  line-height: 1.35;
}

p,
li {
  overflow-wrap: break-word;
}

p {
  margin: 0 0 var(--s4);
}

ul,
ol {
  margin: 0 0 var(--s4);
  padding-left: var(--s5);
}

li {
  margin-bottom: var(--s2);
}

/* One list language site-wide: the same dash marker the cards use, rather than discs on the
   content pages and dashes in the cards. */
main ul {
  list-style: none;
  padding-left: var(--s5);
}

main ul > li {
  position: relative;
}

main ul > li::before {
  content: "";
  position: absolute;
  left: calc(var(--s5) * -1);
  top: 0.62em;
  width: 10px;
  height: 1px;
  background: var(--hairline-lit);
}

a {
  color: inherit;
  text-underline-offset: 0.18em;
  text-decoration-color: var(--hairline-lit);
}

a:hover {
  text-decoration-thickness: 2px;
  text-decoration-color: currentcolor;
}

:focus-visible {
  outline: 2px solid var(--claude);
  outline-offset: 2px;
  border-radius: 2px;
}

code,
kbd {
  font-family: var(--font-mono);
  font-size: 0.92em;
  word-break: break-word;
}

hr {
  border: 0;
  border-top: 1px solid var(--hairline);
  margin: var(--s8) 0;
}

.eyebrow {
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 500;
  line-height: 1.4;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--claude);
  margin: 0 0 var(--s3);
}

.lead {
  font-size: 20px;
  line-height: 1.5;
  color: var(--muted);
  max-width: 62ch;
  margin-bottom: var(--s5);
}

.note {
  color: var(--muted);
  font-size: 15px;
  line-height: 1.55;
  max-width: 70ch;
}

.pill {
  display: inline-block;
  border: 1px solid var(--hairline);
  border-radius: 999px;
  background: var(--surface-2);
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  padding: var(--s1) var(--s3);
  color: var(--muted);
}

/* ------------------------------------------------------------------ layout */

.wrap {
  max-width: var(--container);
  /* margin-inline, not `margin: 0 auto`: the shorthand would beat the `section` rule below on
     specificity and quietly flatten the space between every section on the page. */
  margin-inline: auto;
  padding: 0 var(--gutter);
}

section {
  margin: 0 0 var(--section);
}

/* The first section of a page that does not open with the full-bleed hero needs the space the
   sticky header would otherwise eat. */
main > section:first-child:not(.hero) {
  margin-top: var(--s8);
}

/* A heading that follows a block of content rather than opening a section carries the
   section rhythm itself. */
.wrap > h2 {
  margin-top: var(--section);
}

.wrap > h3 {
  margin-top: var(--s7);
}

.wrap > h2:first-child,
.wrap > h3:first-child,
.section__head > h2 {
  margin-top: 0;
}

/* A section whose own children carry the rhythm needs no gap of its own. */
section section {
  margin-bottom: var(--s8);
}

.prose {
  max-width: 72ch;
}

/* Running text directly inside a section gets the prose measure. A bare <ul> in a 1200px
   container runs to a hundred and thirty characters a line, which nobody reads. */
.wrap > p,
.wrap > ul,
.wrap > ol,
.wrap > dl {
  max-width: 72ch;
}

.skip {
  position: absolute;
  left: -9999px;
}

.skip:focus {
  position: static;
  display: inline-block;
  padding: var(--s2) var(--gutter);
}

/* --------------------------------------------------------------- the header */

.bar {
  position: sticky;
  top: 0;
  z-index: 20;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s2) var(--s6);
  background: var(--ground);
  border-bottom: 1px solid var(--hairline);
  padding: 0 var(--gutter);
}

.bar > * {
  min-height: var(--header);
  display: flex;
  align-items: center;
}

.bar__mark {
  font-family: var(--font-mono);
  font-weight: 600;
  font-size: 18px;
  letter-spacing: 0.01em;
  text-decoration: none;
  margin-right: var(--s2);
}

.bar__nav {
  display: flex;
  gap: var(--s6);
  margin-right: auto;
  font-size: 15px;
}

.bar__nav a {
  color: var(--muted);
  text-decoration: none;
  white-space: nowrap;
  padding: var(--s2) 0;
  border-bottom: 2px solid transparent;
}

.bar__nav a:hover {
  color: var(--ink);
}

.bar__nav a[aria-current="page"] {
  color: var(--ink);
  border-bottom-color: var(--claude);
}

/* On a phone the four nav links get a row of their own that scrolls sideways, so the wordmark
   and the one button that matters keep the first line and nothing is hidden behind a
   hamburger that would need JavaScript to open. */
@media (max-width: 899px) {
  .bar {
    gap: 0 var(--s4);
    padding-bottom: 0;
  }

  .bar > * {
    min-height: 52px;
  }

  .bar__mark {
    order: 1;
  }

  .bar > .btn {
    order: 2;
    margin-left: auto;
    min-height: 40px;
    padding: var(--s2) var(--s4);
    font-size: 14px;
  }

  /* The links wrap onto as many rows as they need. A single row that scrolls sideways puts
     half a word against the edge of the screen with nothing to say it can be moved, which is
     worse than two tidy rows. Everything fits in two down to 320px. */
  .bar__nav {
    order: 3;
    flex-basis: 100%;
    flex-wrap: wrap;
    align-items: center;
    margin-right: 0;
    gap: 2px var(--s5);
    min-height: 0;
    padding-bottom: var(--s2);
    font-size: 13px;
    line-height: 1.3;
    overflow: visible;
  }

  .bar__nav a {
    padding: 2px 0;
    line-height: 1.3;
    white-space: nowrap;
  }
}

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

.foot {
  border-top: 1px solid var(--hairline);
  margin-top: var(--section);
  background: var(--surface-2);
}

.foot__row {
  display: grid;
  gap: var(--s6);
  grid-template-columns: 1fr;
  align-items: start;
  max-width: var(--container);
  margin-inline: auto;
  padding: var(--s7) var(--gutter);
  font-size: 15px;
}

@media (min-width: 720px) {
  .foot__row {
    grid-template-columns: 1.4fr 1fr auto;
    gap: var(--s8);
  }
}

.foot__mark {
  font-family: var(--font-mono);
  font-weight: 600;
  font-size: 17px;
  text-decoration: none;
  display: inline-block;
  margin-bottom: var(--s2);
}

.foot__line {
  margin: 0;
  color: var(--muted);
  font-size: 14px;
  max-width: 34ch;
}

.foot__nav {
  display: flex;
  flex-direction: column;
  gap: var(--s2);
}

.foot__nav a {
  color: var(--muted);
  text-decoration: none;
}

.foot__nav a:hover {
  color: var(--ink);
  text-decoration: underline;
}

.foot__title {
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted);
  margin: 0 0 var(--s3);
}

/* ------------------------------------------------------------------ controls */

.btn,
.toggle {
  font-family: inherit;
  font-size: 16px;
  font-weight: 600;
  line-height: 1.2;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s2);
  border: 1px solid var(--hairline-lit);
  border-radius: var(--radius-sm);
  background: transparent;
  color: inherit;
  padding: var(--s3) var(--s5);
  min-height: 44px;
  text-decoration: none;
  cursor: pointer;
  transition:
    border-color 120ms linear,
    background-color 120ms linear;
}

.btn:hover,
.toggle:hover {
  border-color: var(--ink);
  background: var(--surface-2);
}

/* display:inline-flex above outranks the user-agent rule for [hidden], so without this the
   theme toggle and the pause buttons would paint before site.js reveals them -- visible and
   inert, and permanently so with JavaScript off. */
.btn[hidden],
.toggle[hidden] {
  display: none;
}

.btn--solid {
  background: var(--ink);
  color: var(--ground);
  border-color: var(--ink);
}

.btn--solid:hover {
  background: var(--ink);
  border-color: var(--ink);
  opacity: 0.86;
}

.btn--ghost {
  border-color: transparent;
  padding-inline: 0;
  text-decoration: underline;
  text-decoration-color: var(--hairline-lit);
}

.btn--ghost:hover {
  background: transparent;
  border-color: transparent;
}

.btns {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s3);
  margin: 0 0 var(--s4);
}

label {
  display: block;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: var(--s1);
}

input[type="text"],
input[type="email"] {
  font: inherit;
  font-size: 16px;
  width: 100%;
  min-height: 46px;
  padding: var(--s3);
  color: inherit;
  background: var(--surface);
  border: 1px solid var(--hairline-lit);
  border-radius: var(--radius-sm);
}

input[type="text"]:focus,
input[type="email"]:focus {
  border-color: var(--ink);
}

input::placeholder {
  color: var(--muted);
  opacity: 1;
}

/* ------------------------------------------------------------- the art box */

/* `.fig` is the box the page's layout decides on; `.art` fills it and is what the animation
   module measures; `.art__stack` is exactly as large as the grid and is centred inside it.
   Splitting the three is what lets the cell size be derived from the layout rather than
   declared, so the grid reaches the edges of its column at every viewport width. */
.fig {
  position: relative;
  margin: 0;
  min-width: 0;
}

.art {
  position: absolute;
  inset: 0;
  overflow: hidden;
  contain: content;
  font-family: var(--font-mono);
  font-weight: 400;
  font-variant-ligatures: none;
  font-size: var(--cell-font, 12px);
  line-height: var(--cell-line, 15px);
  user-select: none;
  color: var(--ink);
}

/* The stack fills the art box rather than being sized by the baked sizer and centred in it.
   Centring a stack sized from the *baked* row count inside a box the live grid has grown to
   fill opens a dead band at the top and clips the same number of rows off the bottom, which
   is exactly what it used to do. Top-left, full bleed, and the only slack left is the
   remainder of one line-height. */
.art__stack {
  position: absolute;
  inset: 0;
}

.art pre {
  margin: 0;
  font: inherit;
  line-height: inherit;
  white-space: pre;
}

/* In flow, so it reserves the exact box from the font the browser actually resolved; hidden,
   so nobody reads it. Everything else in the stack is painted on top of it. */
.art__sizer {
  position: absolute;
  top: 0;
  left: 0;
  visibility: hidden;
  /* max-content so the box is the width of the text and not of the column it sits in. */
  width: max-content;
}

/* Absolutely positioned, so `contain: strict` cannot collapse them: their size comes from the
   insets. A textContent swap on a layer therefore cannot invalidate the page's layout. */
.art__layer {
  position: absolute;
  inset: 0;
  contain: strict;
}

/* Five colours in the hero, four in the turnstile, and not one <span> per character: each
   layer is a whole-grid <pre> of its own with one colour on it.

   There is exactly one colour per river. Brightness is carried by the glyph -- a quiet reach
   of the flow is `.` and `:` in the same gold its spine is `%` and `▓` in -- which is what
   lets three sources, a confluence and an object share five layers. */
[data-layer="ambient"] {
  color: var(--field);
}

[data-layer="claude"] {
  color: var(--claude);
}

[data-layer="chatgpt"] {
  color: var(--chatgpt);
}

[data-layer="muse"] {
  color: var(--muse);
}

/* The connector card, the responses leaving it, and the cells where two rivers arrive at
   once. The only bright things in the panel. */
[data-layer="ink"] {
  color: var(--ink);
}

/* Turnstile: the lattice and the belt are faintest, the rails and the queue are muted, the bar
   and the counter carry the accent, and the write the gate is holding is the one thing in
   ink. */
[data-layer="base"] {
  color: var(--muted);
}

[data-layer="gate"] {
  color: var(--claude);
}

[data-layer="held"] {
  color: var(--ink);
}

/* Exactly one grid is ever displayed; the other is display:none, which is also how the
   animation module knows which one to measure. */
@media (max-width: 1179px) {
  [data-set="wide"] {
    display: none;
  }
}

@media (min-width: 1180px) {
  [data-set="narrow"] {
    display: none;
  }
}

/* The caption and the pause control belong to the art, not to the page: a 40px strip inside
   its bottom edge, on a translucent plate so the grid still shows through behind them. A
   figure caption floating in the gap between the art and the headline is what made the old
   hero read as three unrelated things stacked up. */
.fig__plate {
  position: absolute;
  inset: auto 0 0 0;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s4);
  min-height: var(--plate);
  padding: 0 var(--s4);
  background: color-mix(in srgb, var(--ground) 88%, transparent);
  border-top: 1px solid var(--hairline);
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
}

.fig__plate p {
  margin: 0;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.fig__short {
  display: none;
}

@media (max-width: 899px) {
  .fig__long {
    display: none;
  }

  .fig__short {
    display: inline;
  }

  .fig__plate {
    padding: 0 var(--s3);
  }

  .fig__plate .toggle {
    font-size: 10px;
    padding: var(--s1) var(--s2);
  }
}

.fig__plate .toggle {
  flex: none;
  min-height: 28px;
  padding: var(--s1) var(--s3);
  border-radius: 999px;
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
  background: var(--ground);
}

.fig__plate .toggle:hover {
  color: var(--ink);
}

/* The gate figure is a plate inside running text, so it carries a border and a radius of its
   own where the hero runs off the edge of the page. */
.fig--gate {
  height: clamp(190px, 21vw, 280px);
  margin-bottom: var(--s6);
  border: 1px solid var(--hairline);
  border-radius: var(--radius);
  background: var(--surface);
  overflow: hidden;
}

/* Nine rows is few enough that a 40px overlay would swallow the counter and the standing
   label, so here the plate takes its own strip and the grid ends above it. In the hero,
   where the bottom rows carry nothing but lattice, the plate stays an overlay. */
.fig--gate .art {
  inset: 0 0 var(--plate) 0;
}

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

.hero {
  margin-bottom: var(--section);
  border-bottom: 1px solid var(--hairline);
}

.hero__row {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  align-items: center;
}

.hero__text {
  padding: var(--s7) var(--gutter) var(--s8);
}

.hero__text h1 {
  /* Smaller than the page-level h1: this one lives in a column roughly a third the width of
     a content page's measure, and at 4.6vw it would be seven lines deep and push the scanner
     form off the first screen. The art is the thing that has to be big here. */
  font-size: clamp(34px, 3.4vw, 50px);
  max-width: 24ch;
}

.hero__note {
  margin: var(--s5) 0 0;
  font-size: 14px;
}

/* Phone and small tablet: the art sits above the text -- by `order`, because the headline
   comes first in the document for the browser's sake -- and is capped so the headline is
   always on the first screen. 52vh of an iPhone is about 420px, which is also the ceiling,
   so the cap is the same on a tall phone and a short one. */
.fig--hero {
  order: -1;
  /* Half the screen, never more than 420px, and never so much of a short screen that the
     headline is pushed off it: the last term is what keeps the h1 above the fold on a 320px
     phone as well as on a 375px one. */
  height: max(180px, min(52vh, 420px, calc(100vh - 390px)));
  border-bottom: 1px solid var(--hairline);
}

@media (max-width: 719px) {
  .hero__text {
    padding-top: var(--s6);
  }
}

@media (min-width: 720px) and (max-width: 1179px) {
  .fig--hero {
    height: min(46vh, 380px);
  }
}

/* Two columns only once there is room for a real measure beside the art. Below this the
   stacked hero is the better page: at 1024 a 42% column is 348px, which runs the lead to
   seven lines and puts the scanner button under the fold. */
@media (min-width: 1180px) {
  .hero {
    border-bottom: 0;
  }

  /* Two columns filling the first screen: the art on the left, running off the viewport's own
     edge, and the text on the right, its right margin lined up with the container every other
     section uses. The streams therefore enter from off-screen and converge towards the
     headline and the form -- agents flowing into your business, read left to right. */
  .hero__row {
    grid-template-columns: minmax(0, 58fr) auto;
    min-height: min(calc(100vh - var(--header)), 880px);
  }

  .fig--hero {
    order: 1;
    height: 100%;
    /* The row is as tall as the text column needs; the art is not, so the caption strip at
       its bottom edge always lands on the first screen. Start-aligned and four pixels short
       of the header's own outside edge, which is what keeps the strip above the fold at
       1280x800 where the text column is the taller of the two. */
    max-height: calc(100vh - var(--header) - var(--s1));
    align-self: start;
    border-bottom: 0;
    border-right: 1px solid var(--hairline);
  }

  .hero__text {
    order: 2;
    /* content-box on purpose: the width below is the measure the type is set to, and the
       padding that lines its right edge up with the container is added outside it. */
    box-sizing: content-box;
    /* The measure, chosen so the art keeps roughly 56% of the viewport at every desktop
       width while the headline, the lead and the scanner all still clear the fold at
       1440x900. */
    width: clamp(420px, 34vw, 520px);
    padding: var(--s7) max(var(--gutter), calc((100vw - var(--container)) / 2))
      var(--s7) var(--s7);
  }

  .hero__text .lead {
    max-width: none;
  }
}

/* ------------------------------------------------------------- the scanner */

.scan {
  display: grid;
  gap: var(--s3);
  max-width: 36rem;
  margin-top: var(--s5);
}

/* Each field is its own column in flex so a label that wraps to two lines still leaves its
   input bottom-aligned with the one beside it. */
.scan__field {
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  min-width: 0;
}

.scan .btn {
  justify-self: start;
}

/* Once there is room, the two fields share a line: it saves seventy pixels of the first
   screen, which is the difference between the submit button being above the fold and below
   it. */
@media (min-width: 1200px) {
  .scan {
    grid-template-columns: 1fr 1fr;
  }

  .scan .btn {
    grid-column: 1 / -1;
  }
}

/* ------------------------------------------------------------------- cards */

.cards {
  display: grid;
  gap: var(--s5);
  grid-template-columns: 1fr;
}

@media (min-width: 720px) {
  .cards {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}

/* One grid, equal heights, and the button pinned to the bottom of every card whatever the
   copy above it does -- three cards of different lengths ending at different heights is the
   single fastest way to make a page look unfinished. */
.card {
  position: relative;
  display: flex;
  flex-direction: column;
  min-width: 0;
  border: 1px solid var(--hairline);
  border-radius: var(--radius);
  background: var(--surface);
  padding: var(--s6);
  transition: border-color 120ms linear;
}

.card::before {
  content: "";
  position: absolute;
  inset: 0 0 auto 0;
  height: 2px;
  border-radius: var(--radius) var(--radius) 0 0;
  background: var(--accent, var(--hairline-lit));
}

.card:hover {
  border-color: var(--hairline-lit);
}

.card--scan {
  --accent: var(--muse);
}

.card--build {
  --accent: var(--claude);
}

.card--host {
  --accent: var(--chatgpt);
}

.card h3 {
  margin-bottom: var(--s2);
}

.card__price {
  font-family: var(--font-mono);
  font-size: 36px;
  font-weight: 500;
  line-height: 1.1;
  letter-spacing: -0.02em;
  margin: 0 0 var(--s4);
}

.card__price span {
  font-size: 16px;
  font-weight: 400;
  letter-spacing: 0;
  color: var(--muted);
}

.card p {
  font-size: 16px;
}

/* After `.card p`, and one class heavier than it, or the figure comes out at body size. */
.card p.card__price {
  font-size: 36px;
}

.card__list {
  list-style: none;
  padding: 0;
  margin: 0 0 var(--s5);
  font-size: 15px;
  color: var(--muted);
}

.card__list li {
  position: relative;
  padding-left: var(--s5);
  margin-bottom: var(--s2);
}

.card__list li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.62em;
  width: 8px;
  height: 1px;
  background: var(--accent, var(--hairline-lit));
}

.card p:last-of-type {
  margin-bottom: var(--s5);
}

.card .btn {
  margin-top: auto;
  align-self: stretch;
}

/* A footnote under a row of cards belongs to the row, not to the next thing down. */
.cards + .note {
  margin-top: var(--s5);
  margin-bottom: 0;
}

/* ------------------------------------------------------------------- steps */

/* The five steps as one mono ribbon. It is a paragraph, not a list, because it is one
   sentence read left to right. */
.steps {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s2) var(--s3);
  border: 1px solid var(--hairline);
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  padding: var(--s3) var(--s5);
  margin: 0 0 var(--s6);
  width: fit-content;
  max-width: 100%;
  font-family: var(--font-mono);
  font-size: 15px;
  letter-spacing: 0.02em;
}

.steps span {
  color: var(--claude);
  font-size: 13px;
}

/* --------------------------------------------------------------- the bands */

/* A closing call to action, the one place on the site where the ground is inverted. */
.band {
  background: var(--ink);
  color: var(--ground);
  border-radius: var(--radius);
  padding: var(--s8) var(--s7);
  margin-bottom: 0;
}

.band h2 {
  margin-bottom: var(--s4);
  max-width: 28ch;
}

.band p {
  color: color-mix(in srgb, var(--ground) 76%, var(--ink));
  font-size: 18px;
  max-width: 58ch;
}

/* The band inverts the ground, so the accent has to be pulled halfway back towards it or it
   lands at about three to one on the wrong side. Fifty per cent clears 4.5:1 in both themes. */
.band .eyebrow {
  color: color-mix(in srgb, var(--claude) 50%, var(--ground));
}

.band .btn {
  border-color: color-mix(in srgb, var(--ground) 58%, var(--ink));
}

.band .btn:hover {
  border-color: var(--ground);
  background: color-mix(in srgb, var(--ground) 12%, var(--ink));
}

.band .btn--solid {
  background: var(--ground);
  color: var(--ink);
  border-color: var(--ground);
}

.band .btn--solid:hover {
  background: var(--ground);
  opacity: 0.9;
}

@media (max-width: 719px) {
  .band {
    padding: var(--s7) var(--s5);
  }
}

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

.scroll {
  overflow-x: auto;
  margin: 0 0 var(--s6);
  border: 1px solid var(--hairline);
  border-radius: var(--radius);
  background: var(--surface);
}

table {
  border-collapse: collapse;
  width: 100%;
  font-size: 16px;
}

caption {
  text-align: left;
  padding: var(--s5) var(--s5) 0;
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted);
}

th,
td {
  border-bottom: 1px solid var(--hairline);
  padding: var(--s4) var(--s5);
  text-align: left;
  vertical-align: top;
}

/* Everything from here to the pricing matrix is ordered by ascending specificity rather
   than by subject. Tables are the one place on the site where element, row and class rules
   all address the same cells, so reading down is the only way to see which one wins: a more
   specific rule written earlier would be an override that silently cannot lose. */

/* (0,0,2) */
thead th {
  font-size: 17px;
  vertical-align: bottom;
  border-bottom-width: 1px;
  background: var(--surface-2);
}

/* (0,0,2) */
tbody th {
  font-weight: 400;
  color: var(--muted);
  font-size: 15px;
}

/* The pricing matrix: offers across the top, what you get down the side. */
.matrix {
  min-width: 52rem;
}

/* (0,1,2) */
.matrix thead td {
  background: var(--surface-2);
  border-bottom: 1px solid var(--hairline);
}

/* (0,1,2) */
.matrix thead th + th {
  border-left: 1px solid var(--hairline);
}

/* (0,1,2) */
.matrix tbody td {
  border-left: 1px solid var(--hairline);
  text-align: center;
  vertical-align: middle;
}

/* (0,1,3) -- the border is drawn on every cell above and then taken off the final row. */
tbody tr:last-child th,
tbody tr:last-child td {
  border-bottom: 0;
}

/* (0,2,1) */
.matrix th[scope="row"] {
  width: 26%;
  color: var(--ink);
  font-size: 15px;
}

/* (0,2,3) -- the price row and the "who it is for" row are prose, not marks. */
.matrix tbody tr:nth-child(-n + 2) td {
  text-align: left;
  vertical-align: top;
  font-size: 15px;
  color: var(--muted);
}

.matrix__price {
  display: block;
  font-family: var(--font-mono);
  font-size: 26px;
  font-weight: 500;
  letter-spacing: -0.02em;
  line-height: 1.15;
  margin-bottom: var(--s1);
}

.matrix__unit {
  display: block;
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 400;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
}

.matrix__yes {
  color: var(--chatgpt);
  font-size: 18px;
}

.matrix__no {
  color: var(--muted);
}

/* --------------------------------------------------------------------- faq */

dl.faq {
  margin: 0;
  max-width: 72ch;
}

dl.faq dt {
  font-weight: 600;
  font-size: 18px;
  padding-top: var(--s5);
  border-top: 1px solid var(--hairline);
  margin-top: var(--s5);
}

dl.faq dt:first-of-type {
  margin-top: 0;
}

dl.faq dd {
  margin: var(--s2) 0 0;
  color: var(--muted);
}

/* --------------------------------------------------------------- utilities */

.cols2,
.cols3 {
  display: grid;
  gap: var(--s6) var(--s7);
  grid-template-columns: 1fr;
}

@media (min-width: 720px) {
  .cols2 {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .cols3 {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}

.cols2 > * > :last-child,
.cols3 > * > :last-child {
  margin-bottom: 0;
}

.cols3 h3,
.cols2 h3 {
  padding-top: var(--s4);
  border-top: 2px solid var(--hairline-lit);
}

.stack > * + * {
  margin-top: var(--s5);
}

/* A block that closes a section: full width under whatever grid came before it, with the
   section's own gap above, rather than hanging sixteen pixels under the longest column. */
.aside {
  max-width: 72ch;
  margin-top: var(--s9);
  padding-top: var(--s5);
  border-top: 2px solid var(--hairline-lit);
}

.aside > :last-child {
  margin-bottom: 0;
}

/* A whole page of prose keeps the container's width, so its left edge lines up with the
   header and the footer, and limits the line length of its children instead of centring a
   narrow column inside a wide one.

   It lives here, at the end, rather than beside the other layout rules: `.wrap.prose > *`
   and `.wrap.prose > h2` are the most specific rules on the site whose key selector is `*`
   and `h2`, so anything of theirs declared after them -- `.bar > *`, `.stack > * + *`,
   `.band h2` -- would be reading as an override that silently cannot win. */
.wrap.prose {
  max-width: var(--container);
}

.wrap.prose > * {
  max-width: 72ch;
}

.wrap.prose > h2 {
  margin-top: var(--s8);
}

/* A label only a screen reader reads: the tick and the dash in the pricing matrix are
   decoration, and "included" / "not included" is the actual cell value. */
.vh {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.section__head {
  max-width: 62ch;
  margin-bottom: var(--s7);
}

.section__head > :last-child {
  margin-bottom: 0;
}
