/* =============================================================================
   kotohi landing page — one stylesheet, no build step.

   Identity: the companion app's warm-paper design language (2px ink borders,
   hard never-blurred shadows, Silkscreen labels over Nunito body), on the CREAM
   the product photography is shot against — so a device render sits on the page
   with no visible frame or letterbox.

   Colour sources, all screen-space and none of them read out of hero_skin.py
   (those are AgX-precompensated Blender inputs and come out wrong in CSS):
     ground  #f4efe2  the photography backdrop (enclosure/hero_backdrop.py)
     accents apps/webapp/src/app.css — #5f8f39 / #a9d276 / #7fae52 / #243318
     case    the range shot's ribbon tokens (enclosure/hero_backdrop_five.py):
             pistachio #a9d276, pink #ae5270, ocean #76b9d2
     neutral ink #37342e, chalk #dbd6cc — ink-on-paper, NOT the reserved
             black/white case pair, which stays in rendered imagery only.

   The three case accents differentiate the feature sections, one per section.
   Every one of them is a light value: they carry rules, plates and chips, and
   each has a DARKENED sibling (--*-ink) for text. Measured against the cream:
     #a9d276 1.5:1  → text uses #4a7029 5.02:1
     #ae5270 4.3:1  → text uses #9c4762 5.24:1
     #76b9d2 1.9:1  → text uses #2f6d88 4.99:1

   THE PRICE LIVES HERE, ON ONE LINE, AND NOWHERE ELSE — see --price below.
   ========================================================================== */

/* ---------------------------------------------------------------- fonts ---- */
/* Self-hosted in type/. Base (Latin) subsets only: this page has no
   Latin-Extended and no CJK text, so the -ext and DotGothic16 faces the app
   ships are deliberately absent. Zero external requests. */
@font-face {
  font-family: "Silkscreen";
  font-style: normal;
  font-weight: 400;
  font-display: block;
  src: url("type/silkscreen-400.woff2") format("woff2");
}
@font-face {
  font-family: "Silkscreen";
  font-style: normal;
  font-weight: 700;
  font-display: block;
  src: url("type/silkscreen-700.woff2") format("woff2");
}
@font-face {
  font-family: "Nunito";
  font-style: normal;
  font-weight: 400 800;
  font-display: block;
  src: url("type/nunito-var.woff2") format("woff2");
}

:root {
  color-scheme: light;

  /* ===========================================================================
     PRICE — the single source. €99 is an ESTIMATE and the hero says so in
     words; change this string and the whole page changes.
     ======================================================================== */
  --price: "€99";

  --font-pixel: "Silkscreen", ui-monospace, monospace;
  --font-sans:
    "Nunito", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial,
    sans-serif;

  /* -- Neutrals ------------------------------------------------------------ */
  --ink: #2f2a24; /* the app's body ink */
  --ink-warm: #37342e; /* the ribbon ink, for large display type */
  --muted: #6a6455;
  --label: #9a9280;
  --chalk: #dbd6cc;
  --hairline-2: #c9c3b1;

  --bg: #f4efe2; /* THE GROUND — same cream the renders are shot on */
  /* THE HERO BAND. A light green behind the masthead and the first screen, so
     the fold reads as its own place and the cream below it starts the page
     proper.

     Its own value rather than a token: it sits deliberately between
     `--accent-weak` (#eaf1dc, which at 1.01 against the cream was too subtle
     to register as a band at all) and the dither's darker tone (#d0dbbb, which
     starts competing with the content). This is 1.10 against the cream.

     IT IS THE COLOUR THE HERO RENDER ACTUALLY PRODUCES, measured off the
     footage's corner pixel, not a colour the render was asked to match. The
     approved value was #dee9c6 and the render lands at #dfe5c1; matching in
     THIS direction is free, where chasing the render to an exact hex is a
     tuning loop against a backdrop whose gain and saturation do not respond
     linearly (see `enclosure/hero_backdrop_plain.py`). The two are a hair
     apart in isolation and the difference is only visible where they meet —
     which is the whole length of the hero figure's edge, so they have to be
     equal rather than close.

     Everything on the band keeps its contrast: ink 11.2, muted 4.65. The two
     that don't clear AA — the small caps label (2.44) and the inline link
     (3.03) — do not clear it on the page's own cream either (2.69 / 3.34), so
     they are the palette's existing call, not this band's. Do not push the
     green much past here without revisiting both.

     READ `.hero-shot` BEFORE CHANGING THIS. The hero footage is rendered on
     `--bg`, not on this, and the two are reconciled in CSS until the shot is
     re-rendered. A stronger band makes that seam more visible, not less. */
  --hero-bg: #dfe5c1;
  --bg-sunken: #e7e3d5;
  --surface: #fbf8f0;
  --surface-2: #f2eddf;
  --border: #e4ddc9;
  --border-strong: var(--hairline-2);

  /* -- Primary (the app's green) -------------------------------------------- */
  --accent: #5f8f39;
  --accent-weak: #eaf1dc;
  --accent-fill: #a9d276;
  --accent-line: #7fae52;
  --accent-ink: #243318;
  --accent-shadow: #6f9e4e;

  /* -- Case colourways: fill (as rendered) + ink (darkened, for text) ------- */
  --pistachio: #a9d276;
  --pistachio-ink: #4a7029;
  --pistachio-weak: #eaf1dc;
  --pink: #ae5270;
  --pink-ink: #9c4762;
  --pink-weak: #f7e6ea;
  --ocean: #76b9d2;
  --ocean-ink: #2f6d88;
  --ocean-weak: #e3f0f5;

  /* Warm secondary surface. The physical body beige, and hero_skin.py:119-129
     records that it has almost no separation from this ground — so it is used
     ONLY as a large flat surface with ink on it (7.5:1), never as a hairline
     against the cream and never as text. */
  --beige: #dfc78d;

  --new-bg: #fbf3e6;
  --new-ink: #c79a4e;
  --danger-rose: #c07d6e;
  --danger-tint-1: #f5e7e4;

  /* -- Section accent: defaults to the green, overridden per section -------- */
  --sec: var(--accent-fill);
  --sec-ink: var(--pistachio-ink);
  --sec-weak: var(--accent-weak);

  --s-1: 0.125rem;
  --s-2: 0.25rem;
  --s-4: 0.5rem;
  --s-5: 0.75rem;
  --s-6: 1rem;
  --s-7: 1.25rem;
  --s-8: 1.5rem;
  --s-9: 2rem;

  --r-tile: 1.25rem;
  --r-btn: 1rem;
  --r-ctl: 0.8125rem;
  --r-chip: 999px;

  --bw-hair: 1px;
  --bw-ink: 2px;

  --shadow-btn: 0 3px 0 var(--accent-shadow);
  --shadow-btn-a: 0 1px 0 var(--accent-shadow);
  --shadow-plate: 0 5px 0 rgba(47, 42, 36, 0.22);

  --dither: repeating-linear-gradient(45deg, #dbe4c8 0 7px, #d0dbbb 7px 14px);

  --tap: 44px;
  --gutter: var(--s-6);
  --measure: 68rem;
}

.sec-pistachio {
  --sec: var(--pistachio);
  --sec-ink: var(--pistachio-ink);
  --sec-weak: var(--pistachio-weak);
}
.sec-pink {
  --sec: var(--pink);
  --sec-ink: var(--pink-ink);
  --sec-weak: var(--pink-weak);
}
.sec-ocean {
  --sec: var(--ocean);
  --sec-ink: var(--ocean-ink);
  --sec-weak: var(--ocean-weak);
}

@media (min-width: 48rem) {
  :root {
    --gutter: var(--s-9);
  }
}

/* ----------------------------------------------------------------- base ---- */
*,
*::before,
*::after {
  box-sizing: border-box;
}
* {
  margin: 0;
}
body {
  font-family: var(--font-sans);
  font-size: 1rem;
  line-height: 1.55;
  color: var(--ink);
  background: var(--bg);
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}
img,
video,
svg {
  display: block;
  max-width: 100%;
  height: auto;
}
h1,
h2,
h3 {
  line-height: 1.15;
  font-weight: 700;
  letter-spacing: -0.01em;
  text-wrap: balance;
  color: var(--ink-warm);
}
a {
  color: var(--accent);
  text-underline-offset: 0.15em;
}
code {
  font-family: var(--font-pixel);
  font-size: 0.85em;
}
strong {
  font-weight: 800;
}
:focus-visible {
  outline: var(--bw-ink) solid var(--accent);
  outline-offset: 2px;
  border-radius: var(--s-2);
}
.px {
  font-family: var(--font-pixel);
  font-weight: 700;
  letter-spacing: 0.02em;
}
.skip {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 10;
  padding: var(--s-4) var(--s-6);
  background: var(--surface);
  border: var(--bw-ink) solid var(--ink);
  border-radius: var(--r-ctl);
}
.skip:focus {
  left: var(--s-4);
  top: var(--s-4);
}

/* ---------------------------------------------------------------- shell ---- */
.bar,
main,
.foot {
  max-width: var(--measure);
  margin-inline: auto;
  padding-inline: var(--gutter);
}
.bar {
  display: flex;
  align-items: center;
  gap: var(--s-5);
  padding-block: var(--s-6);
}
.wordmark {
  font-family: var(--font-pixel);
  font-weight: 700;
  font-size: 1.25rem;
  letter-spacing: 0.02em;
}
.tag {
  font-size: 0.625rem;
  text-transform: uppercase;
  padding: var(--s-1) var(--s-4);
  border-radius: var(--r-chip);
  background: var(--new-bg);
  color: var(--new-ink);
  border: var(--bw-hair) solid #ecdcc0;
}

/* --------------------------------------------------------------- button ---- */
.btn {
  /* Each variant sets --shadow; declaring it here keeps the property valid for
     variants that want no ledge. */
  --shadow: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: var(--tap);
  padding: var(--s-4) var(--s-7);
  gap: var(--s-4);
  border: var(--bw-ink) solid transparent;
  border-radius: var(--r-btn);
  font-size: 0.8125rem;
  text-transform: uppercase;
  text-decoration: none;
  cursor: pointer;
  box-shadow: var(--shadow);
  transition:
    transform 60ms linear,
    box-shadow 60ms linear,
    background-color 60ms linear;
}
.btn.primary {
  --shadow: var(--shadow-btn);
  background: var(--accent-fill);
  border-color: var(--ink);
  color: var(--accent-ink);
}
.btn.secondary {
  background: var(--surface);
  border-color: var(--border-strong);
  color: var(--muted);
}
.btn.primary:active {
  --shadow: var(--shadow-btn-a);
  transform: translateY(2px);
}
.btn.secondary:active {
  transform: translateY(1px);
}
.btn[aria-disabled="true"],
.btn:disabled {
  opacity: 0.55;
  cursor: default;
}
@media (hover: hover) {
  .btn.primary:hover:not(:disabled) {
    filter: brightness(1.03);
  }
  .btn.secondary:hover {
    background: var(--surface-2);
    text-decoration: none;
  }
}

/* ----------------------------------------------------------------- hero ---- */
/* The masthead and the hero share one full-width green band. They are two
   separate max-width containers (see `shell`), so the colour has to be carried
   past their edges.

   SPREAD plus a clip, not a 100vw pseudo-element and not shadow OFFSETS.
   Both of the obvious alternatives are wrong here:

   - `width: 100vw` is the viewport INCLUDING the scrollbar, so on a desktop
     with a classic scrollbar it is ~15px too wide and gives the whole page a
     horizontal scroll.
   - `box-shadow: -100vmax 0 0` looks right and is not: an offset shadow is a
     COPY of the box moved sideways, so once the offset exceeds the element's
     own width the copy no longer reaches it and leaves an uncovered gap either
     side. (Measured: at 1280px the masthead's band stopped 96px short.)

   A spread shadow grows outward from every edge with no gap at any size, and
   `clip-path` then trims the vertical bleed while leaving the horizontal —
   negative insets on left and right only. Neither property creates a layout
   box, so neither can produce a scrollbar.

   The two must stay vertically flush: `main` deliberately has no block
   padding, so the bar's bottom edge IS the hero's top edge. A gap between them
   would show as a cream stripe across the middle of the band. */
.bar,
.hero {
  background: var(--hero-bg);
  box-shadow: 0 0 0 100vmax var(--hero-bg);
  clip-path: inset(0 -100vmax);
}
.hero {
  display: grid;
  gap: var(--s-9);
  padding-block: var(--s-8) var(--s-9);
}
/* ON A PHONE THE DEVICES COME FIRST, above the headline.
   A single column has room for one thing at the top, and on the fold that
   matters it should be the object, not five words about it — a reader who
   scrolls past a headline has not seen the product yet.
   This is a VISUAL reorder only: the DOM keeps the <h1> before the figure, so
   a screen reader still hears the heading first, which is the right reading
   order and the one a swapped DOM would have cost us. Reset at the two-column
   breakpoint, where copy-left/figure-right already puts them side by side. */
.hero-copy {
  order: 2;
}
.hero-shot {
  order: 1;
}
h1 {
  font-size: clamp(2rem, 8.5vw, 3.25rem);
  letter-spacing: -0.02em;
}
/* There is ONE headline. The ?v=a|b|c A/B switch that used to live here is
   gone, along with the `.hl` rules that showed one of three spans — the
   mechanism it left behind is now language selection (?lang=, validated in the
   page head, landing on <html lang>). When a translation arrives this is where
   its rule goes: hide the blocks whose lang is not the document's, e.g.

     [lang]:not(:lang(en)) { display: none; }

   which needs nothing added to the inline script beyond a code in LANGS. */
.sub {
  margin-top: var(--s-6);
  font-size: 1.125rem;
  color: var(--muted);
  max-width: 34ch;
}
.pricing {
  margin-top: var(--s-7);
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: var(--s-4);
}
.price::after {
  /* The price string comes from --price, defined once at the top of this file. */
  content: var(--price);
}
.price {
  font-size: 1.5rem;
  padding: var(--s-2) var(--s-5);
  border: var(--bw-ink) solid var(--ink);
  border-radius: var(--r-ctl);
  background: var(--accent-weak);
}
.pricing-note {
  color: var(--muted);
  font-size: 0.9375rem;
}
.alt {
  margin-top: var(--s-6);
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--s-5);
}
.alt .free {
  font-family: var(--font-pixel);
  font-size: 0.6875rem;
  text-transform: uppercase;
  color: var(--pistachio-ink);
}

/* Pause/play, bottom-right of the figure. Present whenever there is a screen
   video, in both directions — a reader who has motion turned off can still
   choose to watch, and vice versa. */
.motion {
  position: absolute;
  /* Bottom-right of the FIGURE, which is the bottom-right of the device once the
     chassis PNG is tight-cropped. If the real render carries transparent margin,
     nudge with --motion-inset rather than moving this rule. */
  right: var(--motion-inset, 0px);
  bottom: var(--motion-inset, 0px);
  z-index: 2;
  min-height: 32px;
  padding: 0 var(--s-5);
  font-family: var(--font-pixel);
  font-size: 0.625rem;
  text-transform: uppercase;
  color: var(--muted);
  background: var(--surface);
  border: var(--bw-hair) solid var(--border-strong);
  border-radius: var(--r-chip);
  cursor: pointer;
}

/* -----------------------------------------------------------------------------
   ...and the same stack before the assets exist. `class="film pending"` draws
   a plate at exactly the final aspect ratio, hides the (missing) layers, and
   labels itself. `--interim` optionally shows a real render behind the label so
   the page never has an empty box in it.

   TO WIRE A REAL ASSET IN: drop the files into img/ and delete the word
   `pending` from that one figure. Nothing else changes, and nothing reflows —
   the plate already occupies the final box.
   -------------------------------------------------------------------------- */
.film.pending > * {
  visibility: hidden;
}
.film.pending {
  display: grid;
  place-items: center;
  border-radius: var(--r-tile);
  border: var(--bw-ink) dashed var(--border-strong);
  background:
    linear-gradient(rgba(244, 239, 226, 0.72), rgba(244, 239, 226, 0.72)), var(--interim, none),
    var(--dither);
  background-size: cover;
  background-position: center;
}
.film.pending::after {
  content: attr(data-pending);
  grid-area: 1 / 1;
  max-width: 80%;
  padding: var(--s-4) var(--s-5);
  font-family: var(--font-pixel);
  font-weight: 700;
  font-size: 0.625rem;
  line-height: 1.5;
  text-transform: uppercase;
  text-align: center;
  color: var(--muted);
  background: var(--surface);
  border: var(--bw-hair) solid var(--border-strong);
  border-radius: var(--r-chip);
}

/* =============================================================================
   EVERY SHOT ON THIS PAGE IS A WHOLE RENDER
   -----------------------------------------------------------------------------
   One <video> on the page's own cream: no chassis PNG over it, no glass layer,
   nothing to position inside it. The device is IN the footage, because the UI is
   mapped through the panel material and the camera foreshortens the screen along
   with the case.

   This replaced a three-layer CSS composite (device plate + positioned screen +
   glass highlight) that placed the panel with four measured numbers per figure.
   It read as pasted on and no amount of measuring fixed it: a flat axis-aligned
   rectangle in front of a perspective case is two projections in one picture,
   and the eye finds that immediately. The fix was never a better rectangle. All
   the machinery that placed one — `.device`, `.chassis`, `.glass`, --panel-x
   and friends, and img/device-*.webp — is gone rather than left switched off.

   --shot-w / --shot-h are the RENDER'S OWN PIXEL DIMENSIONS, set per figure and
   fixed by the shot script, not by the render. They reserve the box before the
   poster decodes so the fold never jumps, which means they MUST match the file.
   Each shot is composed for its own section, so no two are the same shape.
   ========================================================================== */
.film {
  position: relative;
  width: 100%;
}
.film > .screen {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: var(--shot-w) / var(--shot-h);
}

/* The hero footage is rendered ON the band (2026-08-23), so its edge meets the
   page with nothing to cut off — the same premise the feature rows have always
   had against the cream, and the reason none of them carry a frame either.

   THERE WAS AN INTERIM CARD TREATMENT HERE AND IT IS GONE. While the footage
   was still rendered on cream it sat on the green as a hard-edged rectangle,
   and a radius plus a hairline made that read as a photo card rather than as a
   seam. Deleted with the re-render, which is what it was marked for. If a
   future shot's ground and `--hero-bg` ever drift apart again, that is the
   symptom, and re-rendering is the fix rather than re-adding the frame:
   `hero_backdrop_plain.py` owns the ground for this shot and documents why the
   value cannot simply be converted from this hex. */
.hero-shot {
  --shot-w: 1400;
  --shot-h: 1200;
  justify-self: center;
  width: 100%;
  /* Wider than the feature rows: this is two devices side by side, and at the
     portrait max-width they were each half the size of a single-device shot.
     Deliberately above the column width so the column ratio decides, not this. */
  max-width: 44rem;
}
/* A single device does not need the full column width on a phone either —
   full-bleed makes each of five figures half a screen tall.

   The cap is per-shot because the frames are no longer one shape. Each render
   carries its own cream margin around the device, so a frame that is wider than
   it is tall spends more of its width on ground and needs a larger cap to put
   the same amount of DEVICE on the page. Decks is the landscape one; Memo is the
   tallest. Sized so the apparent device is even down the page, which is what a
   reader notices — not the frame. */
.row-shot {
  width: 100%;
  max-width: 21rem;
  margin-inline: auto;
  /* Every shot carries cream margin around the device, so the figure's corner
     is not the device's corner. The control sits inside by roughly that margin
     rather than out on empty ground — the same reason the old device plates
     set this, for the same kind of margin. */
  --motion-inset: 5%;
}
/* The caps are MEASURED, not chosen. Each render carries its own cream margin,
   so an identical box puts a different amount of DEVICE on the page — and the
   device is what a reader compares down the column, not the frame around it.
   Measured device width as a fraction of frame width, off the poster of each:

     study 73.9%   care 66.7%   focus 70.8%   memo 72.7%   decks 71.6%

   Each cap is 17.5rem / that fraction, so every device lands at ~17.5rem wide.
   For reference the old device plates were 86.7% of their canvas at a 20rem
   cap — 17.3rem of device — so this holds the page's established size.

   Care is the outlier and is meant to be: it is the nearest, lowest camera, so
   the case sits smallest in its frame and needs the widest box to match.
   Re-measure if a shot is ever recomposed; the numbers are only true of the
   renders that are in img/ right now. */
.row-shot.shot-study  { --shot-w: 1000; --shot-h: 1350; max-width: 23.5rem; }
.row-shot.shot-care   { --shot-w: 1000; --shot-h: 1200; max-width: 26rem; }
.row-shot.shot-focus  { --shot-w: 1100; --shot-h: 1200; max-width: 24.5rem; }
.row-shot.shot-memo   { --shot-w: 1000; --shot-h: 1300; max-width: 24rem; }
.row-shot.shot-decks  { --shot-w: 850;  --shot-h: 1100; max-width: 24.5rem; }

/* ----------------------------------------------------------------- form ---- */
.signup {
  margin-top: var(--s-8);
  padding: var(--s-7);
  background: var(--surface);
  border: var(--bw-ink) solid var(--border);
  border-radius: var(--r-tile);
  box-shadow: var(--shadow-plate);
}
.field label {
  display: block;
  font-family: var(--font-pixel);
  font-size: 0.6875rem;
  text-transform: uppercase;
  color: var(--label);
  margin-bottom: var(--s-4);
}
.inline {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-4);
}
.signup input[type="email"] {
  flex: 1 1 12rem;
  min-width: 0;
  min-height: var(--tap);
  padding: var(--s-4) var(--s-5);
  /* 16px floor: iOS Safari zooms any focused field under 16px and never zooms
     cleanly back. Same constant, same reason, as the app's --text-field. */
  font: inherit;
  font-size: 1rem;
  color: var(--ink);
  background: var(--bg);
  border: var(--bw-ink) solid var(--border-strong);
  border-radius: var(--r-ctl);
}
.signup input[type="email"]:focus-visible {
  border-color: var(--accent-line);
}
.consent {
  display: flex;
  gap: var(--s-5);
  margin-top: var(--s-6);
}
.consent input {
  flex: none;
  width: 1.25rem;
  height: 1.25rem;
  margin-top: 0.15rem;
  accent-color: var(--accent);
}
.consent label {
  font-size: 0.875rem;
  color: var(--muted);
}
.msg {
  margin-top: var(--s-6);
  padding: var(--s-5);
  font-size: 0.9375rem;
  border-radius: var(--r-ctl);
  border: var(--bw-hair) solid transparent;
}
.msg:empty {
  display: none;
}
.msg.ok {
  background: var(--accent-weak);
  border-color: var(--accent-line);
  color: var(--accent-ink);
}
.msg.bad {
  background: var(--danger-tint-1);
  border-color: var(--danger-rose);
  color: #7a3c31;
}

/* ------------------------------------------------------------- features ---- */
.features {
  display: grid;
  gap: 3rem;
  padding-block: var(--s-9);
}
.features .row {
  display: grid;
  gap: var(--s-7);
  align-items: center;
}
/* The section's case colour, carried by a rule and an eyebrow rather than by
   tinted body text — the fills are light and would fail on type. */
.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: var(--s-4);
  margin-bottom: var(--s-5);
  font-family: var(--font-pixel);
  font-weight: 700;
  font-size: 0.625rem;
  text-transform: uppercase;
  color: var(--sec-ink);
}
.eyebrow::before {
  content: "";
  width: 1.75rem;
  height: 0.5rem;
  border-radius: var(--r-chip);
  background: var(--sec);
}
.row-copy h2 {
  font-size: clamp(1.375rem, 5vw, 1.75rem);
  margin-bottom: var(--s-5);
}
.row-copy p + p,
.panel p + p {
  margin-top: var(--s-5);
}
.row-copy p {
  color: var(--muted);
  max-width: 46ch;
}
.aside {
  font-size: 0.9375rem;
  padding-left: var(--s-6);
  border-left: var(--bw-ink) solid var(--sec);
}
.chip {
  display: inline-block;
  vertical-align: middle;
  font-family: var(--font-pixel);
  font-size: 0.625rem;
  text-transform: uppercase;
  padding: var(--s-1) var(--s-4);
  border-radius: var(--r-chip);
  background: var(--new-bg);
  color: var(--new-ink);
  border: var(--bw-hair) solid #ecdcc0;
}

/* -------------------------------------------------- colours + panels ------- */
.colours,
.panel {
  padding-block: var(--s-9);
}
.colours h2,
.panel h2 {
  font-size: clamp(1.375rem, 5vw, 1.75rem);
  margin-bottom: var(--s-5);
}
.colours p,
.panel p {
  color: var(--muted);
  max-width: 60ch;
  margin-bottom: var(--s-7);
}
/* The range shot is photographed on this exact cream, so it gets no frame and
   no border — it bleeds into the page, which is the point of the ground colour. */
.colours img {
  border-radius: var(--r-tile);
}
.panel {
  margin-block: var(--s-6);
  padding: var(--s-8);
  background: var(--surface);
  border: var(--bw-ink) solid var(--border);
  border-radius: var(--r-tile);
}

/* ---------------------------------------------------------------- faq ------ */
/* Six <details> in a stack. The closed state is the reading state, so the
   summary carries the weight and the marker is the page's own ink triangle
   rather than the platform's — Safari and Firefox draw different glyphs at
   different sizes, and next to Silkscreen both of them look like a bug. */
.faq {
  padding-block: var(--s-9);
}
.faq h2 {
  font-size: clamp(1.375rem, 5vw, 1.75rem);
  margin-bottom: var(--s-6);
}
.faq details {
  border-block-end: var(--bw-hair) solid var(--border);
}
.faq details:first-of-type {
  border-block-start: var(--bw-hair) solid var(--border);
}
.faq summary {
  /* `list-style` is what removes the marker in Firefox and Chrome; the
     ::-webkit-details-marker rule below is the one Safari reads. */
  list-style: none;
  cursor: pointer;
  display: flex;
  gap: var(--s-4);
  align-items: baseline;
  padding-block: var(--s-5);
  font-weight: 700;
  font-size: 1.0625rem;
}
.faq summary::-webkit-details-marker {
  display: none;
}
.faq summary::before {
  content: "▸";
  color: var(--pistachio-ink);
  font-size: 0.875em;
  /* Rotates in place, so the question does not shift when it opens. */
  transition: transform 120ms ease;
}
.faq details[open] summary::before {
  transform: rotate(90deg);
}
.faq summary:focus-visible {
  outline: var(--bw-ink) solid var(--ink);
  outline-offset: var(--s-2);
  border-radius: var(--s-2);
}
.faq details p {
  /* Indented to clear the marker, so the answer hangs under the question
     rather than under the triangle. */
  margin: 0 0 var(--s-6);
  padding-inline-start: calc(0.875em + var(--s-4));
  color: var(--muted);
  max-width: 62ch;
}
@media (prefers-reduced-motion: reduce) {
  .faq summary::before {
    transition: none;
  }
}

/* -------------------------------------------------------------- legal ------ */
/* Placeholder legal identity, deliberately loud. deploy/Dockerfile.landing
   greps for `class="todo"` and refuses to build while any remain, so this can
   never ship by accident — see deploy/README.md. */
.todo {
  display: inline-block;
  padding: var(--s-2) var(--s-4);
  background: #ffe9b0;
  color: #7a4a00;
  border: var(--bw-ink) dashed #b57f19;
  border-radius: var(--s-2);
  font-family: var(--font-pixel);
  font-size: 0.75rem;
}
.todo-banner {
  margin-block: var(--s-6);
  padding: var(--s-6);
  background: #ffe9b0;
  color: #7a4a00;
  border: var(--bw-ink) solid #b57f19;
  border-radius: var(--r-ctl);
  font-weight: 800;
}
.legal {
  max-width: 44rem;
  margin-inline: auto;
  padding: var(--s-9) var(--gutter);
}
.legal h1 {
  font-size: clamp(1.75rem, 6vw, 2.25rem);
  margin-bottom: var(--s-7);
}
.legal h2 {
  font-size: 1.125rem;
  margin-top: var(--s-9);
  margin-bottom: var(--s-4);
}
.legal p,
.legal li {
  color: var(--muted);
  margin-bottom: var(--s-5);
}
.legal ul {
  padding-left: var(--s-8);
}
.legal .updated {
  font-family: var(--font-pixel);
  font-size: 0.6875rem;
  text-transform: uppercase;
  color: var(--label);
}

/* --------------------------------------------------------------- footer ---- */
.foot {
  padding-block: var(--s-9);
  border-top: var(--bw-hair) solid var(--border);
  margin-top: var(--s-9);
}
.foot nav {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-7);
  margin-bottom: var(--s-5);
}
.foot p {
  font-size: 0.625rem;
  text-transform: uppercase;
  color: var(--label);
}

/* -------------------------------------------------------------- counter ---- */
/* A 1998 hit counter, drawn with this page's materials rather than a GIF:
   the plate is the same 2px ink border and hard ledge as every other surface
   here, and the digits are Silkscreen, which is already loaded. No new asset,
   no new request.

   It sits IN THE HERO, under the signup form — it is social proof, and social
   proof three screens below the form it is meant to support is decoration. The
   job here is to be noticed without competing: hence the hairline rule above
   it and the small type, so the eye reaches it after the field and the two
   buttons rather than instead of them.

   Every digit is its own cell (main.js builds them) so the plate is a fixed
   grid: it grows by one cell at ten times the traffic and never reflows in
   between. `font-variant-numeric: tabular-nums` is belt-and-braces — Silkscreen
   is fixed-width already, but the fallback stack is not. */
.counter {
  margin-top: var(--s-6);
  padding-top: var(--s-6);
  /* Ink at low alpha rather than `--border`: that token is a warm cream line,
     which was invisible-going-on-wrong once this block moved onto the green
     band. A tint of the page's own ink reads correctly on either ground, so
     the rule does not have to be revisited if the band colour changes. */
  border-top: var(--bw-hair) solid rgba(47, 42, 36, 0.13);
  /* Held to the copy column's measure rather than the hero's: the bar is a
     length being read against a target, and a very wide one stops reading as
     a proportion at a glance. */
  max-width: 26rem;
}
.odo-line {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-5);
  margin-bottom: var(--s-5);
}
.odo {
  display: inline-flex;
  gap: 2px;
  padding: var(--s-2);
  background: var(--ink);
  border: var(--bw-ink) solid var(--ink);
  border-radius: var(--r-ctl);
  box-shadow: var(--shadow-plate);
  font-variant-numeric: tabular-nums;
}
.odo .d {
  min-width: 1.1em;
  padding: var(--s-2) 3px;
  text-align: center;
  /* Not pure black behind not-pure-green: this is a warm-paper page, and the
     plate reads as an object on it rather than a hole cut in it. */
  background: #23201b;
  color: var(--accent-fill);
  font-family: var(--font-pixel);
  font-size: 1.125rem;
  line-height: 1;
  border-radius: 3px;
}
.odo-label {
  font-size: 0.8125rem;
  color: var(--muted);
}
.odo-label strong {
  color: var(--ink);
  font-weight: 400;
}

.goal-line {
  font-size: 0.8125rem;
  color: var(--muted);
  margin-bottom: var(--s-4);
}
.goal-line strong {
  color: var(--accent-ink);
  font-weight: 400;
}
/* A native <progress>, restyled. Both vendor tracks have to be written out
   separately — a combined selector list is dropped WHOLE by both engines the
   moment one pseudo-element in it is unrecognised, which is how this silently
   renders as the OS default bar if you tidy it up. */
.goal-bar {
  -webkit-appearance: none;
  appearance: none;
  display: block;
  width: 100%;
  height: 0.875rem;
  border: var(--bw-ink) solid var(--ink);
  border-radius: var(--r-chip);
  background: var(--bg-sunken);
  overflow: hidden;
}
.goal-bar::-webkit-progress-bar {
  background: var(--bg-sunken);
}
.goal-bar::-webkit-progress-value {
  background: var(--accent-fill);
  border-right: var(--bw-hair) solid var(--accent-line);
}
.goal-bar::-moz-progress-bar {
  background: var(--accent-fill);
}
.counter-note {
  margin-top: var(--s-4);
  font-size: 0.6875rem;
  color: var(--label);
}

/* ------------------------------------------------------------- ≥ 48rem ---- */
@media (min-width: 48rem) {
  .hero {
    /* The devices get the LARGER share, not the copy. The headline is five words
       and the form is one field; the product is what has to carry this fold. Note
       the figure is column-limited rather than capped by `.hero-shot`'s
       max-width — at 0.95fr the column was ~31rem, under the cap, so raising the
       cap alone did nothing. This ratio is the actual lever. */
    grid-template-columns: 0.85fr 1.15fr;
    align-items: center;
    gap: var(--s-9);
    padding-block: var(--s-9) 3rem;
  }
  /* Two columns place these; the phone's stack order must not leak in here. */
  .hero-copy,
  .hero-shot {
    order: 0;
  }
  .features .row {
    grid-template-columns: 1fr 1fr;
    gap: var(--s-9);
  }
  /* Alternating rows. On narrow screens every row is eyebrow → heading → text →
     image, in DOM order, which is the sensible reading order; the swap only
     exists once there are two columns. */
  .features .row:nth-child(even) .row-copy {
    order: 2;
  }
  .features .row:nth-child(even) .row-shot {
    order: 1;
  }
  .row-shot {
    max-width: 22rem;
    justify-self: center;
  }
  .sub {
    max-width: 30ch;
  }
}

/* -------------------------------------------------- reduced motion -------- */
/* The screens are <video>, not GIFs, precisely so this can be honoured: a GIF
   cannot be stopped. Here nothing autoplays, each poster frame stands in — the
   device PNG plus a still card is a complete, deliberate picture — and main.js
   leaves the play control visible so motion stays available on request. */
@media (prefers-reduced-motion: reduce) {
  * {
    transition: none !important;
    animation: none !important;
  }
}
