/* Marketing site styles.
 *
 * The site is the product's first performance claim, so it practices the same discipline: one
 * stylesheet, no framework, no web fonts, no JS. A page that argues about Core Web Vitals while
 * shipping 400 KB of JavaScript is not credible. */

@import "brand/tokens.css";

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

html {
  scroll-behavior: smooth;
}

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }
}

body {
  margin: 0;
  background: var(--surface);
  color: var(--ink-900);
  font-family: var(--font-sans);
  font-size: 17px;
  line-height: 1.62;
  -webkit-font-smoothing: antialiased;
}

/* A ceiling and a gutter, and nothing that holds a width of its own — see `--page` and `--gutter`
 * in tokens.css. Everything inside is a proportion, so the page spreads into the window. */
.page {
  max-width: var(--page);
  margin: 0 auto;
  padding: 0 var(--gutter);
}

/* The icon sprite is referenced with <use>, never rendered directly. */
.sprite {
  position: absolute;
  width: 0;
  height: 0;
  overflow: hidden;
}

/* ---------- Header ---------- */

/* No underline. In the design the header floats on the page and the hero illustration's shadow
 * supplies the separation, so a hairline here would draw a line the design does not have. */
header.site {
  position: sticky;
  top: 0;
  background: color-mix(in srgb, var(--surface) 88%, transparent);
  backdrop-filter: saturate(150%) blur(8px);
  z-index: 10;
}

header.site .page {
  display: flex;
  align-items: center;
  gap: 6px;
  min-height: 76px;
}

.brand {
  display: inline-flex;
  align-items: center;
  order: 1;
  text-decoration: none;
}

/* The logo is a lockup: the mark is the full height of the artwork and the wordmark occupies only
 * about 28% of it. So sizing the logo is really sizing the wordmark, and 34px — which is a normal
 * header logo height — leaves the name nine pixels tall and unreadable.
 *
 * 62px in a 76px header is the point where the wordmark reads at a glance without the mark
 * crowding the bar. */
.brand img {
  display: block;
  height: 62px;
  width: auto;
}

/* The header's own button, and the smallest on the page: it stands in a 76px bar next to the logo and
 * the nav, where the 50px the body buttons use reads as a second slab of ink. It is sized off the
 * window rather than fixed, so it is not one 44px height on a 375px phone and on a 1920 monitor: 34px
 * at the narrow end, 42px at the wide one, with the inset and the type following it.
 *
 * Both classes on the selector, and that is load-bearing: `.btn` sets `height`, `padding` and
 * `font-size` too and is defined further down this file at the same specificity, so a bare
 * `.header-cta` loses to it — which is what had been happening, and why the header button rendered as
 * the page's 50px one with its 15px type whatever this rule said.
 *
 * 34 is below the 44px touch-target guideline, and that is the trade this button makes knowingly: it
 * is a text link with an arrow rather than a form control, the phone layout puts it alone on the
 * right of the bar, and the page's own 50px buttons are still waiting one screen down. The floor is
 * the number to raise if that turns out to be wrong in the hand rather than on paper. */
.btn.header-cta {
  order: 4;
  margin-left: 4px;
  height: clamp(34px, 2.2vw, 42px);
  padding: 0 clamp(12px, 1vw, 18px);
  font-size: clamp(13px, 0.85vw, 14.5px);
  gap: 7px;
}

/* The mobile menu control. Hidden on desktop, where the nav is always visible. */
.nav-toggle {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
}

.nav-burger,
.site-panel {
  display: none;
}

/* ---------- Navigation ---------- */

/* `display: contents` is what lets one markup serve both layouts: on desktop the nav and the
 * actions behave as if they were direct children of the header row, and on mobile the same element
 * becomes a real box that drops out from under it. */
.site-panel {
  display: contents;
}

nav.site {
  display: flex;
  align-items: center;
  gap: 2px;
  font-size: 15px;
  margin-left: auto;
  margin-right: auto;
  order: 2;
}

nav.site a:not(.btn) {
  color: var(--ink-900);
  text-decoration: none;
  padding: 8px 11px;
  border-radius: 8px;
}

nav.site a:not(.btn):hover {
  color: var(--ink-900);
  background: var(--surface-sunken);
}

/* Dropdowns open on hover and on keyboard focus, in pure CSS: the panel is hidden while the
 * `.menu` is idle and shown while it is hovered or contains focus. The trigger is a real link,
 * so it still navigates on click (to the section the menu is mostly about), and the panel keeps
 * working when focus moves into it. The 8px `top` gap is bridged with a transparent pseudo-
 * element so moving the pointer down the 8px cannot drop the hover and slam the panel shut —
 * the classic hover-menu flicker. No JavaScript, which is the same reason the storefront widget
 * is small: every script we do not ship is one that cannot break or slow somebody's checkout. */
.menu {
  position: relative;
}

.menu-top {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 8px 11px;
  border-radius: 8px;
  font-size: 15px;
  font-weight: 500;
  color: var(--ink-900);
  text-decoration: none;
  cursor: pointer;
  user-select: none;
}

.menu-top:hover,
.menu:focus-within > .menu-top,
.menu:hover > .menu-top {
  color: var(--ink-900);
  background: var(--surface-sunken);
}

.caret {
  width: 13px;
  height: 13px;
  color: var(--ink-300);
}

.menu:focus-within .caret,
.menu:hover .caret {
  color: var(--ink-700);
}

/* The bridge: covers the 8px gap between trigger and panel so the hover survives the journey. */
.menu::after {
  content: "";
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  height: 9px;
}

.menu-panel {
  position: absolute;
  top: calc(100% + 8px);
  left: 0;
  z-index: 20;
  min-width: 286px;
  display: grid;
  gap: 2px;
  padding: 8px;
  background: var(--surface-card);
  border: 1px solid var(--border);
  border-radius: 12px;
  box-shadow: var(--elevate);

  /* Hidden until hover or focus-within says otherwise. The 4px drop and fade make the reveal
   * read as the panel lifting off the bar. */
  visibility: hidden;
  opacity: 0;
  transform: translateY(4px);
  transition:
    opacity 0.14s ease,
    transform 0.14s ease,
    visibility 0s linear 0.14s;
}

.menu:hover > .menu-panel,
.menu:focus-within > .menu-panel {
  visibility: visible;
  opacity: 1;
  transform: translateY(0);
  transition-delay: 0s;
}

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

.menu-panel-narrow {
  min-width: 208px;
  left: auto;
  right: 0;
}

nav.site .menu-panel a {
  display: grid;
  grid-template-columns: auto 1fr;
  column-gap: 11px;
  padding: 9px 11px;
  border-radius: 8px;
  font-size: 14px;
  line-height: 1.4;
  color: var(--ink-500);
}

/* The item's icon: a quiet line drawing in the site's own stroke weight, sized to the two text
 * lines it labels. The grid column keeps the text block ragged-right while every icon sits on a
 * shared left rail. */
.menu-ico {
  width: 19px;
  height: 19px;
  margin-top: 1px;
  color: var(--accent);
}

nav.site .menu-panel a:hover .menu-ico {
  color: var(--accent-bright);
}

nav.site .menu-panel a strong {
  /* The title is its own line: the icon grid's text cell is one inline flow, so without this the
   * headline and the description run together as a paragraph. */
  display: block;
  font-size: 14px;
  font-weight: 640;
  color: var(--ink-900);
}

nav.site .menu-panel a:hover {
  background: var(--surface-sunken);
}

.site-actions {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 15px;
  order: 3;
}

.site-actions .menu-top {
  gap: 7px;
  font-size: 14px;
  font-weight: 500;
}

.globe {
  width: 15px;
  height: 15px;
  color: var(--ink-500);
}

.login {
  padding: 8px 12px;
  border-radius: 8px;
  color: var(--ink-700);
  text-decoration: none;
}

.login:hover {
  color: var(--ink-900);
  background: var(--surface-sunken);
}

.menu-current,
.menu-soon {
  padding: 8px 11px;
  font-size: 14px;
  border-radius: 8px;
}

.menu-current {
  color: var(--ink-900);
  font-weight: 620;
  background: var(--surface-sunken);
}

.menu-soon {
  color: var(--ink-300);
}

/* ---------- Buttons ----------
 *
 * Two fills — near-black for the action we want taken, white with a hairline for the second
 * option. Both fills are the design's: the primary is not green, which is worth noting because
 * green is the brand colour and reaching for it here is the instinctive choice. In the design the
 * green is reserved for type and icons, and the button is the darkest thing on the page. That is
 * what makes the green read as considered rather than decorative.
 *
 * The corners are `--radius`, not a pill. A pill is the default shape for a button that is one of
 * several and has to read as a chip; these are the two decisions the page is asking for, and the
 * design draws them as rectangles with a corner radius of about a fifth of their height (measured
 * off the exported design at 6x: an 8px corner on a 42px button). A pill also fights the button's
 * own job here — the artwork it sits under is full of pills and rounded panels, and the shape was
 * the one thing making our button look like one of them. */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 50px;
  gap: 9px;
  padding: 0 21px;
  border-radius: var(--radius);
  font-size: 15px;
  font-weight: 600;
  text-decoration: none;
  border: 1px solid transparent;
  cursor: pointer;
  white-space: nowrap;
}

.btn-primary {
  background: var(--btn-fill);
  color: #fff;
}

.btn-primary:hover {
  background: var(--btn-fill-hover);
}

.btn-quiet {
  border-color: var(--border);
  color: var(--ink-900);
  background: var(--surface-card);
}

.btn-quiet:hover {
  border-color: var(--ink-300);
}

.btn-lg {
  padding: 0 24px;
  font-size: 15.5px;
}

/* The arrow in the design's buttons is a stroke, not a glyph, so it inherits the button's colour
 * and needs no second asset. */
.btn .arrow {
  width: 16px;
  height: 16px;
  flex: none;
}

/* The play disc on the demo button. Darker than the button's own ink so it reads as a control rather
 * than as a second word, which is what the design does with it. */
.btn .play {
  width: 19px;
  height: 19px;
  flex: none;
  color: var(--ink-900);
}

/* ---------- Sections ---------- */

section {
  padding: 64px 0;
  border-bottom: 1px solid var(--border);
}

section:last-of-type {
  border-bottom: 0;
}

h1 {
  font-size: clamp(32px, 5vw, 50px);
  line-height: 1.12;
  letter-spacing: -0.02em;
  margin: 0 0 18px;
  max-width: 20ch;
}

h2 {
  font-size: clamp(23px, 3vw, 30px);
  line-height: 1.2;
  letter-spacing: -0.01em;
  margin: 0 0 14px;
  max-width: 26ch;
}

h3 {
  font-size: 17px;
  margin: 0 0 6px;
}

p {
  max-width: var(--measure);
  margin: 0 0 16px;
  color: var(--ink-700);
}

.lede {
  font-size: 20px;
  color: var(--ink-500);
  max-width: 56ch;
}

.eyebrow {
  font-size: 13px;
  font-weight: 650;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--accent);
  margin: 0 0 12px;
}

/* ---------- Hero ---------- */

.hero {
  padding: 60px 0;
}

/* The stat band supplies its own bottom spacing, so the home hero does not add more.
 *
 * The hero sits on a photograph: a wall with a diagonal band of light across it, and two paper bags
 * in the right of the frame. It is 21 KB, it replaces a flat fill, and it is the first thing on the
 * page — the most visible surface we have, so it is also the one place where a colour that is
 * merely "close" would be noticed.
 *
 * Three things about how it is placed:
 *
 *   * `cover`, anchored to the bottom right. The right edge keeps the props in the right column,
 *     where the illustration is, and the copy lands on the part of the wall that is nearly white —
 *     which is why the headline reads at full contrast on a surface that is no longer a flat
 *     colour.
 *   * Bottom and not top, which is the difference between a decision and an accident. A hero is
 *     wider than the photograph's 1.63:1, so `cover` always crops one edge off it, and the question
 *     is only which. Anchored to the top, the crop removes the bottom of the frame — and the
 *     bottom of the frame is where the shopping bag is, so the bag arrives sliced in half by the
 *     section edge, which is exactly what it looks like: a mistake. Anchored to the bottom, the
 *     crop removes wall instead, and the props are whole.
 *   * It is positioned rather than cropped, so the band of light keeps its exact angle from the
 *     design instead of being trimmed to whatever box the hero happens to be.
 *   * `background-color` is not set here on purpose. The page's own surface already matches the
 *     photograph's edges to within a level or two, so the hero ends without a seam instead of with
 *     a rectangle.
 *
 * The hero is the whole first screen: the section fills the viewport below the sticky header
 * (`min-height`, never `height`, so a short window — or a phone, where one column stacks more than
 * one screen of content already — simply grows past the fold). `svh` measures the smallest viewport
 * a browser can present, so the fold never jumps as the mobile URL bar collapses; the `vh` line
 * above it is the fallback for browsers without `svh`.
 *
 * The hero's content is centred in that height as one block — the copy, the illustration and the stat
 * band together — so the extra space is split above and below it rather than piling up under it. A
 * flex column with `justify-content: center` on the page does that, and it is the only place the
 * slack may go: the stage is left at its content height, so the band still follows the copy. Let the
 * stage grow to swallow the slack instead (a `flex: 1` on it) and the band is torn away from the
 * copy it belongs to and stranded on the fold — which is the one thing the grouping is there to
 * prevent. */
.hero-main {
  position: relative;
  /* 30px rather than 0, and it is the band's own 30px of bottom padding that sets the number. The
   * hero's top padding and the band's bottom padding are the two ends of the free space the block is
   * centred in, so making them equal is what puts the copy's mass on the section's centre line
   * instead of 15px below it. */
  padding-bottom: 30px;
  background-color: var(--surface);
  background-image: url("brand/hero-wall.webp");
  background-repeat: no-repeat;
  background-size: cover;
  background-position: right bottom;
  display: flex;
  flex-direction: column;
  min-height: calc(100vh - 76px);
  min-height: calc(100svh - 76px);
}

.hero .page {
  position: relative;
}

/* The column that centres the hero's contents, and only `hero-main`'s — the pricing hero is a normal
 * section and stays one. The page is the hero's only flex child and takes all of its height, so the
 * centred block is the stage and the band, in that order and with the band's own margin between
 * them. */
.hero-main > .page {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: center;
}

/* The extra left offset is a desktop-only composition choice; below 940px the hero stacks and the
 * gutter alone is the right inset. */
@media (min-width: 941px) {
  .hero-main > .page {
    padding-left: calc(var(--gutter) + 40px);
  }
}

.hero-stage {
  display: grid;
  /* The design's own split: 43.5% copy to 56.5% illustration, around a gutter. It is still weighted
   * towards the illustration — the mock is the only place on the page where small type has to stay
   * legible, so it keeps the larger column — but the copy column is now wide enough for the stat
   * figures below it to sit four across with their labels on one line, which is what the design
   * shows and what a narrower column turned into three-line labels. */
  grid-template-columns: minmax(0, 0.87fr) minmax(0, 1.13fr);
  gap: 44px;
  align-items: center;
}

/* The "Built for Shopify" strip is fluid, like every element around it: the hero's h1 grows
 * 40px → 89px across the width range, and a strip pinned at 14px/30px at every one of those widths
 * is proportionally backwards — it eats ~76% of a 305px phone row while vanishing beside an 89px
 * headline at 1920. One knob drives the whole strip: the font-size clamp below, with the gap,
 * margin and lockup height in `em` so they track it at the design's exact ratios (the 14px
 * reference drew 9px of gap, 18px of margin and a 30px lockup). At ~1200 the strip is today's
 * look; it shrinks gently to the 12px floor on phones and grows to 20px on very wide screens.
 *
 * The `margin-left: 40px` this rule once carried is gone rather than fixed: the `margin`
 * shorthand after it was resetting it to 0, so it was dead code — and the alignment it would
 * have created is not the design's; the strip sits on the copy margin, flush with the h1. */
.hero-eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 0.64em;
  margin: 0 0 1.29em;
  font-size: clamp(12px, 7px + 0.5vw, 20px);
  font-weight: 700;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: var(--ink-300);
}

/* The Shopify lockup is Shopify's own artwork, unscaled by CSS beyond a height, so the ratio is
 * whatever their file says — the `em` height tracks the eyebrow's fluid size, nothing else. It
 * sits a little larger than the surrounding caps because the wordmark inside the lockup is doing
 * the reading and "BUILT FOR" is only a label. */
.hero-eyebrow-logo {
  height: 2.14em;
  width: auto;
}

/* The headline is three fixed lines — "Convert more" / "visitors into" / "customers." — and it is
 * sized from its own column so that the longest of them fits at every width.
 *
 * `cqw` is one percent of the width of the nearest container, and `.hero-copy` is that container
 * (`container-type: inline-size`): the copy column, which is where the headline is and what decides
 * whether it fits. The longest line, "Convert more", sets about 6.1 times its own size in this
 * weight, so 12cqw leaves it at a little under three quarters of the column — which is the design's
 * proportion measured off the frame, where its headline fills most of the copy column and the
 * smaller lines fall well inside it.
 *
 * What this replaces was a size from the viewport (`clamp(37px, 4.9vw, 58px)`), and the viewport is
 * the wrong thing to measure in both directions. It stopped growing at about 1180px, so on a 1920
 * screen the headline was the size it is on a laptop, and between about 1000 and 1500px the column
 * was too narrow for the size it did get, so the lines wrapped wherever they happened to fit
 * instead of where they were set. The floor is the old minimum and the ceiling is a guard: the
 * column caps at 760px, which is 91px of headline, so 96 is only there in case `--page` grows. */
.hero-copy {
  container-type: inline-size;
}

.hero h1 {
  font-size: clamp(37px, 12cqw, 96px);
  line-height: 1.04;
  letter-spacing: -0.035em;
  font-weight: 800;
  margin: 0 0 18px;
  max-width: none;
}

/* The one phrase each of these headlines turns on, in green and in italic. Italic on a sans that has
 * no italic face is a synthesised oblique — the browser slants the upright — and that is what this
 * is: it reads as emphasis on the page's own type rather than as a second typeface, which is the
 * point. All three accents are on the rule together, so the hero's "customers.", the band's "AI
 * sales agent" and the solution's "sales agent" cannot drift apart into two styles. */
.hero h1 .accent,
.cap-head h2 .accent,
.solution h2 .accent {
  color: var(--accent);
  font-style: italic;
}

.hero-lede {
  /* The lede grows with the page too, but only a little and only above about 1670px: body copy is
   * read, not looked at, and the thing that makes it unreadable on a big screen is length, which
   * `max-width` in `ch` already handles. */
  font-size: clamp(17.5px, 1.05vw, 20px);
  line-height: 1.58;
  color: var(--ink-500);
  max-width: 44ch;
  margin: 0;
}

.hero-actions {
  display: flex;
  gap: 14px;
  flex-wrap: wrap;
  margin: 26px 0 0;
}

/* On phones the two hero buttons sit side by side: full-size padding and type would force
 * "Install on Shopify" onto its own row at 320–390px, so the pair shares the line at a smaller
 * size instead — same hierarchy (primary still reads primary), one row, no orphan. `flex: 1 1
 * auto` keeps each label on its natural width and splits the leftover space; `wrap` stays as
 * the safety net for sub-300px containers. */
@media (max-width: 480px) {
  .hero-actions {
    gap: 10px;
  }

  .hero-actions .btn {
    flex: 1 1 auto;
    min-width: 0;
    height: 46px;
    padding: 0 10px;
    font-size: 13.5px;
    gap: 6px;
  }
}

.hero-trust {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: 9px 20px;
  padding: 0;
  margin: 24px 0 0;
  font-size: 13.5px;
  color: var(--ink-500);
}

.hero-trust li {
  display: flex;
  align-items: center;
  gap: 7px;
}

/* Filled circles with a white check, as in the design. The brighter of the two greens is used
 * here: at 17px the headline green reads as a grey smudge, which is why the design does not use
 * it for the small marks either. */
.tick {
  width: 17px;
  height: 17px;
  padding: 3px;
  flex: none;
  border-radius: 50%;
  color: #fff;
  background: var(--accent-bright);
}

/* ---------- Hero illustration ----------
 *
 * One image, 117 KB, and it replaces about four hundred lines of hand-drawn CSS mockup that could
 * only ever approximate a real storefront. It sits on the hero's photograph, so it is a card on a
 * wall rather than a card on a flat fill — which is what the design shows, and the reason the
 * artwork's own rounded card reads as an object here instead of as a rectangle. */

/* No rules for the illustration's column, and that is deliberate: it holds one image at the width of
 * the track, so there is nothing to size, float or centre. The two numbers that decide the hero are
 * below and in `--page` in tokens.css.
 *
 * At desktop widths the illustration is the hero's tallest column, so it sets the height of the
 * hero's content — and it then fills that height exactly, because there is nothing else in its
 * column. (The hero's viewport-height floor, its centring, and what those may not move: see
 * `.hero-main`.)
 *
 * That is the whole trick, and it is why the artwork's ratio is the number to watch: the mock is
 * 1.57:1, so its height is its column's width divided by 1.57, and whether it beats the copy column
 * (400–500px of type, depending on how the headline breaks, which depends on the column) is decided
 * by the page width and by nothing inside the hero. At the old fixed 1200 it lost — 626px of column
 * is 399px of mock against a 453px copy column — so the copy set the hero's height and the
 * illustration floated inside it, smaller than the text beside it. On a 1920 screen it wins by a wide
 * margin (986px of column is 628px of mock against a 433px copy column), the hero grows to the
 * picture, and the two columns line up edge to edge the way the design draws them.
 *
 * It wins from about 1536px up. From 1024 to 1500 the copy column wins and the illustration is
 * centred in a row it no longer fills, which is what it always did, and is not a fault to fix in the
 * CSS: a 1.57:1 mock simply cannot outgrow three lines of display type in a 500px column. Under
 * 941px the stage is one column and the illustration is the full width of the page.
 *
 * No panel, no frame, no border standing in for spacing the design does not have: the artwork is one
 * object with the soft shadow the design gives it, floating over the wall, and the shadow is not
 * part of the asset. The artwork is trimmed flush to its own edges and carries no shadow of its own
 * — its alpha reaches 0 within a pixel or two of the trim — so the float in the design is reproduced
 * here. The values are measured off the design: see --hero-shadow in tokens.css. */
.hero-shot {
  display: block;
  width: 100%;
  height: auto;
  filter: var(--hero-shadow);
}

/* ---------- Hero stat band ----------
 *
 * The figures sit under the copy column, not across the page. Read edge to edge they line up under
 * the illustration, where they belong to nothing; read under the copy they close the argument it
 * makes — the numbers that answer "so what".
 *
 * The band is left-aligned as a whole: the four columns are sized to their own content
 * (`max-content`), so the first figure starts on the copy's left margin — the same edge the eyebrow,
 * the headline and the buttons start on — and the rest follow it in order. Nothing is stretched to
 * fill the column, and that is what keeps that edge honest. An earlier version shared the slack out
 * between the tracks so the four figures spanned the column, and it cost every divider its job: a
 * rule 14px from the figure after it and 190px from the figure before it is not a divider, it is the
 * next figure's left edge. The slack went somewhere, and the only place it can go without stranding
 * the rules is the gap.
 *
 * So the space between two figures is one number, `--stat-gap`, and each rule is drawn half of it in
 * from the figure that follows — dead centre of the gap, at every width, because it is the same
 * number the gap is. In `vw`, like every other space on the page, so it grows with the window; the
 * floor is the tightest gap that still reads as two separate figures rather than one crowded pair. */
.stat-band {
  --stat-gap: clamp(28px, 5vw, 120px);

  display: grid;
  grid-template-columns: repeat(4, max-content);
  column-gap: var(--stat-gap);
  list-style: none;
  margin: 34px 0 0;
  padding: 0 0 30px;
}

/* Dividers, as the design draws them. Hairline, and confined to the band's own height, so they read
 * as the columns of one figure rather than as a grid the content is trapped in — which was the
 * reason they were originally left out, and the reason they are back: the design's band is read as
 * four related numbers, and the rules are what group them.
 *
 * A pseudo-element rather than a border, which is the whole trick of this band. A border can only
 * sit on the cell's own edge: 14px from the label on one side, the whole width of the gap on the
 * other, which is the layout the rules were being asked to stop looking like. Pulled `-0.5 *
 * --stat-gap` out of the cell instead, the rule lands in the middle of the gap — the padding on both
 * sides of the gap is the same 14px, so the middle of the text-to-text space is exactly half the gap
 * — and it stays there at every width, because it is the same number the gap is. `top: 0; bottom: 0`
 * makes it the height of the row rather than of the text, so the four rules agree. */
.stat-band li {
  display: grid;
  gap: 3px;
  /* The figure is centred on its label rather than sharing its left edge. `justify-items` rather than
   * `text-align`, so the value's box is its own width and the centring is the column's rather than the
   * text's: a `text-align` would do the same thing here only because both spans happen to be left
   * aligned. The column is `max-content`, so it is the label — the longer of the two lines — that sets
   * the width, and the figure lands in the middle of it, which is what makes the pair read as one unit
   * instead of two lines that started together.
   *
   * This does not move the band's left edge: the first cell still begins on the copy's margin, so the
   * first label is still flush with the eyebrow, the headline and the buttons. What changes is only
   * where the figures sit inside their own columns, so the dividers — which are positioned off the
   * cell's edges — do not move at all. Below 941 the cells are stretched halves of the page rather
   * than `max-content` columns, so the centring is turned off there: see the reflow block, where the
   * same cells are the layout's own width and centring would be centring a block, not a figure. */
  justify-items: center;
  /* Tighter than the page's own 22px, because four cells inside one column is a different job from
   * four cells across a page: at 22px the longest label wraps to three lines. */
  padding: 0 14px;
}

.stat-band li + li {
  position: relative;
}

.stat-band li + li::before {
  content: "";
  position: absolute;
  left: calc(-0.5 * var(--stat-gap));
  top: 0;
  bottom: 0;
  width: 1px;
  /* --border is all but invisible on the hero's pale ground; the dividers read at --ink-300. */
  background: var(--ink-300);
}

/* When the figures reflow, the dividers have to follow them.
 *
 * Once the stage is one column the copy is the full page width, so the figures get the room the
 * design gives them: two even halves, and they stay two even halves all the way down — the phone
 * layout keeps the 2×2, with its divider between the columns, because that is what the design
 * draws. Half of the gap is not the middle of anything there, so the floating rule is dropped and
 * the divider goes back to being the edge of the cell that follows — which is what a column edge is
 * for. The padding follows the same logic, so the first cell of each row sits flush with the page's
 * margin. */
@media (max-width: 940px) {
  /* Two even halves of the page. `max-content` in the minimum keeps each half wide enough for its
   * widest line — `minmax(0, 1fr)` would let a half shrink inside its 12.5% average label and wrap
   * "Carts recovered" onto two lines — and 262px of minimum band (2 × 126 + 10) is inside the 271px
   * the hero page has at 320, which is why nothing scrolls sideways there. The 941+ band has no such
   * pressure: its tracks are `max-content` and the row is 4 across. */
  .stat-band {
    grid-template-columns: repeat(2, minmax(max-content, 1fr));
    gap: 16px 10px;
  }

  .stat-band li + li {
    border-left: 0;
  }

  .stat-band li + li::before {
    content: none;
  }

  .stat-band li:nth-child(even) {
    border-left: 1px solid var(--border);
  }

  /* And the pairs are centred in their halves, at the product owner's call: the phone band is a 2×2
   * tile with a hairline between the columns, and a tile reads centred — flush-left pairs in
   * stretched halves left a ragged right quarter on every phone width.
   *
   * Uniform padding is what makes the centring true: the earlier attempt kept the desktop's
   * odd-left/even-right zero padding, which pushed each pair 14px off its half's centre in opposite
   * directions — centred in the content box, visibly not centred in the half. With 14px both sides
   * every pair sits level with its divider clearance, and the value stays centred on its label for
   * free, because both are centred on the same axis. 8px rather than the desktop's 14: at 320 the
   * band's 14px padding pushed its min-content past what the hero page could hold and dragged the
   * whole document 17px sideways — 8px a side costs the pair 16px of inset, which at a 257px half is
   * still clearly a tile, and buys back the scroll. */
  .stat-band li {
    justify-items: center;
    padding: 0 8px;
  }
}

/* Desktop only, and scoped on purpose: the first cell sits flush with the copy's margin in the
 * four-across band. Below 941 the pairs are centred in their halves with uniform padding, and this
 * rule — same specificity, later in the file — would strip the first cell's left padding and knock
 * its pair 7px off centre. The fourth same-specificity override in this file to need scoping; see
 * `.btn.header-cta` and `.cap-cards > .card p` for the other two. */
@media (min-width: 941px) {
  .stat-band li:first-child {
    padding-left: 0;
  }
}

.stat-value {
  font-size: 26px;
  font-weight: 750;
  letter-spacing: -0.03em;
  color: var(--ink-900);
  /* Both lines are single-line by construction: a wrapped label is the one thing that makes a band
   * of four figures look broken, and the column sizing above exists to make sure it cannot happen. */
  white-space: nowrap;
}

.stat-label {
  font-size: 13px;
  /* --ink-300 washed out at this size; --ink-500 keeps the label secondary but legible. */
  color: var(--ink-500);
  white-space: nowrap;
}

/* ---------- Hero responsive ---------- */

@media (max-width: 1080px) {
  /* The header row keeps the logo and the one call to action; everything else moves into a panel
   * behind the hamburger. The panel is positioned against the header rather than the page, so it
   * spans the full viewport width and can scroll on its own if a short screen cannot hold it. */
  .site-panel {
    display: none;
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    max-height: calc(100vh - 76px);
    overflow-y: auto;
    padding: 2px 24px 16px;
    background: var(--surface);
    border-bottom: 1px solid var(--border);
    box-shadow: var(--elevate);
  }

  .nav-toggle:checked ~ .site-panel {
    display: block;
  }

  /* Both classes, for the same reason the desktop rule carries them: `.btn.header-cta` is two
   * classes and this block is one, so a bare `.header-cta` here loses on every property they share —
   * including the `order: 4` that puts the CTA after the burger. The auto margin is what plants the
   * CTA-and-burger group on the page's right margin; the desktop rule's 4px left it floating 73px
   * short of it on a 360px phone. */
  .btn.header-cta {
    order: 3;
    margin-left: auto;
  }

  /* Last in the row, at the product owner's call: the thumb sits at the screen's right edge, so the
   * control that opens the menu goes where the thumb is, and the CTA — a link, not a control — sits
   * beside it. `margin-left: 4px` from the desktop rule keeps the two from touching. */
  .nav-burger {
    display: grid;
    place-items: center;
    order: 4;
    width: 40px;
    height: 40px;
    border-radius: 10px;
    color: var(--ink-700);
    cursor: pointer;
  }

  .nav-burger:hover {
    background: var(--surface-sunken);
  }

  /* Three bars from one element and its two pseudo-elements, rotating into a cross when the menu
   * is open. The same principle as everywhere else on this site: the state lives in CSS, and there
   * is no script to load, fail or block. */
  .nav-burger-bars,
  .nav-burger-bars::before,
  .nav-burger-bars::after {
    display: block;
    width: 18px;
    height: 2px;
    border-radius: 2px;
    background: currentColor;
  }

  .nav-burger-bars {
    position: relative;
  }

  .nav-burger-bars::before,
  .nav-burger-bars::after {
    content: "";
    position: absolute;
    left: 0;
  }

  .nav-burger-bars::before {
    top: -6px;
  }

  .nav-burger-bars::after {
    top: 6px;
  }

  .nav-toggle:checked ~ .nav-burger .nav-burger-bars {
    background: transparent;
  }

  .nav-toggle:checked ~ .nav-burger .nav-burger-bars::before {
    top: 0;
    transform: rotate(45deg);
  }

  .nav-toggle:checked ~ .nav-burger .nav-burger-bars::after {
    top: 0;
    transform: rotate(-45deg);
  }

  /* Inside the panel there is no hover to open a dropdown, so the menus become inline sections and
   * everything stacks. */
  nav.site {
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    margin-right: 0;
  }

  nav.site a:not(.btn),
  .site-panel .menu-top {
    justify-content: space-between;
    padding: 13px 2px;
    font-size: 16px;
    border-radius: 0;
  }

  .site-panel .menu-top:hover,
  .site-panel .menu:focus-within > .menu-top,
  nav.site a:not(.btn):hover {
    background: transparent;
    color: var(--accent);
  }

  /* In the drawer the menus are inline sections: the panel is always visible under its heading,
   * hover-reveal is switched off, and the bridge pseudo-element is meaningless without a panel
   * that drops, so it goes too. */
  .site-panel .menu::after {
    content: none;
  }

  .site-panel .menu-panel {
    position: static;
    min-width: 0;
    padding: 0 0 10px 12px;
    border: 0;
    border-radius: 0;
    box-shadow: none;
    background: transparent;
    visibility: visible;
    opacity: 1;
    transform: none;
    transition: none;
  }

  /* In the drawer the icon drops to the text's own line and inherits its rhythm. */
  .site-panel .menu-panel a {
    grid-template-columns: auto 1fr;
    align-items: start;
  }

  .site-panel .menu-ico {
    margin-top: 4px;
  }

  .site-panel .menu-current,
  .site-panel .menu-soon {
    padding: 6px 2px;
    border-radius: 0;
    background: transparent;
  }

  .site-panel .site-actions {
    display: flex;
    margin-top: 6px;
    padding-top: 14px;
    border-top: 1px solid var(--border);
  }
}

@media (max-width: 940px) {
  /* A different file, not the same one cropped by CSS.
   *
   * The scene is wide, so `cover` on a phone does not crop it — it magnifies it, and the props end
   * up filling the screen sliced down the middle. The narrow variant is a 2.5 KB crop of the wall
   * alone, which keeps what the photograph is there for at this size (the light band) and drops
   * what only works beside the illustration (the bags and the plant). */
  .hero-main {
    background-image: url("brand/hero-wall-sm.webp");
    background-position: center top;
  }

  .hero-stage {
    grid-template-columns: minmax(0, 1fr);
    gap: 40px;
  }

  /* In one column the copy column is the whole page, so `12cqw` measures that and would set a 768px
   * tablet's headline at 84px. That is the column's proportion and the wrong call at this size: it
   * is a tablet, not a billboard, and it also makes the headline jump by half at the 941px
   * breakpoint, where the copy column halves as the stage becomes two columns. So it goes back to a
   * size in `vw` here — 56px at 768, 40px on a phone — with the same three set lines, each of which
   * fits: "Convert more" is about 6.1× the type size, and 40px of type is 244px of line against the
   * 257px column a 320px phone has. */
  .hero h1 {
    font-size: clamp(40px, 7vw, 56px);
  }

  .stat-band {
    grid-template-columns: repeat(2, minmax(max-content, 1fr));
    gap: 22px 12px;
  }
}

@media (max-width: 620px) {
  .hero {
    padding-top: 40px;
  }

  /* The band deliberately stays 2×2 here: the 940 block's halves and its column-edge dividers are
   * what the design draws on a phone (two figures a row, hairline between), and four stacked rows
   * would spend a screen's height on what two rows say. The 1080 block's `gap: 22px 12px` still
   * applies below this, which is the row spacing the design uses.
   *
   * The language selector stays in the panel at every width: it is a menu now, not a header row, so
   * there is no longer any reason to hide it. */
  .nav-burger {
    width: 36px;
    height: 36px;
  }
}

/* ---------- The capabilities band ----------
 *
 * The first section under the hero: six cards across the page, one per thing the agent does. The head
 * is a chip and a headline and nothing else — the design's supporting paragraph beside it is gone, so
 * the card row is the section's only argument and it starts sooner. */
.cap-head {
  display: block;
}

/* Six cards and no supporting paragraph: the head is a chip and a headline, and the cards carry the
 * section alone. (What the paragraph rule used to say — 52ch, right-aligned, the design's own
 * proportion — went out with the paragraph.) */

/* Eighteen tracks, and each card takes 3, 6, 9 or all 18 of them — six across, three, two, one.
 *
 * Counts are decided by the width the row actually has rather than by the window's: six across is
 * 287px a card at 1920 and 190px at 1200, and a viewport breakpoint cannot tell the second of those
 * from a 1200px window in which the same six cards would be 150px. The thresholds are `--card-min`
 * plus the gaps: 6 × 180 + 5 × 14 = 1150 for six across, 568 for three, 260 for two — two across
 * holds at phone widths (~150px a card on a 360px viewport), and only sub-260px containers fall
 * to one.
 *
 * 180 rather than a rounder number because it is a measurement: the design draws six across on a
 * ~1200px page, which is a 190px card, minus the 14px gap it draws between them. Sizing this off the
 * design's *page* instead of its cards is how six across becomes three at every width but the
 * widest, which is not what the design shows at 1200 and not what it shows at 1920 either. */
.cap-cards {
  --card-min: 180px;

  /* The six tints, sampled off the design at the tiles' interiors rather than eyeballed: #dbeafe,
   * #ede9fe, #fce7f3, #d1fae5, #fef3c7 and #d1fae5 again. They live here rather than in tokens.css
   * because they are decorative, they are used nowhere else, and none of them carries meaning a
   * reader could learn — the tiles would read the same if all six were grey. The copy is what says
   * which card is which. */
  --tile-blue: #dbeafe;
  --tile-violet: #ede9fe;
  --tile-pink: #fce7f3;
  --tile-green: #d1fae5;
  --tile-amber: #fef3c7;

  display: grid;
  grid-template-columns: repeat(18, minmax(0, 1fr));
  gap: 14px;
  margin: 40px 0 0;
  padding: 0;
  list-style: none;
  container-type: inline-size;
}

/* No padding here, so the cards take `.card`'s own 26px and this section is on the page's card size
 * rather than on a second one. The design draws these cards 168px wide with the tile 12px in from the
 * edge, which is where the previous 16px came from, but one card size for the page is worth more than
 * the last 6px of a drawing. The cost is at the narrow end of six across, which is also where six
 * across is closest to the design's own page: at 1280 a card is 186px, so with 26px of inset its
 * content is 134px and four titles wrap to two lines. Below 1150 the row drops to three across and
 * every title is back on one line. */
.cap-cards > .card {
  grid-column: span 18;
}

/* The hover: the border turns green and a ring of the same green sits outside it, which reads as one
 * 2px edge rather than as a colour change on a hairline. `--accent-bright` is the tick green rather
 * than the headline green — at 1px the darker one is nearly the card's own border. Instant, with no
 * `transition`: this page has no animation anywhere, and a hover that eases in is animation. */
.cap-cards > .card:hover {
  border-color: var(--accent-bright);
  box-shadow: 0 0 0 1px var(--accent-bright);
}

/* The tile the icon sits in. 48px with a 22px glyph inside it: at the design's 168px card the tile is
 * 52px and the icon 24px, and the difference at 1280 is what the extra 18px of card width is for —
 * the copy gets the room, not the decoration. */
.tile {
  display: grid;
  place-items: center;
  width: 48px;
  height: 48px;
  border-radius: 14px;
  background: var(--surface-sunken);
  margin-bottom: 14px;
}

.tile svg {
  width: 22px;
  height: 22px;
  color: var(--ink-900);
}

.tile-blue { background: var(--tile-blue); }
.tile-violet { background: var(--tile-violet); }
.tile-pink { background: var(--tile-pink); }
.tile-green { background: var(--tile-green); }
.tile-amber { background: var(--tile-amber); }

/* Still a size of its own, but no longer the smallest on the page: a 16px title and a 15.5px body
 * against `h3`'s 17 and `.card p`'s 15. The design sets "Answer questions" at 128px wide, which is a
 * 14px face, and 14/13.5 was measured from it — these two numbers are a deliberate step up from that,
 * because at a 186px card the design's sizes are read rather than scanned. They stop just short of the
 * page's own card type on purpose: six titles in one row are labels, and at 17px the row stops being a
 * band and starts being six headings.
 *
 * The body rule is scoped to the card, and that is load-bearing rather than tidy: `.card p` sets the
 * same property at the same specificity and is defined further down the file, so a bare `.cap-cards p`
 * loses to it and the body quietly renders at 15px — which is what it had been doing. The title needs
 * no such help, because `h3` is a type selector and loses to any class. */
.cap-cards h3 {
  font-size: 16px;
  line-height: 1.3;
  margin-bottom: 6px;
}

.cap-cards > .card p {
  font-size: 15.5px;
  line-height: 1.5;
}

@container (min-width: 260px) {
  .cap-cards > .card {
    grid-column: span 9;
  }
}

@container (min-width: 568px) {
  .cap-cards > .card {
    grid-column: span 6;
  }
}

@container (min-width: 1150px) {
  .cap-cards > .card {
    grid-column: span 3;
  }
}

/* The head is one block at every width now — there is no second column to drop beneath the headline,
 * and the cards need no help from this breakpoint: their own container queries have already taken
 * them down to one across by here. (The old rule put the paragraph under the headline below 940; the
 * paragraph is gone, so this is gone with it.) */

/* The section after this one, on the hero's own split: 0.87 / 1.13 around the same 44px gutter, so
 * the page carries one argument downward instead of starting a new layout here. */
.solution {
  display: grid;
  grid-template-columns: minmax(0, 0.87fr) minmax(0, 1.13fr);
  gap: 44px;
  align-items: center;
}

.solution-copy {
  container-type: inline-size;
}

/* A pill, and the one place on the site where that shape is right: a pill is the shape for a chip in
 * a row of chips, which is exactly what this label is. The buttons stay rectangles — see the note on
 * `.btn` — and a chip is not a button. */
.chip {
  display: inline-block;
  margin: 0 0 18px;
  padding: 5px 12px;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--surface-card);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--ink-500);
}

/* A statement rather than a label, so it is set at statement size: the same proportion of its column
 * the hero's headline takes, which is a little under three quarters of it for the longest line. The
 * floor is the base h2's ceiling, so the section never reads smaller than a heading, and the ceiling
 * is a guard for a wider `--page`. Both arguments are the hero's, and both sections that carry a
 * statement rather than a label are on this rule. */
/* The statement-size headline: the same three declarations on every section that carries one, so
 * the page's statements cannot drift apart.
 *
 * The SIZE is a viewport share, `clamp(27px, 3.2vw, 50px)`, not a column share — and that is the fix
 * for the inconsistency the product owner caught: the cap band's `cqw` resolved against the full
 * page, the solution's and the analytics' against their copy columns (44% and 25% of it), so the
 * same formula produced 50px, 31px and 27px side by side at 1285. Statements on one page read as one
 * voice only if they are one size. A column-share formula cannot give that, because the columns are
 * deliberately different widths — the design's own proportions, kept everywhere else.
 *
 * The floor stays 27 (the base h2's ceiling), the ceiling 50 (the guard for `--page`), and 3.2vw
 * bridges them: it reaches the ceiling at 1563, so every statement is its final size on the widths
 * the design is drawn for. The hero's h1 is a different animal — 12cqw of its own column, the page's
 * one display size — and stays on its own rule. */
.cap-head h2,
.solution h2,
.analytics h2,
.plans-head h2 {
  font-size: clamp(27px, 3.2vw, 50px);
  line-height: 1.12;
  letter-spacing: -0.025em;
  max-width: none;
  margin-bottom: 16px;
}

/* ---------- The solution ----------
 *
 * Copy on the left, the agent's own screen on the right, on the split the hero uses. There is no rule
 * for either column's width: the copy is sized by the grid and the picture below fills its track.
 */

/* Four claims in one column, each with the tick the hero's trust list uses. One check mark on the
 * site, at two sizes, and no new sprite symbol for it.
 *
 * A column rather than a wrapped row, which is the design's arrangement and the honest one: these
 * are four separate capabilities read downwards, and a row that wraps makes "Learns and improves
 * over time" look like a fifth item on a continuing line. */
ul.ticks {
  list-style: none;
  display: grid;
  gap: 10px;
  padding: 0;
  margin: 24px 0 0;
  font-size: 15px;
  color: var(--ink-700);
}

ul.ticks li {
  display: flex;
  align-items: center;
  gap: 10px;
}

/* No drop-shadow on this artwork: the PNG's composition already carries its own depth (the
 * panel's edge and the floating cards read against the pale ground), and a CSS shadow on top
 * doubled the edge. Product call, after trying the shared shadow. */
.solution-shot {
  display: block;
  width: 100%;
  height: auto;
}

/* The artwork's column collapses at 940, which is the hero's number rather than a new one, and the
 * reason is that this column's content has no minimum width of its own — a picture shrinks. So the
 * number is not a threshold the artwork fails; it is where the picture stops being worth half a page.
 *
 * At 940 the column is about
 * 470px, the chat panel inside the artwork is about two fifths of that — 185px — and a drawing of a
 * screen at 185px is an illustration rather than something anyone reads. Below this the picture
 * takes the page instead, which is the trade every stacked layout here makes: the copy carries the
 * claims and the artwork illustrates them. */
@media (max-width: 940px) {
  .solution {
    grid-template-columns: minmax(0, 1fr);
    gap: 36px;
  }

  /* No size override here: the statement rule is a viewport share now, so one column changes nothing
   * it resolves against. (The old override returned a column-share `cqw` to `vw` below the collapse;
   * with `vw` in the base rule the two regimes were the same number by construction, and the override
   * only existed to keep them from disagreeing.) */
}

/* ---------- The analytics ----------
 *
 * The dashboard picture on the left, copy on the right. The swap against the two sections above is
 * the design's arrangement, and it is also the page's rhythm: the hero and the solution both put
 * their artwork on the right, and a third in a row reads as a template rather than as pages turning.
 *
 * The ratio is the design's own, measured off its export rather than mirrored from the solution:
 * 70.7% picture, 4.6% gap, 24.8% copy. Mirroring the solution's 0.87/1.13 was the first build here,
 * and at 1920 it left the copy column twice as wide as its content — the section's right quarter was
 * dead wall, which is the same "not responsive" symptom the old 1200px island had. The design's
 * copy column is deliberately narrow because its copy is short; the solution's copy is two sentences
 * longer and needs its wider share. The gap stays the system's 44px, which is 2.3% of the page here
 * against the design's 4.6% — the design's gap is wider because its page is 1000px; ours scales. */
.analytics {
  display: grid;
  grid-template-columns: minmax(0, 70.7fr) minmax(0, 24.8fr);
  gap: 44px;
  align-items: center;

  /* The photograph itself is on `#analytics` — the section, not this grid — so it fills the
   * section edge to edge and the section's 64px of vertical padding rests ON the photo, the
   * hero's structure exactly. On the grid it was an inset panel, which read as a card pretending
   * to be a section. */
}

/* The section sits on a photograph, the way the hero does: a cream wall with soft bands of
 * light, and a band of foliage shadow down the frame's right edge. It is 11.4 KB — smooth
 * subjects compress better in WebP than the flat fill they replace — and it is built by
 * ci/build-analytics-bg.sh, with the same byte and banding budgets as the hero's background.
 *
 * Three decisions in how it is placed, measured rather than defaulted:
 *
 *   * On the SECTION, not the inner grid: the photo spans the full width of the viewport and
 *     the full height of the section, padding included — the hero's structure. On the grid it
 *     was an inset panel with the page's own surface showing around it on all four sides.
 *   * `cover`, anchored to the bottom LEFT. The foliage shadow lives at the right edge of the
 *     frame — exactly where the copy column sits — and the design draws it showing at the top
 *     right of the section, so on the wide file it stays. Anchoring left is what keeps it there
 *     at every width: anchor right and wide windows crop the shadow off instead of the plainer
 *     top of the frame.
 *   * `background-color` is not set: the photo's edges (≈228,218,210 at the left) sit within a
 *     level or two of the page's own surface, so the section ends without a seam the way the
 *     hero's does. */
#analytics {
  background-image: url("brand/analytics-wall.webp");
  background-repeat: no-repeat;
  background-size: cover;
  background-position: left bottom;
}

.analytics-copy {
  container-type: inline-size;
}

/* The analytics h2's size is NOT redeclared here on purpose: it is on the shared statement-size rule
 * above (`.cap-head h2, .solution h2, .analytics h2, .plans-head h2`), and a second copy here would
 * be one more place to drift when the shared formula changes. This file groups rules by subject,
 * not cascade
 * order — which is why overrides must carry extra classes, and why redeclarations must not happen. */

.analytics-lede {
  font-size: 17.5px;
  line-height: 1.58;
  color: var(--ink-500);
  max-width: 40ch;
  margin: 0;
}

/* A shadow, and the difference from `.solution-shot` is the artwork, not a preference: the dashboard
 * window is drawn flat — no baked shadow — so without a CSS shadow it has no separation from the
 * section at all. The solution's mock carries its own translucent edge in the file, which is why
 * that rule refuses one. The hero's `.hero-shot` uses this same token; two drawn windows, one
 * shadow. */
.dashboard-shot {
  display: block;
  width: 100%;
  height: auto;
  /* The shared two-layer token instead of the single faint shadow this had: one number for every
   * floating mock on the page, so the mocks float at the same height. */
  filter: var(--hero-shadow);
}

/* The same collapse the solution has at 940, for the same reason: the picture's column is ~470px
 * there, and a drawing of a screen at 470px is an illustration rather than something anyone reads.
 * Below it the copy leads and the picture illustrates. The artwork keeps its swap order in the
 * markup, so on one column the picture lands ABOVE the copy — which is what the design's stacked
 * form shows, and the right order here: the section's argument is "this is what it looks like", and
 * the picture is the point. */
@media (max-width: 940px) {
  .analytics {
    grid-template-columns: minmax(0, 1fr);
    gap: 36px;
  }

  #analytics {
    /* A different file, not the same one cropped by CSS — the hero background's reason exactly:
     * `cover` on a phone magnifies a wide scene rather than cropping it, and this frame's right
     * edge carries the foliage shadow, which behind full-width text would read as dirt on the
     * screen. The narrow file is the wall-only left crop (2.9 KB), foliage guard enforced by the
     * build script, positioned top so the light bands lead. */
    background-image: url("brand/analytics-wall-sm.webp");
    background-position: center top;
  }

  /* No size override, for the solution's reason: `vw` needs no breakpoint to keep agreeing with
   * itself. */
}

/* ---------- Merchants, plans, closing band ----------
 *
 * The three sections after the analytics. They share the page's `.card` and one row shape — a grid
 * of `minmax(260px, 1fr)` tracks, the measurement the benchmark cards' `.grid` used before it left
 * with the numbers section — and differ in what each card carries. */

.merchants-head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 18px;
}

/* The row is full-bleed: the cards run under the gutters to the window's edge, the way a
 * conveyor reads, while the head above stays on the page's own margins. */
.merchants-viewport {
  margin-top: 26px;
  overflow: hidden;
  position: relative;
}

/* The fade at each edge: the cards dissolve into the background instead of stopping at a hard
 * clip, so the marquee reads as continuing beyond the window rather than being cut by it. Both
 * fades sit over the track, pointer-transparent so they never block pausing the row on hover,
 * and sampled from the section's own --surface so the melt is invisible at the seam. */
.merchants-viewport::before,
.merchants-viewport::after {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  width: clamp(64px, 12vw, 180px);
  z-index: 2;
  pointer-events: none;
}

.merchants-viewport::before {
  left: 0;
  background: linear-gradient(90deg, var(--surface) 12%, transparent);
}

.merchants-viewport::after {
  right: 0;
  background: linear-gradient(-90deg, var(--surface) 12%, transparent);
}

.merchants-track {
  display: flex;
  width: max-content;
  animation: merchants-scroll 55s linear infinite;
}

/* Half the track is one list, so -50% is exactly the seam. */
@keyframes merchants-scroll {
  to {
    transform: translateX(-50%);
  }
}

/* Pausing on hover (and while anything inside has focus) is what makes a moving row readable
 * rather than merely watchable. */
.merchants-viewport:hover .merchants-track,
.merchants-viewport:focus-within .merchants-track {
  animation-play-state: paused;
}

.merchants-list {
  display: flex;
  gap: 14px;
}

/* Under reduced motion the drift is gone: the loop copy is hidden, the row becomes a static,
 * wrapping grid of the ten cards, and the viewport stops clipping. */
@media (prefers-reduced-motion: reduce) {
  .merchants-track {
    animation: none;
    width: 100%;
  }

  .merchants-list {
    flex-wrap: wrap;
  }

  .merchants-list[aria-hidden="true"] {
    display: none;
  }

  .merchants-viewport {
    overflow-x: visible;
  }

  /* Nothing is drifting past the edges any more, so the melts would only wash the first and
   * last cards; the wrapped grid ends honestly instead. */
  .merchants-viewport::before,
  .merchants-viewport::after {
    content: none;
  }
}

/* Fixed card width — the marquee needs a predictable card to hold a steady pace, and the
 * breakpoints step it down so a phone shows roughly one card and a hint of the next. */
.quote-card {
  flex: none;
  width: 330px;

  /* The user-agent gives <figure> 1em 40px of margin, which is what blew the row out to ~94px
   * between cards. The flex gap above is the only spacing the marquee should have. */
  margin: 0;
}

@media (max-width: 680px) {
  .quote-card {
    width: 262px;
  }
}

@media (max-width: 430px) {
  .quote-card {
    width: 76vw;
  }
}

.plans-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 20px;
  margin: 26px 0 0;
  padding: 0;
  list-style: none;
}

/* The quote card. The blockquote's paragraph outranks `.card p` deliberately, not accidentally:
 * the quote is the card's main voice, and the caption grey `.card p` sets would read the whole
 * testimonial as an aside. */
.quote-card blockquote {
  margin: 0;
}

.quote-card blockquote p {
  font-size: 15.5px;
  line-height: 1.55;
  color: var(--ink-700);
  margin: 0;
}

.merchant {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 14px;
}

/* Initials, not photographs — the reasoning is in the section's comment in index.html: a face is
 * a claim about a person, and it ships only with consent and a build script. */
.merchant-avatar {
  display: grid;
  place-items: center;
  flex: none;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  background: var(--surface-sunken);
  font-size: 13.5px;
  font-weight: 700;
  letter-spacing: 0.02em;
  color: var(--ink-500);
}

.merchant-who {
  display: grid;
  gap: 1px;
  font-size: 13px;
  line-height: 1.35;
  color: var(--ink-300);
}

.merchant-who strong {
  font-size: 14px;
  font-weight: 640;
  color: var(--ink-900);
}

/* The stars sit at the card's right edge and wrap under the name on the narrowest cards. The gold
 * is sampled from the design's ratings rather than from its brand surfaces, and it appears nowhere
 * else on the page — which is what keeps it a rating and not a fifth brand colour. */
.stars {
  display: inline-flex;
  gap: 2px;
  margin-left: auto;
  color: #f0a13c;
}

.stars svg {
  width: 15px;
  height: 15px;
  flex: none;
}

@media (max-width: 620px) {
  .merchant {
    flex-wrap: wrap;
  }

  .stars {
    margin-left: 0;
  }
}

/* The plan cards. The head keeps the chip and the statement headline and gains the billing
 * toggle, which the design draws as Monthly | Yearly | Save 20%. It is static on purpose — this
 * page ships no JavaScript, and a control that cannot switch is a lie — so the drawn state is
 * the monthly column and the yearly saving is stated beside it. The plan figures are the
 * design's placeholders; pricing.html carries the real story and the cards' buttons go to the
 * listing, which holds current pricing. */
.plans-head {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: 18px 24px;
}

.plans-sub {
  margin: 0;
  font-size: 14px;
  color: var(--ink-500);
}

.plans-toggle {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

.plans-toggle-btn {
  padding: 8px 16px;
  border-radius: 999px;
  font-size: 14px;
  font-weight: 600;
  color: var(--ink-500);
}

/* The toggle is a hidden checkbox and two labels; the checked state is the styling, and the
 * selectors reach from the input to its siblings — the head with the labels, the row with the
 * prices. (The input is a direct child of the section's column for exactly this reason; see
 * the comment in index.html.) */
.plans-toggle-input {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
}

.plans-btn-monthly,
.plans-btn-yearly {
  cursor: pointer;
  user-select: none;
}

#billing-yearly:not(:checked) ~ .plans-head .plans-btn-monthly,
#billing-yearly:checked ~ .plans-head .plans-btn-yearly {
  background: var(--btn-fill);
  color: #fff;
}

/* The input itself is visually hidden, so keyboard focus is announced on the pill group. */
.plans-toggle-input:focus-visible ~ .plans-head .plans-toggle {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}

.price-y,
.per-y,
.price-was {
  display: none;
}

#billing-yearly:checked ~ .plans-row .price-m,
#billing-yearly:checked ~ .plans-row .per-m {
  display: none;
}

/* In the yearly state the monthly rate stays visible as a struck "was" price, so the 20% saving
 * is read off the card rather than taken on trust from the pill. It is aria-hidden because the
 * struck figure is presentation: the discounted price and the "billed yearly" note are the whole
 * statement a screen reader needs. */
#billing-yearly:checked ~ .plans-row .price-was {
  display: inline;
  margin-right: 6px;
  font-size: 22px;
  font-weight: 600;
  color: var(--ink-300);
}

#billing-yearly:checked ~ .plans-row .price-y,
#billing-yearly:checked ~ .plans-row .per-y {
  display: inline;
}

.plans-save {
  padding: 7px 13px;
  border-radius: 999px;
  background: var(--accent-wash);
  font-size: 13px;
  font-weight: 650;
  color: var(--accent);
}

.plan-card {
  position: relative;
  display: flex;
  flex-direction: column;
}

/* The popular card wears the capabilities cards' hover treatment — green border plus a ring of
 * the same green, which reads as one 2px edge — made permanent rather than hover-bound. */
.plan-card.is-popular {
  border-color: var(--accent-bright);
  box-shadow: 0 0 0 1px var(--accent-bright);
}

.plan-flag {
  position: absolute;
  top: 18px;
  right: 18px;
  padding: 4px 11px;
  border-radius: 999px;
  background: var(--accent);
  color: #fff;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
}

.plan-card h3 {
  font-size: 19px;
  margin: 0 0 4px;
}

/* Two-class selectors throughout, for the reason `.btn.header-cta` records above: `.card p`
 * sets these same properties at lower specificity further up the file, and a one-class rule
 * would lose to it — the same trap that bit `.cap-cards p`. */
.plan-card .plan-for {
  margin: 0 0 16px;
  font-size: 14px;
  color: var(--ink-500);
}

.plan-card .plan-price {
  display: flex;
  align-items: baseline;
  gap: 7px;
  margin: 0 0 18px;
}

.plan-amount {
  font-size: 36px;
  font-weight: 750;
  letter-spacing: -0.03em;
  color: var(--ink-900);
}

.plan-per {
  font-size: 14px;
  color: var(--ink-300);
}

.plan-list {
  display: grid;
  gap: 10px;
  margin: 0 0 24px;
  padding: 0;
  list-style: none;
  font-size: 14.5px;
  color: var(--ink-700);
}

.plan-list li {
  display: flex;
  align-items: center;
  gap: 9px;
}

/* The bare check, no disc: the hero's `.tick` is a filled green circle with a white mark, and
 * the design's plan rows draw the mark alone. Same sprite symbol, so one check mark on the page
 * in two treatments. */
.tick-plain {
  width: 15px;
  height: 15px;
  flex: none;
  color: var(--accent-bright);
}

/* Full width, and pushed to the cards' common baseline by the flexible column above it — three
 * plans with three different lengths of feature list, one shared button edge. */
.plan-cta {
  width: 100%;
  margin-top: auto;
}

/* ---------- The closing band ---------- */

/* The tinted band is the page's one outright ask, and the wash — not a card — is what sets it
 * apart. It is also the page's last section, so the hairline every other section carries is
 * gone here by the `section:last-of-type` rule above: the page closes on the brand's own pale
 * green rather than on a rule with nothing under it. */
.cta-band {
  padding: 48px 0;
  background: var(--accent-wash);
}

.cta {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  gap: 24px 28px;
  align-items: center;
}

.cta-glyph {
  display: grid;
  place-items: center;
  width: 56px;
  height: 56px;
  border-radius: 16px;
  background: var(--surface-card);
  box-shadow: 0 1px 2px rgb(6 8 12 / 0.06);
}

.cta-glyph svg {
  width: 26px;
  height: 26px;
  color: var(--accent-bright);
}

.cta h2 {
  margin: 0 0 4px;
  font-size: clamp(22px, 2.2vw, 30px);
  line-height: 1.2;
  letter-spacing: -0.01em;
}

.cta-copy p {
  margin: 0;
  font-size: 15px;
}

.cta-action {
  display: grid;
  gap: 8px;
  justify-items: end;
}

.cta-fine {
  margin: 0;
  font-size: 13px;
  color: var(--ink-500);
}

/* The action drops under the copy, the order every stacked section on this page uses. The glyph
 * keeps its place beside the headline — at 44px it still fits a 257px phone column with room
 * for the words — so the band's shape survives the collapse rather than flattening into a list. */
@media (max-width: 940px) {
  .cta {
    grid-template-columns: auto minmax(0, 1fr);
  }

  .cta-action {
    grid-column: 1 / -1;
    justify-items: start;
  }
}

/* ---------- Cards ---------- */

.card {
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  /* 26px, measured off the design's own cards rather than chosen. The capabilities band's cards
   * are tighter than this — see `.cap-cards > .card`, which is the design's second card size
   * rather than a preference. */
  padding: 26px;
  background: var(--surface-card);
}

.card p {
  font-size: 15px;
  margin: 0;
  /* Grey, not the near-black `p` defaults to: in the design the question and the note under it are
   * two different voices, and the card is read as one line of type with a caption rather than as a
   * paragraph with a heading. */
  color: var(--ink-500);
}

.card .metric {
  font-family: var(--font-mono);
  font-size: 27px;
  font-weight: 650;
  letter-spacing: -0.02em;
  color: var(--accent);
  display: block;
  margin-bottom: 4px;
}

/* ---------- Lists ---------- */

ul.plain {
  list-style: none;
  padding: 0;
  margin: 18px 0;
  max-width: var(--measure);
}

ul.plain li {
  padding-left: 26px;
  position: relative;
  margin-bottom: 11px;
  color: var(--ink-700);
}

ul.plain li::before {
  content: "";
  position: absolute;
  left: 6px;
  top: 0.62em;
  width: 7px;
  height: 7px;
  border-radius: 2px;
  background: var(--accent);
}

/* ---------- Callout ---------- */

.callout {
  border-left: 3px solid var(--accent);
  background: var(--accent-wash);
  padding: 16px 20px;
  border-radius: 0 var(--radius) var(--radius) 0;
  margin: 24px 0;
}

.callout p:last-child {
  margin-bottom: 0;
}

/* ---------- Legal and long-form ---------- */

.prose h2 {
  max-width: none;
  margin-top: 34px;
}

.prose h3 {
  margin-top: 24px;
}

/* A data table is the one thing on these pages that has a minimum width of its own, and the legal
 * pages carry four of them. (The marketing page's comparison table, which wrapped itself in a
 * `.table-scroll` div, left with the section that carried it.) `display: block` with `overflow-x: auto` gives the table the page's
 * width as its box and lets its columns overflow that box and scroll inside it, so a 423px
 * sub-processor table on a 320px phone scrolls instead of dragging the whole document sideways.
 * The columns stay aligned because the header and the body are adjacent row groups and so share one
 * anonymous table box; the alignment is verified at 320 and 1920 in the width sweep. */
.prose table {
  display: block;
  overflow-x: auto;
  width: 100%;
  border-collapse: collapse;
  margin: 18px 0;
  font-size: 15px;
}

.prose th,
.prose td {
  text-align: left;
  padding: 9px 12px;
  border-bottom: 1px solid var(--border);
}

.prose code {
  font-family: var(--font-mono);
  font-size: 14px;
  background: var(--surface-sunken);
  padding: 1px 5px;
  border-radius: 4px;
}

.muted {
  color: var(--ink-300);
  font-size: 14px;
}

.mono {
  font-family: var(--font-mono);
  font-size: 14px;
}

/* ---------- Footer ---------- */

footer.site {
  border-top: 1px solid var(--border);
  padding: 44px 0 34px;
  font-size: 14px;
  color: var(--ink-500);
}

footer.site .footer-grid {
  display: grid;
  grid-template-columns: minmax(0, 1.6fr) repeat(3, minmax(0, 1fr));
  gap: 32px 40px;
}

.footer-brand img {
  display: block;
  height: 40px;
  width: auto;
  margin-bottom: 12px;
}

.footer-brand p {
  margin: 0;
  font-size: 13.5px;
  max-width: 34ch;
}

.footer-col {
  display: grid;
  gap: 9px;
  align-content: start;
}

.footer-col h3 {
  margin: 0 0 4px;
  font-size: 12px;
  font-weight: 650;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--ink-300);
}

footer.site a {
  color: var(--ink-500);
  text-decoration: none;
}

footer.site a:hover {
  color: var(--ink-900);
}

.footer-bottom {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 24px;
  justify-content: space-between;
  margin-top: 36px;
  padding-top: 18px;
  border-top: 1px solid var(--border);
  font-size: 13px;
  color: var(--ink-300);
}

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

  .footer-brand {
    grid-column: 1 / -1;
  }
}

/* ---------- Legal popups ----------
 *
 * The one-pager's privacy and terms open in place: the link's fragment IS the popup's state
 * (`:target`), the backdrop and the close button are plain links to a fragment that does not
 * exist, which unsets the target without scrolling. No JavaScript, and the full crawlable
 * pages still live under legal/ for the App Store crawlers. */

.legal-pop {
  display: none;
  position: fixed;
  inset: 0;
  z-index: 60;
}

.legal-pop:target {
  display: block;
}

/* Locks the page behind an open popup. */
body:has(.legal-pop:target) {
  overflow: hidden;
}

.legal-pop-backdrop {
  position: absolute;
  inset: 0;
  background: rgb(10 13 17 / 0.44);
  cursor: default;
}

.legal-pop-card {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  width: min(640px, calc(100vw - 32px));
  max-height: 82vh;
  overflow-y: auto;
  padding: 28px;
  background: var(--surface-card);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--elevate);
}

.legal-pop-card h2 {
  margin-top: 0;
}

.legal-pop-lede {
  font-size: 16px;
  color: var(--ink-500);
}

.legal-pop-close {
  position: absolute;
  top: 10px;
  right: 16px;
  padding: 4px 8px;
  font-size: 22px;
  line-height: 1;
  color: var(--ink-300);
  text-decoration: none;
}

.legal-pop-close:hover {
  color: var(--ink-900);
}

/* ---------- Blog ----------
 *
 * The blog pages opt out of the generic section divider: a hairline under a hero that already
 * ends in whitespace reads as a stray border on every prose page (the index, all three posts),
 * so prose pages draw their own edges instead. */

main.prose-page section {
  border-bottom: 0;
}

/* Without the dividers, the homepage's 64px section paddings double up into a hole between the
 * hero and the content below it. The hero carries no bottom padding and the section that follows
 * opens tighter — the headline block and the cards read as one page, not two stacked ones. */
main.prose-page .hero,
main.prose-page .post-hero {
  padding-bottom: 0;
}

main.prose-page section + section {
  padding-top: 40px;
}

.blog-grid {
  display: grid;
  /* Three across on desktop, per product call; steps down to two, then one. */
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 20px;
  margin: 0;
  padding: 0;
  list-style: none;
}

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

@media (max-width: 640px) {
  .blog-grid {
    grid-template-columns: 1fr;
  }
}

.blog-card a {
  display: block;
  color: inherit;
  text-decoration: none;
}

.blog-card-body {
  display: block;
}

/* Same hover language as the capabilities cards: a green 2px edge, instantly. The border lives
 * on the <li> (from `.card`), so the selector must reach up from the hovered link — the old
 * `a:hover` form styled a border the anchor never had. */
.blog-card:has(a:hover) {
  border-color: var(--accent-bright);
  box-shadow: 0 0 0 1px var(--accent-bright);
}

.blog-card .blog-meta {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  margin: 0 0 8px;
  font-size: 12px;
  font-weight: 650;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--ink-500);
}

/* Each meta item owns its trailing dot inside a no-wrap group, so a narrow card wraps between
 * groups and never strands a dot at the end of a line. */
.blog-m {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  white-space: nowrap;
}

.blog-tag {
  color: var(--accent);
  text-decoration: none;
}

a.blog-tag:hover {
  text-decoration: underline;
}

.blog-dot {
  width: 3px;
  height: 3px;
  border-radius: 50%;
  background: currentColor;
  opacity: 0.55;
}

.blog-card h2 {
  font-size: 20px;
  line-height: 1.3;
  margin: 0 0 10px;

  /* The global h2 caps at 26ch for the homepage's section statements; a card headline is not one
   * of those — it should fill its own card, wrapping where the card ends, like the description
   * under it already does. */
  max-width: none;
}

.blog-card .blog-desc {
  display: block;
  font-size: 15px;
}

/* Card art: the brand screenshots, scaled and cropped inside their own window rather than
 * dropped in as whole images, so every card gets a consistent tile without new assets. The art
 * bleeds over the card's padding — by the padding, and no further: a descendant paints over the
 * card's own border, so a margin that reaches the outer edge would hide the stroke behind the
 * image. The border stays the frontmost line on every edge. */
.blog-art {
  position: relative;
  display: block;
  height: 172px;
  margin: -26px -26px 18px;
  overflow: hidden;
  border-bottom: 1px solid var(--border);
  border-radius: calc(var(--radius-lg) - 1px) calc(var(--radius-lg) - 1px) 0 0;
  background: var(--surface-sunken);
}

.blog-art img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: top center;
}

.blog-more {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  margin-top: 6px;
  font-size: 14px;
  font-weight: 600;
  color: var(--accent);
}

.blog-more .arrow {
  width: 14px;
  height: 14px;
}

.blog-back {
  font-weight: 600;
}

/* ---------- Post pages ----------
 *
 * All three posts share one spine — meta, headline, standfirst, byline, captioned art — and
 * differ in body format: the data piece carries a stat strip, the essay closes on a statement
 * band, the technical one gets a spec table and a verification note. */

.post-hero {
  padding: 56px 0 44px;
}

.post-meta {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  margin: 0 0 18px;
  font-size: 13px;
  font-weight: 650;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--ink-500);
}

.post-byline {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 28px 0 0;
}

.post-avatar {
  display: grid;
  place-items: center;
  width: 42px;
  height: 42px;
  border: 1px solid var(--border);
  border-radius: 50%;
  background: var(--surface-sunken);
  font-size: 14px;
  font-weight: 650;
  color: var(--ink-700);
}

.post-byline > span > strong {
  display: block;
  font-size: 14.5px;
}

.post-byline > span > span {
  font-size: 13.5px;
  color: var(--ink-500);
}

.post-art {
  margin: 30px 0 0;
}

.post-art img {
  display: block;
  width: 100%;
  height: auto;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
}

.post-art figcaption {
  margin-top: 10px;
  font-size: 13.5px;
  color: var(--ink-300);
}

.post-toc {
  margin: 0 0 32px;
  padding: 16px 20px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface-card);
}

.post-toc-title {
  margin: 0 0 8px;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--ink-300);
}

.post-toc ol {
  margin: 0;
  padding-left: 20px;
}

.post-toc li {
  margin: 4px 0;
}

.post-toc a {
  color: var(--ink-700);
  text-decoration: none;
}

.post-toc a:hover {
  color: var(--accent);
  text-decoration: underline;
}

/* The stat strip: the data post's three numbers, set like the hero's band but inside the
 * article, where a reader who only skims will still meet them. */
.post-stats {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 20px;
  margin: 30px 0;
  padding: 24px 26px;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--surface-card);
}

.post-stats dt {
  font-size: 34px;
  font-weight: 700;
  letter-spacing: -0.02em;
  color: var(--ink-900);
}

.post-stats dd {
  margin: 6px 0 0;
  font-size: 14px;
  line-height: 1.5;
  color: var(--ink-500);
}

@media (max-width: 640px) {
  .post-stats {
    grid-template-columns: 1fr;
    gap: 18px;
  }
}

.post-quote {
  margin: 30px 0;
  padding: 4px 0 4px 24px;
  border-left: 3px solid var(--accent);
}

.post-quote p {
  margin: 0;
  font-size: 20px;
  line-height: 1.5;
  color: var(--ink-900);
}

.post-takeaways {
  margin: 34px 0 0;
  padding: 22px 24px;
  border-radius: var(--radius-lg);
  background: var(--accent-wash);
}

.post-takeaways-title {
  margin: 0 0 10px;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--accent);
}

.post-takeaways ul {
  margin: 0;
}

.post-takeaways li {
  margin: 6px 0;
}

.post-statement {
  margin: 40px 0 0;
  padding: 40px 34px;
  border-radius: var(--radius-lg);
  background: var(--accent-ink);
}

.post-statement p {
  margin: 0;
  font-size: clamp(20px, 2.4vw, 26px);
  line-height: 1.4;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: #fff;
}

.post-spec {
  width: 100%;
  margin: 28px 0;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  border-collapse: separate;
  border-spacing: 0;
  overflow: hidden;
  font-size: 15px;
}

.post-spec th,
.post-spec td {
  text-align: left;
  padding: 10px 14px;
  border-bottom: 1px solid var(--border);
}

.post-spec th:not(:first-child),
.post-spec td:not(:first-child) {
  border-left: 1px solid var(--border);
}

.post-spec thead th {
  background: var(--surface-sunken);
  font-size: 12.5px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--ink-500);
}

.post-spec tbody tr:last-child th,
.post-spec tbody tr:last-child td {
  border-bottom: 0;
}

.post-verify {
  margin: 30px 0 0;
  padding: 20px 24px;
  border: 1px dashed var(--ink-300);
  border-radius: var(--radius);
}

.post-verify-title {
  margin: 0 0 8px;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--accent);
}

.post-verify p:not(.post-verify-title) {
  margin: 0;
}

.post-nextprev {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 14px;
  margin: 40px 0 26px;
}

.post-nav-card {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 16px 18px;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--surface-card);
  text-decoration: none;
}

.post-nav-card:hover {
  border-color: var(--accent-bright);
  box-shadow: 0 0 0 1px var(--accent-bright);
}

.post-nav-label {
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--ink-300);
}

.post-nav-title {
  font-size: 15px;
  font-weight: 600;
  color: var(--ink-900);
}

.post-nav-next {
  text-align: right;
}

@media (max-width: 640px) {
  .post-nextprev {
    grid-template-columns: 1fr;
  }

  .post-nav-next {
    text-align: left;
  }
}
