/* ==========================================================================
   usezap.app — stylesheet

   Values come from `design/web/design_handoff_zap_website/README.md`, which
   states them as `desktop / mobile`. That pairing is expressed here as custom
   properties redefined at one breakpoint, so a value appears once and the
   breakpoint appears once, rather than the same number being written into
   several rules that then drift apart.

   Breakpoint is 900px. The handoff notes the About hero's two-column layout
   stops fitting "above about 1000px wide"; that layout is now flex-sized rather
   than absolutely positioned, so it survives the narrower switch.
   ========================================================================== */

/* --------------------------------------------------------------------------
   Type
   🔴 SF Pro Rounded renders on Apple platforms ONLY.

   `ui-rounded` is a CSS generic that maps to SF Pro Rounded on Apple, and the
   named `'SF Pro Rounded'` resolves only where the font is installed locally.
   On Windows and Android neither exists, so without a webfont behind them the
   whole site falls through to generic sans-serif.

   Apple's licence covers designing and developing apps FOR Apple platforms and
   does not permit serving SF Pro as a webfont, so the real thing cannot be
   self-hosted. Nunito (SIL Open Font License 1.1) is the fallback: rounded
   terminals, humanist proportions, the closest widely-available match.

   ⚠️ Apple devices never download it. A browser fetches a webfont only when it
   is the first family in the stack that can render the text — `ui-rounded`
   resolves first there, so the request is never made.

   Variable, 400–700, which is exactly the range the design uses.
   -------------------------------------------------------------------------- */

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

@font-face {
  font-family: 'Nunito';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url('/assets/fonts/nunito-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
                 U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,
                 U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* --------------------------------------------------------------------------
   Tokens
   -------------------------------------------------------------------------- */

:root {
  /* Colour ---------------------------------------------------------------- */
  --accent: #E5FF44;
  --ink-on-accent: #000000;
  --ink-on-accent-soft: #1A1A19;
  --page: #0D0D0C;
  --surface: #1A1A19;
  --card: linear-gradient(#232322, #20201F);
  --card-hairline: rgba(255, 255, 255, .07);
  --row-surface: #232322;
  --row-hairline: rgba(255, 255, 255, .09);
  --row-hover: #2A2A28;
  --row-hover-hairline: rgba(255, 255, 255, .16);
  --text: #FFFFFF;
  --text-2: #9A9A97;
  /*
   * 🔴 Raised from the handoff's .42 and .30 to clear WCAG AA.
   *
   * At the design's values these composite to 4.07:1 and 2.71:1 on the surface,
   * against the 4.5:1 AA needs for normal text — and they carry real copy: the
   * hero microcopy, the closing microcopy, review attributions, footer links
   * and the copyright. Measured, not estimated; .50 lands at 5.03:1 and .48 at
   * 4.70:1. The greys are marginally lighter than drawn, which is the cost of
   * the text being readable.
   */
  --text-3: rgba(255, 255, 255, .50);
  --text-4: rgba(255, 255, 255, .50);
  --ghost: rgba(255, 255, 255, .10);
  --ghost-hover: rgba(255, 255, 255, .16);
  /* The atmosphere wash. Handoff value is .10; raised on the owner's call,
     14 Aug 2026. This is the single lever — change it here, not per page. */
  --wash: rgba(72, 65, 150, .40);
  --nav-hairline: rgba(255, 255, 255, .08);
  --flame: linear-gradient(160deg, #FF9A2E 0%, #FF5A1F 46%, #D92414 100%);

  /* Type — weights 400/500/600/700 only, never 800 ------------------------ */
  /* Apple first (no download), then the self-hosted fallback, then generics. */
  --font: ui-rounded, 'SF Pro Rounded', 'Hiragino Maru Gothic ProN',
          'Nunito', -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;

  --t-h1: 68px;          /* home */
  --t-hero-sub: 20px;
  --t-h2: 44px;          /* section */
  --t-cta-h2: 52px;      /* closing CTA */
  --t-legal-h1: 52px;
  --t-about-h1: 48px;
  --t-about-h2: 32px;
  --t-404-h1: 44px;
  --t-social-tagline: 25px;
  --t-card-h3: 23px;
  --t-body: 17px;
  --t-nav: 15px;
  --t-button: 17px;
  --t-micro: 15px;
  --t-footer: 14px;
  --t-trust: 15px;

  /* Spacing — `padding` shorthands, straight from the handoff table -------- */
  --gutter: 72px;
  --p-nav: 30px var(--gutter) 32px;
  --p-hero: 148px var(--gutter) 112px;
  --p-stage: 0 var(--gutter) 0;
  --p-trust: 20px 20px 24px;
  --p-section-first: 144px var(--gutter) 208px;
  --p-section: 0 var(--gutter) 208px;
  --p-closing: 0 var(--gutter) 232px;
  --p-about-hero: 144px var(--gutter) 0;
  --p-about-heading: 84px var(--gutter) 0;
  --p-about-cards: 28px var(--gutter) 176px;
  --p-about-body: 120px var(--gutter) 224px;
  --p-legal-head: 96px var(--gutter) 0;
  --p-legal-body: 68px var(--gutter) 176px;
  --p-footer: 56px var(--gutter) 64px;
  --p-404: 96px var(--gutter) 120px;
  --p-social: 112px 40px 140px;

  /* Content maxima */
  --max-grid: 1120px;
  --max-legal: 832px;
  --max-about: 1080px;
  --max-social: 440px;
  --max-hero-h1: 760px;
  --max-hero-sub: 520px;
  --max-faq: 760px;

  /* Stage and phone */
  /* 8% larger than the handoff's 1:1 — owner's call; the phone is the strongest
     proof on the page. The stage must grow with it or the scale is clipped. */
  --stage-h: 952px;
  --phone-scale: 1.08;

  /* Radii ----------------------------------------------------------------- */
  --r-pill: 999px;
  --r-feature: 22px;
  --r-card: 18px;         /* review cards, FAQ rows, /social rows, founder cards */
  --r-privacy-tile: 20px;
  --r-photo-card: 16px;   /* in-app photo card */
  --r-media: 14px;
  --r-focus: 8px;
  --r-phone: 55px;

  /* Shadows --------------------------------------------------------------- */
  --sh-phone: 0 44px 110px rgba(0, 0, 0, .66), 0 10px 28px rgba(0, 0, 0, .42);
  --sh-founder: 0 30px 70px rgba(0, 0, 0, .5);
  --sh-menu: 0 26px 64px rgba(0, 0, 0, .55);
  --sh-privacy-glow: 0 0 70px rgba(229, 255, 68, .11);

  /* Motion — one curve for everything --------------------------------------*/
  --ease: cubic-bezier(.22, .61, .36, 1);
  --d-opacity: 440ms;
  --d-transform: 480ms;
  --rise: 14px;
}

@media (max-width: 900px) {
  :root {
    --gutter: 20px;
    --t-h1: 38px;
    --t-hero-sub: 17px;
    --t-h2: 30px;
    --t-cta-h2: 34px;
    --t-legal-h1: 34px;
    --t-about-h1: 34px;
    --t-about-h2: 26px;
    --t-404-h1: 32px;
    --t-social-tagline: 27px;
    --t-body: 16px;

    --p-nav: 19px var(--gutter) 21px;
    --p-hero: 72px var(--gutter) 68px;
    --p-trust: 16px 20px 16px;
    --p-section-first: 96px var(--gutter) 132px;
    --p-section: 0 var(--gutter) 132px;
    --p-closing: 0 var(--gutter) 160px;
    --p-about-hero: 72px var(--gutter) 0;
    --p-about-heading: 64px var(--gutter) 0;
    --p-about-cards: 20px var(--gutter) 112px;
    --p-about-body: 72px var(--gutter) 144px;
    --p-legal-head: 56px var(--gutter) 0;
    --p-legal-body: 48px var(--gutter) 120px;
    --p-footer: 36px var(--gutter) 44px;
    --p-404: 72px var(--gutter) 96px;
    --p-social: 56px 24px 52px;

    --stage-h: 640px;
    --phone-scale: .72;
  }
}

/* --------------------------------------------------------------------------
   Base
   -------------------------------------------------------------------------- */

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

html, body {
  margin: 0;
  background: var(--page);
  -webkit-font-smoothing: antialiased;
}

/*
 * `--surface` on the body, not `--page`.
 *
 * The nav sits OUTSIDE <main>, so while the body carried `--page` (#0D0D0C) a
 * transparent nav revealed a darker strip against the `--surface` content below
 * — a band, just a different one from the opaque nav it replaced. The page
 * colour is kept on <html>, where it shows only under overscroll.
 */
/*
 * 🔴 `--surface`, deliberately NOT `--page`.
 *
 * The handoff distinguishes a page background (#0D0D0C) from a content
 * background (#1A1A19) because its prototype renders inside a device frame,
 * where the darker colour is the area *around* the phone. A real website has no
 * such area — the only place it can ever show is overflow and overscroll.
 *
 * And it did show, three times: below the footer on short pages, past the
 * bottom of /social, and behind a transparent nav. Every one was a different
 * cause with the same symptom, because any overflow at all becomes a visible
 * black band when html and body disagree. Matching them removes the whole class
 * of defect rather than the current instance of it.
 *
 * `--page` is still used for ink on the accent, where it is doing real work.
 */
html { background: var(--surface); }

body {
  font-family: var(--font);
  color: var(--text);
  font-size: var(--t-body);
  line-height: 1.6;
  min-height: 100vh;
  min-height: 100svh;
  display: flex;
  flex-direction: column;
  background: var(--surface);

  /*
   * 🔴 `z-index: 0` is load-bearing — it is what makes body a STACKING CONTEXT.
   *
   * `position: relative` alone leaves `z-index: auto`, which creates no
   * stacking context, so `.wash-clip` at `z-index: -1` lands in the ROOT
   * context's negative layer — and body's own opaque background paints after
   * that, hiding the wash completely. The glow was rendering the whole time and
   * being painted over.
   *
   * With a stacking context here, -1 means "behind body's content, above body's
   * background", which is what the wash needs. It also gives the absolutely
   * positioned `.wash-clip` its containing block.
   */
  position: relative;
  z-index: 0;
}

/*
 * The footer must pin to the bottom of the viewport when the page is shorter
 * than the window. The body is the flex column; `main` grows, the footer does
 * not. The handoff achieves this with `flex:none; margin-top:auto` inside a
 * scroll container — here the document itself scrolls, so `main { flex: 1 }`
 * does the same job without a nested scroller.
 */
main { flex: 1 0 auto; }

img { max-width: 100%; display: block; }

a { color: var(--accent); text-decoration: none; transition: color 150ms var(--ease); }
a:hover { color: var(--text); }

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

/* ⚠️ `figure` and `ul` carry user-agent margins that were never reset — the
   About founder figures inherited 40px of horizontal margin, and the trust strip
   gained 15px above and below from the list. */
h1, h2, h3, p, figure, ul { margin: 0; }
ul { padding: 0; list-style: none; }

/*
 * 🔴 Anchor targets must clear the sticky nav.
 *
 * Without this, `#privacy`, `#reviews`, `#faqs` and `#how` all scroll their
 * section to y=0 — directly underneath the nav, which is ~107px tall on
 * desktop — so the heading you navigated to is the one thing you cannot see.
 * `scroll-margin-top` is the property that exists for exactly this and needs no
 * JavaScript.
 */
:target, [id] { scroll-margin-top: 132px; }
@media (max-width: 900px) { :target, [id] { scroll-margin-top: 96px; } }

.skip-link {
  position: absolute; left: 16px; top: -60px; z-index: 100;
  padding: 12px 18px; border-radius: var(--r-pill);
  background: var(--accent); color: var(--ink-on-accent);
  font-size: 15px; font-weight: 700;
  transition: top 150ms var(--ease);
}
.skip-link:focus { top: 16px; color: var(--ink-on-accent); }

.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* --------------------------------------------------------------------------
   The atmosphere wash
   🔴 ONE element at page level. Never a section background — each section then
   clips it at its own edges and leaves visible horizontal seams where two meet.
   -------------------------------------------------------------------------- */

/* No background and no stacking context — the wash is a sibling above it in the
   document and must show through the whole column, nav included. */
.route { position: relative; }

/*
 * ⚠️ The far stop is `rgba(72,65,150,0)`, NOT the `transparent` keyword.
 * `transparent` is `rgba(0,0,0,0)`, so the gradient interpolates towards BLACK
 * as it fades and can leave a dirty edge on a dark page. Same hue at zero alpha
 * fades cleanly to nothing.
 */
/*
 * 🔴 The clip is required, not decorative.
 *
 * `.wash` is 1720px tall and absolutely positioned. On a page shorter than
 * that — About, Privacy, Terms, Support, 404 — it extends the scrollable area
 * well past the footer, and the overflow shows <html>'s darker `--page` as a
 * large black band below the site. `.wash-clip` is `inset: 0` against the body,
 * so it is exactly the page's height and cannot add scroll, and `overflow:
 * hidden` trims the wash to it.
 *
 * The wash keeps its own fixed height because the gradient's `26%` origin is
 * relative to the element: stretched to the full page height it would put the
 * glow far too low.
 */
/*
 * Glow hierarchy — owner's call, 14 Aug 2026. Every page inheriting the same
 * wash made "purple" read as the site's background rather than as atmosphere.
 * Graded, it becomes intentional: marketing pages carry some, legal pages none.
 *
 *   strong  home
 *   soft    about
 *   faint   support, /social, 404
 *   none    privacy, terms
 */
.wash-clip {
  position: absolute;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
  z-index: -1;
}
.wash-clip[data-glow='soft']  .wash { opacity: .55; }
.wash-clip[data-glow='faint'] .wash { opacity: .22; }
.wash-clip[data-glow='none']  { display: none; }

.wash {
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 1720px;
  background: radial-gradient(1180px 1000px at 50% 26%, var(--wash), rgba(72, 65, 150, 0) 72%);
}

@media (max-width: 900px) {
  .wash {
    height: 1240px;
    background: radial-gradient(760px 1020px at 50% 26%, var(--wash), rgba(72, 65, 150, 0) 74%);
  }
}

/* --------------------------------------------------------------------------
   Masked-PNG artwork
   The wordmark, lock and flame are black-alpha PNGs used as CSS masks and
   painted at runtime, so one asset serves every colour it appears in.
   -------------------------------------------------------------------------- */

.mask {
  display: inline-block;
  background-color: currentColor;
  -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
  -webkit-mask-position: center; mask-position: center;
  -webkit-mask-size: contain; mask-size: contain;
}

/* 709×244 native, so the ratio is 2.905:1 — every size below preserves it. */
.wordmark {
  color: var(--accent);
  -webkit-mask-image: url('/assets/wordmark/zapLogo.png');
  mask-image: url('/assets/wordmark/zapLogo.png');
  width: 86px; height: 30px;
}
@media (max-width: 900px) { .wordmark { width: 74px; height: 26px; } }

/* Larger in the nav than the handoff's 86×30 — owner's call, 14 Aug 2026. */
.nav .wordmark { width: 110px; height: 38px; }
@media (max-width: 900px) { .nav .wordmark { width: 94px; height: 32px; } }

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

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 10px;
  height: 56px; padding: 0 26px;
  border: 0; border-radius: var(--r-pill);
  font-family: inherit; font-size: var(--t-button); font-weight: 700;
  cursor: pointer;
  transition: filter 150ms var(--ease), background 150ms var(--ease),
              transform 90ms var(--ease), color 150ms var(--ease);
}

.btn-accent { background: var(--accent); color: var(--ink-on-accent); }
.btn-accent:hover { filter: brightness(1.04); color: var(--ink-on-accent); }
.btn-ghost { background: var(--ghost); color: var(--text); }
.btn-ghost:hover { background: var(--ghost-hover); color: var(--text); }
.btn:active { transform: scale(.98); }

/*
 * Inactive means INERT, not broken. No hover, no press, a default cursor —
 * nothing that suggests pressing it will do something. When the App Store URL
 * exists the button becomes full acid with normal interaction states, so the
 * colour change carries real meaning: acid means this works now.
 */
.btn[aria-disabled='true'] {
  background: rgba(229, 255, 68, .28);
  color: rgba(0, 0, 0, .62);
  cursor: default;
}
.btn[aria-disabled='true'] .apple { color: rgba(0, 0, 0, .62); }
.btn[aria-disabled='true']:hover { filter: none; background: rgba(229, 255, 68, .28); }
.btn[aria-disabled='true']:active { transform: none; }

.btn .apple {
  width: 18px; height: 18px; flex: none;
  color: var(--ink-on-accent);
  -webkit-mask-image: url('/assets/icons/apple.png');
  mask-image: url('/assets/icons/apple.png');
}

.btn-sm { height: 44px; padding: 0 20px; font-size: var(--t-nav); }

/* --------------------------------------------------------------------------
   Nav
   -------------------------------------------------------------------------- */

/*
 * The nav has NO background and NO hairline, at any scroll position. Owner's
 * call, 14 Aug 2026, overriding the handoff's "sticky, #1A1A19, 1px bottom
 * hairline". The wordmark, links, pill and burger stand free on the page.
 *
 * Why the opaque version had to go: the nav sits above the atmosphere wash, so
 * a filled bar cut the glow into a hard horizontal band across the top of every
 * page — the same seam the handoff warns about when the wash is painted
 * per-section rather than once at page level.
 *
 * ⚠️ The cost is legibility. Nothing separates the links from whatever scrolls
 * under them, so over a photograph or a pale card they can drop to unreadable.
 * If that shows up, the fix is a `backdrop-filter: blur()` with no colour —
 * which separates without reintroducing a band.
 */
/*
 * ⚠️ While the mobile menu is open the nav takes the panel's surface, so the
 * two read as ONE sheet.
 *
 * The scrim is `inset: 0` and therefore sits behind the nav as well as the
 * page. A transparent nav paints above it but contributes no colour, so the
 * scrim showed straight through and the bar went near-black the moment the menu
 * opened — the opposite of the free-standing treatment, and a hard seam against
 * the panel below. The panel's `0 0 24px 24px` radius only reads correctly if
 * whatever sits above it is solid anyway.
 */
/* ⚠️ Declared AFTER `.nav.is-stuck` on purpose. Both are (0,2,0), so source
   order decides — and with the menu open over a scrolled page the scrim was
   winning, giving the bar the translucent gradient instead of the panel's solid
   surface, which is the seam this rule exists to remove. */

.nav {
  position: sticky; top: 0; z-index: 40;
  background: transparent;
  padding: var(--p-nav);
  display: flex; align-items: center; gap: 16px;
  transition: background 220ms var(--ease);
}

/*
 * Once scrolled: a SOFT GRADIENT SCRIM. No fill, no blur, no hairline.
 *
 * A flat translucent panel — even a blurred one — still ends somewhere, and
 * wherever it ends is an edge. This fades from a near-opaque surface at the top
 * to nothing at the bottom of the nav, so there is no line to see at any scroll
 * position, and the wash and page content run through it uninterrupted.
 *
 * It buys legibility where it is actually needed (behind the links, at the top
 * of the bar) and costs nothing where it is not.
 *
 * ⚠️ The scrim is taller than the nav's own box via `padding-bottom` on the
 * gradient's extent — it must reach zero BEFORE the nav's edge, or the fade is
 * itself cut off and you are back to an edge.
 */
.nav.is-menu-open { background: var(--surface) !important; transition: none; }

.nav.is-stuck {
  background: linear-gradient(
    to bottom,
    rgba(26, 26, 25, .94) 0%,
    rgba(26, 26, 25, .86) 45%,
    rgba(26, 26, 25, 0) 100%
  );
}

/* 🔴 `display: flex` on the link, not just on the nav.
   The wordmark is an inline-block; inside an inline <a> it sits on the text
   baseline, so the link box gains descender space BELOW the glyph and centring
   that box leaves the wordmark visibly high. Flex removes the baseline entirely. */
.nav-logo { display: flex; align-items: center; }

.nav-links { display: flex; align-items: center; gap: 26px; margin-left: auto; }

.nav-link {
  font-size: var(--t-nav); font-weight: 500; color: var(--text-2);
  transition: color 150ms var(--ease);
}
.nav-link:hover { color: var(--text); }
.nav-link.is-active { color: var(--accent); }

.nav-cta { margin-left: 26px; }

/* 🔴 `flex-direction: column` is load-bearing. The default is `row`, which lays
   the three bars out side by side — 3 × 17px = 51px inside a 44px button, so
   they overflow it and read as two stray lines across the disc. */
.burger {
  display: none;
  width: 44px; height: 44px; flex: none;
  flex-direction: column;
  align-items: center; justify-content: center;
  background: var(--ghost); border: 0; border-radius: var(--r-pill);
  cursor: pointer; padding: 0;
}
.burger span {
  display: block; width: 17px; height: 1.5px; background: var(--text);
  border-radius: 2px; transition: transform 200ms var(--ease), opacity 200ms var(--ease);
}
.burger span + span { margin-top: 4px; }
.burger[aria-expanded='true'] span:nth-child(1) { transform: translateY(5.5px) rotate(45deg); }
.burger[aria-expanded='true'] span:nth-child(2) { opacity: 0; }
.burger[aria-expanded='true'] span:nth-child(3) { transform: translateY(-5.5px) rotate(-45deg); }

@media (max-width: 900px) {
  .nav-links { display: none; }
  .nav { gap: 12px; }
  .nav-cta { margin-left: auto; }
  .burger { display: flex; }
}

/* Mobile menu — expands with the 0fr → 1fr grid technique */
/*
 * 🔴 The scrim is NEVER display:none and never `hidden`.
 *
 * It carries a `backdrop-filter`, and toggling display destroys and recreates
 * its compositor layer — so the blur snapped on at full strength instead of
 * fading with the opacity. That is the flash when the menu opens, and it is
 * worst on mobile Safari, where layer promotion is most aggressive.
 *
 * Staying in the tree with `visibility` keeps one stable layer for the whole
 * page's life. `will-change` holds the promotion so the first open is not the
 * frame that pays for it. Visibility flips instantly on open and waits out the
 * fade on close, so it never blocks taps while invisible.
 */
.menu-scrim {
  position: fixed; inset: 0; z-index: 30;
  background: rgba(9, 9, 8, .58);
  backdrop-filter: blur(16px) saturate(115%);
  -webkit-backdrop-filter: blur(16px) saturate(115%);
  opacity: 0; pointer-events: none; visibility: hidden;
  will-change: opacity;
  transition: opacity 280ms var(--ease), visibility 0s linear 280ms;
}
.menu-scrim.is-open {
  opacity: 1; pointer-events: auto; visibility: visible;
  transition: opacity 280ms var(--ease), visibility 0s linear 0s;
}

/*
 * ⚠️ `visibility: hidden` while closed is load-bearing, not tidiness.
 *
 * `.menu-inner` carries `box-shadow: 0 26px 64px rgba(0,0,0,.55)`, and a box of
 * ZERO height still paints its shadow — so a 64px-blurred dark band sat under
 * the nav permanently, growing and shrinking with the animation. Hiding the
 * subtree stops it painting at all.
 */
.menu {
  position: fixed; left: 0; right: 0; z-index: 35;
  display: grid; grid-template-rows: 0fr;
  visibility: hidden;
  transition: grid-template-rows 280ms var(--ease), visibility 0s linear 280ms;
}
.menu.is-open {
  grid-template-rows: 1fr;
  visibility: visible;
  transition: grid-template-rows 280ms var(--ease), visibility 0s linear 0s;
}
/* ⚠️ Bounded and scrollable. Without a max-height the panel runs past the
   bottom of a landscape phone and its last rows cannot be reached at all. */
.menu-inner {
  overflow: hidden;
  max-height: calc(100svh - 84px);
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  background: var(--surface);
  border-radius: 0 0 24px 24px;
  box-shadow: var(--sh-menu);
}
.menu-list { padding: 8px 20px 22px; }
/*
 * ⚠️ The resting state is VISIBLE. The stagger is an entrance animation with
 * `both` fill, not a reveal that content depends on.
 *
 * These rows previously sat at `opacity: 0` and were brought in by the
 * animation — so if the animation did not run (reduced motion, a background
 * tab), the menu opened to an empty panel. Same defect as the page-wide arrival
 * choreography, one component down.
 */
.menu-row {
  display: block; padding: 14px 0;
  font-size: 19px; font-weight: 600; color: var(--text);
  border-bottom: 1px solid var(--row-hairline);
}
.menu-row:last-child { border-bottom: 0; }
.menu.is-open .menu-row {
  animation: menu-in 280ms var(--ease) both;
  animation-delay: calc(120ms + var(--i) * 30ms);
}
@keyframes menu-in {
  from { opacity: 0; transform: translateY(6px); }
  to { opacity: 1; transform: none; }
}

/* --------------------------------------------------------------------------
   Arrival and reveal choreography
   -------------------------------------------------------------------------- */

/*
 * 🔴 EVERY RULE HERE IS SCOPED TO `.js`, AND THAT IS THE WHOLE POINT.
 *
 * `.js` is set by a synchronous inline script in <head>. Without it nothing is
 * hidden, so the site renders in full when JavaScript is blocked, still
 * downloading, or has thrown.
 *
 * Hiding content by default and revealing it from a script makes the page's
 * legibility depend on that script running. It does not always run: a tab
 * opened in the background throttles `requestAnimationFrame` indefinitely, so
 * the reveal never fires and the visitor gets a blank page. That was observed
 * here, not theorised — the mobile home page rendered empty in a backgrounded
 * pane while the DOM was entirely correct.
 *
 * site.js also arms a timeout alongside its rAF for the same reason.
 */
.js [data-arrive], .js [data-reveal] {
  opacity: 0;
  transform: translateY(var(--rise, 14px));
}

.js [data-arrive].is-in, .js [data-reveal].is-in {
  opacity: 1;
  transform: none;
  transition: opacity var(--d-opacity) var(--ease) var(--delay, 0ms),
              transform var(--d-transform) var(--ease) var(--delay, 0ms);
}

/* Route arrival for the page as a whole: 260ms opacity, 300ms transform */
.js .route[data-route-arrive] { opacity: 0; transform: translateY(8px); }
.js .route[data-route-arrive].is-in {
  opacity: 1; transform: none;
  transition: opacity 260ms var(--ease), transform 300ms var(--ease);
}

/* Privacy and terms are exempt — reading them is the job, arriving is not. */
.route.is-static, .route.is-static [data-arrive], .route.is-static [data-reveal] {
  opacity: 1 !important; transform: none !important; transition: none !important;
}

/* --------------------------------------------------------------------------
   Home — hero
   -------------------------------------------------------------------------- */

.hero { padding: var(--p-hero); text-align: center; }

.hero h1 {
  font-size: var(--t-h1); font-weight: 700; letter-spacing: -.021em; line-height: 1.05;
  max-width: var(--max-hero-h1); margin: 0 auto;
}

.hero-sub {
  font-size: var(--t-hero-sub); font-weight: 500; line-height: 1.5; color: var(--text-2);
  max-width: var(--max-hero-sub); margin: 22px auto 0;
}

.hero-actions {
  display: flex; align-items: center; justify-content: center; gap: 12px;
  margin-top: 34px; flex-wrap: wrap;
}

.hero-allowance {
  margin-top: 20px; font-size: var(--t-micro); font-weight: 500; color: var(--text-3);
}

/* --------------------------------------------------------------------------
   Home — phone stage
   🔴 The phone is shown WHOLE. Earlier versions cropped it and covered the cut
   with a bottom fade; the crop landed 40px below the in-app dock, so any fade
   tall enough to read washed over the bin and keep buttons. If you find
   yourself adding a fade here, the stage is too short.
   -------------------------------------------------------------------------- */

/*
 * ⚠️ NO `overflow: hidden` here.
 *
 * The prototype clips this stage, but the phone already fits inside it — 874px
 * in an 882px stage on desktop, 629px scaled in 640px on mobile — so the only
 * thing the clip ever removed was the phone's own drop shadow
 * (`0 44px 110px`), which was cut off as a hard horizontal edge above and below
 * the device. The shadow is allowed to fall across the trust strip; it is soft
 * and that is what a shadow is for.
 */
.stage {
  padding: var(--p-stage);
  height: var(--stage-h);
  display: flex; align-items: flex-start; justify-content: center;
}

.phone-scale { transform: scale(var(--phone-scale)); transform-origin: top center; }

.phone {
  width: 402px; height: 874px;
  border-radius: var(--r-phone);
  background: var(--surface);
  border: 1px solid var(--card-hairline);
  box-shadow: var(--sh-phone);
  overflow: hidden;
  flex: none;
  display: flex; flex-direction: column;
  color: var(--text);
}

.phone-status { height: 46px; flex: none; }

.phone-topbar {
  flex: none;
  padding: 9px 20px 0;
  display: flex; align-items: center; gap: 10px; min-height: 44px;
}
.phone-spacer { flex: 1; }

.phone-wordmark {
  width: 92px; height: 32px; color: var(--accent);
  -webkit-mask-image: url('/assets/wordmark/zapLogo.png');
  mask-image: url('/assets/wordmark/zapLogo.png');
  -webkit-mask-position: left center; mask-position: left center;
}

.phone-streak, .phone-refer {
  display: flex; align-items: center; gap: 5px;
  min-height: 34px; padding: 0 12px; border-radius: var(--r-pill);
  font-size: 13px; font-weight: 700;
}
.phone-streak { background: var(--accent); color: var(--ink-on-accent-soft); }
.phone-streak b { font-variant-numeric: tabular-nums; font-weight: 700; }
.phone-streak .flame {
  width: 16px; height: 16px; color: var(--ink-on-accent-soft);
  -webkit-mask-image: url('/assets/header/flame-fill.png');
  mask-image: url('/assets/header/flame-fill.png');
}
.phone-refer { background: rgba(255, 255, 255, .14); color: #fff; gap: 6px; }
.phone-refer .gift {
  width: 17px; height: 17px; color: #fff;
  -webkit-mask-image: url('/assets/header/gift.png');
  mask-image: url('/assets/header/gift.png');
}

.phone-more {
  width: 44px; height: 44px; border-radius: var(--r-pill);
  background: rgba(255, 255, 255, .12);
  display: flex; align-items: center; justify-content: center; gap: 4px;
}
.phone-more i { width: 5px; height: 5px; border-radius: var(--r-pill); background: #fff; flex: none; }

.phone-deck { flex: 1; position: relative; margin: 10px 20px 48px; }

.phone-card {
  position: absolute; inset: 0;
  border-radius: var(--r-photo-card); overflow: hidden;
}
.phone-card img { width: 100%; height: 100%; object-fit: cover; }
.phone-card-next { transform: scale(.965) translateY(8px); }
.phone-card-tint { position: absolute; inset: 0; background: rgba(11, 21, 51, .28); }
.phone-card-front { transform-origin: 50% 100%; }

.phone-pill {
  position: absolute; top: 14px; left: 14px;
  height: 34px; padding: 0 13px; border-radius: var(--r-pill);
  background: rgba(0, 0, 0, .62); color: #fff;
  display: flex; align-items: center; font-size: 13px; font-weight: 600;
}

.phone-dock {
  position: absolute; left: 21px; right: 21px; bottom: -29px;
  display: flex; justify-content: space-between; align-items: center;
}
.dock-btn {
  width: var(--d); height: var(--d); border-radius: var(--r-pill);
  background: rgba(0, 0, 0, .74); border: 1px solid rgba(255, 255, 255, .24);
  display: flex; align-items: center; justify-content: center; flex: none;
}
.dock-glyph {
  width: var(--w); height: var(--h); background-color: var(--c);
  -webkit-mask-image: var(--g); mask-image: var(--g);
}

.phone-sheet {
  flex: none; margin: 0 20px;
  background: #fff; border-radius: 35px;
  padding: 13px 10px 13px 20px;
  display: flex; align-items: center; gap: 8px;
}
.phone-sheet-text { flex: 1; display: block; }
.phone-sheet-label { display: block; font-size: 16px; font-weight: 600; color: var(--ink-on-accent-soft); }
.phone-progress {
  display: block; margin-top: 8px; height: 4px; border-radius: var(--r-pill);
  background: rgba(26, 26, 25, .13); overflow: hidden;
}
.phone-progress span { display: block; height: 100%; border-radius: var(--r-pill); background: var(--ink-on-accent-soft); }
.phone-chevron { flex: none; width: 44px; height: 44px; display: flex; align-items: center; justify-content: center; }
/*
 * A stroked path with ROUND caps and a round join, not two CSS borders.
 *
 * The deck frame draws this chevron with `border-left` + `border-top` rotated
 * 45°, which mitres to sharp points. Every chevron in iOS is an SF Symbol —
 * `chevron.up` — with rounded terminals, so the border version reads as
 * subtly not-Apple on a mockup whose whole job is to look like the app.
 */
/* 24px box: the path spans 11.2 of 16 units, so the drawn chevron is 16.8px
   wide and 8.4px tall — the diagonal span of the 12×12 rotated-border version it
   replaces (17.0 × 8.5). Stroke 1.7 renders 2.55px against the old 2.5px. */
.phone-chevron svg { width: 24px; height: 24px; color: #6E6E6B; }

/*
 * ⚠️ The frame's own trailing 30px. This is the gap that appears to sit above
 * the trust strip; it is inside the phone, below its bottom sheet.
 */
.phone-tail { height: 30px; flex: none; }

/* The one hint swipe, driven from site.js. Once only, never looped. */
.phone-card-front.is-hinting { transition: transform 620ms var(--ease); transform: translateX(-42%) rotate(-10deg); }
.phone-card-next.is-hinting { transition: transform 620ms var(--ease); transform: scale(1) translateY(0); }
.phone-card-front.is-returning { transition: transform 560ms var(--ease); transform: none; }
.phone-card-next.is-returning { transition: transform 560ms var(--ease); transform: scale(.965) translateY(8px); }

/* --------------------------------------------------------------------------
   Home — trust strip
   ⚠️ ~30px of the apparent gap above this strip is inside the phone frame
   itself, below its own bottom sheet. Do not try to close it with padding.
   -------------------------------------------------------------------------- */

/*
 * `z-index: 1` puts the strip ABOVE the phone's drop shadow. The stage no
 * longer clips that shadow, so it falls across this row — and a soft black
 * gradient over 15/600 grey text is a legibility loss for no gain. The shadow
 * still lands on the page behind, which is what makes the device sit in space.
 */
.trust {
  position: relative;
  z-index: 1;
  padding: var(--p-trust);
  display: flex; align-items: center; justify-content: center; gap: 84px;
  font-size: var(--t-trust); font-weight: 600; color: var(--text-2);
}
.trust li { display: flex; align-items: center; gap: 10px; }
.trust svg, .trust .mask { width: 20px; height: 20px; flex: none; color: var(--text-2); }

@media (max-width: 900px) {
  .trust { flex-direction: column; gap: 20px; }
}

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

.section { padding: var(--p-section); }
/* `.section-first` is a MODIFIER — it must be used alongside `.section`, or it
   inherits none of the section typography. It silently did not, and the
   "Three moves" heading rendered at body size, left-aligned. */
.section-first { padding: var(--p-section-first); }

.section h2 {
  font-size: var(--t-h2); font-weight: 700; letter-spacing: -.018em; line-height: 1.11;
  text-align: center;
}

.grid { max-width: var(--max-grid); margin: 0 auto; }

/* How it works — 3-up */
.how { display: grid; grid-template-columns: repeat(3, 1fr); gap: 24px; margin-top: 72px; }

.feature {
  background: var(--card);
  border: 1px solid var(--card-hairline);
  border-radius: var(--r-feature);
  padding: 22px;
  transition: transform 150ms var(--ease), border-color 150ms var(--ease);
}
.feature:hover { transform: translateY(-2px); border-color: var(--row-hover-hairline); }

.feature-media {
  height: 240px; border-radius: var(--r-media);
  overflow: hidden; position: relative; background: #171716;
}
.feature-media img { width: 100%; height: 100%; object-fit: cover; }

.feature h3 {
  font-size: var(--t-card-h3); font-weight: 700; letter-spacing: -.01em; line-height: 1.2;
  margin-top: 20px;
}
.feature p { margin-top: 8px; font-size: 16px; font-weight: 500; line-height: 1.55; color: var(--text-2); }

@media (max-width: 900px) {
  .how { grid-template-columns: 1fr; gap: 16px; margin-top: 40px; }
  .feature-media { height: 170px; }
}

/* The swipe-left tint on the Swipe card — the app's own bin colour, bleeding in
   from the leading edge rather than washing the whole photograph. */
.swipe-tint {
  position: absolute; inset: 0;
  background: linear-gradient(90deg, rgba(255, 74, 110, .55), rgba(255, 74, 110, 0) 62%);
}
.card-bin { position: absolute; left: 18px; top: 50%; transform: translateY(-50%); }

/* The streak pill on the Come back card */
.streak {
  position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
  display: inline-flex; align-items: center; gap: 8px;
  height: 40px; padding: 0 16px; border-radius: var(--r-pill);
  background: var(--accent); color: var(--ink-on-accent);
  font-size: 20px; font-weight: 700;
}
.streak .flame {
  width: 18px; height: 20px;
  background: var(--flame);
  -webkit-mask-image: url('/assets/header/flame-fill.png');
  mask-image: url('/assets/header/flame-fill.png');
  -webkit-mask-size: contain; mask-size: contain;
  -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
  -webkit-mask-position: center; mask-position: center;
}

/* The in-card trash bar on the Empty the bin card */
.trash-bar {
  position: absolute; left: 12px; right: 12px; bottom: 12px;
  display: flex; align-items: center; gap: 10px;
  height: 44px; padding: 0 6px 0 14px;
  border-radius: var(--r-pill); background: #fff; color: #1A1A19;
  font-size: 14px; font-weight: 600;
}
.trash-bar .fill { flex: 1; }
.trash-bar .empty {
  height: 32px; padding: 0 14px; border-radius: var(--r-pill);
  background: var(--accent); color: var(--ink-on-accent);
  display: inline-flex; align-items: center; font-size: 14px; font-weight: 700;
}

/* --------------------------------------------------------------------------
   Home — privacy block
   -------------------------------------------------------------------------- */

.privacy-block { text-align: center; max-width: 720px; margin: 0 auto; }

.privacy-tile {
  width: 64px; height: 64px; margin: 0 auto 28px;
  border-radius: var(--r-privacy-tile);
  background: rgba(229, 255, 68, .08);
  box-shadow: var(--sh-privacy-glow);
  display: flex; align-items: center; justify-content: center;
}
/* 🔴 Aspect is 257:320 (0.803). Set the height and let `aspect-ratio` supply
   the width — squaring this glyph visibly fattens the lock body. */
.lock {
  display: inline-block; flex: none;
  aspect-ratio: 257 / 320;
  background-color: currentColor;
  -webkit-mask: url('/assets/help/lock-filled.png') center/contain no-repeat;
  mask: url('/assets/help/lock-filled.png') center/contain no-repeat;
}
.privacy-tile .lock { height: 26px; color: var(--accent); }
.trust .lock { height: 18px; }
.social-lock .lock { height: 14px; }

.privacy-block p {
  margin-top: 20px; font-size: var(--t-body); font-weight: 500; line-height: 1.6; color: var(--text-2);
}
.privacy-block .micro {
  margin-top: 18px; font-size: var(--t-micro); font-weight: 500; color: var(--text-3);
}

/* --------------------------------------------------------------------------
   Home — reviews
   -------------------------------------------------------------------------- */

.rating { text-align: center; margin-top: 34px; }
.rating b { font-size: 34px; font-weight: 700; letter-spacing: -.02em; }
.rating span { font-size: var(--t-micro); font-weight: 500; color: var(--text-2); margin-left: 6px; }

.reviews { display: grid; grid-template-columns: repeat(3, 1fr); gap: 24px; margin-top: 44px; }

/* Flex column + `margin-top:auto` on the byline. Grid already makes cards in a
   row equal height; without this the reviewer row floats directly under a
   2-line quote while its neighbour's sits under a 3-line one. */
.review {
  display: flex; flex-direction: column;
  background: var(--card);
  border: 1px solid var(--card-hairline);
  border-radius: var(--r-card);
  padding: 22px;
  transition: transform 150ms var(--ease), border-color 150ms var(--ease);
}
.review:hover { transform: translateY(-2px); border-color: var(--row-hover-hairline); }

.stars { display: flex; gap: 3px; color: var(--accent); }
/* Icon-sheet glyphs: square, one size, coloured by `currentColor`. */
.glyph {
  display: block; flex: none;
  width: 1em; height: 1em;
  background-color: currentColor;
  -webkit-mask: var(--g) center/contain no-repeat;
  mask: var(--g) center/contain no-repeat;
}
.stars .glyph { width: 13px; height: 13px; }

.review h3 { margin-top: 14px; font-size: 17px; font-weight: 700; line-height: 1.3; }
.review p { margin-top: 8px; font-size: 15px; font-weight: 500; line-height: 1.55; color: var(--text-2); }

.reviewer { display: flex; align-items: center; gap: 10px; margin-top: auto; padding-top: 20px; }
.reviewer .disc {
  width: 34px; height: 34px; border-radius: var(--r-pill); flex: none;
  background: var(--ghost); color: var(--text);
  display: flex; align-items: center; justify-content: center;
  font-size: 14px; font-weight: 700;
}
.reviewer .who { font-size: 14px; font-weight: 600; }
.reviewer .src { font-size: 14px; font-weight: 500; color: var(--text-3); }

/* Six cards on desktop, three on mobile. */
@media (max-width: 900px) {
  .reviews { grid-template-columns: 1fr; gap: 12px; margin-top: 28px; }
  .review:nth-child(n + 4) { display: none; }
}

/* --------------------------------------------------------------------------
   Home — FAQs
   -------------------------------------------------------------------------- */

.faqs { max-width: var(--max-faq); margin: 64px auto 0; display: grid; gap: 12px; }

.faq {
  background: var(--card);
  border: 1px solid var(--card-hairline);
  border-radius: var(--r-card);
  overflow: hidden;
}

/*
 * 🔴 An INWARD ring. `.faq` sets `overflow: hidden` for its rounded corners, and
 * the summary's border box coincides exactly with that clip — so a ring drawn at
 * the global `outline-offset: 3px` lands entirely outside the clip region and is
 * never painted. Measured: 0 accent pixels with the clip, ~4000 without.
 * A negative offset puts the ring inside the card, where it survives.
 * WCAG 2.4.7 — this is the only interactive control in those sections.
 */
.faq-q:focus-visible { outline-offset: -4px; border-radius: var(--r-card); }

.faq-q { list-style: none; }
.faq-q::-webkit-details-marker { display: none; }
.faq-q {
  width: 100%; display: flex; align-items: center; gap: 16px;
  padding: 20px 22px; background: none; border: 0; cursor: pointer;
  font-family: inherit; font-size: 17px; font-weight: 600; color: var(--text);
  text-align: left;
}
.faq-q .plus {
  margin-left: auto; flex: none; width: 16px; height: 16px;
  position: relative; transition: transform 280ms var(--ease);
}
.faq-q .plus::before, .faq-q .plus::after {
  content: ''; position: absolute; background: var(--text-3); border-radius: 2px;
}
.faq-q .plus::before { left: 0; right: 0; top: 7px; height: 2px; }
.faq-q .plus::after { top: 0; bottom: 0; left: 7px; width: 2px; }
.faq.is-open .faq-q .plus { transform: rotate(45deg); }

/*
 * 🔴 The open state is `.is-open`, NOT `[open]`.
 *
 * A <details> element has its non-summary children set to `display: none` by
 * the user agent the instant the `open` attribute is removed. Animating against
 * `[open]` therefore only ever animates in ONE direction: the expand can run,
 * but the collapse is over before it starts because the content has already
 * been taken out of the box.
 *
 * site.js keeps `open` on the element for the length of the close animation and
 * drives `.is-open` instead, so both directions play. Without that script the
 * accordion still works — it just opens and closes instantly, which is the
 * correct thing for it to degrade to.
 */
/* ⚠️ `.js`-scoped, like the arrival choreography. Unscoped, the collapse applies
   even when site.js never runs — a native <details> would open and still show
   nothing, leaving 7 answers across two pages unreadable. Scoped, the accordion
   degrades to plain open/close, which is the correct fallback. */
.js .faq-a { display: grid; grid-template-rows: 0fr; transition: grid-template-rows 280ms var(--ease); }
.js .faq.is-open .faq-a { grid-template-rows: 1fr; }
.faq-a > div { overflow: hidden; }
.faq-a p {
  padding: 0 22px 22px; font-size: 16px; font-weight: 500; line-height: 1.6; color: var(--text-2);
}
.js .faq-a p { opacity: 0; transition: opacity 200ms var(--ease); }
.js .faq.is-open .faq-a p { opacity: 1; }

/* --------------------------------------------------------------------------
   Home — closing CTA
   -------------------------------------------------------------------------- */

.closing { padding: var(--p-closing); text-align: center; }
.closing h2 {
  font-size: var(--t-cta-h2); font-weight: 700; letter-spacing: -.02em; line-height: 1.08;
}
.closing .btn { margin-top: 30px; }
.closing .micro { margin-top: 18px; font-size: var(--t-micro); font-weight: 500; color: var(--text-3); }

/*
 * The QR is for people reading on a desktop or an iPad — it hands them the site
 * on the phone they will actually install on. Hidden below the breakpoint,
 * because scanning a code with the device already displaying it is pointless.
 *
 * `aria-hidden` because it duplicates the download button beside it; a screen
 * reader gains nothing from an unscannable image, and the CTA is right there.
 *
 * ⚠️ Encodes usezap.app until `site.appStoreUrl` exists, then the App Store
 * link automatically — the SVG is generated at build time from that value.
 */
.qr { margin-top: 56px; display: flex; flex-direction: column; align-items: center; gap: 14px; }
.qr svg { width: 132px; height: 132px; border-radius: 14px; display: block; }
.qr p { font-size: 14px; font-weight: 500; color: var(--text-3); }
@media (max-width: 900px) { .qr { display: none; } }

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

.footer {
  flex: none;
  border-top: 1px solid var(--nav-hairline);
  padding: var(--p-footer);
  display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: 24px;
  background: var(--surface);
}

.footer-brand { display: flex; align-items: center; gap: 12px; }
.footer-brand .wordmark { color: rgba(255, 255, 255, .34); width: 66px; height: 23px; }
.footer-copy { font-size: var(--t-footer); font-weight: 500; color: var(--text-4); }

.footer-social { display: flex; align-items: center; justify-content: center; gap: 22px; }
/* 🔴 The tap area, not the glyph, must clear 24×24 (WCAG 2.2 AA, 2.5.8).
   These were 17×17 — the icon's own size with nothing around it. */
.footer-social a {
  color: var(--text-3); display: flex; align-items: center; justify-content: center;
  width: 32px; height: 32px; margin: -7px;
}
.footer-social a:hover { color: var(--text); }
.footer-social svg { width: 17px; height: 17px; }

.footer-links { display: flex; align-items: center; justify-content: flex-end; gap: 18px; }
/* Padding lifts these from 22px to 28px tall without moving them visually. */
.footer-links a {
  font-size: var(--t-footer); font-weight: 500; color: var(--text-3);
  display: inline-flex; align-items: center; min-height: 28px;
}
.footer-links a:hover { color: var(--text); }
.footer-links a.is-active { color: var(--text); }

@media (max-width: 900px) {
  .footer { grid-template-columns: 1fr; justify-items: start; gap: 20px; }
  .footer-social { justify-content: flex-start; }
  .footer-links { justify-content: flex-start; flex-wrap: wrap; gap: 14px; }
}

/* --------------------------------------------------------------------------
   About
   -------------------------------------------------------------------------- */

.about-hero {
  padding: var(--p-about-hero);
}
.about-hero-inner {
  max-width: var(--max-about); margin: 0 auto;
  display: grid; grid-template-columns: 1fr 1fr; gap: 64px; align-items: center;
}
.about-hero h1 {
  font-size: var(--t-about-h1); font-weight: 700; letter-spacing: -.022em; line-height: 1.06;
  max-width: 380px;   /* so it can never reach the column edge */
}
.about-hero p {
  margin-top: 22px; font-size: 16px; font-weight: 500; line-height: 1.6; color: var(--text-2);
  max-width: 420px;
}

/*
 * The founder cards size FROM THE COLUMN.
 * They were originally absolutely positioned at a hard-coded ±112px from
 * centre, which only stays inside the column above about 1000px wide; below
 * that the first card crossed the headline.
 */
.founders { display: flex; justify-content: center; align-items: center; }
.founder {
  flex: 1 1 0; max-width: 220px;
  aspect-ratio: 220 / 293;
  border-radius: var(--r-card);
  overflow: hidden; position: relative;
  box-shadow: var(--sh-founder);
  transform: translateX(var(--x, 0)) rotate(0deg) scale(.97);
}
.founder + .founder { margin-left: -12%; }
.founder img { width: 100%; height: 100%; object-fit: cover; }

.founder .pill {
  position: absolute; left: 12px; bottom: 12px;
  height: 34px; padding: 0 14px; border-radius: var(--r-pill);
  background: rgba(0, 0, 0, .62); color: #fff;
  display: inline-flex; align-items: center; font-size: 14px; font-weight: 600;
}

/* "Fan open" — waits for both photographs to finish decoding. */
.founders.is-fanned .founder {
  transition: transform 620ms var(--ease);
  transform: translateX(0) rotate(var(--r)) scale(1);
}
.founders.is-fanned .founder:nth-child(1) { --r: -6deg; transition-delay: 0ms; }
.founders.is-fanned .founder:nth-child(2) { --r: 5deg; transition-delay: 80ms; }

@media (max-width: 900px) {
  .about-hero-inner { grid-template-columns: 1fr; gap: 40px; }
  .about-hero h1, .about-hero p { max-width: none; }
  .founder { max-width: 178px; }
  .founders.is-fanned .founder:nth-child(1) { --r: -5deg; }
  .founders.is-fanned .founder:nth-child(2) { --r: 4deg; }
}

.about-heading { padding: var(--p-about-heading); }
.about-heading-inner { max-width: var(--max-about); margin: 0 auto; }
.about-heading h2 {
  font-size: var(--t-about-h2); font-weight: 700; letter-spacing: -.018em; line-height: 1.15;
}
.about-heading p {
  margin-top: 14px; font-size: 16px; font-weight: 500; line-height: 1.6; color: var(--text-2);
  max-width: 620px;
}

.about-cards { padding: var(--p-about-cards); }
.about-cards-inner {
  max-width: var(--max-about); margin: 0 auto;
  display: grid; grid-template-columns: 1fr 1fr; gap: 24px;
}
/*
 * Glass rather than a flat card — owner's call, 14 Aug 2026. Translucent so the
 * atmosphere wash reads through it, blurred so the text stays crisp, with an
 * inset top highlight for the lit edge that makes it look like a surface rather
 * than a hole. Revert to `background: var(--card)` if it does not land.
 */
.role, .contact-card {
  background: linear-gradient(rgba(255, 255, 255, .07), rgba(255, 255, 255, .025));
  backdrop-filter: blur(22px) saturate(140%);
  -webkit-backdrop-filter: blur(22px) saturate(140%);
  border: 1px solid rgba(255, 255, 255, .11);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .10), 0 20px 50px rgba(0, 0, 0, .34);
}
.role { border-radius: var(--r-feature); padding: 26px; }
.role h3 { font-size: 21px; font-weight: 700; }
.role .job { margin-top: 4px; font-size: 15px; font-weight: 600; color: var(--accent); }
.role p { margin-top: 16px; font-size: 15px; font-weight: 500; line-height: 1.6; color: var(--text-2); }
.role .ig {
  display: inline-flex; align-items: center; gap: 8px; margin-top: 20px;
  height: 34px; padding: 0 14px; border-radius: var(--r-pill);
  background: var(--ghost); color: var(--text); font-size: 14px; font-weight: 600;
}
.role .ig:hover { background: var(--ghost-hover); }
.role .ig svg { width: 14px; height: 14px; }

.about-body { padding: var(--p-about-body); }
.about-body-inner {
  max-width: var(--max-about); margin: 0 auto;
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 40px;
}
.about-body h3 { font-size: 20px; font-weight: 700; }
.about-body p { margin-top: 14px; font-size: 15px; font-weight: 500; line-height: 1.62; color: var(--text-2); }
.about-body .btn { margin-top: 20px; }

@media (max-width: 900px) {
  .about-cards-inner, .about-body-inner { grid-template-columns: 1fr; gap: 24px; }
}

/* --------------------------------------------------------------------------
   Legal and support — one shared 832px template
   -------------------------------------------------------------------------- */

.legal-head { padding: var(--p-legal-head); }
.legal-inner { max-width: var(--max-legal); margin: 0 auto; }
.legal-head h1 {
  font-size: var(--t-legal-h1); font-weight: 700; letter-spacing: -.02em; line-height: 1.06;
}
.legal-updated { margin-top: 14px; font-size: var(--t-micro); font-weight: 500; color: var(--text-3); }
.legal-standfirst { margin-top: 18px; font-size: var(--t-body); font-weight: 500; line-height: 1.6; color: var(--text-2); }

.legal-body { padding: var(--p-legal-body); }
.legal-section + .legal-section { margin-top: 44px; }
.legal-section h2 { font-size: 15px; font-weight: 600; line-height: 1.4; }
.legal-section p {
  margin-top: 12px; font-size: 16px; font-weight: 500; line-height: 1.62; color: var(--text-2);
}
.legal-section p + p { margin-top: 14px; }

.contacts { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; margin-bottom: 56px; }
/* Flex column + `margin-top:auto` on the address, so a 3-line body does not
   push its mailto below the neighbours' — same fix as the review bylines. */
.contact-card {
  display: flex; flex-direction: column;
  border-radius: var(--r-card); padding: 20px;
  transition: transform 150ms var(--ease), border-color 150ms var(--ease);
}
.contact-card:hover { transform: translateY(-2px); border-color: var(--row-hover-hairline); }
.contact-card h3 { font-size: 17px; font-weight: 700; }
.contact-card p { margin-top: 8px; font-size: 15px; font-weight: 500; line-height: 1.55; color: var(--text-2); }
.contact-card a { align-self: flex-start; margin-top: auto; padding-top: 14px; font-size: 15px; font-weight: 600; }

@media (max-width: 900px) { .contacts { grid-template-columns: 1fr; margin-bottom: 36px; } }

/* --------------------------------------------------------------------------
   Support — About's composition, without its expressiveness.

   The page previously ran heading → three boxes → four more boxes: seven
   rectangles of equal weight, so nothing led. Now it is human hero → one
   obvious primary action → self-service answers, and the measure is narrower
   than the marketing pages because the content has far less visual mass.
   -------------------------------------------------------------------------- */

.support-inner { max-width: 940px; margin: 0 auto; }

.support-hero { padding: 96px var(--gutter) 0; }
.support-hero .support-inner {
  display: grid; grid-template-columns: 1fr auto; gap: 48px; align-items: start;
}
.support-hero h1 {
  font-size: var(--t-legal-h1); font-weight: 700; letter-spacing: -.02em; line-height: 1.06;
}
.support-lede {
  margin-top: 18px; max-width: 420px;
  font-size: 20px; font-weight: 500; line-height: 1.45; color: var(--text);
}
.support-standfirst {
  margin-top: 14px; max-width: 460px;
  font-size: 16px; font-weight: 500; line-height: 1.6; color: var(--text-2);
}

/* The two founders, overlapping — the page's proposition is that a person
   reads this, so the people are the illustration. */
.support-humans { text-align: center; }
.support-avatars { display: flex; justify-content: center; }
.support-avatars img {
  width: 64px; height: 64px; border-radius: var(--r-pill); object-fit: cover;
  border: 2px solid var(--surface);
}
.support-avatars img + img { margin-left: -18px; }
.support-names { margin-top: 12px; font-size: 15px; font-weight: 600; }
.support-note { margin-top: 4px; font-size: 14px; font-weight: 500; color: var(--text-3); }

@media (max-width: 900px) {
  .support-hero { padding: 56px var(--gutter) 0; }
  .support-hero .support-inner { grid-template-columns: 1fr; gap: 32px; justify-items: start; }
  .support-humans { text-align: left; }
  .support-avatars { justify-content: flex-start; }
  .support-lede, .support-standfirst { max-width: none; }
}

.support-body { padding: 56px var(--gutter) 140px; }

.contacts { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; }

.contact-icon { display: block; color: var(--text-3); }
.contact-icon .glyph { width: 24px; height: 24px; }

.contact-card {
  display: flex; flex-direction: column;
  background: linear-gradient(rgba(255, 255, 255, .07), rgba(255, 255, 255, .025));
  backdrop-filter: blur(22px) saturate(140%);
  -webkit-backdrop-filter: blur(22px) saturate(140%);
  border: 1px solid rgba(255, 255, 255, .11);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .10), 0 20px 50px rgba(0, 0, 0, .34);
  border-radius: var(--r-card); padding: 22px;
  transition: transform 150ms var(--ease), border-color 150ms var(--ease);
}
.contact-card:hover { transform: translateY(-2px); border-color: var(--row-hover-hairline); }
.contact-card h3 { margin-top: 14px; font-size: 17px; font-weight: 700; }
.contact-card p { margin-top: 8px; font-size: 15px; font-weight: 500; line-height: 1.55; color: var(--text-2); }
.contact-card a { align-self: flex-start; margin-top: auto; padding-top: 16px; font-size: 15px; font-weight: 600; }

/* The primary route: an acid icon and a brighter edge. Enough to lead the eye,
   not so much that the other two read as disabled. */
.contact-card.is-primary { border-color: rgba(229, 255, 68, .28); }
.contact-card.is-primary .contact-icon { color: var(--accent); }

@media (max-width: 900px) {
  .contacts { grid-template-columns: 1fr; gap: 12px; }
  .support-body { padding: 40px var(--gutter) 96px; }
}

.topics-heading {
  margin-top: 88px;
  font-size: var(--t-about-h2); font-weight: 700; letter-spacing: -.018em; line-height: 1.15;
}
.support-answers { max-width: none; margin-top: 24px; }
@media (max-width: 900px) { .topics-heading { margin-top: 56px; } }

/* --------------------------------------------------------------------------
   /social — link-in-bio. No nav, no footer.
   Designed against iPhone 15, 393 × 852.
   -------------------------------------------------------------------------- */

/*
 * 🔴 No background and no stacking context here.
 *
 * This carried `background: var(--surface); position: relative; z-index: 0`,
 * which painted an opaque #1A1A19 over the whole page — and the atmosphere wash
 * sits at `z-index: -1` inside `.route`, so /social had NO glow at all. The
 * `.route` behind it already paints the surface colour, so nothing is lost by
 * dropping it and the wash comes back.
 */
.social-page {
  padding: var(--p-social);
  min-height: 100vh; min-height: 100svh;
  display: flex; flex-direction: column; align-items: center;
}
.social-col {
  width: 100%; max-width: var(--max-social);
  display: flex; flex-direction: column; align-items: center;
  flex: 1;
}
.social-col .wordmark { width: 108px; height: 38px; }
@media (max-width: 900px) { .social-col .wordmark { width: 134px; height: 47px; } }

.social-cta { width: 100%; }

.social-tagline {
  margin-top: 26px; text-align: center;
  font-size: var(--t-social-tagline); font-weight: 700; letter-spacing: -.018em; line-height: 1.2;
  color: rgba(255, 255, 255, .92);
}
.social-sub { margin-top: 14px; font-size: 16px; font-weight: 500; color: var(--text-2); text-align: center; }
.social-page .btn { margin-top: 30px; width: 100%; }
.social-allowance { margin-top: 16px; font-size: var(--t-micro); font-weight: 500; color: var(--text-3); text-align: center; }
/* `justify-content: center` — this is a flex ROW, so it does not inherit the
   column's `text-align: center` and was the one item hugging the left edge. */
/* .38 and .42 measured 3.54:1 and 3.95:1 — below the 4.5:1 the token comment
   at the top of this file establishes for body text. Raised to match. */
.social-lock {
  margin-top: 12px; display: flex; align-items: center; justify-content: center; gap: 8px;
  font-size: 14px; font-weight: 500; color: rgba(255, 255, 255, .52);
}


.follow-label {
  margin-top: 44px; font-size: 13px; font-weight: 600; letter-spacing: .12em;
  text-transform: uppercase; color: var(--text-3);
}

.social-rows { width: 100%; margin-top: 18px; display: grid; gap: 10px; }
.social-row {
  display: flex; align-items: center; gap: 14px;
  height: 58px; padding: 0 16px;
  background: var(--row-surface); border: 1px solid var(--row-hairline);
  border-radius: var(--r-card); color: var(--text);
  transition: background 150ms var(--ease), border-color 150ms var(--ease), transform 90ms var(--ease);
}
.social-row:hover { background: var(--row-hover); border-color: var(--row-hover-hairline); color: var(--text); }
.social-row:active { transform: translateY(2px); }
.social-row svg.brand { width: 20px; height: 20px; flex: none; }
.social-row .name { font-size: 17px; font-weight: 600; }
.social-row .handle { margin-left: auto; font-size: 16px; font-weight: 500; color: rgba(255, 255, 255, .56); }
.social-row .go { width: 13px; height: 13px; color: rgba(255, 255, 255, .34); flex: none; }
/* The Lucide glyphs carry a 24px intrinsic size; the box has to constrain them. */
.social-row .go svg { width: 100%; height: 100%; display: block; }

.social-foot { margin-top: auto; padding-top: 44px; text-align: center; }
.social-foot .pair { display: flex; align-items: center; justify-content: center; gap: 10px; }
.social-foot a {
  font-size: var(--t-footer); font-weight: 500; color: var(--text-3);
  display: inline-flex; align-items: center; min-height: 28px;
}
.social-foot a:hover { color: var(--text); }
.social-foot .dot { color: var(--text-4); font-size: 12px; }
.social-foot .copy { margin-top: 10px; font-size: 13px; font-weight: 500; color: var(--text-4); }

/* --------------------------------------------------------------------------
   404
   -------------------------------------------------------------------------- */

.notfound { padding: var(--p-404); text-align: center; }
.notfound .wordmark { margin: 0 auto; width: 76px; height: 27px; }
.notfound h1 {
  margin-top: 26px;
  font-size: var(--t-404-h1); font-weight: 700; letter-spacing: -.02em; line-height: 1.1;
}
.notfound p { margin-top: 14px; font-size: 16px; font-weight: 500; color: var(--text-2); }
.notfound-actions {
  margin-top: 30px; display: flex; align-items: center; justify-content: center; gap: 18px; flex-wrap: wrap;
}
.notfound-actions a.text { font-size: 16px; font-weight: 600; color: var(--text-2); }
.notfound-actions a.text:hover { color: var(--text); }

/* --------------------------------------------------------------------------
   Reduced motion
   -------------------------------------------------------------------------- */

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

  [data-arrive], [data-reveal], .route[data-route-arrive] { transform: none !important; }
  [data-arrive].is-in, [data-reveal].is-in, .route[data-route-arrive].is-in {
    transition: opacity 150ms linear !important;
  }

  /* No fan, no hint swipe. */
  .founder, .founders.is-fanned .founder { transform: none !important; transition: none !important; }
  .founder + .founder { margin-left: 12px; }

  /* No staggered entrance on the mobile menu. The rows' resting state is
     visible, so removing the animation leaves them readable rather than blank. */
  .menu.is-open .menu-row { animation: none !important; }

  .btn:active, .social-row:active, .feature:hover, .review:hover, .contact-card:hover {
    transform: none !important;
  }
}
