/* ---------------------------------------------------------------------------
 * Marketing site stylesheet. One file, no build step, no external requests.
 *
 * THE SITE TAKES ITS PALETTE, TYPE AND RHYTHM FROM THE APP, AND OWNS ITS OWN
 * COMPONENT VOCABULARY. That split is deliberate and was decided on 2026-09-18;
 * before then this file was a straight port of DreamDate/Core/DesignSystem/,
 * including the parts that make the app read as playful.
 *
 * What still comes from the app, and must not drift from it:
 *
 *   PlaydateTheme.swift    the five accents, the six gradients, Spacing
 *                          (4/8/16/24/32), Baloo 2 (for headings — body text
 *                          is the site's own Figtree since 2026-10-10), and
 *                          the cream page colour.
 *                          The site takes the LIGHT appearance only and does
 *                          not follow prefers-color-scheme — see --background.
 *
 * What is now the site's own, because a marketing site for this business has a
 * different job from the product it markets — it is read by a venue manager, an
 * Apple reviewer and a Stripe risk analyst before it is ever read by a member:
 *
 *   .card        flat surface on a 1px hairline, radius 6 — NOT the app's
 *                2.5px gradient stroke at radius 20, which docs/WEBSITE.md
 *                correctly identified as most of why the app reads as playful
 *   .cta         a solid rectangle, no gradient and no glow
 *   .site-header cream with a hairline, and the sunrise gradient reduced to a
 *                4px rule above it rather than a full banner
 *   .chip        an uppercase label, not a gradient pill
 *
 * ⚠️ **ONE PLACE KEEPS THE APP'S VOCABULARY, AND IT MATTERS.** `.phone` and
 * `.mock-*` DEPICT the app. A figure drawn in the site's corporate vocabulary
 * would be showing the reader a product that does not exist — so those keep the
 * 2.5px gradient stroke, the 20pt radius and the gradient chips, on the
 * --app-* tokens below. The page is corporate; the product on the page is not.
 *
 * Nothing keeps the shared half in sync. If an accent or a gradient changes in
 * Swift, change it here — drift there shows up as a site that looks
 * almost-but-not-quite like the app, which is worse than looking deliberately
 * different. Drift in the site's own half is not drift at all.
 *
 * NO THIRD-PARTY REQUESTS ANYWHERE. No analytics, no CDN fonts, no tag
 * manager. A site that makes no external requests sets no cookies, so it needs
 * no consent banner, and the privacy policy can say "no cookies, no trackers"
 * as a fact. For a product whose pitch is partly safety, not tracking its own
 * visitors is the cheapest credibility available — and one <script src> undoes
 * all of it.
 *
 * PACIFICO IS DELIBERATELY NOT LOADED, though the app bundles it. It has one
 * caller in the app (PlaydateWordmark), costs 141KB gzipped — more than the
 * rest of the site combined — to render a single word, and is itself a
 * placeholder ("a stand-in for the brand's licensed Candice"). When real brand
 * assets exist the wordmark should ship as an SVG: a logo is artwork, not
 * text, so it costs ~2KB, cannot flash unstyled, and carries no font licence.
 * See fonts/README.md.
 * ------------------------------------------------------------------------- */

/* WOFF2, subset to Latin — built by `scripts/site/build-fonts.sh` from the app's own TTFs
   (`DreamDate/Resources/Fonts/`), which the site's copies were byte-identical to. Roughly 18K a
   weight against the TTFs' 24K gzipped, and nothing the pages use was dropped.

   ⚠️ **Baloo 2 is now the HEADING face and Figtree the READING face** (owner, 2026-10-10). One
   rounded display face carried every job here, and at body sizes it read casual and dense — most
   of all in the small secondary copy inside cards. The app keeps Baloo 2 throughout; this is the
   marketing site's own vocabulary, which it owns (see the top of this file). */
@font-face {
  font-family: "Baloo 2";
  src: url("fonts/Baloo2-400.woff2") format("woff2");
  font-weight: 400;
  font-display: swap;
}
@font-face {
  font-family: "Baloo 2";
  src: url("fonts/Baloo2-500.woff2") format("woff2");
  font-weight: 500;
  font-display: swap;
}
@font-face {
  font-family: "Baloo 2";
  src: url("fonts/Baloo2-600.woff2") format("woff2");
  font-weight: 600;
  font-display: swap;
}
@font-face {
  font-family: "Baloo 2";
  src: url("fonts/Baloo2-700.woff2") format("woff2");
  font-weight: 700;
  font-display: swap;
}
@font-face {
  font-family: "Baloo 2";
  src: url("fonts/Baloo2-800.woff2") format("woff2");
  font-weight: 800;
  font-display: swap;
}
/* One variable file for every weight, so no weight the CSS asks for is ever synthesized. */
@font-face {
  font-family: "Figtree";
  src: url("fonts/Figtree-Variable.woff2") format("woff2");
  font-weight: 300 900;
  font-display: swap;
}

:root {
  /* Brand accents — constant across light and dark, exactly as in the app. */
  --cyan: #02fbfc;
  --green: #68fc7a;
  --yellow: #fdd15a;
  --pink: #f779a3;
  --coral: #fe7243;

  /* ⚠️ **The accent you may set TEXT in. --coral measures 2.57:1 on the cream
     and cannot carry a word of copy** — it is the app's own selected-tab colour
     and has the same problem there. This site leans much harder on solid colour
     than the gradient one did (links, headings, button fills), so it needs a
     coral that reads:

         #C6451A on cream (#FFF7EC)   4.63:1   AA
         #C6451A on white (#FFFFFF)   4.92:1   AA
         white   on #C6451A           4.92:1   AA

     One value therefore serves as both the link colour and the button fill.
     ⚠️ It does NOT clear AA on the deep-brown footer (3.55:1) — footer links use
     --on-deep-brown, which is 15.96:1. --coral survives for rules, borders and
     large display type only. */
  --coral-ink: #c6451a;

  /* Surfaces and ink.
     THE SITE COMMITS TO THE APP'S LIGHT APPEARANCE and does not follow
     prefers-color-scheme. The cream is what backs every screen in the app, so
     it is the product's page colour; taking the dark-mode surface for the
     whole site instead made it read as a dark product, which the app is not.
     The dark brown survives in exactly one place — the footer — where it works
     as a deliberate base rather than as the whole page. */
  --background: #fff7ec;
  --surface: #ffffff;
  --ink: #33231c;
  /* ⚠️ **Darkened from the app's #8A7466, which measures 4.14:1 on the cream and
     FAILS WCAG AA for body text.** It survives in the app because it labels
     short meta lines; this site leans on it for ledes and captions, where it is
     ordinary reading text. #7E6857 is the same warm brown two steps down and
     measures 4.94:1 on cream, 5.24:1 on white. */
  --ink-secondary: #7e6857;
  --hairline: rgba(51, 35, 28, 0.12);
  /* A visible line, for the structural rules a clean layout is built from —
     --hairline is for dividers INSIDE a component. */
  --rule-ink: rgba(51, 35, 28, 0.22);

  /* PlaydateTheme's dark-appearance pair, kept as named tokens because the
     footer uses them on purpose rather than as a fallback. */
  --deep-brown: #201813;
  --on-deep-brown: #fff3e2;

  /* The app's six gradients, kept in full even though this site now spends only
     two of them.
     ⚠️ **--grad-cta is no longer this site's button**, and the old comment here
     said it was "reserved for tappable things" — true of the app, and false here
     the moment the CTA became a solid rectangle. The set stays complete because
     `.phone` and `.mock-*` draw the app, and a figure that needs sherbet should
     find sherbet rather than inventing one. --grad-sunrise is the header rule. */
  --grad-cta: linear-gradient(135deg, var(--pink), var(--coral));
  --grad-splash: linear-gradient(90deg, var(--cyan), var(--green));
  --grad-tropic: linear-gradient(135deg, var(--cyan), var(--yellow));
  --grad-sherbet: linear-gradient(135deg, var(--pink), var(--yellow));
  --grad-blitz: linear-gradient(135deg, var(--pink), var(--green));
  /* ⚠️ **The site's sunrise is NOT the app's**, and this is the second deliberate
     colour divergence on this page (the first is --ink-secondary). The app ramps
     #FF3633 → #FF904D, and the designer's note was that the left end is "too
     saturated". Measured, that is exactly what it is: the left end is HSV
     saturation 0.80 against the right end's 0.70, so the band reads as a hot
     red cooling into orange rather than as one colour getting lighter.
     #E8654A is saturation 0.68 — level with the right end — so the ramp now
     varies in lightness alone. Ink on it improves to 5.20:1 (was 4.74:1), and
     the white wordmark sits at 3.29:1, still well clear of the 2.25:1 the RIGHT
     end of this same band has always given it. */
  --grad-sunrise: linear-gradient(90deg, #e8654a, #ff904d);

  /* Ink for anything sitting on a gradient. Always this, never --ink: the
     gradients are constant, so text on them must be constant too. Measured
     against the CTA gradient it is 6.69:1 / 6.27:1, where white is 2.56:1 /
     2.73:1 — see the note on .cta. */
  --on-gradient-ink: #2a1710;

  --font-body: "Figtree", system-ui, -apple-system, "Segoe UI", sans-serif;
  /* ⚠️ **`--font-display` is back, and the reason it was removed still holds.** It used to list
     the SYSTEM rounded face before "Baloo 2", so the one element meant to carry the brand was the
     one not set in it. Baloo 2 now comes first, and the token has a real job: headings, buttons,
     the nav and section labels. */
  --font-display: "Baloo 2", ui-rounded, "SF Pro Rounded", system-ui, -apple-system, sans-serif;

  /* PlaydateTheme.Spacing */
  --space-xs: 4px;
  --space-sm: 8px;
  --space-md: 16px;
  --space-lg: 24px;
  --space-xl: 32px;

  /* The site's own radii. Small and nearly square: a 20px card and a 28px pill
     are the single loudest signal that a page belongs to a consumer social app,
     and this one is read by people deciding whether to trust a business. */
  /* ⚠️ **Raised from 6/4/3 on the designer's note** ("CTA corners a little
     rounder", "Match Hero Corner Radius"). The original values were chosen to be
     "small and nearly square" because a 20px card and a 28px pill are a consumer
     social app's loudest signal — that reasoning still holds, which is why these
     are 12/8/4 and not the app's 20/28. The hero image and the cards share
     --radius-block on purpose: that IS the match the note asked for. */
  --radius-block: 12px;
  --radius-control: 8px;
  --radius-tag: 4px;

  /* PlaydateTheme.Radius — used ONLY where the page depicts the app. See the
     note at the top of this file: the mock must look like the product. */
  --app-radius-card: 20px;
  --app-radius-pill: 28px;

  --measure: 68ch;
  --gutter: clamp(20px, 5vw, 48px);
}

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
}

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

/* --- Layout -------------------------------------------------------------- */

.wrap {
  max-width: 1040px;
  margin: 0 auto;
  padding-inline: var(--gutter);
}

.prose {
  max-width: var(--measure);
}

section {
  padding-block: clamp(40px, 7vw, 80px);
}

/* --- Type ---------------------------------------------------------------- */

h1,
h2,
h3 {
  font-family: var(--font-display);
  line-height: 1.15;
  text-wrap: balance;
  margin: 0 0 0.4em;
  font-weight: 700;
}

h1 {
  font-size: clamp(2.1rem, 6vw, 3.4rem);
}
h2 {
  font-size: clamp(1.6rem, 4vw, 2.2rem);
}
h3 {
  font-size: 1.2rem;
}

/* `PlaydateWordmark` — cyan and pink offset copies behind a face, shipped as an
   SVG of Pacifico outlines rather than the 141K font. See
   `scripts/wordmark/build-wordmark-svg.py` and `fonts/README.md`, which called
   this shot before it was taken.

   ⚠️ **The face is white, so it only works on colour.** The header is the sunrise
   band and the footer is the deep brown; both are fine. Never put it on
   `--background` — an ink variant existed briefly, for the cream header the
   corporate pass introduced, and went with that header. */
.wordmark {
  display: inline-flex;
  align-items: center;
}
.wordmark img {
  height: 34px;
  width: auto;
  display: block;
}

/* PlaydateSectionLabel: headline weight, ink, left aligned — the app's way of
   opening a block of content. */
.section-label {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.05rem;
  margin-bottom: var(--space-md);
}

.lede {
  font-size: clamp(1.1rem, 2.4vw, 1.35rem);
  color: var(--ink-secondary);
}

/* The small label above a hero's heading: which audience the page is for, or —
   on the home page — where the product is live. Uppercase and tracked, in the one
   accent that clears AA on the cream. */
.kicker {
  font-size: 0.8rem;
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--coral-ink);
}

p,
li {
  max-width: var(--measure);
}

/* Body links carry the accent. ⚠️ --coral-ink, never --coral — see the token. */
a {
  color: var(--coral-ink);
  text-underline-offset: 0.18em;
  text-decoration-thickness: from-font;
}

/* ⚠️ The focus ring is --ink, not the accent. On the cream the accent ring is a
   soft orange halo that reads as a hover state; the point of a focus ring is
   that a keyboard user can find it at a glance. */
a:focus-visible,
button:focus-visible {
  outline: 3px solid var(--ink);
  outline-offset: 3px;
  border-radius: var(--radius-control);
}

/* --- Buttons ------------------------------------------------------------- */

/* ⚠️ **No longer PlaydateButtons.swift.** The app's primary is a gradient pill
   at radius 28 with a 0.35-opacity coral glow; all three of those are the
   playful register this site is deliberately out of. What is kept is the size
   and the weight, so the button still feels like the same product's button.

   The old file carried a long note about the app using a WHITE label on the CTA
   gradient at 2.56:1 and the site diverging to dark ink. That divergence is now
   moot here — there is no gradient button left — but the app's problem is real
   and unchanged, and the note belongs with it rather than being lost: see
   `PlaydateButtons.swift`. */
.cta,
.cta-secondary {
  display: inline-block;
  padding: 14px var(--space-lg);
  border-radius: var(--radius-control);
  font-family: var(--font-display);
  /* `PlaydateFont.button` is Baloo2-ExtraBold 18. Kept — the weight is the part
     that carries across, and a thin label in a solid rectangle reads as a form
     control rather than a call to action. */
  font-size: 1.0625rem;
  font-weight: 800;
  line-height: 1.2;
  text-decoration: none;
  border: 0;
  cursor: pointer;
  transition: background-image 0.12s ease-out, box-shadow 0.12s ease-out;
}

/* Primary: the app's own gradient button, back on the designer's note.
   ⚠️ **Dark ink on it, never white** — and this is the one place where copying
   the app exactly would be a bug. `PlaydateButtons.swift` puts a WHITE label on
   this gradient, which measures 2.56:1 and fails. --on-gradient-ink is 6.69:1
   on the same fill. The app's problem is real and unchanged; this is the site
   declining to inherit it.

   ⚠️ And no solid-colour fallback is possible here: the brand coral #FE7243 is
   2.57:1 on cream and 2.73:1 behind white, and EVERY warmer variant measured
   below the 4.5:1 floor too. The gradient with dark ink is what makes the brand
   colour usable on a button at all. */
.cta {
  background: var(--grad-cta);
  color: var(--on-gradient-ink);
}
.cta:hover {
  /* Darkened by a wash rather than a second gradient, so the ramp is identical
     and only the value moves. */
  background-image: linear-gradient(rgba(42, 23, 16, 0.14), rgba(42, 23, 16, 0.14)), var(--grad-cta);
}

/* Secondary: the same shape, drawn rather than filled, and now carrying the
   coral so the pair reads as one family. A 1px line, because a 2px outline
   beside a filled button reads as two primaries. */
.cta-secondary {
  background: transparent;
  color: var(--coral-ink);
  box-shadow: inset 0 0 0 1px rgba(198, 69, 26, 0.45);
}
.cta-secondary:hover {
  box-shadow: inset 0 0 0 1px var(--coral-ink);
}

/* ⚠️ No `transform: scale()` on press. The app scales its buttons because a
   touch target needs to acknowledge a finger; a pointer already has a hover
   state, and a page of rectangles that shrink when clicked reads as a toy. */

/* Buttons stack on a phone; inline-blocks in a <p> would only get leading. */
.cta-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-md);
  margin-block: var(--space-lg);
}

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

/* ⚠️ **This is the change.** The app outlines a card in a 2.5px GRADIENT stroke
   at radius 20 and cycles the gradient every fourth card so a feed is never
   monotone — and `docs/WEBSITE.md` was right that it is "most of why the app
   reads as playful rather than corporate". A grid of eight of them on a
   marketing page is eight competing colours around eight paragraphs of prose.

   What replaces it is a flat surface on a single hairline. The variety the
   cycling provided is not needed here: a marketing grid holds four things that
   are the SAME kind of thing, and colouring them differently implied a
   distinction that did not exist.

   ⚠️ **Partly reversed 2026-09-19, on the designer's note** ("Use Gradient as
   Box Border"). The reasoning above was about the APP's treatment — a 2.5px
   stroke that CYCLES through four gradients every fourth card — and that part
   stays rejected: a marketing grid holds four things that are the same kind of
   thing, and colouring them differently implies a distinction that is not
   there. What comes back is a single gradient, at 2px, on every card alike. So
   the brand is on the component and the grid still reads as one set.

   ⚠️ `--grad-tropic`, deliberately NOT `--grad-cta`. The CTA is now a
   pink-to-coral gradient button; putting the same ramp around a card would make
   every card look pressable. Cyan-to-yellow is cool, sits behind the coral
   rather than competing with it, and is still unmistakably the palette.

   The technique is two backgrounds and a transparent border — `padding-box` for
   the surface, `border-box` for the gradient — because a gradient cannot be a
   `border-color`. Both layers are needed: drop the first and the gradient fills
   the card. */
.card {
  border: 2px solid transparent;
  border-radius: var(--radius-block);
  background:
    linear-gradient(var(--surface), var(--surface)) padding-box,
    var(--grad-tropic) border-box;
  padding: var(--space-lg);
}

/* --- Card iconography ----------------------------------------------------- */

/* ⚠️ **Inline SVG, never an icon font or a sprite from a CDN.** The
   no-third-party-requests rule is what lets the privacy policy say "no cookies,
   no trackers" as a fact, and it is why these are drawn into each page rather
   than fetched. An external `<use href="...">` is same-origin and would work,
   but several browsers decline to render it, and a silently missing icon is
   worse than a repeated path.

   Top-aligned and centred per the designer's note. The icon is decoration — the
   heading under it already names the thing — so every one carries
   `aria-hidden="true"` and nothing is lost when it does not paint. */
.card-icon {
  display: block;
  width: 34px;
  height: 34px;
  margin: 0 auto var(--space-md);
  /* Line art, so it takes the ink colour and needs no fills. 1.6 is heavy
     enough to hold at 34px without reading as a bold glyph. */
  color: var(--coral-ink);
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* A card carrying an icon centres its heading with it; the body copy stays
   ranged left, because centred prose is hard to read at any length.
   `~` rather than `+`: on `/venues/` and `/guidebook/` a `.chip` label sits
   between the icon and the heading, so they are general siblings, not adjacent. */
.card-icon ~ h3 {
  text-align: center;
}

/* And the label between them centres too, or the stack steps left-right-left.
   A `.chip` is an inline-block, so it takes a width and centres on its margins
   rather than on `text-align`. */
.card-icon ~ .chip {
  display: block;
  width: fit-content;
  margin-inline: auto;
}

.card > :first-child {
  margin-top: 0;
}
.card > :last-child {
  margin-bottom: 0;
}

/* A card whose top edge carries an accent, for the one or two places on a page
   that genuinely rank or separate. ⚠️ Sparingly: if every card has one, it is
   the cycling gradient again in a thinner coat. */
.card-accent {
  border-top: 3px solid var(--coral);
}

.grid {
  display: grid;
  gap: var(--space-md);
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
}

/* ⚠️ **A four-card section lands 3 + 1, and the orphan reads as unfinished.** The
   wrap is 1040px, which fits exactly three 260px tracks, so every section with
   four cards — Safety, the Arcade, both on `/venues/` — left one card alone on
   its own row.

   Two-by-two fills, and at roughly 460px a card reads better than it would at
   the ~220px a fourth track would force. The alternative was narrowing the track
   minimum until four fit, which would have squeezed every OTHER section to pay
   for these.

   The rule keys off the content rather than a hand-applied class: `:has()` picks
   out a grid whose fourth child is also its last. Add a fifth card and it goes
   back to `auto-fit` on its own, which is the point — a class someone has to
   remember is a class someone will forget. Browsers without `:has()` get today's
   3 + 1 and nothing breaks. */
@media (min-width: 760px) {
  .grid:has(> :nth-child(4):last-child) {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

/* --- Labels and tags ------------------------------------------------------ */

/* ⚠️ **Not the app's gradient chip.** A capsule of pink-to-coral with rounded
   ends is a category tag in a feed; on a page of prose it reads as a button
   nobody can press. This is a label: uppercase, tracked, small, on a tint.

   The app's chip survives on `.mock-card`, where it is depicting a feed.

   ⚠️ **The ink is --coral-ink moved 15% toward --ink, not --coral-ink itself.**
   On its own tint over the cream, --coral-ink measured 4.10:1 — under the 4.5
   that 0.72rem text needs, on every Atlas and Marquee label since 2026-09-18.
   Found 2026-10-11 (PR 4) by computing the pair rather than assuming the
   accent's 4.63:1 on bare cream carried over to a tinted chip. #b0401a is
   4.87:1 on the cream chip and 5.14:1 on a white one. */
.chip {
  display: inline-block;
  padding: 4px 8px;
  border-radius: var(--radius-tag);
  background: rgba(198, 69, 26, 0.09);
  color: #b0401a;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  line-height: 1.4;
}

/* The neutral variant, for a label that is classifying rather than accenting —
   most labels on most pages.

   ⚠️ **Not --ink-secondary, which measured 4.41:1 on this chip over the cream** —
   under the 4.5 that 0.72rem text needs (2026-10-11, PR 4, when the audience
   pages put eighteen of these on cream). #766151 is ink-secondary moved 10%
   toward --ink: 4.91:1 on cream, 5.22:1 on white. */
.chip-muted {
  background: rgba(51, 35, 28, 0.06);
  color: #766151;
}

.chip-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-sm);
  margin-block: var(--space-md);
  padding: 0;
  list-style: none;
}
.chip-row li {
  max-width: none;
}

/* --- Hero and rules -------------------------------------------------------- */

/* ⚠️ **The sunrise gradient is now a 4px rule at the very top of the page, not a
   banner.** It used to be the whole header, which is what the app does at the
   top of a screen — and on a 12-page site that meant every page opened with an
   orange slab. As a rule it still says whose page this is, in the one place a
   reader looks first, and it stops competing with the words underneath it.

   The `hero-*` variants stay as no-ops so each page still declares which accent
   it belongs to, and so restoring per-page colour is one rule rather than an
   edit to twelve files. */
.hero {
  /* ⚠️ Raised on the designer's note ("A little more padding in Content"). The
     hero is the one block on the page that is mostly air, and it was carrying
     the same rhythm as an ordinary section. */
  padding-block: clamp(56px, 9vw, 96px) clamp(48px, 8vw, 84px);
}

/* The hero's text column. Its children were spaced by their own margins, which
   is why the headline, the lede, the denial and the buttons all sat at slightly
   different intervals; one gap sets the rhythm instead. */
.hero-copy {
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
}
.hero-copy > * {
  margin-block: 0;
}

.hero-layout {
  display: grid;
  gap: var(--space-xl);
  align-items: center;
  grid-template-columns: 1fr;
}
@media (min-width: 880px) {
  /* ⚠️ **Top-aligned, not centred, beside the screenshot** (owner, 2026-10-10).
     The capture is ~690px tall, so centring the copy against it started the
     headline a third of the way down and pushed the App Preview line to the
     fold. The text starts just below the image's top edge instead. */
  .hero-layout {
    grid-template-columns: 1.15fr 0.85fr;
    align-items: start;
  }
  .hero-layout .hero-copy {
    padding-top: var(--space-lg);
  }
}

/* --- Inner-page header ------------------------------------------------------ */

/* Every audience page opens the same way (2026-10-10): a kicker naming who the
   page is for, the heading, the lede, a button where there is one to press, links
   to the page's own sections, and — beside it — a picture of the thing or, on
   /support/, the two things to know before emailing. A row of facts closes it.

   ⚠️ It replaced a bare heading, one paragraph and a rule, identical on four
   pages, which was most of why the inner pages read as unfinished: nothing said
   which page you were on, and nothing gave you a reason to scroll. */
.page-hero-layout {
  display: grid;
  gap: var(--space-xl);
  grid-template-columns: 1fr;
  align-items: center;
}
@media (min-width: 880px) {
  .page-hero-layout {
    grid-template-columns: 1.2fr 0.8fr;
  }
}
.page-hero .page-index {
  margin-top: 0;
}
.page-hero-layout > .shot {
  justify-self: center;
  max-width: 420px;
}

/* A full-screen capture, cropped to its top for a header. ⚠️ `object-fit`, never a
   re-cut file: the frame stays the one `build-shots.swift` made, and the crop is
   chosen per use — here, the top of the form rather than the date further down. */
.shot-crop {
  aspect-ratio: 4 / 5;
  object-fit: cover;
  object-position: top;
}
.page-hero-layout > .shot-crop {
  max-width: 320px;
}

.page-hero-panel {
  display: grid;
  gap: var(--space-lg);
  padding: var(--space-lg);
  background: var(--surface);
  border: 1px solid var(--rule-ink);
  border-radius: var(--radius-block);
}
.page-hero-panel h2 {
  font-size: 1.15rem;
  margin-bottom: 6px;
}
.page-hero-panel p {
  margin: 0;
  color: var(--ink-secondary);
}
.page-hero-panel strong {
  color: var(--ink);
}
.page-hero-panel > div + div {
  border-top: 1px solid var(--hairline);
  padding-top: var(--space-lg);
}

/* Four short facts in a ruled row — the page's claims at a glance, each one a
   section further down. Two-by-two on a phone. */
.facts {
  list-style: none;
  margin: clamp(32px, 5vw, 56px) 0 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  border-top: 1px solid var(--rule-ink);
}
.facts li {
  max-width: none;
  padding: var(--space-md) var(--space-md) 0 0;
  color: var(--ink-secondary);
  font-size: 0.95rem;
  line-height: 1.4;
}
.facts strong {
  display: block;
  font-family: var(--font-display);
  font-size: 1.1rem;
  font-weight: 700;
  color: var(--ink);
}
@media (max-width: 759.98px) {
  .facts li:nth-child(even) {
    padding-left: var(--space-md);
    border-left: 1px solid var(--hairline);
  }
  .facts li:nth-child(n + 3) {
    margin-top: var(--space-md);
    border-top: 1px solid var(--hairline);
  }
}
@media (min-width: 760px) {
  .facts {
    grid-template-columns: repeat(4, minmax(0, 1fr));
  }
  .facts li + li {
    padding-left: var(--space-md);
    border-left: 1px solid var(--hairline);
  }
}

/* ⚠️ A 1px line, not the 4px gradient pill this used to be. Structure is what
   separates sections on this site; colour is spent on the header rule, the
   buttons and the labels, and a divider is not one of those places. */
.rule {
  height: 1px;
  border: 0;
  background: var(--rule-ink);
  margin-block: var(--space-xl);
}

/* --- The phone mock, removed 2026-09-18 ----------------------------------- */

/* `.phone`, `.mock-card`, `.mock-media`, `.mock-body`, `.mock-title`, `.mock-meta`,
   `.mock-row`, `.chip-app` and `.chip-flair` lived here and just above the hero
   rules. They drew the app's Explore card in HTML from these tokens; the home
   hero was their only user, and the owner replaced it with a real capture — so
   all of it became dead.

   ⚠️ **Keep the lesson, because it cost an afternoon once.** Those rules existed
   to hold the APP's vocabulary — the 2.5px gradient stroke, the 20pt radius, the
   gradient chip — inside a file that had deliberately stopped using it. Restyling
   `.chip` globally silently restyled the chip inside the mock too, so the hero
   drew a feed card with a small-caps tag: a screen the app does not render. If a
   drawn figure of the product ever comes back, it needs its own class namespace
   again for exactly that reason.

   ⚠️ **And the mock's whole argument — that a reconstruction cannot go stale —
   was not true in practice.** Its `aria-label` announced "6.2 kilometres" while
   the card it described read "3.9 mi". A drawing drifts too; it just drifts
   quietly, because nothing re-renders it against the thing it depicts. That is
   the case for `.shot` over `.mock-*`, and it is why every `.shot` is listed in
   `scripts/site/build-shots.swift` with a note saying what it must still show. */

/* --- Site chrome ----------------------------------------------------------- */

/* **The sunrise band, matching the web client's header** (`web/src/ui/Shell.tsx`),
   down to the same `--grad-sunrise`. The corporate pass had moved the header to
   cream with the gradient reduced to a 4px rule; the owner asked for the band
   back, so the two surfaces now open identically.

   ⚠️ **The colour changed; the vocabulary did not.** Everything below the header
   stays flat cards, hairlines and solid rectangles — the band is the brand
   announcing itself once, at the top, which is what it does in the app. It is
   not licence for the gradient card strokes to come back.

   ⚠️ **Nav text is `--on-gradient-ink`, and WHITE WOULD FAIL.** Measured across
   the band: ink is 4.74:1 at the red end rising to 7.61:1 at the orange, while
   white is 3.60:1 falling to 2.25:1 — under AA at every point, and under the
   3:1 large-text floor across most of it. The wordmark is white because it is
   artwork carrying an `aria-label`, not text. */
.site-header {
  background: var(--grad-sunrise);
  color: var(--on-gradient-ink);
}
.site-header a {
  color: var(--on-gradient-ink);
}

/* The links live in their own flex group so they wrap as a unit. Previously
   `margin-left:auto` on a spacer plus flex-wrap sent the first link to the far
   right and dropped the rest onto a second line. */
.nav {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-sm) var(--space-lg);
  padding-block: var(--space-md);
}
.nav-links {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-sm) var(--space-lg);
}
.nav a {
  font-family: var(--font-display);
  text-decoration: none;
  font-weight: 600;
  padding-block: 6px;
  /* Reserved whether or not the tab is current, so nothing shifts by 2px when
     you land on the page you just clicked. */
  border-bottom: 2px solid transparent;
}
/* ⚠️ Not `--rule-ink`, which is an alpha black tuned for the cream page and all
   but disappears on the band. A translucent wash of the header's own ink is the
   same idea at a strength that reads here. */
.nav a:hover {
  border-bottom-color: rgba(42, 23, 16, 0.4);
}

/* ⚠️ **The current tab is marked by `aria-current="page"`, and the CSS follows
   the attribute rather than a class.** There is no JavaScript on this site, so
   the state is authored into each page by the shell generator — and hanging the
   style off the same attribute a screen reader uses means the two cannot
   disagree, which is the usual way a "you are here" marker goes wrong.

   ⚠️ Marked by the UNDERLINE, not by a colour change. On the cream header this
   was `--coral-ink`; on the sunrise band a dark orange on orange is the one
   accent that cannot be seen. So the only ink on this bar is the one measured
   to work on it, and "current" is the rule being solid rather than transparent. */
.nav a[aria-current="page"] {
  border-bottom-color: var(--on-gradient-ink);
  border-bottom-width: 3px;
}

/* The header's one button: the App Preview. Deep brown on the band, which is the
   only fill that reads as a button there — the CTA gradient is the band's own
   colour, and --on-deep-brown on it measures 15.96:1.

   ⚠️ **Shown from 1000px.** It is the last item in `.nav-links`. With five links
   the row fits from 950px with the button and from 740px without it (measured
   2026-10-11, PR 4: each width rendered in an iframe, a link counted as on the
   row when its centre sits inside the wordmark's box). Between 760 and 1000 the
   links stay and the button goes — every page's hero carries the same link — and
   below 760 the menu has its own copy. It was 900 with four links; "For members"
   added 99px. */
.nav a.nav-cta {
  background: var(--deep-brown);
  color: var(--on-deep-brown);
  padding: 8px 16px;
  border-bottom: 0;
  border-radius: var(--radius-control);
  font-weight: 700;
  font-size: 0.95rem;
  line-height: 1.2;
}
.nav a.nav-cta:hover {
  background: #3d2e25;
}
@media (max-width: 999.98px) {
  .nav a.nav-cta {
    display: none;
  }
}

/* --- The narrow-screen menu -------------------------------------------------

   ⚠️ **No JavaScript, so it is a `<details>` disclosure.** It opens and closes on
   its own, works from the keyboard, and reports expanded/collapsed to a screen
   reader. The shell renders the links twice (`.nav-links` inline, `.nav-menu`
   collapsed) and exactly one is displayed; `display: none` also takes the other
   out of the accessibility tree.

   **The breakpoint is where the inline row stops fitting:** the wordmark is
   146px wide and the five links 99 + 70 + 82 + 82 + 61px plus four gaps, so the
   row wraps below 740px (measured — see .nav-cta). Below 760px the links fold
   into the menu rather than wrapping onto a ragged second line under the logo.
   It was 680 with four links. Add a sixth and re-measure.

   Following a link loads a new page, which closes the menu; tapping the button
   again closes it in place. Nothing else needs to. */
.nav-menu {
  display: none;
}
@media (max-width: 759.98px) {
  .nav-links {
    display: none;
  }
  .nav-menu {
    display: block;
  }
  /* The header is the containing block, so the open panel spans the full width
     of the band instead of the button's own width. */
  .site-header {
    position: relative;
  }
  .nav-menu summary {
    list-style: none;
    cursor: pointer;
    display: grid;
    place-items: center;
    width: 44px;
    height: 44px;
    margin-right: -10px;
    border-radius: var(--radius-control);
    color: var(--on-gradient-ink);
  }
  .nav-menu summary::-webkit-details-marker {
    display: none;
  }
  .nav-menu summary:focus-visible {
    outline: 3px solid var(--on-gradient-ink);
    outline-offset: 0;
  }
  .nav-menu svg {
    width: 26px;
    height: 26px;
    fill: none;
    stroke: currentColor;
    stroke-width: 2.25;
    stroke-linecap: round;
  }
  .nav-menu-close,
  .nav-menu[open] .nav-menu-open {
    display: none;
  }
  .nav-menu[open] .nav-menu-close {
    display: block;
  }
  /* The panel continues the band below the bar, so it reads as the header
     opening rather than a card floating over the page. Ink on the band is the
     one pairing measured to pass (see .site-header). */
  .nav-menu-list {
    position: absolute;
    left: 0;
    right: 0;
    top: 100%;
    z-index: 10;
    margin: 0;
    padding: var(--space-xs) var(--gutter) var(--space-md);
    list-style: none;
    background: var(--grad-sunrise);
    border-top: 1px solid rgba(42, 23, 16, 0.15);
    box-shadow: 0 12px 24px rgba(32, 24, 19, 0.18);
  }
  .nav-menu-list li {
    max-width: none;
  }
  .nav-menu-list li + li {
    border-top: 1px solid rgba(42, 23, 16, 0.12);
  }
  /* 48px rows: a comfortable thumb target, well above the 44px minimum. */
  .nav-menu-list a {
    display: flex;
    align-items: center;
    min-height: 48px;
    padding-block: 0;
    font-size: 1.1rem;
    border-bottom: 0;
  }
  /* "You are here" is still keyed to aria-current, as on the inline row; in a
     vertical list a left rule reads where an underline would not. */
  .nav-menu-list a[aria-current="page"] {
    border-bottom: 0;
    font-weight: 700;
    box-shadow: inset 3px 0 0 var(--on-gradient-ink);
    padding-left: var(--space-sm);
  }
  /* The App Preview as the menu's last row, drawn as the header button is. */
  .nav-menu-list li.nav-menu-cta {
    border-top: 0;
    padding-top: var(--space-sm);
  }
  .nav-menu-list .nav-menu-cta a {
    justify-content: center;
    background: var(--deep-brown);
    color: var(--on-deep-brown);
    border-radius: var(--radius-control);
  }
}

/* The dark brown, used deliberately as a base to close the page rather than as
   the page itself. */
.site-footer {
  background: var(--deep-brown);
  color: var(--on-deep-brown);
  padding-block: var(--space-xl);
  font-size: 0.95rem;
}
/* ⚠️ --on-deep-brown (15.96:1), never --coral-ink, which is 3.55:1 here and is
   the one surface on the site where the accent cannot carry text. */
.site-footer a {
  color: var(--on-deep-brown);
}
.footer-grid {
  display: grid;
  gap: var(--space-lg);
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  margin-bottom: var(--space-lg);
}
.footer-grid h2 {
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  opacity: 0.66;
  margin-bottom: var(--space-sm);
}
.footer-list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: grid;
  gap: 6px;
}
.footer-list li {
  max-width: none;
}
.footer-fine {
  border-top: 1px solid rgba(255, 243, 226, 0.16);
  padding-top: var(--space-md);
  opacity: 0.72;
}

/* --- Social links ---------------------------------------------------------- */

/* ⚠️ **Inline SVG, never an icon font or a CDN sprite.** The no-third-party-
   requests rule is what lets the privacy policy say "no cookies, no trackers"
   as a fact rather than an intention, and a single icon CDN would end that. */
.social-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-sm);
  list-style: none;
  padding: 0;
  margin: 0;
}
.social-row li {
  max-width: none;
}
.social-row a {
  display: grid;
  place-items: center;
  width: 38px;
  height: 38px;
  border-radius: var(--radius-control);
  border: 1px solid rgba(255, 243, 226, 0.24);
}
.social-row a:hover {
  border-color: var(--on-deep-brown);
}
.social-row svg {
  width: 18px;
  height: 18px;
  fill: currentColor;
  display: block;
}

/* --- Getting the app ------------------------------------------------------- */

/* ⚠️ **A destination that does not exist yet renders as a STATEMENT, not a
   link.** The App Store listing is unlisted — deliberately, documented in
   `docs/BRANDING_GOLIVE.md`. A button pointing at it returns a 200 with a page
   saying the thing is unavailable, which means a link checker passes on it and a
   reader does not. `scripts/site/build-shell.py` holds one constant per
   destination; empty renders `.store-badge.is-pending`.

   ⚠️ **Not the official badges, and not shaped like them.** Apple's and Google's
   badges are artwork with usage rules, and Apple's may only link to a live
   product page. A phone outline and "Coming soon" is what is true today; the
   official art replaces the live state when a listing is public. */
.store-badges {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-sm);
  list-style: none;
  margin: 0;
  padding: 0;
}
.store-badges li {
  max-width: none;
}
.store-badge {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 8px 16px 8px 12px;
  border-radius: var(--radius-control);
  border: 1px solid var(--deep-brown);
  background: var(--deep-brown);
  color: var(--on-deep-brown);
  text-decoration: none;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.05rem;
  line-height: 1.1;
}
.store-badge small {
  display: block;
  font-family: var(--font-body);
  font-weight: 500;
  font-size: 0.74rem;
  opacity: 0.8;
}
.store-badge svg {
  width: 22px;
  height: 22px;
  flex: none;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linecap: round;
}
/* Drawn rather than filled, in the secondary ink: present, but plainly not a
   button. --ink-secondary is 4.94:1 on the cream. */
.store-badge.is-pending {
  background: transparent;
  border-color: var(--rule-ink);
  color: var(--ink-secondary);
}
.store-badge.is-pending small {
  opacity: 1;
}

/* The "get the app" block: the App Preview, which works today, beside the apps,
   which do not yet — one panel with two halves. It replaced three boxes, two of
   which said "not available" in the most prominent place on the page. */
.get-app {
  display: grid;
  grid-template-columns: 1fr;
  margin-block: var(--space-lg);
  background: var(--surface);
  border: 1px solid var(--rule-ink);
  border-radius: var(--radius-block);
}
.get-app-half {
  padding: var(--space-lg);
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-sm);
}
.get-app-half > * {
  margin: 0;
}
.get-app-half p {
  color: var(--ink-secondary);
}
.get-app-half h3 {
  font-size: 1.3rem;
}
.get-app-half .cta {
  margin-top: var(--space-sm);
}
.get-app-half .store-badges {
  margin-top: var(--space-sm);
}
.get-app-half + .get-app-half {
  border-top: 1px solid var(--hairline);
}
@media (min-width: 760px) {
  .get-app {
    grid-template-columns: 1fr 1fr;
  }
  .get-app-half + .get-app-half {
    border-top: 0;
    border-left: 1px solid var(--hairline);
  }
}

/* The line under a hero's buttons that says what the App Preview is — on a phone
   it opens; on a computer it shows a code to scan. Pulled up to the buttons it
   explains, so it reads as their caption rather than as a paragraph. */
.hero-note {
  font-size: 0.95rem;
  color: var(--ink-secondary);
}
.hero-copy > .cta-row + .hero-note {
  margin-top: calc(var(--space-sm) * -1);
}

/* --- Long-form legal ----------------------------------------------------- */

.legal {
  max-width: 62ch;
}
.legal h2 {
  font-size: 1.35rem;
  margin-top: 2.2em;
}
.legal h3 {
  font-size: 1.05rem;
  margin-top: 1.6em;
}
.legal .updated {
  color: var(--ink-secondary);
  font-size: 0.95rem;
}

/* Inline code — a calendar feed's address, an HTML tag, a user agent. Baloo 2 is
   a rounded display face and sets a `<link rel=…>` snippet illegibly, so this is
   the one place on the site that leaves the brand face.
   ⚠️ A SYSTEM mono stack, not a web font: adding a fourth family to carry six
   words would cost more than the rest of the stylesheet, and the no-third-party
   rule means it could not come from a CDN anyway. */
code {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.86em;
  background: rgba(51, 35, 28, 0.06);
  border-radius: var(--radius-tag);
  padding: 0.12em 0.36em;
  /* Long enough to overflow a 375px column otherwise — an `.ics` URL in a list
     item is exactly the content that makes a phone scroll sideways. */
  overflow-wrap: anywhere;
}

/* --- The Guidebook's section index ---------------------------------------- */

/* ⚠️ **Deliberately absent until there were four sections.** Two entries read as
   a page that expected to be longer, which is why `docs/WEBSITE.md` recorded the
   third section as the trigger rather than adding one up front.

   A `<nav>` of plain anchors — no JavaScript, and no scroll-spy, because the
   page has no script and highlighting the current section would need one. */
.page-index {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-sm) var(--space-md);
  list-style: none;
  margin: var(--space-lg) 0 0;
  padding: 0;
}
.page-index li {
  max-width: none;
}
.page-index a {
  display: inline-block;
  padding: 6px 14px;
  border: 1px solid var(--rule-ink);
  border-radius: var(--radius-control);
  color: var(--ink);
  font-weight: 700;
  font-size: 0.95rem;
  text-decoration: none;
}
.page-index a:hover,
.page-index a:focus-visible {
  border-color: var(--coral-ink);
  color: var(--coral-ink);
}

/* --- Product screenshots -------------------------------------------------- */

/* ⚠️ **These are real captures, and `.phone` / `.mock-*` are not** — the two must
   not be confused. The mock is a reconstruction drawn from the design tokens: it
   cannot go stale, and it keeps the APP's vocabulary on purpose. A `.shot` is a
   photograph of the shipping app on a real device. It is the more persuasive of
   the two and the one that CAN go stale, so every one of them is listed in
   `scripts/site/build-shots.swift` with a note saying what it is there to show.

   The frame is the site's own vocabulary, not the app's — a hairline and a 6px
   radius, no phone bezel. A bezel around a CROP would be a lie about what was
   captured, and the full-screen frames do not need one to read as a phone. */
.shot {
  display: block;
  width: 100%;
  height: auto;
  border: 1px solid var(--hairline);
  border-radius: var(--radius-block);
  /* The app's page colour, so a JPEG's edge blends into the frame instead of
     showing a seam against the cream. */
  background: var(--background);
}

.figure {
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
}

/* Captions carry the claim the picture is evidence for, so they are read, not
   skimmed past — full measure, and the same size as body text rather than the
   shrunk-down grey a caption usually gets. */
.figure figcaption {
  color: var(--ink-secondary);
  font-size: 0.95rem;
  line-height: 1.5;
  max-width: var(--measure);
}
.figure figcaption strong {
  color: var(--ink);
}

/* A row of figures. `auto-fit` rather than a fixed count: the three feed cards
   want three across and the two full-screen frames want two, and neither needs a
   class to say so. ⚠️ `align-items: start` matters — the crops are deliberately
   different heights (an Atlas card is shorter than an Outing card, because it
   has no time row), and stretching them would distort the screenshots. */
.shot-row {
  display: grid;
  gap: var(--space-lg);
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  align-items: start;
  margin-block: var(--space-lg);
}

/* The full-screen frames are 620x1347. At three-across they would be postage
   stamps, so they cap out at a phone's worth of width and centre in their
   track. */
.shot-tall {
  max-width: 300px;
  margin-inline: auto;
}

/* The hero frame. ⚠️ **It needs a cap or it fills the hero column**, which at the
   1.15fr / 0.85fr split is ~420px — and a full phone screen 420px wide is 913px
   tall, so the headline beside it would sit alone at the top of a very long row.
   320px puts it at ~693px, about the height of the copy it sits next to. It is
   also the one image on the site that is NOT lazy-loaded: it is above the fold,
   so it carries `loading="eager"` and `fetchpriority="high"`. */
.shot-hero {
  max-width: 320px;
  margin-inline: auto;
}

/* Wide content scrolls inside its own box; the page body never scrolls
   sideways on a phone. */
.scroll-x {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}

table {
  border-collapse: collapse;
  width: 100%;
}
th,
td {
  text-align: left;
  padding: 10px 14px;
  border-bottom: 1px solid var(--hairline);
  vertical-align: top;
}
th {
  font-weight: 700;
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* --- The home page — site refresh PR 3, 2026-10-10 ------------------------

   The mockup's structure (the "Wanderist Site Refresh" review): the sunrise band
   carried down into the hero, a ruled row of facts, numbered steps, two
   image/text splits, one dark band for safety, two audience cards and a closing
   band. Seven shapes where there was one — the page used to be 24 identical
   cards, which is most of why it read as a template.

   The long explanations those cards carried moved to the Guidebook verbatim
   (D6); what is here is a headline, a sentence or two and a link. */

:root {
  /* How far the hero capture hangs below the band, into the page. The section
     after the hero pads by the same amount, so nothing ever sits under it. */
  --hero-hang: clamp(48px, 7vw, 80px);
  /* Secondary text on the deep brown. --on-deep-brown at 72% over #201813
     composites to #C1B6A8: 8.76:1. */
  --on-deep-brown-2: rgba(255, 243, 226, 0.72);
}

/* ⚠️ **D2 (owner, 2026-10-10): on the home page the gradient fills the hero, with a
   dividing line between the nav and the hero.** It reverses "the gradient banner
   appears once" for this page only — inner pages keep the bar. The header is
   generated and identical everywhere, so the band is the hero's own background:
   the same --grad-sunrise at the same angle across the same width, so header and
   hero meet with no seam except the line drawn on purpose.

   ⚠️ Every word on it is --on-gradient-ink, as on the header — 5.20:1 at the left
   end, 7.61:1 at the right. Nothing here may use --ink-secondary or --coral-ink:
   both are tuned for the cream and fail on orange. */
.hero-band {
  background: var(--grad-sunrise);
  color: var(--on-gradient-ink);
  padding-block: 0;
}
/* The divider runs along the content edge rather than the window's, so it reads
   as part of the layout and not as the header's bottom border. A wash of the
   band's own ink, like the menu panel's rules. */
.hero-band .hero-layout {
  border-top: 1px solid rgba(42, 23, 16, 0.2);
  padding-top: clamp(36px, 6vw, 64px);
}
.hero-band .hero-copy {
  padding-bottom: clamp(40px, 7vw, 72px);
}
@media (min-width: 880px) {
  .hero-band .hero-layout .hero-copy {
    padding-top: 0;
  }
}
/* The kicker becomes a tag on the band: a translucent cream lozenge, which lifts
   the ink to 9.79:1 at its worst and stops the label reading as one more line of
   copy. */
.hero-band .kicker {
  align-self: flex-start;
  color: var(--on-gradient-ink);
  background: rgba(255, 247, 236, 0.55);
  padding: 5px 12px;
  border-radius: var(--radius-control);
}
.hero-band .lede,
.hero-band .hero-note {
  color: var(--on-gradient-ink);
}
/* ⚠️ **The primary is deep brown here, not the CTA gradient.** Pink-to-coral on a
   red-to-orange band is a gradient button on a gradient, and it all but
   disappears — the same reason the header's App Preview button is deep brown.
   --on-deep-brown on it is 15.96:1. Everywhere else the CTA stays the gradient. */
.hero-band .cta {
  background: var(--deep-brown);
  color: var(--on-deep-brown);
}
.hero-band .cta:hover {
  background: #3d2e25;
}
/* ⚠️ A SOLID ink outline. A translucent one (55%) measured 2.50:1 against the red
   end — under the 3:1 a control's boundary needs (WCAG 1.4.11). Solid is 5.20:1. */
.hero-band .cta-secondary {
  color: var(--on-gradient-ink);
  box-shadow: inset 0 0 0 1.5px var(--on-gradient-ink);
}
.hero-band .cta-secondary:hover {
  background: rgba(255, 247, 236, 0.22);
}
.hero-band .store-badge.is-pending {
  color: var(--on-gradient-ink);
  border-color: rgba(42, 23, 16, 0.4);
}
.hero-band a:focus-visible {
  outline-color: var(--on-gradient-ink);
}
/* The capture hangs over the band's bottom edge into the page — the one bold
   moment, as the mockup had it. Positioned so it paints above the next section.

   ⚠️ **Bottom-aligned, so the hang is always exactly --hero-hang.** Top-aligned,
   the copy column set the band's height and the picture hung by whatever was left
   over — 61px at 1280 wide against the 104 the next section padded for, which
   left an 80px hole under it. */
.hero-band .shot-hero {
  align-self: end;
  position: relative;
  z-index: 1;
  margin-bottom: calc(-1 * var(--hero-hang));
  border-color: rgba(42, 23, 16, 0.18);
  box-shadow: 0 32px 60px -28px rgba(42, 23, 16, 0.55);
}

.facts-band {
  padding-top: calc(var(--hero-hang) + clamp(24px, 4vw, 40px));
  padding-bottom: clamp(24px, 4vw, 40px);
}
.facts-band .facts {
  margin-top: 0;
}

/* A section's kicker and heading, spaced as one unit. */
.sec-head {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
  margin-bottom: clamp(24px, 4vw, 40px);
}
.sec-head > * {
  margin: 0;
}

/* A white band, so the cream plates inside it read as panels. */
.band-light {
  background: var(--surface);
}

/* --- Numbered steps --- */
.steps {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: clamp(28px, 4vw, 40px);
  grid-template-columns: 1fr;
}
@media (min-width: 760px) {
  .steps {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}
.step {
  max-width: none;
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
}
.step > * {
  margin: 0;
}
.step h3 {
  display: flex;
  align-items: center;
  gap: 12px;
  font-size: 1.3rem;
}
.step p {
  color: var(--ink-secondary);
}
/* The number is the ordered list's own, drawn; `aria-hidden` because the <ol>
   already says "1 of 3". */
.step-n {
  flex: none;
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  background: var(--deep-brown);
  color: var(--on-deep-brown);
  font-size: 1rem;
  font-weight: 800;
}

/* ⚠️ **A plate, so crops of different shapes sit in frames of one shape.** The
   three step pictures are 600×638, 600×487 and 600×691 — an Outing card, the
   foot of its page, the middle of it — and laid out at their own heights the
   row's text would start at three different lines. Each is CONTAINED in a square
   of the app's cream; the captures' own background is exactly #FFF7EC (sampled),
   so the picture's edge disappears into the plate and no seam shows. ⚠️ No
   `.shot` border on these: on a contained image the border would outline the
   plate, not the picture. */
.plate {
  position: relative;
  aspect-ratio: 1 / 1;
  background: var(--background);
  border-radius: var(--radius-block);
}
.plate img {
  position: absolute;
  inset: var(--space-md);
  width: calc(100% - 2 * var(--space-md));
  height: calc(100% - 2 * var(--space-md));
  object-fit: contain;
}

/* --- Image and text, side by side --- */
.split {
  display: grid;
  gap: clamp(28px, 6vw, 72px);
  grid-template-columns: 1fr;
  align-items: center;
}
@media (min-width: 760px) {
  .split {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  }
  /* The picture leads on the first split and follows on the second, so the two
     read as a pair rather than as one block twice. On a phone the words come
     first in both. */
  .split:not(.split-flip) > .split-art {
    order: -1;
  }
}
.split-copy {
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
}
.split-copy > * {
  margin: 0;
}
.split-art > .shot {
  max-width: 340px;
  margin-inline: auto;
}
/* Two splits in a row share the space between them rather than doubling it. */
.split-section + .split-section {
  padding-top: 0;
}

/* The three kinds, each a label and a line — the old three-card summary, as a
   list. */
.kinds {
  display: grid;
  margin: 0;
}
.kinds > div {
  display: grid;
  grid-template-columns: 6.5rem minmax(0, 1fr);
  gap: var(--space-md);
  align-items: baseline;
  padding-block: 12px;
  border-top: 1px solid var(--hairline);
}
.kinds > div:last-child {
  border-bottom: 1px solid var(--hairline);
}
.kinds dt,
.kinds dd {
  margin: 0;
}
.kinds dd {
  color: var(--ink-secondary);
}
.kinds dd strong {
  color: var(--ink);
}

/* --- The dark band: safety --- */

/* The deep brown, which until now appeared only in the footer. It sets the
   section apart without a card or a colour, and the tools sit on rules rather
   than in boxes. ⚠️ --coral-ink is 3.55:1 here and cannot carry text, so the
   accent on this band is --yellow (12.02:1). */
.band-dark {
  background: var(--deep-brown);
  color: var(--on-deep-brown);
}
.band-dark .kicker,
.band-dark a {
  color: var(--yellow);
}
.band-dark .lede {
  color: var(--on-deep-brown-2);
}
.band-dark strong {
  color: var(--on-deep-brown);
}
.band-dark a:focus-visible {
  outline-color: var(--yellow);
}
.tools {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 0 var(--space-lg);
  grid-template-columns: 1fr;
}
@media (min-width: 560px) {
  .tools {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}
@media (min-width: 960px) {
  .tools {
    grid-template-columns: repeat(4, minmax(0, 1fr));
  }
}
.tools li {
  max-width: none;
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
  padding-block: var(--space-lg) var(--space-md);
  border-top: 1px solid rgba(255, 243, 226, 0.2);
}
.tools li > * {
  margin: 0;
}
.tools h3 {
  font-size: 1.15rem;
}
.tools p {
  color: var(--on-deep-brown-2);
  font-size: 0.98rem;
}
/* The same line icons as the cards, in the band's accent. */
.tools svg {
  width: 30px;
  height: 30px;
  color: var(--yellow);
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.band-foot {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--space-sm) var(--space-lg);
  margin-top: var(--space-lg);
  color: var(--on-deep-brown-2);
}
.band-foot > * {
  margin: 0;
}

/* --- Audience cards --- */
.aud {
  display: grid;
  gap: var(--space-lg);
  grid-template-columns: 1fr;
}
/* Three cards — members, hosts, venues — so three columns or one: two columns
   would leave the third alone on a row. */
@media (min-width: 760px) {
  .aud {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}
.aud-card {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
}
.aud-card > * {
  margin: 0;
}
.aud-card .plate {
  aspect-ratio: 4 / 3;
  margin-bottom: var(--space-sm);
}
.aud-card h3 {
  font-size: 1.4rem;
}
.aud-card p:not(.kicker) {
  color: var(--ink-secondary);
}
/* The link sits on the card's bottom edge, so the two line up whatever the copy
   above them runs to. */
.aud-card .aud-link {
  margin-top: auto;
  padding-top: var(--space-sm);
}

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

/* `--grad-tropic`, the mockup's closing colour — cool, so it does not echo the
   sunrise at the top, and already the site's card border. Ink on it is
   --on-gradient-ink: 13.19:1 at the cyan end, 11.76:1 at the yellow, 11.25:1 at
   its lowest point between. The store panel
   stays white on it, which is what makes its two halves read as one thing. */
.band-close {
  background: var(--grad-tropic);
  color: var(--on-gradient-ink);
}
.band-close .sec-head {
  margin-bottom: 0;
}
.band-close .get-app {
  border-color: rgba(42, 23, 16, 0.16);
  box-shadow: 0 18px 40px -24px rgba(42, 23, 16, 0.45);
}

/* --- The audience pages (PR 4 of the refresh, 2026-10-11) -------------------

   /members/, /hosts/ and /venues/ are built from the home page's shapes — the
   splits, the steps, the tools row, the dark band, the closing band — rather
   than from new ones. What follows is only what those shapes lacked. */

/* The tools row on a LIGHT band. Its default is the dark band's: a cream rule, a
   yellow icon and 72% cream text, which on white are all but invisible. Here it
   takes the page's own inks — the icon in --coral-ink, as on the cards. */
.band-light .tools li {
  border-top-color: var(--rule-ink);
}
.band-light .tools svg {
  color: var(--coral-ink);
}
.band-light .tools p {
  color: var(--ink-secondary);
}
/* A heading over a tools row that follows other content in the same band. */
.tools-head {
  margin: clamp(40px, 6vw, 64px) 0 var(--space-sm);
  font-size: 1.3rem;
}
/* The closing line under a light band's tools row. */
.going-foot {
  margin: var(--space-lg) 0 0;
  color: var(--ink-secondary);
}
.going-foot strong {
  color: var(--ink);
}

/* Two ways to prove a venue is yours, side by side — a pair of choices, so two
   columns on a rule rather than two cards. */
.claim-ways {
  display: grid;
  gap: 0 var(--space-xl);
  grid-template-columns: 1fr;
}
@media (min-width: 760px) {
  .claim-ways {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}
.claim-ways > div {
  padding-block: var(--space-md);
  border-top: 1px solid var(--rule-ink);
}
.claim-ways h3 {
  margin: 0 0 var(--space-sm);
  font-size: 1.2rem;
}
.claim-ways p {
  margin: 0;
  color: var(--ink-secondary);
}

/* /members/' header capture is a whole screen's worth of card — 600×846 — so at
   the inner header's usual 420px it stood taller than the copy beside it. */
.page-hero-layout > .shot-member {
  max-width: 340px;
}

/* A short, wide capture (an Atlas card is 600×324) beside a column of copy: at the
   split's usual 340px it shrank to a strip. */
.split-art > .shot-wide {
  max-width: 480px;
}

/* Links in the closing band's copy. ⚠️ Not the default --coral-ink, which is
   tuned for the cream; the band's own ink is 11.25:1 at its weakest point. */
.band-close .sec-head a {
  color: var(--on-gradient-ink);
}

/* Where a `.shot-crop` window sits on its frame, when the top is not the part the
   page is about. ⚠️ Classes, not a `style` attribute: the page CSP is
   `style-src 'self'`, which refuses inline styles. */
/* The Charades lobby from Players down: the Invite friends button and the greyed
   Start game are the friends-only claim, and a top crop cut both off. */
.shot-crop.crop-low {
  object-position: 50% 82%;
}
/* The interests editor's foot: the hidden chips and "Limit reached (20)". */
.shot-crop.crop-foot {
  object-position: bottom;
}

/* A split whose picture is much shorter than its copy — an Atlas card, the
   Requests inbox. Centred, it floated in a gap above and below; here it starts
   level with the copy and stays in view while the column scrolls past it. Wide
   screens only: stacked on a phone, there is nothing for it to stick against. */
@media (min-width: 760px) {
  .split-sticky {
    align-items: start;
  }
  .split-sticky > .split-art {
    position: sticky;
    top: var(--space-xl);
  }
}

/* `.kinds` on a phone: the 6.5rem label column left the text about 170px wide,
   so below 560px the label sits above its line instead of beside it. */
@media (max-width: 559.98px) {
  .kinds > div {
    grid-template-columns: 1fr;
    gap: 6px;
  }
}

/* --- The PR 5 slots (2026-10-11) ----------------------------------------------

   Three blocks that `build-shell.py` renders only once their real asset exists:
   the activity tiles (A10, commissioned illustrations), the quotes (A12, real
   members, with written permission) and the official store badges (A13, at
   launch). Until then none of this matches anything on the site — styled ahead
   so filling a slot is a content change, not a design task under deadline. */

/* Activity tiles: eight across two rows on a wide screen, two columns on a
   phone. The illustration carries the colour; the tile is the plain white card
   so eight different pictures still read as one set. */
.acts {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--space-md);
  grid-template-columns: repeat(2, minmax(0, 1fr));
}
@media (min-width: 760px) {
  .acts {
    grid-template-columns: repeat(4, minmax(0, 1fr));
  }
}
.acts li {
  max-width: none;
}
.act {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-sm);
  padding: var(--space-lg) var(--space-md) var(--space-md);
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: var(--radius-block);
  text-align: center;
}
.act img {
  width: auto;
  max-width: 100%;
  height: 120px;
}
.act span {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.1rem;
}
.acts-foot {
  margin-top: var(--space-lg);
  color: var(--ink-secondary);
}

/* Quotes: one column on a phone, side by side from 760px however many there are
   (two or three — QUOTES_MIN / QUOTES_MAX). The coral rule is the one accent: a
   quote is somebody else's voice, and it should not look like our own headline. */
.quotes {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--space-lg);
  grid-template-columns: 1fr;
}
@media (min-width: 760px) {
  .quotes {
    grid-auto-flow: column;
    grid-auto-columns: minmax(0, 1fr);
  }
}
.quotes li {
  max-width: none;
}
.quote {
  margin: 0;
  height: 100%;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: var(--space-md);
  padding: var(--space-lg);
  background: var(--surface);
  border-radius: var(--radius-block);
  border-top: 4px solid var(--coral);
}
.quote blockquote {
  margin: 0;
}
.quote blockquote p {
  margin: 0;
  font-family: var(--font-display);
  font-size: 1.2rem;
  font-weight: 600;
  line-height: 1.35;
}
.quote figcaption {
  color: var(--ink-secondary);
  font-size: 0.95rem;
}
.quote figcaption b {
  color: var(--ink);
}
/* The FTC disclosure, when there is one: its own line, never smaller than the
   caption it belongs to — a disclosure set in fine print is not "clear and
   conspicuous" (16 CFR §465.1). */
.quote-connection {
  display: block;
}

/* The official badges. ⚠️ The art is never restyled — no border, radius, filter
   or recolour; Apple and Google both forbid it. Only its size and the space
   around it are ours: 48px tall (BADGE_HEIGHT) and a gap of a quarter of that,
   the clear space both require. */
.store-badges.has-art {
  gap: 12px;
  align-items: center;
}
.store-badge-art {
  display: block;
  line-height: 0;
}
.store-badge-art img {
  display: block;
  width: auto;
  height: 48px;
}
.store-badge-art:focus-visible {
  outline-offset: 4px;
}
/* The hero's link down to "Get the app", once a badge is live (Apple allows one
   App Store badge per layout). On the sunrise band, so in its ink: 5.20:1. */
.hero-apps {
  margin: 0;
}
.hero-apps a {
  color: var(--on-gradient-ink);
  font-weight: 600;
}
