@charset "UTF-8";
/* ===========================================================================
   Qashly — marketing site
   ---------------------------------------------------------------------------
   Single authored stylesheet, organised with @layer. Cascade order is fixed by
   the layer declaration below, so a rule's power comes from WHICH LAYER it is
   in, never from source order or specificity games.

   Tokens are ported from the app so recreated screens sit natively beside real
   screenshots:
     constants/Colors.ts
     components/onboarding/ui/onboardingTokens.ts

   House rules inherited from the app (do not break these):
     - Motion is TIMING-BASED. Springs are forbidden; the team found stacked
       springs read as screen shake.
     - Frame numbers as what's LEFT, never what's used.

   Two house rules were deliberately REVISED in 2026-07 after the first build
   read as static, and the reasons are worth keeping:

     - "Travel is tiny: 10px drift." True in a 393pt phone viewport. On a
       1440px page, 10px over 220ms is below the perceptual threshold — it
       reads as a repaint, not a motion. Web travel is 32px over 680ms.
     - "Only two looping animations exist on this page." Ambient animation was
       removed from the APP in 2026-07 because ~20 concurrent Reanimated loops
       cooked phone batteries. The web has no such constraint provided every
       loop is compositor-only and PAUSED OFF-SCREEN — which is what the gate's
       data-on attribute exists to do. Loops that repaint are still forbidden.

   The compositor allowlist for anything per-frame or looping:
     transform · translate · rotate · scale · opacity
   Never: width, height, top, left, background-position, background-color,
   box-shadow, border-radius, clip-path, gradient stops.
   =========================================================================== */

@layer reset, tokens, base, layout, components, sections, utilities, motion;

/* ═══ tokens ═══════════════════════════════════════════════════════════════ */
@layer tokens {
  :root {
    color-scheme: dark;

    /* ── Ground ────────────────────────────────────────────────────────── */
    /* The onboarding flat fill, not the app gradient. The app's own note:
       "30pt headlines need a quiet field behind them" — truer still at 76px. */
    --bg: #17183a;
    --bg-deep: #141534;
    --bg-raised: #1d1e3f;
    --bg-app-grad: linear-gradient(160deg, #232259 0%, #2f2e57 55%, #1d1e3f 100%);

    /* ── Brand ─────────────────────────────────────────────────────────── */
    /* Indigo is the system; violet is the halo. The mascot and app icon sit on
       a violet ground, the product UI is indigo — without this split the page
       reads as two products. --halo-violet appears ONLY in radial-gradient(). */
    --brand: #6366f1;
    --brand-deep: #4f46e5;
    --brand-soft: #a5b4fc; /* ALL brand-coloured TEXT on dark — 8.58:1 */
    --brand-cta-from: #5a5dee; /* white on #6366F1 is 4.47:1 — AA fail */
    --brand-cta-to: #4f46e5;
    --halo-violet: #6b46c1;

    /* ── Surfaces ──────────────────────────────────────────────────────── */
    --surface: rgb(255 255 255 / 0.05);
    --surface-hover: rgb(255 255 255 / 0.07);
    --surface-raised: rgb(255 255 255 / 0.08);
    --surface-high: rgb(255 255 255 / 0.12);
    --surface-select: rgb(99 102 241 / 0.15);
    --surface-input: rgb(255 255 255 / 0.03); /* the app's inputBackground */

    --line: rgb(255 255 255 / 0.1); /* decorative hairline */
    --line-soft: rgb(255 255 255 / 0.08);
    --line-strong: rgb(255 255 255 / 0.16); /* hover */
    --line-control: rgb(255 255 255 / 0.38); /* 3.53:1 — sole boundary of a control */
    --line-inner: inset 0 1px 0 rgb(255 255 255 / 0.07);

    /* ── Text ──────────────────────────────────────────────────────────── */
    --text: #ffffff; /* 17.10:1 */
    --text-2: #9a9ab7; /*  6.25:1 */
    --text-3: #9394b0; /*  5.78:1 — solid, never stacked opacity */
    --text-micro: #9394b0;

    /* ── Semantic ──────────────────────────────────────────────────────── */
    --income: #86efac; /* 12.18:1 */
    --expense: #f87171; /*  6.18:1 */
    --warning: #ff9f0a; /*  8.32:1 */
    --ok: #4cd964;
    --warn: #ff9500;
    --over: #ff3b30; /* STROKE/FILL only — as text use --expense */

    /* ── Pastels — icon chips, donut segments, category dots ───────────── */
    --p-sky: #7dd3fc;
    --p-amber: #fcd34d;
    --p-indigo: #a5b4fc;
    --p-orange: #fdba74;
    --p-violet: #c4b5fd;
    --p-pink: #f9a8d4;
    --p-green: #86efac;
    --p-red: #f87171;
    --p-teal: #5eead4;
    --p-gold: #ffd60a;

    /* ── Tool accents (match the app's Explore grid) ───────────────────── */
    --t-subs: var(--p-pink);
    --t-loans: var(--p-indigo);
    --t-calc: var(--p-sky);
    --t-fx: var(--p-teal);
    --t-goals: var(--p-amber);
    --t-bills: var(--p-green);
    --t-inv: var(--p-orange);
    --t-tax: var(--p-violet);

    /* ── Gradients ─────────────────────────────────────────────────────── */
    --grad-cta: linear-gradient(135deg, var(--brand-cta-from), var(--brand-cta-to));
    --grad-ai: linear-gradient(160deg, rgb(99 102 241 / 0.16), rgb(79 70 229 / 0.05));
    --grad-halo: radial-gradient(
      50% 50% at 50% 45%,
      color-mix(in oklab, var(--halo-violet) 55%, transparent) 0%,
      color-mix(in oklab, var(--halo-violet) 18%, transparent) 45%,
      transparent 72%
    );

    /* ── Grounds for the full-bleed moments ────────────────────────────
       The first build ran four navies within a ~9-value luminance spread,
       which on a real monitor is no transition at all. These are the three
       places the page actually changes state. */

    /* Paper — the privacy ledger. Warm, not white: pure #fff against a dark
       page is a flashbang, and a bank statement isn't white either. */
    --paper: #f4f2ee;
    --paper-ink: #141534;
    --paper-ink-2: #4a4a63; /* 7.28:1 on --paper */
    --paper-rule: #d9d5cd;
    --paper-in: #16a34a; /* every pastel fails on white; these are the */
    --paper-out: #c2410c; /* saturated equivalents, both AA on --paper */
    --paper-brand: #4338ca;

    /* Violet — the mascot field. The one place --halo-violet appears at
       scale, which extends the indigo/violet split rather than breaking it. */
    --grad-violet: radial-gradient(90% 70% at 62% 40%, #3a1c6e, #1b0d33 60%, #0c0627);

    /* Ember — the celebration. Verbatim from StreakCelebrationPopup CARD_BG. */
    --grad-ember: linear-gradient(180deg, #20214c 0%, #141335 55%, #0e0c24 100%);
    --ember-hot: #ff5f3d;
    --ember-warm: #ff9f43;
    --ember-gold: #ffc53d;

    /* ── Night — the ONE NIGHT act grounds ─────────────────────────────────
       The page is one night: dusk → midnight → the violet night shift → a
       paper daybreak → lamp-lit tax → pre-dawn → ember dawn. Grades are
       DISCRETE per-section grounds joined by pre-painted gradebands; nothing
       ever animates a gradient stop (M6). */
    --night: #0b0c26; /* dusk ground, one step darker than --bg */
    --night-deep: #070818; /* the very top of the page */
    --midnight: #10112b; /* Act II — the fold */
    --violet-shift: #2b2154; /* Act III — engines at work */
    --violet-ai: #312e81; /* Act III — the console's electric core */
    --lamp-ground: #191a38; /* Act IV — tax desk under the lamp */
    --predawn: #1b1c3a; /* Act V — numbers, pricing, FAQ */
    --ember-ground: #191033; /* Act VI — the finale's floor */
    --grad-night: linear-gradient(180deg, #070818 0%, #0b0c26 45%, #17183a 100%);

    /* Aurora — painted in the app's own colours: brand indigo, the splash
       violet, and the income green as a cold veil. Gradient stops only. */
    --aurora-indigo: #6366f1;
    --aurora-violet: #a78bfa;
    --aurora-green: #86efac;
    --aurora-sky: #7dd3fc;
    --grain-a: 0.05; /* film-grain opacity ceiling, site-wide */

    /* Screenshot geometry — build-assets SHOT_CROP crops every shot to this.
       The frame that wraps one must adopt it or the image letterboxes. */
    --shot-ar: 1320 / 2709;

    /* ── Type ──────────────────────────────────────────────────────────── */
    --font: "Plus Jakarta Sans", "PJS Fallback", system-ui, -apple-system, sans-serif;

    /* Fluid 375 → 1440. Growth stops at 1440: headlines that keep inflating at
       1920 read as a slide deck. rem+vw form so browser zoom still works. */
    /* --t-stat is the page's display register: the pinned payoff, the four
       numbers band, the streak count. Used exactly six times. Without a tier
       this far above --t-h2 the page has no typographic peak at all — the
       first build ran 40px → 15px for its entire length below the hero. */
    --t-stat: clamp(3.5rem, 2rem + 8.5vw, 7.5rem); /*  56 → 120 */
    --t-quote: clamp(1.75rem, 1.2rem + 2.4vw, 3rem); /*  28 →  48 */
    --t-display: clamp(2.5rem, 1.66rem + 3.58vw, 4.75rem);
    --t-h1: clamp(2.125rem, 1.6rem + 2.24vw, 3.5rem);
    --t-h2: clamp(1.75rem, 1.4rem + 1.5vw, 2.5rem);
    --t-h3: clamp(1.25rem, 1.14rem + 0.47vw, 1.625rem);
    --t-lead: clamp(1.0625rem, 0.97rem + 0.38vw, 1.3125rem);
    --t-body: clamp(1rem, 0.98rem + 0.09vw, 1.0625rem);
    --t-option: clamp(1.0625rem, 1.05rem + 0.09vw, 1.125rem);
    --t-cta: 1.0625rem;
    --t-meta: 0.8125rem;
    --t-micro: 0.75rem;
    --t-caption: 0.75rem;

    --measure: 65ch;
    --measure-lead: 46ch;
    --measure-title: 17ch;
    --measure-prose: 72ch;

    /* ── Geometry ──────────────────────────────────────────────────────── */
    --r-chip: 14px;
    --r-tile: 18px;
    --r-panel: 22px;
    --r-card: 24px;
    --r-plate: 28px;
    --r-pill: 999px;

    --s-1: 4px;
    --s-2: 8px;
    --s-3: 11px; /* the app's inter-card gap */
    --s-4: 14px; /* the app's card padding */
    --s-5: 18px;
    --s-6: 24px; /* onboarding gutter */
    --s-7: 32px;
    --s-8: 44px;
    --s-9: 64px;
    --s-10: 96px;

    --card-pad: clamp(18px, 1.6vw, 26px);
    --section-y: clamp(72px, 8.5vw, 136px);
    --section-y-tight: clamp(48px, 5vw, 80px);
    --section-y-tall: clamp(96px, 10vw, 168px);
    --gutter: clamp(20px, 5vw, 40px);

    /* ── Shadows — always tinted toward the ground, never pure black ───── */
    --sh-1: 0 1px 2px rgb(8 8 24 / 0.4);
    --sh-2: 0 4px 12px -2px rgb(8 8 24 / 0.45);
    --sh-3: 0 12px 28px -8px rgb(8 8 24 / 0.55);
    --sh-4: 0 28px 64px -20px rgb(8 8 24 / 0.7);
    --sh-phone: 0 40px 90px -30px rgb(0 0 0 / 0.75), 0 8px 24px -8px rgb(0 0 0 / 0.5);

    --glow-cta: 0 8px 32px -4px rgb(99 102 241 / 0.45), 0 2px 6px rgb(79 70 229 / 0.35);
    --glow-cta-hover: 0 10px 44px -4px rgb(99 102 241 / 0.6), 0 2px 8px rgb(79 70 229 / 0.45);
    --glow-focus: 0 0 0 4px rgb(99 102 241 / 0.35);

    /* ── Motion — the app's Reanimated curves, exactly ─────────────────── */
    --ease-out: cubic-bezier(0.33, 1, 0.68, 1); /* Easing.out(Easing.cubic) */
    --ease-in-out: cubic-bezier(0.45, 0, 0.55, 1);
    /* A longer tail than --ease-out. At 680ms of travel the cubic settles too
       early and the last third reads as drift; this holds velocity longer and
       arrives. */
    --ease-reveal: cubic-bezier(0.16, 1, 0.3, 1);

    --d-press-in: 90ms;
    --d-press-out: 160ms;
    --d-micro: 180ms;
    --d-enter: 220ms;
    --d-enter-lg: 260ms;
    --d-select: 200ms;
    --d-reveal: 680ms;
    --d-chart: 1100ms;
    --d-draw: 1200ms;
    --d-shimmer: 1800ms;
    --d-dwell: 1400ms;
    --d-reduced: 120ms;

    --travel-y: 32px;
    --travel-x: 28px;
    --enter-scale: 0.94;
    --press-scale: 0.97;
    --stagger: 80ms;

    /* ── z-index — a short, closed list. Nothing else gets one. ────────── */
    --z-raised: 1;
    --z-sticky: 10;
    --z-nav: 100;
    --z-dialog: 1000;
    --z-toast: 1100;
  }
}

/* ═══ reset ════════════════════════════════════════════════════════════════ */
@layer reset {
  *,
  *::before,
  *::after {
    box-sizing: border-box;
  }
  * {
    margin: 0;
  }
  html {
    -webkit-text-size-adjust: 100%;
    scroll-behavior: smooth;
    scroll-padding-top: 88px;
  }
  body {
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;
    min-height: 100svh;
  }
  img,
  picture,
  svg,
  video {
    display: block;
    max-width: 100%;
  }
  /* Every <img> carries width/height attributes to reserve layout (CLS 0).
     Without this, constraining only the width leaves the HEIGHT attribute in
     force and the image renders stretched. Components that size deliberately
     (the store badges) override from a later layer. */
  img {
    height: auto;
  }
  input,
  button,
  textarea,
  select {
    font: inherit;
    color: inherit;
  }
  button {
    background: none;
    border: 0;
    cursor: pointer;
  }
  ul,
  ol {
    list-style: none;
    padding: 0;
  }
  a {
    color: inherit;
    text-decoration: none;
  }
  :target {
    scroll-margin-top: 96px;
  }
}

/* ═══ base ═════════════════════════════════════════════════════════════════ */
@layer base {
  @font-face {
    font-family: "Plus Jakarta Sans";
    font-style: normal;
    font-weight: 500 800; /* one variable file covers all four weights */
    font-display: swap;
    src: url("/assets/fonts/plus-jakarta-sans-latin-var.woff2") format("woff2");
    /* Verbatim from Google's CSS2 response. £ is U+00A3, inside U+0000-00FF. */
    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;
  }

  /* Metric-matched fallback so font-display:swap causes no layout shift. */
  @font-face {
    font-family: "PJS Fallback";
    src: local("Arial"), local("Helvetica Neue"), local("Roboto");
    size-adjust: 105.5%;
    ascent-override: 100%;
    descent-override: 24%;
    line-gap-override: 0%;
  }

  body {
    font-family: var(--font);
    font-size: var(--t-body);
    font-weight: 500; /* 400 goes thin and grey on a dark ground */
    line-height: 1.6;
    color: var(--text);
    background: var(--bg);
  }

  h1,
  h2,
  h3,
  h4 {
    font-weight: 800;
    line-height: 1.1;
    letter-spacing: -0.024em;
    text-wrap: balance;
  }
  h1 {
    font-size: var(--t-display);
    line-height: 1.04;
    letter-spacing: -0.028em;
  }
  h2 {
    font-size: var(--t-h2);
    line-height: 1.14;
    letter-spacing: -0.02em;
  }
  h3 {
    font-size: var(--t-h3);
    font-weight: 700;
    line-height: 1.28;
    letter-spacing: -0.012em;
  }

  p {
    text-wrap: pretty;
  }

  strong,
  b {
    font-weight: 700;
    color: var(--text);
  }

  /* Every number on this page is tabular. */
  .num,
  [data-num],
  table {
    font-variant-numeric: tabular-nums;
  }

  :focus-visible {
    outline: 2px solid var(--brand-soft);
    outline-offset: 3px;
    border-radius: 6px;
  }

  ::selection {
    background: rgb(99 102 241 / 0.45);
    color: #fff;
  }

  /* Windows High Contrast */
  @media (forced-colors: active) {
    .c-cta {
      border: 2px solid ButtonText;
      forced-color-adjust: none;
      background: ButtonFace;
      color: ButtonText;
    }
    .c-cta::after {
      display: none;
    }
    .c-card,
    .c-plan {
      border: 1px solid CanvasText;
    }
  }
}

/* ═══ layout ═══════════════════════════════════════════════════════════════ */
@layer layout {
  .l-container {
    width: 100%;
    max-width: 1200px;
    margin-inline: auto;
    padding-inline: var(--gutter);
  }
  .l-container--wide {
    max-width: 1360px;
  }
  .l-container--narrow {
    max-width: 760px;
  }
  @media (min-width: 1600px) {
    .l-container {
      max-width: 1280px;
    }
    .l-container--wide {
      max-width: 1440px;
    }
  }

  .l-section {
    padding-block: var(--section-y);
    position: relative;
  }
  .l-section--raised {
    background: var(--bg-raised);
  }
  .l-section--deep {
    background: var(--bg-deep);
  }

  .l-head {
    display: grid;
    gap: var(--s-4);
    margin-bottom: clamp(32px, 4vw, 64px);
    max-width: var(--measure-lead);
  }
  .l-head--center {
    margin-inline: auto;
    text-align: center;
  }
  .l-head > p {
    font-size: var(--t-lead);
    color: var(--text-2);
    line-height: 1.5;
    letter-spacing: -0.008em;
  }

  .l-grid {
    display: grid;
    gap: clamp(12px, 1.6vw, 24px);
  }
  .l-grid--2 {
    grid-template-columns: 1fr;
  }
  .l-grid--3 {
    grid-template-columns: 1fr;
  }
  .l-grid--4 {
    grid-template-columns: repeat(2, 1fr);
  }
  @media (min-width: 768px) {
    .l-grid--2 {
      grid-template-columns: repeat(2, 1fr);
    }
    .l-grid--3 {
      grid-template-columns: repeat(2, 1fr);
    }
    .l-grid--4 {
      grid-template-columns: repeat(4, 1fr);
    }
  }
  @media (min-width: 1024px) {
    .l-grid--3 {
      grid-template-columns: repeat(3, 1fr);
    }
  }

  .l-skip {
    position: absolute;
    left: -9999px;
  }
  .l-skip:focus {
    left: var(--gutter);
    top: 12px;
    z-index: var(--z-toast);
    padding: 12px 18px;
    background: var(--bg-deep);
    border: 1px solid var(--line-control);
    border-radius: 10px;
  }
}

/* ═══ components ═══════════════════════════════════════════════════════════ */
@layer components {
  /* ── Micro-label — the app's signature ─────────────────────────────────
     App is 11px / +0.5px / opacity .5. On the web: 12px (11px at 1x on a
     desktop is genuinely small), a solid colour (opacity-stacking is what
     pushed the app's tertiary to a failing 4.41:1), and 0.06em (0.5px at 11px
     = 0.045em reads under-tracked where web hinting differs). */
  .c-micro {
    font-size: var(--t-micro);
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--text-micro);
    line-height: 1.3;
  }

  /* ── Nav ───────────────────────────────────────────────────────────── */
  .c-nav {
    position: sticky;
    top: 0;
    z-index: var(--z-nav);
    height: 64px;
    display: flex;
    align-items: center;
    transition: height var(--d-enter) var(--ease-out),
      background-color var(--d-enter) var(--ease-out),
      border-color var(--d-enter) var(--ease-out);
    border-bottom: 1px solid transparent;
  }
  @media (min-width: 1024px) {
    .c-nav {
      height: 72px;
    }
  }
  .is-scrolled .c-nav {
    height: 60px;
    background: color-mix(in srgb, var(--bg) 78%, transparent);
    backdrop-filter: blur(20px) saturate(140%);
    -webkit-backdrop-filter: blur(20px) saturate(140%);
    border-bottom-color: var(--line-soft);
  }
  .c-nav__inner {
    display: flex;
    align-items: center;
    gap: var(--s-6);
    width: 100%;
  }
  .c-nav__brand {
    display: flex;
    align-items: center;
    gap: 10px;
    font-weight: 800;
    font-size: 1.125rem;
    letter-spacing: -0.02em;
    margin-inline-end: auto;
  }
  .c-nav__brand svg,
  .c-nav__brand img {
    width: 30px;
    height: 30px;
    flex: none;
  }
  .c-nav__links {
    display: none;
    align-items: center;
    gap: 2px;
  }
  @media (min-width: 1024px) {
    .c-nav__links {
      display: flex;
    }
  }
  .c-nav__links a {
    color: var(--text-2);
    padding: 10px 14px;
    border-radius: 10px;
    font-size: 0.9375rem;
    font-weight: 600;
    transition: color var(--d-micro) var(--ease-out),
      background-color var(--d-micro) var(--ease-out);
  }
  .c-nav__links a:hover {
    color: var(--text);
    background: var(--surface);
  }
  /* Always visible — the first frame a visitor sees must carry the CTA.
     (It used to fade in on scroll, which left a hole in the nav at the top
     of the page.) */
  @media (max-width: 419px) {
    .c-nav__cta {
      display: none;
    }
  }

  /* ── The signature CTA ─────────────────────────────────────────────────
     64pt pill, indigo gradient, a white band sweeping every 1800ms, and an
     indigo glow beneath. This is the most distinctive interactive element in
     the product — see components/onboarding/OnboardingCTA.tsx. */
  .c-cta {
    position: relative;
    display: inline-grid;
    grid-auto-flow: column;
    align-items: center;
    justify-content: center;
    gap: 10px;
    block-size: 60px;
    padding-inline: 30px;
    border-radius: var(--r-pill);
    background: var(--grad-cta);
    color: #fff;
    font-size: var(--t-cta);
    font-weight: 800;
    line-height: 1;
    letter-spacing: -0.008em;
    overflow: clip; /* not `hidden` — Safari bleeds at pill radii */
    isolation: isolate;
    box-shadow: var(--glow-cta);
    transition: box-shadow var(--d-enter) var(--ease-out),
      transform var(--d-press-out) var(--ease-out), filter var(--d-micro) var(--ease-out);
  }
  .c-cta::after {
    content: "";
    position: absolute;
    inset-block: 0;
    z-index: -1; /* under the label, over the gradient */
    inline-size: 32%;
    background: linear-gradient(
      100deg,
      rgb(255 255 255 / 0) 0%,
      rgb(255 255 255 / 0.28) 50%,
      rgb(255 255 255 / 0) 100%
    );
    translate: -160% 0;
    animation: cta-sweep var(--d-shimmer) linear infinite;
    pointer-events: none;
  }
  @keyframes cta-sweep {
    to {
      translate: 420% 0;
    }
  }
  .c-cta:hover {
    box-shadow: var(--glow-cta-hover);
    filter: brightness(1.06);
  }
  .c-cta:active {
    transform: scale(var(--press-scale));
    transition-duration: var(--d-press-in);
  }
  .c-cta:focus-visible {
    outline: 2px solid var(--brand-soft);
    outline-offset: 3px;
    box-shadow: var(--glow-cta), var(--glow-focus);
  }
  .c-cta--sm {
    block-size: 46px;
    padding-inline: 20px;
    font-size: 0.9375rem;
  }
  .c-cta--ghost {
    background: var(--surface);
    color: var(--text-2);
    box-shadow: none;
    block-size: 52px;
    border: 1px solid var(--line-control);
  }
  .c-cta--ghost::after {
    display: none;
  }
  .c-cta--ghost:hover {
    background: var(--surface-hover);
    color: var(--text);
    filter: none;
    box-shadow: none;
  }
  @media (max-width: 639px) {
    .c-cta--hero {
      inline-size: 100%;
    }
  }

  /* ── Store badges ──────────────────────────────────────────────────────
     Apple: >=40px tall, clear space >=1/4 height, no recolouring, and the
     App Store badge goes FIRST in the lineup.
     Google: no recolouring, and the Play badge must match or exceed the size
     of other store badges.
     Clear space is expressed as calc(height / 4) so it cannot drift. */
  .c-badges {
    --badge-h: 48px;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: calc(var(--badge-h) / 4);
    margin: calc(var(--badge-h) / -8);
    padding: calc(var(--badge-h) / 8);
  }
  .c-badge {
    display: inline-block;
    border-radius: 9px;
    transition: transform var(--d-micro) var(--ease-out),
      filter var(--d-micro) var(--ease-out);
  }
  .c-badge img,
  .c-badge svg {
    height: var(--badge-h);
    width: auto;
    display: block;
  }
  /* Google's official artwork has ~10% built-in clear space baked into the
     PNG, so at an equal box height its visible mark reads smaller than
     Apple's. Google's guidelines require the Play badge to match or exceed
     the other store badges, so compensate on the box to equalise the MARKS. */
  .c-badge--play img {
    height: calc(var(--badge-h) * 1.2);
  }
  .c-badge:hover {
    transform: translateY(-2px);
    filter: brightness(1.08);
  }
  .c-badge:focus-visible {
    outline: 2px solid var(--brand-soft);
    outline-offset: 4px;
  }

  /* ── Card ──────────────────────────────────────────────────────────── */
  .c-card {
    border-radius: var(--r-card);
    padding: var(--card-pad);
    background: var(--surface);
    border: 1px solid var(--line);
    box-shadow: var(--line-inner);
    display: grid;
    gap: var(--s-4);
    align-content: start;
    transition: background-color var(--d-micro) var(--ease-out),
      border-color var(--d-micro) var(--ease-out), transform var(--d-micro) var(--ease-out);
  }
  .c-card:hover {
    background: var(--surface-hover);
    border-color: var(--line-strong);
    transform: translateY(-2px);
  }
  .c-card p {
    color: var(--text-2);
    max-width: 44ch;
  }

  /* ── Icon chip — squircle, not a circle ────────────────────────────── */
  .c-chip {
    --accent: var(--brand);
    inline-size: 44px;
    block-size: 44px;
    display: grid;
    place-items: center;
    border-radius: var(--r-chip);
    background: color-mix(in srgb, var(--accent) 22%, transparent);
    color: var(--accent);
    flex: none;
  }
  .c-chip svg {
    width: 22px;
    height: 22px;
  }
  @supports (corner-shape: squircle) {
    .c-chip {
      border-radius: 42%;
      corner-shape: squircle;
    }
  }

  /* ── Phone frame ───────────────────────────────────────────────────────
     --u is one app point expressed in CSS px, so every interior number below
     is literally the number from the app source (card radius 24, padding 14,
     gap 11, chip 44/14). Never transform:scale() — once will-change is set,
     Chrome rasterises pre-transform and the text goes soft. */
  .c-phone {
    --pw: 320px;
    --u: calc(var(--pw) / 393); /* iPhone 15 logical width = 393pt */
    inline-size: var(--pw);
    aspect-ratio: 393 / 852;
    position: relative;
    border-radius: calc(48 * var(--u));
    background: #0b0c21;
    border: calc(2 * var(--u)) solid rgb(255 255 255 / 0.14);
    padding: calc(5 * var(--u));
    box-shadow: var(--sh-phone), inset 0 1px 0 rgb(255 255 255 / 0.22);
    isolation: isolate;
    flex: none;
  }
  @media (max-width: 767px) {
    .c-phone {
      --pw: 250px;
    }
  }
  .c-phone__screen {
    position: relative;
    height: 100%;
    overflow: hidden;
    border-radius: calc(42 * var(--u));
    background: var(--bg-app-grad);
    padding: calc(52 * var(--u)) calc(16 * var(--u)) calc(16 * var(--u));
    display: flex;
    flex-direction: column;
    gap: calc(11 * var(--u));
  }
  .c-phone__screen::before {
    /* Dynamic Island */
    content: "";
    position: absolute;
    z-index: 3;
    top: calc(11 * var(--u));
    left: 50%;
    translate: -50% 0;
    inline-size: calc(96 * var(--u));
    block-size: calc(30 * var(--u));
    border-radius: var(--r-pill);
    background: #000;
  }
  .c-phone__screen::after {
    /* Home indicator */
    content: "";
    position: absolute;
    z-index: 3;
    bottom: calc(8 * var(--u));
    left: 50%;
    translate: -50% 0;
    inline-size: calc(134 * var(--u));
    block-size: calc(5 * var(--u));
    border-radius: var(--r-pill);
    background: rgb(255 255 255 / 0.28);
  }

  /* Phone interior primitives */
  .ph-micro {
    font-size: calc(11 * var(--u));
    font-weight: 600;
    letter-spacing: 0.055em;
    text-transform: uppercase;
    color: #9394b0;
    line-height: 1.3;
  }
  .ph-card {
    border-radius: calc(24 * var(--u));
    padding: calc(14 * var(--u));
    background: var(--surface);
    border: 1px solid var(--line);
    box-shadow: var(--line-inner);
    display: grid;
    gap: calc(8 * var(--u));
    align-content: start;
  }
  .ph-amount {
    font-size: calc(26 * var(--u));
    font-weight: 800;
    letter-spacing: -0.03em;
    color: #fff;
    font-variant-numeric: tabular-nums;
    line-height: 1.1;
  }
  .ph-foot {
    font-size: calc(12 * var(--u));
    font-weight: 500;
    color: #9a9ab7;
    line-height: 1.35;
  }
  .ph-pair {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: calc(11 * var(--u));
  }
  /* Dashboard greeting + AI hero card — mirrors components/dashboard/AiHeroCard */
  .ph-greet {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: calc(8 * var(--u));
  }
  .ph-greet__hi {
    font-size: calc(19 * var(--u));
    font-weight: 800;
    letter-spacing: -0.02em;
    line-height: 1.15;
    margin-bottom: calc(3 * var(--u));
  }
  .ph-pro {
    padding: calc(3 * var(--u)) calc(9 * var(--u));
    border-radius: var(--r-pill);
    background: var(--brand-cta-from);
    color: #fff;
    font-size: calc(10.5 * var(--u));
    font-weight: 700;
    flex: none;
  }
  .ph-ai {
    border-radius: calc(24 * var(--u));
    padding: calc(14 * var(--u));
    background: var(--grad-ai);
    border: 1px solid rgb(99 102 241 / 0.28);
    display: grid;
    gap: calc(12 * var(--u));
  }
  .ph-ai__top {
    display: grid;
    grid-template-columns: auto 1fr;
    gap: calc(12 * var(--u));
    align-items: center;
  }
  .ph-orb {
    inline-size: calc(46 * var(--u));
    block-size: calc(46 * var(--u));
    border-radius: 50%;
    background: var(--brand);
    display: grid;
    place-items: center;
    box-shadow: 0 0 0 calc(4 * var(--u)) rgb(99 102 241 / 0.16);
  }
  .ph-orb svg {
    inline-size: calc(20 * var(--u));
    block-size: calc(20 * var(--u));
  }
  .ph-ai__h {
    font-size: calc(15 * var(--u));
    font-weight: 800;
    letter-spacing: -0.01em;
    margin-bottom: calc(2 * var(--u));
  }
  .ph-ai__tiles {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: calc(8 * var(--u));
  }
  .ph-ai__tiles span {
    padding: calc(9 * var(--u)) 0;
    border-radius: calc(14 * var(--u));
    background: rgb(255 255 255 / 0.06);
    border: 1px solid var(--line);
    text-align: center;
    font-size: calc(12 * var(--u));
    font-weight: 600;
  }

  .ph-row {
    display: grid;
    grid-template-columns: calc(8 * var(--u)) 1fr auto;
    gap: calc(9 * var(--u));
    align-items: center;
    font-size: calc(12.5 * var(--u));
  }
  .ph-row__dot {
    inline-size: calc(8 * var(--u));
    block-size: calc(8 * var(--u));
    border-radius: 50%;
    background: var(--c, var(--brand));
  }
  .ph-row__n {
    color: #fff;
    font-weight: 600;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .ph-row__a {
    font-weight: 700;
    font-variant-numeric: tabular-nums;
  }
  .ph-row__a--out {
    color: var(--expense);
  }

  .ph-card--ring {
    grid-template-columns: auto 1fr;
    align-items: center;
    column-gap: calc(12 * var(--u));
  }

  /* ── Set-piece primitive: play on load, pause while hidden ─────────────
     Every chart on this page uses this pattern, and it is the fix for the
     class of bug that made the hero render broken:

       The FINISHED state is the animation's natural resting state, reached
       via `both` fill with only a `from` keyframe. So it is correct with JS
       off, with JS broken, and under reduced motion (where `animation: none`
       simply lands on it).

       [data-r="pre"] — which only motion.js applies, and only to things below
       the fold — pauses the animation at frame 0. Reaching [data-r="in"]
       un-pauses it and it plays.

     The previous version keyed the finished state off `.is-in`, a class the
     hero's ancestry never received, so the hero's bar chart rendered at
     `scale: 1 0` — invisible — on every single load. Keying off the ABSENCE
     of a state cannot fail that way. */
  .ph-bars {
    display: flex;
    align-items: flex-end;
    gap: calc(5 * var(--u));
    block-size: calc(40 * var(--u));
  }
  .ph-bars i {
    flex: 1;
    border-radius: calc(3 * var(--u));
    background: var(--c, rgb(255 255 255 / 0.22));
    block-size: var(--h);
    transform-origin: bottom;
    animation: bar-grow 620ms var(--ease-out) both;
    animation-delay: calc(var(--bi, 0) * 55ms + 240ms);
  }
  @keyframes bar-grow {
    from {
      scale: 1 0;
    }
  }

  /* Progress ring — clockwise sweep. r=42 ⇒ C = 263.9 */
  .ph-ring {
    inline-size: calc(66 * var(--u));
    block-size: calc(66 * var(--u));
    rotate: -90deg;
    overflow: visible;
    flex: none;
  }
  .ph-ring circle {
    fill: none;
    stroke-width: 9;
    stroke-linecap: round;
  }
  .ph-ring__track {
    stroke: rgb(255 255 255 / 0.1);
  }
  .ph-ring__arc {
    stroke: var(--warn);
    stroke-dasharray: 263.9;
    /* Resting state = the real value. See the .ph-bars note above. */
    stroke-dashoffset: calc(263.9 - 263.9 * var(--pct, 0.84));
    animation: ring-sweep var(--d-chart) var(--ease-out) both;
    animation-delay: 320ms;
  }
  @keyframes ring-sweep {
    from {
      stroke-dashoffset: 263.9;
    }
  }
  .ph-ring__pct {
    rotate: 90deg;
    transform-origin: 50px 50px;
    fill: #fff;
    font-size: 21px;
    font-weight: 800;
    text-anchor: middle;
    dominant-baseline: central;
  }

  /* ── ScreenshotFan — ported from the app's paywall ─────────────────── */
  .c-fan {
    position: relative;
    display: grid;
    place-items: center;
    min-block-size: calc(320px * 852 / 393 + 40px); /* reserve → CLS 0 */
  }
  @media (max-width: 767px) {
    .c-fan {
      min-block-size: calc(250px * 852 / 393 + 24px);
    }
  }
  .c-fan__side {
    position: absolute;
    z-index: 0;
    inline-size: 320px;
    border-radius: 30px;
    border: 2px solid rgb(255 255 255 / 0.14);
    box-shadow: var(--sh-3);
    /* Push the JPEGs back optically so the DOM phone reads as the focal plane */
    filter: saturate(0.92) brightness(0.86);
  }
  /* The app fans three 98pt frames at ±71pt — 72% of a phone's own width.
     Keep that proportion so the side screens actually read. */
  .c-fan__side--l {
    transform: translateX(-64%) translateY(4%) rotate(-9deg) scale(0.86);
  }
  .c-fan__side--r {
    transform: translateX(64%) translateY(4%) rotate(9deg) scale(0.86);
  }
  .c-fan__centre {
    position: relative;
    z-index: var(--z-raised);
  }
  @media (max-width: 767px) {
    .c-fan__side {
      display: none;
    }
  }

  /* ── Pricing ───────────────────────────────────────────────────────── */
  .c-toggle {
    display: inline-flex;
    padding: 4px;
    gap: 2px;
    border-radius: var(--r-pill);
    background: var(--surface);
    border: 1px solid var(--line-control);
    margin-inline: auto;
  }
  .c-toggle button {
    padding: 9px 18px;
    border-radius: var(--r-pill);
    font-size: 0.875rem;
    font-weight: 700;
    color: var(--text-2);
    white-space: nowrap;
    transition: background-color var(--d-select) var(--ease-out),
      color var(--d-select) var(--ease-out);
  }
  .c-toggle button[aria-checked="true"] {
    /* --brand (#6366F1) with white is 4.47:1 — an AA fail. Any SOLID fill
       carrying white text uses --brand-cta-from (4.96:1) instead. */
    background: var(--brand-cta-from);
    color: #fff;
  }
  .c-toggle .c-toggle__save {
    color: var(--income);
    font-size: 0.75rem;
    margin-inline-start: 6px;
  }
  .c-toggle button[aria-checked="true"] .c-toggle__save {
    color: #fff;
  }

  .c-plan {
    border-radius: var(--r-plate);
    padding: clamp(22px, 2vw, 30px);
    background: var(--surface);
    border: 1px solid var(--line-control);
    display: grid;
    gap: var(--s-5);
    align-content: start;
  }
  .c-plan--feature {
    background: var(--surface-select);
    border: 1.5px solid var(--brand);
    box-shadow: 0 0 0 1px rgb(99 102 241 / 0.18), 0 24px 60px -24px rgb(99 102 241 / 0.35);
  }
  .c-plan__name {
    font-size: 1.0625rem;
    font-weight: 800;
    letter-spacing: -0.01em;
  }
  .c-plan__price {
    font-size: clamp(2rem, 1.7rem + 1.2vw, 2.75rem);
    font-weight: 800;
    letter-spacing: -0.03em;
    line-height: 1;
    font-variant-numeric: tabular-nums;
  }
  .c-plan__term {
    font-size: 0.9375rem;
    color: var(--text-2);
    font-weight: 600;
  }
  .c-plan__badge {
    justify-self: start;
    padding: 5px 11px;
    border-radius: var(--r-pill);
    font-size: 0.6875rem;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    background: color-mix(in srgb, var(--warning) 18%, transparent);
    color: var(--warning);
  }
  .c-plan ul {
    display: grid;
    gap: 9px;
    font-size: 0.9375rem;
    color: var(--text-2);
    line-height: 1.45;
  }
  .c-plan li {
    display: grid;
    grid-template-columns: 18px 1fr;
    gap: 10px;
    align-items: start;
  }
  .c-plan li svg {
    width: 16px;
    height: 16px;
    margin-top: 4px;
    color: var(--income);
  }
  .c-plan li[data-no] svg {
    color: var(--text-3);
  }

  /* ── FAQ — zero JS ─────────────────────────────────────────────────── */
  .c-faq details {
    border-bottom: 1px solid var(--line-soft);
  }
  .c-faq summary {
    list-style: none;
    cursor: pointer;
    padding-block: var(--s-5);
    display: grid;
    grid-template-columns: 1fr 24px;
    gap: var(--s-4);
    align-items: center;
    font-size: var(--t-option);
    font-weight: 600;
    line-height: 1.4;
  }
  .c-faq summary::-webkit-details-marker {
    display: none;
  }
  .c-faq summary::after {
    content: "";
    inline-size: 11px;
    block-size: 11px;
    justify-self: center;
    border-right: 2px solid var(--text-2);
    border-bottom: 2px solid var(--text-2);
    rotate: 45deg;
    translate: 0 -3px;
    transition: rotate var(--d-enter) var(--ease-out);
  }
  .c-faq details[open] summary::after {
    rotate: -135deg;
    translate: 0 2px;
  }
  .c-faq__a {
    padding-bottom: var(--s-6);
    color: var(--text-2);
    max-width: var(--measure-prose);
  }
  @supports (interpolate-size: allow-keywords) {
    :root {
      interpolate-size: allow-keywords;
    }
    .c-faq details::details-content {
      block-size: 0;
      overflow: hidden;
      transition: block-size var(--d-enter-lg) var(--ease-out),
        content-visibility var(--d-enter-lg) allow-discrete;
    }
    .c-faq details[open]::details-content {
      block-size: auto;
    }
  }

  /* ── Legal documents (generated pages) ─────────────────────────────── */
  .c-legal {
    max-width: var(--measure-prose);
  }
  .c-legal h1 {
    font-size: var(--t-h1);
    margin-bottom: var(--s-4);
  }
  .c-legal__updated {
    font-size: var(--t-micro);
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--text-micro);
  }
  .c-legal__intro {
    margin-top: var(--s-6);
    font-size: var(--t-lead);
    color: var(--text-2);
    line-height: 1.55;
  }
  .c-legal__section {
    margin-top: var(--s-9);
  }
  .c-legal h2 {
    font-size: var(--t-h3);
    margin-bottom: var(--s-5);
    padding-bottom: var(--s-3);
    border-bottom: 1px solid var(--line-soft);
  }
  .c-legal h3 {
    font-size: 1rem;
    font-weight: 700;
    margin-top: var(--s-6);
    margin-bottom: var(--s-2);
    color: var(--brand-soft);
  }
  .c-legal p {
    color: var(--text-2);
    margin-bottom: var(--s-4);
    line-height: 1.65;
  }
  .c-legal a {
    color: var(--brand-soft);
    text-decoration: underline;
    text-underline-offset: 3px;
  }
  .c-legal__list {
    display: grid;
    gap: 9px;
    margin-bottom: var(--s-5);
  }
  .c-legal__list > li {
    color: var(--text-2);
    line-height: 1.6;
    padding-inline-start: 18px;
    position: relative;
  }
  .c-legal__list > li::before {
    content: "";
    position: absolute;
    inset-inline-start: 2px;
    top: 0.66em;
    inline-size: 5px;
    block-size: 5px;
    border-radius: 50%;
    background: var(--brand);
  }
  .c-legal__sub {
    display: grid;
    gap: 6px;
    margin: 8px 0 4px;
    padding-inline-start: 16px;
  }
  .c-legal__sub li {
    color: var(--text-3);
    font-size: 0.9375rem;
    position: relative;
    padding-inline-start: 14px;
  }
  .c-legal__sub li::before {
    content: "–";
    position: absolute;
    inset-inline-start: 0;
    color: var(--text-3);
  }

  /* ── Footer ────────────────────────────────────────────────────────── */
  .c-footer {
    background: var(--bg-deep);
    border-top: 1px solid var(--line-soft);
    padding-block: var(--s-10) var(--s-7);
  }
  .c-footer__cols {
    display: grid;
    gap: var(--s-8);
    grid-template-columns: 1fr;
  }
  @media (min-width: 640px) {
    .c-footer__cols {
      grid-template-columns: repeat(2, 1fr);
    }
  }
  @media (min-width: 1024px) {
    .c-footer__cols {
      grid-template-columns: 1.6fr 1fr 1fr 1fr;
      gap: var(--s-9);
    }
  }
  .c-footer h3 {
    font-size: var(--t-micro);
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--text-micro);
    margin-bottom: var(--s-4);
  }
  .c-footer ul {
    display: grid;
    gap: 11px;
  }
  .c-footer a {
    color: var(--text-2);
    font-size: 0.9375rem;
    transition: color var(--d-micro) var(--ease-out);
  }
  .c-footer a:hover {
    color: var(--text);
  }
  .c-footer__legal {
    margin-top: var(--s-9);
    padding-top: var(--s-6);
    border-top: 1px solid var(--line-soft);
    font-size: var(--t-caption);
    color: var(--text-3);
    line-height: 1.6;
    max-width: var(--measure-prose);
  }

  /* ── Replay button for set pieces ──────────────────────────────────── */
  .c-replay {
    display: inline-grid;
    grid-auto-flow: column;
    align-items: center;
    gap: 7px;
    min-block-size: 44px;
    padding-inline: 16px;
    border-radius: var(--r-pill);
    background: var(--surface);
    border: 1px solid var(--line-control);
    color: var(--text-2);
    font-size: 0.8125rem;
    font-weight: 600;
    transition: color var(--d-micro) var(--ease-out),
      background-color var(--d-micro) var(--ease-out);
  }
  .c-replay:hover {
    color: var(--text);
    background: var(--surface-hover);
  }
  .c-replay svg {
    width: 14px;
    height: 14px;
  }
}

/* ═══ sections ═════════════════════════════════════════════════════════════ */
@layer sections {
  /* ── Hero ──────────────────────────────────────────────────────────── */
  .s-hero {
    position: relative;
    padding-block: clamp(40px, 6vw, 88px) clamp(64px, 8vw, 120px);
    overflow: clip;
  }
  .s-hero__grid {
    display: grid;
    gap: clamp(44px, 5vw, 72px);
    align-items: center;
    position: relative;
    z-index: var(--z-raised);
  }
  @media (min-width: 1024px) {
    .s-hero__grid {
      grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr);
    }
  }
  .s-hero h1 {
    max-width: var(--measure-title);
  }
  .s-hero__lead {
    font-size: var(--t-lead);
    color: var(--text-2);
    max-width: var(--measure-lead);
    line-height: 1.5;
    letter-spacing: -0.008em;
  }
  .s-hero__actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--s-4);
    align-items: center;
  }
  .s-hero__note {
    font-size: 0.875rem;
    color: var(--text-3);
  }
  /* ── Reframe ───────────────────────────────────────────────────────── */
  .s-console {
    display: grid;
    gap: clamp(32px, 4vw, 56px);
    justify-items: center;
  }
  .s-console__chips {
    display: flex;
    flex-wrap: wrap;
    gap: var(--s-2);
    justify-content: center;
    max-width: 860px;
  }
  .s-chip {
    padding: 10px 16px;
    min-block-size: 44px;
    border-radius: var(--r-pill);
    background: var(--surface);
    border: 1px solid var(--line);
    color: var(--text-2);
    font-size: 0.875rem;
    font-weight: 600;
    text-align: left;
    transition: background-color var(--d-micro) var(--ease-out),
      border-color var(--d-micro) var(--ease-out), color var(--d-micro) var(--ease-out);
  }
  .s-chip:hover {
    background: var(--surface-hover);
    color: var(--text);
    border-color: var(--line-strong);
  }
  .s-chip[aria-pressed="true"] {
    background: var(--surface-select);
    border-color: var(--brand);
    color: var(--text);
  }
  .s-console__note {
    max-width: 62ch;
    text-align: center;
    font-size: 0.9375rem;
    color: var(--text-3);
    line-height: 1.6;
  }

  /* Chat interior */
  .ph-chat {
    display: flex;
    flex-direction: column;
    gap: calc(9 * var(--u));
    overflow: hidden;
    flex: 1;
  }
  .ph-bubble {
    max-inline-size: 82%;
    padding: calc(10 * var(--u)) calc(13 * var(--u));
    border-radius: calc(18 * var(--u));
    font-size: calc(13.5 * var(--u));
    line-height: 1.4;
  }
  .ph-bubble--me {
    align-self: flex-end;
    background: var(--brand-cta-from);
    color: #fff;
    border-bottom-right-radius: calc(6 * var(--u));
  }
  .ph-bubble--ai {
    align-self: flex-start;
    background: var(--surface-raised);
    color: #fff;
    border-bottom-left-radius: calc(6 * var(--u));
  }
  .ph-tool {
    align-self: flex-start;
    display: inline-flex;
    align-items: center;
    gap: calc(7 * var(--u));
    padding: calc(7 * var(--u)) calc(12 * var(--u));
    border-radius: var(--r-pill);
    background: var(--surface);
    border: 1px solid var(--line);
    font-size: calc(11.5 * var(--u));
    color: #9a9ab7;
  }
  .ph-tool__dot {
    inline-size: calc(9 * var(--u));
    block-size: calc(9 * var(--u));
    border-radius: 50%;
    border: calc(2 * var(--u)) solid var(--brand-soft);
    border-top-color: transparent;
    animation: spin 700ms linear infinite;
  }
  @keyframes spin {
    to {
      rotate: 360deg;
    }
  }
  .ph-tool[data-done] .ph-tool__dot {
    animation: none;
    border-color: var(--income);
    background: var(--income);
  }
  .ph-action {
    display: grid;
    gap: calc(9 * var(--u));
    padding: calc(12 * var(--u));
    border-radius: calc(18 * var(--u));
    background: var(--grad-ai);
    border: 1px solid rgb(99 102 241 / 0.34);
  }
  .ph-action__row {
    display: grid;
    grid-template-columns: calc(30 * var(--u)) 1fr auto;
    gap: calc(10 * var(--u));
    align-items: center;
    font-size: calc(12.5 * var(--u));
  }
  .ph-action__dot {
    inline-size: calc(30 * var(--u));
    block-size: calc(30 * var(--u));
    border-radius: calc(10 * var(--u));
    background: color-mix(in srgb, var(--c, var(--brand)) 24%, transparent);
    display: grid;
    place-items: center;
    color: var(--c, var(--brand));
    font-size: calc(13 * var(--u));
    font-weight: 700;
  }
  .ph-action__amt {
    font-weight: 700;
    font-variant-numeric: tabular-nums;
  }
  /* Chat composer — pinned to the base so the console screen reads as a real
     conversation view rather than a card floating in empty space. */
  .ph-input {
    margin-top: auto;
    margin-bottom: calc(14 * var(--u));
    display: grid;
    grid-template-columns: 1fr auto;
    gap: calc(8 * var(--u));
    align-items: center;
  }
  .ph-input__box {
    padding: calc(11 * var(--u)) calc(14 * var(--u));
    border-radius: var(--r-pill);
    background: var(--surface-input);
    border: 1px solid var(--line);
    font-size: calc(12.5 * var(--u));
    color: #9394b0;
  }
  .ph-input__mic {
    inline-size: calc(38 * var(--u));
    block-size: calc(38 * var(--u));
    border-radius: 50%;
    background: var(--brand);
    display: grid;
    place-items: center;
  }
  .ph-input__mic svg {
    inline-size: calc(17 * var(--u));
    block-size: calc(17 * var(--u));
  }

  .ph-action__undo {
    font-size: calc(11 * var(--u));
    color: var(--brand-soft);
    font-weight: 600;
    justify-self: end;
  }

  /* ── Ledger (privacy) ──────────────────────────────────────────────── */
  .s-kit {
    display: grid;
    gap: var(--s-7);
  }
  @media (min-width: 640px) {
    .s-kit {
      grid-template-columns: repeat(2, 1fr);
    }
  }
  @media (min-width: 1024px) {
    .s-kit {
      grid-template-columns: repeat(4, 1fr);
      gap: var(--s-8);
    }
  }
  .s-kit h3 {
    font-size: var(--t-micro);
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--brand-soft);
    margin-bottom: var(--s-4);
  }
  .s-kit ul {
    display: grid;
    gap: 9px;
  }
  .s-kit li {
    font-size: 0.9375rem;
    color: var(--text-2);
    line-height: 1.45;
    padding-inline-start: 14px;
    position: relative;
  }
  .s-kit li::before {
    content: "";
    position: absolute;
    inset-inline-start: 0;
    top: 0.62em;
    inline-size: 5px;
    block-size: 5px;
    border-radius: 50%;
    background: var(--line-control);
  }

  .s-closer {
    text-align: center;
    display: grid;
    gap: var(--s-6);
    justify-items: center;
  }
  .s-closer h2 {
    font-size: var(--t-h1);
    max-width: 18ch;
  }
  .s-closer p {
    color: var(--text-2);
    font-size: var(--t-lead);
    max-width: 42ch;
  }
}

/* ═══ utilities ════════════════════════════════════════════════════════════ */
@layer utilities {
  .u-vh {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
  }
  .u-center {
    text-align: center;
  }
  .u-stack {
    display: grid;
    gap: var(--s-6);
  }
  .u-mt-6 {
    margin-top: var(--s-6);
  }
  .u-mt-7 {
    margin-top: var(--s-7);
  }
  .u-mt-8 {
    margin-top: var(--s-8);
  }
  .u-accent {
    color: var(--brand-soft);
  }
  .u-income {
    color: var(--income);
  }
  .u-nowrap {
    white-space: nowrap;
  }

  /* ── Above-the-fold entrance ───────────────────────────────────────────
     The hero must NEVER wait on JavaScript. Pure CSS animation with `both`
     fill: it plays on load and its final state is the resting state, so if
     scripting is off, broken, or slow, the hero is simply there. */
  @media (prefers-reduced-motion: no-preference) {
    [data-enter] {
      animation: enter-up var(--d-reveal) var(--ease-reveal) both;
      animation-delay: calc(var(--i, 0) * var(--stagger));
    }
    [data-enter="zoom"] {
      animation-name: enter-zoom;
    }
  }
  @keyframes enter-up {
    from {
      opacity: 0;
      translate: 0 var(--travel-y);
    }
  }
  @keyframes enter-zoom {
    from {
      opacity: 0;
      scale: var(--enter-scale);
    }
  }
}

/* ═══ motion ═══════════════════════════════════════════════════════════════
   Last layer, so it wins over any component's resting state.

   OWNERSHIP — the single most important rule in this file:

     CSS's default is the FINAL, VISIBLE state. This layer only styles
     [data-r], an attribute NOTHING but motion.js writes, and which it writes
     per element and only below the fold.

   So a JS failure cannot blank the page, because a page with no JS was never
   hidden. The previous build inverted this — `.js [data-reveal] {opacity:0}`
   hid 91 elements the instant the stylesheet parsed — which forced a 2500ms
   setTimeout to un-hide them, which force-completed every animation on the
   page before the reader had scrolled anywhere. The timer was the symptom;
   ownership was the bug.
   ========================================================================= */
@layer motion {
  @media (prefers-reduced-motion: no-preference) {
    [data-r="pre"] {
      opacity: 0;
      translate: 0 var(--travel-y);
    }
    [data-r="pre"][data-reveal="zoom"] {
      translate: none;
      scale: var(--enter-scale);
    }
    [data-r="pre"][data-reveal="left"] {
      translate: calc(var(--travel-x) * -1) 0;
    }
    [data-r="pre"][data-reveal="right"] {
      translate: var(--travel-x) 0;
    }

    /* The transition lives on the FINISHED state, never the hidden one. So
       applying "pre" is instantaneous — no flash-then-fade on boot — and an
       element that was never hidden never animates at all. */
    [data-r="in"] {
      opacity: 1;
      translate: 0 0;
      scale: 1;
      transition: opacity var(--d-reveal) var(--ease-reveal)
          calc(var(--i, 0) * var(--stagger)),
        translate var(--d-reveal) var(--ease-reveal) calc(var(--i, 0) * var(--stagger)),
        scale var(--d-reveal) var(--ease-reveal) calc(var(--i, 0) * var(--stagger));
    }

    /* Set pieces inside a not-yet-revealed subtree hold at frame 0. */
    [data-r="pre"] .ph-bars i,
    [data-r="pre"] .ph-ring__arc,
    [data-r="pre"] .s-calm-line,
    [data-r="pre"] .c-spark__line,
    [data-r="pre"] .c-spark__fill,
    [data-r="pre"] .c-ring__arc,
    [data-r="pre"] .c-rail__sweep,
    [data-r="pre"] .c-rail__node,
    [data-r="pre"] .c-tl__line,
    [data-r="pre"] .c-tl__node,
    [data-r="pre"] .c-field i {
      animation-play-state: paused;
    }
  }

  /* ── Reduced motion ────────────────────────────────────────────────────
     Reduce ≠ remove — the app's standard, and the right one.

     Most of it falls out of the tokens: zero travel, zero stagger, a 120ms
     duration. What remains is a per-effect ruling:

       kept   — the pin (it is layout; nothing moves the user didn't move),
                beat changes, count-up entrances, colour fields, the FAQ
                chevron, hover states, nav solidify
       frozen — the mesh (it is the ground colour, not decoration)
       once   — flame ignition (a state change, and informative)
       gone   — every ambient loop, the shimmer, the donut's walk, embers,
                and confetti outright. Many-object, high-velocity,
                unpredictable-trajectory motion is the worst vestibular case
                on the web and carries no information, so there is nothing to
                preserve by degrading it. */
  @media (prefers-reduced-motion: reduce) {
    :root {
      --travel-y: 0px;
      --travel-x: 0px;
      --enter-scale: 1;
      --stagger: 0ms;
      --d-reveal: var(--d-reduced);
    }
    html {
      scroll-behavior: auto;
    }
    [data-enter] {
      animation: none;
    }
    [data-r="pre"] {
      opacity: 0;
      translate: none;
      scale: none;
    }
    [data-r="in"] {
      opacity: 1;
      transition: opacity var(--d-reduced) var(--ease-out) 0ms;
    }

    /* Ambient loops: suppressed, but their resting state is the finished
       one, so the mesh keeps its colour and the flame keeps its shape. */
    .c-mesh i,
    .c-cta::after,
    .ph-tool__dot,
    .c-flame__g,
    .c-ember,
    .c-orb {
      animation: none !important;
    }
    .c-confetti {
      display: none;
    }
    /* Charts still draw — that is information, not decoration — just fast. */
    .ph-bars i,
    .ph-ring__arc,
    .s-calm-line,
    .c-spark__line,
    .c-spark__fill,
    .c-ring__arc,
    .c-rail__sweep,
    .c-rail__node,
    .c-tl__line,
    .c-tl__node,
    .c-field i {
      animation-duration: var(--d-reduced) !important;
      animation-delay: 0ms !important;
    }
  }

  /* Print: nothing may be hidden on paper. */
  @media print {
    [data-r] {
      opacity: 1 !important;
      translate: none !important;
      scale: none !important;
    }
  }
}

/* ═══════════════════════════════════════════════════════════════════════════
   PART TWO — the cinematic layer
   ---------------------------------------------------------------------------
   Everything below was added in the 2026-07 motion rebuild. It is appended
   rather than interleaved so the diff against the first build stays readable;
   @layer means source order carries no authority anyway.

   Full-bleed here means "not wrapped in .l-container". There are no negative
   margins and no 100vw anywhere on this page: 100vw includes the scrollbar
   gutter on desktop, so the classic `margin-inline: calc(50% - 50vw)` trick
   produces a horizontal scrollbar at exactly the widths nobody tests.
   ═══════════════════════════════════════════════════════════════════════════ */

@layer layout {
  .l-section--tight {
    padding-block: var(--section-y-tight);
  }
  .l-section--tall {
    padding-block: var(--section-y-tall);
  }

  /* Asymmetric split. The first build put every section in one of three
     centred widths, which is most of why it read as a template. */
  .l-split {
    display: grid;
    gap: clamp(40px, 5vw, 72px);
    align-items: center;
  }
  @media (min-width: 1024px) {
    .l-split {
      grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr);
    }
    .l-split--mirror > :first-child {
      order: 2;
    }
    .l-split--wide-media {
      grid-template-columns: minmax(0, 1fr) minmax(0, 1.25fr);
    }
  }

  /* Aligns a full-bleed child's content with the container it sits beside,
     while letting it run off the opposite edge. */
  .l-edge {
    --edge: max(var(--gutter), calc((100% - 1200px) / 2 + var(--gutter)));
  }
}

@layer components {
  /* ── Display statistic ─────────────────────────────────────────────────
     The box is reserved twice over, deliberately:
       tabular-nums     the right fix — but the subset woff2 is compressed, so
                        whether it retained `tnum` cannot be read statically,
                        and a silent no-op means every digit change reflows.
       min-inline-size  the fix that cannot fail.
     The element's initial text is its FINAL value, so with JS off the number
     is correct and the box was never the wrong size. */
  /* No min-inline-size in ch here. At --t-stat (120px) one ch is ~66px, so a
     6-character reservation is a 400px floor — four of those overflowed the
     container and pushed the fourth statistic off the viewport. The box is
     instead reserved in PIXELS by count(), measured from the element's own
     final text at the moment it starts. Exact, and it cannot blow out. */
  .c-num {
    display: inline-block;
    font-variant-numeric: tabular-nums;
    font-feature-settings: "tnum" 1;
    text-align: center;
  }

  .c-stat {
    display: grid;
    gap: 10px;
    /* Without this the grid container stretches to the tallest sibling and
       distributes the slack between rows, so short captions float apart. */
    align-content: start;
    justify-items: center;
    text-align: center;
    min-inline-size: 0;
  }
  .c-stat__v {
    font-size: var(--t-stat);
    font-weight: 800;
    line-height: 0.92;
    letter-spacing: -0.045em;
    background: linear-gradient(170deg, #fff 30%, #b9bbe4 100%);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
  }
  .c-stat__k {
    font-size: var(--t-option);
    font-weight: 700;
    letter-spacing: -0.012em;
    max-width: 20ch;
  }
  .c-stat__s {
    font-size: 0.9375rem;
    color: var(--text-2);
    max-width: 30ch;
    line-height: 1.5;
  }
  .c-stat__tag {
    font-size: var(--t-micro);
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--brand-soft);
  }

  /* ── Pull quote ────────────────────────────────────────────────────── */
  .c-quote {
    font-size: var(--t-quote);
    font-weight: 800;
    line-height: 1.14;
    letter-spacing: -0.028em;
    text-wrap: balance;
  }

  /* ── Sparkline / area chart ────────────────────────────────────────────
     Line sweeps, then the fill reveals 450ms behind it — the app's own
     TrendLineChart choreography. pathLength="1" normalises the dash maths so
     nothing has to be measured in JS. */
  .c-spark__line {
    fill: none;
    stroke-width: 2.5;
    stroke-linecap: round;
    stroke-linejoin: round;
    stroke-dasharray: 1;
    stroke-dashoffset: 0;
    animation: spark-draw 900ms var(--ease-out) both;
    animation-delay: 180ms;
  }
  @keyframes spark-draw {
    from {
      stroke-dashoffset: 1;
    }
  }
  .c-spark__fill {
    opacity: 1;
    animation: spark-fill 400ms var(--ease-out) both;
    animation-delay: 630ms;
  }
  @keyframes spark-fill {
    from {
      opacity: 0;
    }
  }

  /* ── Progress ring ─────────────────────────────────────────────────────
     One unbroken LINEAR fill, from the app's BuildingStep. Linear is the
     point: an eased progress ring reads as a progress ring that is lying. */
  .c-ring {
    rotate: -90deg;
    overflow: visible;
  }
  .c-ring circle {
    fill: none;
    stroke-linecap: round;
  }
  .c-ring__track {
    stroke: rgb(255 255 255 / 0.08);
  }
  .c-ring__arc {
    stroke: var(--accent, var(--brand));
    stroke-dasharray: 1;
    stroke-dashoffset: calc(1 - var(--pct, 0.72));
    animation: ring-fill 1600ms linear both;
    animation-delay: 200ms;
  }
  @keyframes ring-fill {
    from {
      stroke-dashoffset: 1;
    }
  }

  /* ── Horizontal snap rail ──────────────────────────────────────────────
     A swipe on mobile — where it is genuinely better than a grid — and a
     deliberate edge-bleed on desktop, so the page reads as continuing past
     the viewport rather than stopping politely at 1200px. */
  .c-rail {
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: 78vw;
    gap: var(--s-4);
    overflow-x: auto;
    overscroll-behavior-x: contain;
    scroll-snap-type: x mandatory;
    scrollbar-width: none;
    padding-inline: var(--edge, var(--gutter)) var(--gutter);
    padding-block: 6px 14px;
  }
  .c-rail::-webkit-scrollbar {
    display: none;
  }
  .c-rail > * {
    scroll-snap-align: start;
  }
  @media (min-width: 640px) {
    .c-rail {
      grid-auto-columns: 340px;
    }
  }

  .c-panel {
    --accent: var(--brand);
    display: grid;
    grid-template-rows: auto 1fr auto;
    gap: var(--s-5);
    padding: var(--card-pad);
    border-radius: var(--r-plate);
    background: linear-gradient(
      165deg,
      color-mix(in oklab, var(--accent) 13%, transparent),
      rgb(255 255 255 / 0.035) 55%
    );
    border: 1px solid color-mix(in oklab, var(--accent) 26%, transparent);
    box-shadow: var(--sh-2), var(--line-inner);
    transition: border-color var(--d-micro) var(--ease-out),
      transform var(--d-micro) var(--ease-out), box-shadow var(--d-micro) var(--ease-out);
  }
  .c-panel:hover {
    transform: translateY(-4px);
    border-color: color-mix(in oklab, var(--accent) 55%, transparent);
    box-shadow: var(--sh-3), 0 0 44px -18px color-mix(in oklab, var(--accent) 70%, transparent);
  }
  .c-panel__k {
    font-size: var(--t-micro);
    font-weight: 700;
    letter-spacing: 0.07em;
    text-transform: uppercase;
    color: var(--accent);
  }
  .c-panel h3 {
    font-size: 1.1875rem;
    margin-block: 6px 8px;
  }
  .c-panel p {
    font-size: 0.9375rem;
    color: var(--text-2);
    line-height: 1.5;
  }
  /* Every panel reserves the same box for its miniature so the rail's rhythm
     survives set pieces of different intrinsic heights. */
  .c-panel__fig {
    block-size: 132px;
    display: grid;
    align-content: center;
    position: relative;
  }

  /* ── Renewal radar — the connector sweep + dots ────────────────────────
     From the app's SubscriptionsStep: the line arrives, and each dot pops as
     it passes underneath. The dots sit on an opaque plate so the line does
     not show through them. */
  .c-radar {
    position: relative;
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    align-items: center;
    padding-block: 18px;
  }
  .c-radar__rail {
    position: absolute;
    inset-inline: 6%;
    block-size: 2px;
    border-radius: 2px;
    background: rgb(255 255 255 / 0.1);
    overflow: clip;
  }
  .c-rail__sweep {
    display: block;
    block-size: 100%;
    inline-size: 100%;
    background: var(--brand);
    transform-origin: left;
    scale: 1 1;
    animation: radar-sweep 1100ms var(--ease-out) both;
    animation-delay: 260ms;
  }
  @keyframes radar-sweep {
    from {
      scale: 0 1;
    }
  }
  .c-rail__node {
    position: relative;
    inline-size: 34px;
    block-size: 34px;
    border-radius: 50%;
    display: grid;
    place-items: center;
    justify-self: center;
    background: #1b1c3d;
    box-shadow: inset 0 0 0 2px color-mix(in oklab, var(--c) 60%, transparent);
    color: var(--c);
    font-size: 0.6875rem;
    font-weight: 800;
    animation: node-pop 220ms var(--ease-out) both;
    animation-delay: calc(420ms + var(--ni, 0) * 230ms);
  }
  @keyframes node-pop {
    from {
      opacity: 0;
      scale: 0.5;
    }
  }
  .c-radar__day {
    font-size: 0.625rem;
    text-align: center;
    color: var(--text-3);
    margin-top: 7px;
    letter-spacing: 0.04em;
  }

  /* ── Trial timeline — vertical line grows, nodes land ──────────────── */
  .c-tl {
    display: grid;
    gap: 0;
    position: relative;
    padding-inline-start: 34px;
  }
  .c-tl__line {
    position: absolute;
    inset-block: 12px;
    inset-inline-start: 10px;
    inline-size: 2px;
    border-radius: 2px;
    background: rgb(99 102 241 / 0.6);
    transform-origin: top;
    animation: tl-grow 900ms var(--ease-out) both;
    animation-delay: 300ms;
  }
  @keyframes tl-grow {
    from {
      scale: 1 0;
    }
  }
  .c-tl li {
    position: relative;
    padding-block: 11px;
    font-size: 0.9375rem;
    color: var(--text-2);
    line-height: 1.5;
  }
  .c-tl li b {
    display: block;
    color: var(--text);
  }
  .c-tl__node {
    position: absolute;
    inset-inline-start: -34px;
    top: 14px;
    inline-size: 22px;
    block-size: 22px;
    border-radius: 50%;
    background: rgb(99 102 241 / 0.18);
    box-shadow: inset 0 0 0 1.5px rgb(99 102 241 / 0.55);
    animation: node-pop 220ms var(--ease-out) both;
    animation-delay: calc(350ms + var(--ni, 0) * 260ms);
  }
  .c-tl li:last-child .c-tl__node {
    background: var(--brand-cta-from);
    box-shadow: none;
  }
}

@layer components {
  /* ── The morphing face ─────────────────────────────────────────────────
     Ported from components/onboarding/StressSlider.tsx. Every path and colour
     interpolates off a single 0→1 value, so the whole thing is one variable
     written by an <input type=range>. Interactive, which is rarer and more
     memorable on a marketing page than anything that merely plays. */
  .c-face {
    inline-size: clamp(148px, 22vw, 220px);
    block-size: auto;
    margin-inline: auto;
    overflow: visible;
  }
  .c-face__disc {
    transition: fill 240ms linear;
  }
  .c-face path,
  .c-face ellipse {
    transition: d 240ms linear, stroke 240ms linear, ry 240ms linear;
  }
  .c-face path {
    fill: none;
    stroke-linecap: round;
  }

  .c-slider {
    inline-size: min(100%, 420px);
    margin-inline: auto;
    display: block;
    block-size: 56px; /* the app's touchArea — never smaller on a phone */
    background: none;
    appearance: none;
    cursor: grab;
  }
  .c-slider:active {
    cursor: grabbing;
  }
  .c-slider::-webkit-slider-runnable-track {
    block-size: 8px;
    border-radius: 999px;
    background: linear-gradient(90deg, #818cf8, #ff9f0a 50%, #f87171);
  }
  .c-slider::-moz-range-track {
    block-size: 8px;
    border-radius: 999px;
    background: linear-gradient(90deg, #818cf8, #ff9f0a 50%, #f87171);
  }
  .c-slider::-webkit-slider-thumb {
    appearance: none;
    inline-size: 34px;
    block-size: 34px;
    margin-top: -13px;
    border-radius: 50%;
    background: var(--mood, #818cf8);
    border: 3px solid #fff;
    box-shadow: 0 0 0 10px color-mix(in oklab, var(--mood, #818cf8) 26%, transparent);
    transition: box-shadow var(--d-micro) var(--ease-out);
  }
  .c-slider::-moz-range-thumb {
    inline-size: 28px;
    block-size: 28px;
    border-radius: 50%;
    background: var(--mood, #818cf8);
    border: 3px solid #fff;
    box-shadow: 0 0 0 10px color-mix(in oklab, var(--mood, #818cf8) 26%, transparent);
  }

  /* ── The action field ──────────────────────────────────────────────────
     One glyph per AI action, lit in a DIAGONAL wave — delay = (row + col) ×
     26ms, from the app's PeopleGridStat. The visual IS the statistic: it is
     countable on screen, which is a claim you cannot make with a number
     alone. Three cells stay dark, and they are the honest part. */
  .c-field {
    display: grid;
    grid-template-columns: repeat(var(--cols, 7), 1fr);
    gap: clamp(5px, 0.9vw, 10px);
    max-inline-size: 940px;
    margin-inline: auto;
  }
  @media (min-width: 768px) {
    .c-field {
      grid-template-columns: repeat(var(--cols-lg, 15), 1fr);
    }
  }
  .c-field i {
    aspect-ratio: 1;
    border-radius: 4px;
    background: var(--c, var(--brand));
    animation: field-lite 340ms var(--ease-out) both;
    animation-delay: calc((var(--r, 0) + var(--c-i, 0)) * 26ms + 160ms);
  }
  .c-field i[data-off] {
    background: transparent;
    box-shadow: inset 0 0 0 1px var(--expense);
  }
  @keyframes field-lite {
    from {
      opacity: 0;
      scale: 0.4;
    }
  }
  .c-field__key {
    display: flex;
    flex-wrap: wrap;
    gap: var(--s-3) var(--s-6);
    justify-content: center;
    margin-top: var(--s-6);
    font-size: 0.875rem;
    color: var(--text-2);
  }
  .c-field__key span {
    display: inline-flex;
    align-items: center;
    gap: 8px;
  }
  .c-field__key i {
    inline-size: 11px;
    block-size: 11px;
    border-radius: 3px;
    background: var(--c, var(--brand));
    flex: none;
  }
  .c-field__key i[data-off] {
    background: transparent;
    box-shadow: inset 0 0 0 1px var(--expense);
  }

  /* ── The flame ─────────────────────────────────────────────────────────
     From components/StreakCelebrationPopup.tsx, paths verbatim.

     The glow is a sibling <circle> with a radialGradient fill, NOT
     filter:url(#glow). Safari re-runs an entire SVG filter chain every frame
     when anything inside the filtered subtree animates — so a breathing <g>
     inside a filtered group repaints continuously. It looks fine in Chrome
     and dies on the device this page exists to sell. */
  .c-flame {
    inline-size: clamp(140px, 20vw, 200px);
    block-size: auto;
    overflow: visible;
    animation: flame-ignite 360ms var(--ease-out) both;
    animation-delay: 160ms;
  }
  @keyframes flame-ignite {
    from {
      opacity: 0;
      scale: 0.86;
    }
    62% {
      opacity: 1;
      scale: 1.06;
    }
    to {
      scale: 1;
    }
  }
  .c-flame__g {
    transform-origin: 50px 70px;
    animation: flame-breathe 1500ms var(--ease-in-out) 580ms infinite alternate;
    animation-play-state: paused;
  }
  [data-on] .c-flame__g {
    animation-play-state: running;
  }
  @keyframes flame-breathe {
    to {
      scale: 1.045;
    }
  }
  .c-flame__glow {
    transform-origin: 50px 70px;
    animation: glow-breathe 1500ms var(--ease-in-out) 580ms infinite alternate;
    animation-play-state: paused;
  }
  [data-on] .c-flame__glow {
    animation-play-state: running;
  }
  @keyframes glow-breathe {
    to {
      opacity: 1;
      scale: 1.08;
    }
  }

  .c-embers {
    position: absolute;
    inset: 0;
    pointer-events: none;
    overflow: clip;
  }
  .c-ember {
    position: absolute;
    left: var(--x);
    bottom: 34%;
    inline-size: var(--sz, 5px);
    aspect-ratio: 1;
    border-radius: 50%;
    background: var(--c, var(--ember-warm));
    opacity: 0;
    animation: ember-rise var(--dur, 1000ms) var(--ease-out) var(--lag, 0ms) infinite;
    animation-play-state: paused;
  }
  [data-on] .c-ember {
    animation-play-state: running;
  }
  @keyframes ember-rise {
    0% {
      opacity: 0;
      translate: 0 0;
      scale: 1;
    }
    12% {
      opacity: 1;
    }
    70% {
      opacity: 0.8;
    }
    100% {
      opacity: 0;
      translate: var(--dx, 0) var(--rise, -100px);
      scale: 0.5;
    }
  }

  /* ── Mon–Sun streak strip ──────────────────────────────────────────── */
  .c-week {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
    gap: clamp(6px, 1.4vw, 12px);
    inline-size: min(100%, 340px);
    margin-inline: auto;
  }
  .c-week div {
    display: grid;
    gap: 7px;
    justify-items: center;
  }
  .c-week span {
    font-size: 0.625rem;
    font-weight: 700;
    letter-spacing: 0.06em;
    color: var(--text-3);
  }
  .c-week i {
    inline-size: 100%;
    max-inline-size: 36px;
    aspect-ratio: 1;
    border-radius: 50%;
    background: transparent;
    box-shadow: inset 0 0 0 2px rgb(255 255 255 / 0.16);
    animation: week-fill 200ms var(--ease-out) both;
    animation-delay: calc(720ms + var(--wi, 0) * 80ms);
  }
  .c-week i[data-lit] {
    background: color-mix(in oklab, var(--ember-warm) 82%, transparent);
    box-shadow: none;
  }
  /* Today lands last and overshoots — the app's own 1.16 then settle. */
  .c-week i[data-today] {
    animation-name: week-today;
    animation-duration: 320ms;
  }
  @keyframes week-fill {
    from {
      opacity: 0;
      scale: 0.6;
    }
  }
  @keyframes week-today {
    from {
      opacity: 0;
      scale: 0.6;
    }
    58% {
      opacity: 1;
      scale: 1.16;
    }
    to {
      scale: 1;
    }
  }

  .c-confetti {
    position: absolute;
    inset: 0;
    inline-size: 100%;
    block-size: 100%;
    pointer-events: none;
    z-index: var(--z-raised);
  }
}

@layer sections {
  /* ── §1 Hero ───────────────────────────────────────────────────────────
     Asymmetric, and the media runs off the right edge. The first build fanned
     three phones symmetrically around a centre axis, which is the single most
     template-looking composition available; it also deleted the side phones
     entirely below 768px, so mobile — the audience for a phone app — got one
     250px mockup floating in a lot of navy. Crop, never delete. */
  .s-hero__stack {
    position: relative;
    display: grid;
    justify-items: center;
    min-block-size: calc(340px * 852 / 393 + 40px); /* reserve → CLS 0 */
  }
  @media (max-width: 767px) {
    .s-hero__stack {
      min-block-size: calc(288px * 852 / 393 * 0.78);
      align-items: start;
      overflow: clip;
    }
  }
  .s-hero__lead-phone {
    position: relative;
    z-index: var(--z-raised);
    rotate: -3deg;
  }
  .s-hero__lead-phone .c-phone {
    --pw: 340px;
  }
  @media (max-width: 767px) {
    .s-hero__lead-phone .c-phone {
      --pw: 288px;
    }
  }
  /* The companion screenshot is cropped by the viewport, which is what makes
     it read as a device rather than a thumbnail of one. */
     inside it follow the ground. */

     a cropped one reads as a photograph. */
  /* ── §12 Celebration ───────────────────────────────────────────────── */
  .s-celebrate__count {
    font-size: var(--t-stat);
    font-weight: 800;
    line-height: 0.9;
    letter-spacing: -0.05em;
    color: #fff;
    text-shadow: 0 0 34px rgb(255 140 66 / 0.45);
  }
  .s-celebrate__cap {
    font-size: var(--t-h3);
    font-weight: 700;
    color: #ffd9b0;
  }
}

@layer sections {
  /* ── §6 Console — split, not stacked-centred ───────────────────────── */
  .s-console {
    display: grid;
    gap: clamp(32px, 4vw, 64px);
    justify-items: stretch;
    align-items: center;
  }
  @media (min-width: 1024px) {
    .s-console {
      grid-template-columns: minmax(0, 1fr) auto;
    }
  }
  .s-console__chips {
    display: flex;
    flex-wrap: wrap;
    gap: var(--s-2);
    justify-content: flex-start;
    max-width: 560px;
  }
  @media (max-width: 1023px) {
    /* Four ragged wrapped lines of pills is a mess on a phone; one snapping
       row is how the app itself presents suggestion chips. */
    .s-console__chips {
      display: grid;
      grid-auto-flow: column;
      grid-auto-columns: max-content;
      flex-wrap: nowrap;
      overflow-x: auto;
      scroll-snap-type: x proximity;
      scrollbar-width: none;
      max-width: none;
      padding-bottom: 4px;
    }
    .s-console__chips::-webkit-scrollbar {
      display: none;
    }
    .s-chip {
      scroll-snap-align: start;
      white-space: nowrap;
    }
  }
  .s-console__note {
    max-width: 62ch;
    text-align: left;
    font-size: 0.9375rem;
    color: var(--text-3);
    line-height: 1.6;
  }
  .s-console .c-phone {
    --pw: 340px;
  }
  @media (max-width: 767px) {
    .s-console .c-phone {
      --pw: 300px;
    }
  }

}

@layer components {
  /* ── Press feedback ────────────────────────────────────────────────────
     The first build had exactly ONE :active rule on the whole page. On a
     touch device — the primary audience here — that means almost nothing on
     the page acknowledges being tapped. The app's own numbers: 90ms in,
     160ms out, scale 0.96. Asymmetric durations are what make it feel like a
     physical key rather than a rubber band. */
  .s-chip,
  .c-replay,
  .c-toggle button,
  .c-badge,
  .c-panel,
  .c-tool,
  .c-card {
    transition: background-color var(--d-micro) var(--ease-out),
      border-color var(--d-micro) var(--ease-out), color var(--d-micro) var(--ease-out),
      transform var(--d-press-out) var(--ease-out), box-shadow var(--d-micro) var(--ease-out);
  }
  .s-chip:active,
  .c-replay:active,
  .c-toggle button:active,
  .c-badge:active {
    transform: scale(0.96);
    transition-duration: var(--d-press-in);
  }

  /* Cards lift further and bloom in their own accent rather than all sharing
     one 2px nudge. */
  .c-card:hover,
  .c-tool:hover {
    transform: translateY(-4px);
    box-shadow: var(--sh-3), var(--line-inner);
  }
  .c-tool:hover {
    box-shadow: var(--sh-3), 0 0 40px -20px color-mix(in oklab, var(--accent) 80%, transparent);
  }
  .c-tool .c-chip,
  .c-panel .c-chip {
    transition: scale var(--d-micro) var(--ease-out);
  }
  .c-tool:hover .c-chip,
  .c-panel:hover .c-chip {
    scale: 1.08;
  }

  /* The three most commercially important cards on the page were the only
     ones with no hover state at all. */
  .c-plan {
    transition: border-color var(--d-micro) var(--ease-out),
      transform var(--d-micro) var(--ease-out), box-shadow var(--d-micro) var(--ease-out);
  }
  .c-plan:hover {
    transform: translateY(-4px);
    border-color: var(--line-strong);
    box-shadow: var(--sh-3);
  }
  .c-plan--feature {
    box-shadow: 0 0 0 1px rgb(99 102 241 / 0.2), var(--sh-4),
      0 30px 70px -30px rgb(99 102 241 / 0.5);
  }
  .c-plan--feature:hover {
    border-color: var(--brand);
    box-shadow: 0 0 0 1px rgb(99 102 241 / 0.3), var(--sh-4),
      0 34px 80px -30px rgb(99 102 241 / 0.65);
  }

  /* Section eyebrow that is not the same block eleven times. */
  .c-eyebrow {
    display: inline-flex;
    align-items: center;
    gap: 9px;
    padding: 7px 14px 7px 10px;
    border-radius: var(--r-pill);
    background: var(--surface);
    border: 1px solid var(--line);
    font-size: var(--t-micro);
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--text-2);
  }
  .c-eyebrow i {
    inline-size: 7px;
    block-size: 7px;
    border-radius: 50%;
    background: var(--accent, var(--brand));
    flex: none;
  }
}

@layer utilities {
  .u-bleed-fig {
    max-width: none;
  }
  .u-measure {
    max-width: var(--measure-lead);
  }
  .u-relative {
    position: relative;
  }
}

@layer components {
  /* Console messages are created and appended by JS, so a plain animation on
     the class plays the moment each one lands. The first build appended them
     with no entrance at all — bubbles, the tool pill and the whole action
     card simply blinked into existence, which is the one place the page's
     best set piece looked cheap. */
  .ph-bubble,
  .ph-tool,
  .ph-action {
    animation: bubble-in 280ms var(--ease-reveal) both;
  }
  .ph-action__row {
    animation: bubble-in 240ms var(--ease-reveal) both;
    animation-delay: calc(var(--ri, 0) * 70ms + 60ms);
  }
  @keyframes bubble-in {
    from {
      opacity: 0;
      translate: 0 8px;
      scale: 0.97;
    }
  }
}

@layer sections {
  /* An inline-flex pill inside a grid track still stretches — grid stretches
     items, and inline-flex only governs the box's own inner layout. */
  .c-eyebrow {
    justify-self: start;
  }

  /* A 393x852 frame at 340px is 736px tall; three cards fill about half of it.
     Top-aligning is what a real app screen does, but on a marketing page it
     reads as an unfinished mockup. */
  .s-fold__state {
    justify-content: center;
  }
}

@layer sections {
  /* `.s-celebrate p` is (0,1,1) and `.s-celebrate__count` is (0,1,0), so the
     generic paragraph rule was silently winning and rendering the page's
     largest number at lead size. Scope the generic rule to unclassed <p>. */
  .s-celebrate p {
    font-size: inherit;
    max-width: none;
  }
  .s-celebrate p:not([class]) {
    color: #d6cfe4;
    font-size: var(--t-lead);
    max-width: 42ch;
  }
  .s-celebrate__count {
    font-size: var(--t-stat);
  }
  .s-celebrate__cap {
    font-size: var(--t-h3);
  }

  /* scroll-snap-type: x mandatory snaps the FIRST item to the scrollport
     start on load, which scrolls padding-inline-start straight out of view —
     the rail rendered flush to x=0 despite a 160px pad. scroll-padding is the
     property that actually governs where a snap target lands. */
  .c-rail {
    scroll-padding-inline: var(--edge, var(--gutter)) var(--gutter);
  }

  /* The section header sits in .l-container--wide (1360), not 1200. */
  .l-edge {
    --edge: max(var(--gutter), calc((100% - 1360px) / 2 + var(--gutter)));
  }

  /* Today reads as today. */
  .c-week i[data-today] {
    box-shadow: 0 0 0 2px rgb(255 255 255 / 0.75), 0 0 18px -2px var(--ember-warm);
  }
}

@layer sections {
  /* The render's ground samples near #08051f at the corners — DARKER than the
     violet field — so an under-tight mask leaves a visible dark rectangle.
     The previous radius (120% 100%, opaque to 55%) put the whole opaque stop
     outside the image box, which masked nothing at all.

     mix-blend-mode: lighten does the rest: it keeps whichever of image and
     field is brighter, so the quokka survives untouched and the dark ground
     is replaced by the field it sits on. */
  .s-mascot__fig img {
    mask-image: radial-gradient(72% 58% at 54% 46%, #000 42%, transparent 84%);
    -webkit-mask-image: radial-gradient(72% 58% at 54% 46%, #000 42%, transparent 84%);
    mix-blend-mode: lighten;
  }
}

@layer sections {
  /* <picture> is an inline box. In the hero it would claim a grid cell while
     its absolutely-positioned <img> escapes; in the mascot it would break the
     img's 100% sizing. display:contents removes the wrapper box entirely and
     lets the <img> be the grid item it used to be. */
  .s-hero__stack > picture {
    display: contents;
  }
  .s-mascot__fig picture {
    display: block;
  }

  /* 136 decorative tiles are real style + layout work on every recalc. They
     are also the last thing on a long section, so skipping them until they
     are near the viewport costs nothing and saves the main thread. The
     intrinsic size keeps the box reserved, so CLS stays 0. */
  .c-field {
    content-visibility: auto;
    /* Four-value form: [auto none] for the inline axis, [auto 460px] for the
       block axis. A single length applies to BOTH axes, which put a 460px
       floor on the width and overflowed every viewport narrower than that. */
    contain-intrinsic-size: auto none auto 460px;
  }
}

@layer sections {
  /* A grid/flex item's automatic minimum size is its CONTENT size, so an
     overflow-x:auto scroller nested in a grid still forces the track wide
     enough for all of it — the scrollbar never engages and the document
     overflows instead. Eight nowrap chips did exactly that: 2078px of layout
     inside a 375px viewport.

     min-inline-size: 0 is what lets the scroller actually scroll. */
  .s-console,
  .s-console > *,
  .s-console__chips,
  .c-rail,
  .l-edge {
    min-inline-size: 0;
  }
}

@layer components {
  /* Every child of a grid container becomes a grid item — including bare text
     nodes and inline elements. So `10 AI chat messages … <strong>lifetime</strong>,
     not monthly` split into three items and wrapped into a second row, where
     "lifetime" landed in the icon column on top of the following text.
     Flex keeps the icon fixed and lets the rest stay inline flow. */
  .c-plan li {
    display: flex;
    gap: 10px;
    align-items: flex-start;
  }
  .c-plan li svg {
    flex: none;
  }
}

@layer sections {
  /* The phone column is ~740px tall; centring the copy column against it
     opened a dead gap between the section heading and the first chip. */
  .s-console {
    align-items: start;
  }
}

@layer components {
  /* ── Film grain ────────────────────────────────────────────────────────
     One 64px quantised-noise tile, inlined (1.2KB), tiled as a STATIC
     pseudo-element per act section. No fixed layers, no animation — the
     texture is the point, not the motion. Sits above content at 5%: film
     grain grades the whole frame, type included. */
  .u-grain {
    position: relative;
  }
  .u-grain::before {
    content: "";
    position: absolute;
    inset: 0;
    z-index: 2;
    pointer-events: none;
    background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABAAgMAAADXB5lNAAAADFBMVEX///+qqqpVVVUAAAABM1s0AAAACXBIWXMAAAPoAAAD6AG1e1JrAAAES0lEQVR42gFABL/7AI42H0EDRKI5TmrvnlNYfEAAO+50eJ3Ez05vWZbOs8HevwCt9UY1DtM1hSh5VrRyt1sGANSKTZMJy22prCjoPLMwnF4AP9Avsy1xrI5hHi1JrolRlQA6f5S4og6O4IjHQm8aEFJNACFHI6wpMxZUFJ9S8m+YfYEAKWraDL/XlsvI+fc394tT5ADbvOBptWTHreOFK0ee9UqZACiQ/gxNsDDMLBQCGzxJY1UAmsYRnhbv8r8Iz6N6GaePigDOr9/xyMSYr5LhZALxc41hAKhCIaD8bjLbLsWZxePaP4QALRWTE8DCwTEzFUJ1vccIhwCMQaHcBNrG4cOyhTx+8uaSAObzoOCS2gYXJo/JiR764woAaM5C1gnlPgltT3ENHCIfRgCA4LMrVoC+KYUwDxYyLHFUAI4y4gN7zRTfcmoyphh63V4AgS0UkpfbAEuSAjiBHAd6AACUt4CFPCvZ/7QZwnzWc3/yAEwzibl2lNuQzLMC/PXE9dIAjRY0u+e5W0XCJjXc5+Z1eQDLFdzxPCbA893jgkYA2yK7AONqJfKNoYXtMlINrSttEUwA1+RBa1lAiQ46CrKNcLpyZgAQEE2bDaHz8H6C+s0vyNdvAAga6kdEF4Cs11F67BFLTisAVuEiQ3RuN9E4DqtJQcmb6QD73LRWctYTo0F6o877dGGHAO+EzGfmh44jfiYwVMwwW38AvllAhreVv8Yn5eSOeGFyDgAunomX4ANidejzK1kuRQoqACCOg/Hg5LDNOkL1Oa53qEoArV3K7OmfdzWCjcjfrRkUrwBfEGd/wCqRpjqN90Vi3AKuAFKi/EMtbudPH4AxQF+aZXIAWv6N8oCXaRsfXPAZUaEQkADvwIGE1piKyLLHiAMa3NIYAJYdSVT4i2Srym4Nvrt9YWkAV1xNFLP5HETsmr3j+hQgxQAZK9opLQVlVQN4Z8Ln2DRxAFgMLlR4yyL1qGT2y+/g7lwA9hItS/KyphjMGx+wB5VcpAAnWdbY3mgC5lnGNOzlKQPnAFBbWu14zDB20tU27/KBXRgA+K2wyjXXtRaT86/31d0uFQCPDEslz6zOasgAv2V4Y2JlAAz2M/6heyvzxhNe9U6OwlAAhG6uHMuNMr/2vqWo6qvdzQACGnjIwkHE1dvlRorZk7coAOdtmtlsiAuksglOItg8qz4AONJYZvjvTiVGh8dkKsdv9wB7eE6fcwtOr+WRtDH6rjKgAL9Qj6RJGih/iH3A6jWeI+QAHr7b5Vg7sreXOSMExarO/QDjWvmnAzJL/SqSdDavPxDtAK5hPuBGB5GqlvMt3ygmhRgAVupNU9uFnXPNJ/JbWPQcZQDe85RNT+NmKKttb3Zgttz4ANFY2I4OHbFmWXvrHKtOl4wA+AjLmMtyVsepk19f+ft1+wCPcNGQvIvfzS5iz9nagrsrAD5Ad3cB9go6kgzhspP3YzTaxwTOBhmrXAAAAABJRU5ErkJggg==")
      repeat;
    background-size: 64px 64px;
    opacity: var(--grain-a);
  }
  /* The paper interlude is a printed sheet — heavier tooth reads as stock. */
  .t-paper.u-grain::before {
    opacity: 0.07;
    mix-blend-mode: multiply;
  }

  /* ── Screenshot phone ──────────────────────────────────────────────────
     The frame adopts the SHOT_CROP ratio so the image is never letterboxed;
     the Dynamic Island pseudo supplies the hardware the crop removed. */
  .c-phone--shot {
    aspect-ratio: var(--shot-ar);
  }
  .c-phone--shot .c-phone__screen {
    padding: 0;
    display: block;
  }
  /* The SHOT_CROP already removed the status-bar band, so the app's greeting
     sits at the very top of the image — an overlaid Dynamic Island pseudo
     would cover it. Screen-only, like every store screenshot. */
  .c-phone--shot .c-phone__screen::before {
    display: none;
  }
  .c-phone--shot .c-phone__screen :is(picture, img) {
    display: block;
    inline-size: 100%;
    block-size: 100%;
    object-fit: cover;
    border-radius: inherit;
  }

}

/* ── The act sections ────────────────────────────────────────────────────── */

/* ═══════════════════════════════════════════════════════════════════════════
   PART FOUR — MIDNIGHT
   ---------------------------------------------------------------------------
   The 2026-07-30 rebuild of the rebuild. Verdict on ONE NIGHT: the serif read
   as a broadsheet, the paper interlude broke the spell, and too many scenes
   were re-dressed drafts. MIDNIGHT is one continuous world instead: a single
   near-black ground the whole way down, every scene lit by its own glow
   field, glass surfaces with hairline borders, and a contemporary display
   grotesk (Bricolage, opsz 96 static instance) in place of the serif.

   Discipline unchanged: M1–M6 all hold. Glow is painted in gradient STOPS,
   never blur filters; loops are transform/opacity and gated by [data-on];
   the default CSS state is the final visible state.
   ═══════════════════════════════════════════════════════════════════════════ */

@layer tokens {
  :root {
    /* The one ground. Sections never change it — light does the work. */
    --mid: #060714;
    --mid-2: #0a0b1f; /* washes and card interiors, one step up */
    --mid-3: #0e0f26;

    /* Glow accents as bare triplets, so alpha is composable at use sites. */
    --gl-violet: 99 102 241; /* brand */
    --gl-lilac: 167 139 250;
    --gl-mint: 134 239 172;
    --gl-pink: 249 168 212;
    --gl-sky: 125 211 252;
    --gl-amber: 252 211 77;
    --gl-ember: 255 128 84;

    /* Display face. PJS remains the UI voice; Bricolage speaks in headlines
       and big numerals only. No italic instance exists — accent words take a
       gradient fill instead of a slant. */
    --font-disp: "Bricolage Grotesque", "Bricolage Fallback", system-ui, sans-serif;
    --t-poster: clamp(2.75rem, 1.55rem + 5.1vw, 6.125rem); /* 44 → 98 */
    --t-scene: clamp(2rem, 1.53rem + 2vw, 3.25rem); /* 32 → 52 */

    /* Grain stays, quieter: on a near-black ground 5% reads as dirt. */
    --grain-a: 0.04;
  }
}

/* Self-hosted static instance — 22.4KB. font-weight spans 300–900 so faux
   bold can never be synthesised on top of it. */
@font-face {
  font-family: "Bricolage Grotesque";
  font-style: normal;
  font-weight: 300 900;
  font-display: swap;
  src: url("/assets/fonts/bricolage-latin-opsz96-w700-v9.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: "Bricolage Fallback";
  src: local("Arial Bold"), local("Arial");
  size-adjust: 99%;
  ascent-override: 94%;
  descent-override: 24%;
}

@layer base {
  /* The whole site sits on the midnight ground — features/legal/404 inherit
     it too, which finally puts every page in the same world. */
  body {
    background: var(--mid);
  }
  ::selection {
    background: rgb(var(--gl-violet) / 0.4);
    color: #fff;
  }

  main.t-mid :is(h1, h2):not(.u-vh) {
    font-family: var(--font-disp);
    font-weight: 700;
    letter-spacing: -0.025em;
    line-height: 1.02;
    font-synthesis: none;
  }
  main.t-mid h1 {
    font-size: var(--t-poster);
    letter-spacing: -0.032em;
  }
  main.t-mid :is(h1, h2):not(.u-vh) em {
    /* No italic cut in the static instance — the accent word glows instead.
       The padding/negative-margin pair exists because background-clip only
       paints inside the box, and Bricolage glyphs overhang their advance
       width — without it terminals get shaved off. */
    font-style: normal;
    background: linear-gradient(100deg, #a5b4fc 10%, #7dd3fc 55%, #86efac 105%);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
    padding: 0.06em 0.1em;
    margin: -0.06em -0.1em;
  }
  main.t-mid .c-stat__v,
  main.t-mid .c-plan__price,
  main.t-mid .s-celebrate__count,
  main.t-mid .m-ledger__resv,
  main.t-mid .m-big {
    font-family: var(--font-disp);
    font-weight: 700;
    font-synthesis: none;
  }
}

/* ── Components ──────────────────────────────────────────────────────────── */
@layer components {
  /* Glass surface — the page's one card recipe. --glow tints the border and
     the top light; leave it unset for neutral glass. */
  .m-glass {
    position: relative;
    border-radius: var(--r-card);
    background: linear-gradient(180deg, rgb(255 255 255 / 0.055), rgb(255 255 255 / 0.018) 55%),
      var(--mid-2);
    border: 1px solid rgb(255 255 255 / 0.09);
    box-shadow: 0 28px 70px -32px rgb(0 0 0 / 0.85), inset 0 1px 0 rgb(255 255 255 / 0.08);
  }
  .m-glass--glow {
    border-color: color-mix(in srgb, rgb(var(--glow, var(--gl-violet))) 32%, rgb(255 255 255 / 0.1));
    box-shadow: 0 28px 70px -32px rgb(0 0 0 / 0.85),
      0 0 64px -18px rgb(var(--glow, var(--gl-violet)) / 0.38),
      inset 0 1px 0 rgb(255 255 255 / 0.09);
  }
  .m-glass--glow::before {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: inherit;
    pointer-events: none;
    background: radial-gradient(
      60% 44% at 50% 0%,
      rgb(var(--glow, var(--gl-violet)) / 0.16),
      transparent 70%
    );
  }

  /* Eyebrow — kept from the old kit but recoloured per scene via --accent. */

  /* Proof chips — the honest strip. */
  .m-proof {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    justify-content: center;
  }
  .m-proof li {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    font-size: var(--t-meta);
    font-weight: 600;
    color: #c9cae4;
    padding: 7px 13px;
    border-radius: var(--r-pill);
    background: rgb(255 255 255 / 0.045);
    border: 1px solid rgb(255 255 255 / 0.1);
  }
  .m-proof li[hidden] {
    display: none;
  }
  .m-proof li::before {
    content: "";
    inline-size: 5px;
    block-size: 5px;
    border-radius: 50%;
    background: rgb(var(--gl-mint));
    box-shadow: 0 0 8px rgb(var(--gl-mint) / 0.8);
  }
  .m-proof li svg {
    inline-size: 14px;
    block-size: 14px;
  }
  .m-proof li:has(svg)::before {
    display: none;
  }

  /* Floating data chips around tour screenshots. */
  .m-float {
    position: absolute;
    z-index: 2;
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 9px 13px;
    border-radius: 14px;
    background: linear-gradient(180deg, rgb(20 21 44 / 0.92), rgb(12 13 30 / 0.92));
    border: 1px solid color-mix(in srgb, rgb(var(--fc, var(--gl-violet))) 34%, rgb(255 255 255 / 0.1));
    box-shadow: 0 16px 40px -12px rgb(0 0 0 / 0.7), 0 0 28px -8px rgb(var(--fc, var(--gl-violet)) / 0.35);
    font-size: 0.8125rem;
    font-weight: 700;
    color: #e6e7f5;
    white-space: nowrap;
    translate: 0 calc((var(--p, 0.5) - 0.5) * var(--depth, 0) * -70px);
  }
  .m-float i {
    inline-size: 7px;
    block-size: 7px;
    border-radius: 50%;
    background: rgb(var(--fc, var(--gl-violet)));
    box-shadow: 0 0 10px rgb(var(--fc, var(--gl-violet)) / 0.9);
    flex: none;
  }
  .m-float b {
    font-weight: 800;
    color: #fff;
  }
  .m-float small {
    font-size: 0.6875rem;
    font-weight: 700;
    letter-spacing: 0.07em;
    text-transform: uppercase;
    color: rgb(var(--fc, var(--gl-violet)) / 0.95);
  }
  /* On a phone the screenshot IS the data — chips would sit on top of the
     very numbers they repeat. */
  @media (max-width: 767px) {
    .m-float {
      display: none;
    }
  }

  /* ── The glass ledger (pin scene) ────────────────────────────────────── */
  .m-ledger {
    inline-size: min(100%, 460px);
    margin-inline: auto;
    padding: clamp(18px, 2vw, 28px);
    display: grid;
    gap: 4px;
  }
  .m-ledger__head {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    font-size: var(--t-micro);
    font-weight: 700;
    letter-spacing: 0.09em;
    text-transform: uppercase;
    color: var(--text-3);
    padding-block-end: 12px;
    border-block-end: 1px solid rgb(255 255 255 / 0.08);
    margin-block-end: 8px;
  }
  .m-ledger__row {
    display: grid;
    grid-template-columns: 10px 1fr auto;
    align-items: center;
    column-gap: 12px;
    padding-block: 11px;
    border-block-end: 1px solid rgb(255 255 255 / 0.05);
  }
  .m-ledger__row i {
    inline-size: 8px;
    block-size: 8px;
    border-radius: 50%;
    background: var(--lc, var(--brand-soft));
    box-shadow: 0 0 12px color-mix(in srgb, var(--lc, var(--brand-soft)) 70%, transparent);
  }
  .m-ledger__n {
    font-size: 0.9375rem;
    font-weight: 700;
    color: #eceef8;
    line-height: 1.25;
  }
  .m-ledger__n small {
    display: block;
    font-size: 0.75rem;
    font-weight: 500;
    color: var(--text-3);
    margin-block-start: 1px;
  }
  .m-ledger__a {
    font-size: 0.9375rem;
    font-weight: 800;
    font-variant-numeric: tabular-nums;
    color: #fff;
    letter-spacing: -0.01em;
  }
  .m-ledger__a--in {
    color: var(--income);
  }
  .m-ledger__a--out {
    color: #f5c0c0;
  }
  .m-ledger__tag {
    grid-column: 2 / -1;
    justify-self: start;
    margin-block-start: 7px;
    font-size: 0.6875rem;
    font-weight: 700;
    color: var(--income);
    background: rgb(var(--gl-mint) / 0.1);
    border: 1px solid rgb(var(--gl-mint) / 0.3);
    border-radius: var(--r-pill);
    padding: 3px 9px;
  }
  .m-ledger__div {
    display: flex;
    align-items: center;
    gap: 12px;
    padding-block: 12px 6px;
    font-size: var(--t-meta);
    font-weight: 700;
    color: var(--text-2);
    white-space: nowrap;
  }
  .m-ledger__div::before,
  .m-ledger__div::after {
    content: "";
    block-size: 1px;
    flex: 1;
    background: linear-gradient(90deg, transparent, rgb(255 255 255 / 0.18), transparent);
  }
  .m-ledger__res {
    display: grid;
    justify-items: center;
    gap: 2px;
    padding-block: 10px 6px;
    text-align: center;
  }
  .m-ledger__resk {
    font-size: var(--t-micro);
    font-weight: 700;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--text-3);
  }
  .m-ledger__resv {
    font-size: clamp(3rem, 2.2rem + 3.4vw, 4.75rem);
    line-height: 1;
    letter-spacing: -0.02em;
    color: #fff;
    text-shadow: 0 0 44px rgb(var(--gl-violet) / 0.55);
  }
  .m-ledger__resd {
    font-size: var(--t-body);
    font-weight: 600;
    color: var(--text-2);
  }

  /* ── Bento cell ──────────────────────────────────────────────────────── */
  .m-cell {
    position: relative;
    overflow: clip;
    display: flex;
    flex-direction: column;
    gap: 14px;
    padding: clamp(18px, 1.8vw, 26px);
    border-radius: var(--r-panel);
    transition: translate var(--d-micro) var(--ease-out), border-color var(--d-micro) var(--ease-out),
      box-shadow var(--d-micro) var(--ease-out);
  }
  @media (hover: hover) {
    .m-cell:hover {
      translate: 0 -3px;
      border-color: color-mix(in srgb, rgb(var(--glow, var(--gl-violet))) 45%, rgb(255 255 255 / 0.12));
      box-shadow: 0 28px 70px -32px rgb(0 0 0 / 0.85),
        0 0 54px -14px rgb(var(--glow, var(--gl-violet)) / 0.45),
        inset 0 1px 0 rgb(255 255 255 / 0.09);
    }
  }
  /* Cursor spotlight — JS writes --mx/--my per cell; hover fades it in. */
  .m-cell::after {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: inherit;
    pointer-events: none;
    opacity: 0;
    transition: opacity 320ms var(--ease-out);
    background: radial-gradient(
      200px circle at var(--mx, 50%) var(--my, 50%),
      rgb(255 255 255 / 0.07),
      transparent 70%
    );
  }
  [data-spot]:hover .m-cell::after {
    opacity: 1;
  }
  .m-cell h3 {
    font-size: 1.0625rem;
    font-weight: 800;
    letter-spacing: -0.01em;
    margin-block-start: auto;
  }
  .m-cell > p {
    font-size: 0.875rem;
    color: var(--text-2);
    line-height: 1.55;
    max-inline-size: 44ch;
  }
  .m-cell__k {
    font-size: var(--t-micro);
    font-weight: 700;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: color-mix(in srgb, rgb(var(--glow, var(--gl-violet))) 80%, #fff);
    display: inline-flex;
    align-items: center;
    gap: 8px;
  }
  .m-cell__k::before {
    content: "";
    inline-size: 6px;
    block-size: 6px;
    border-radius: 50%;
    background: rgb(var(--glow, var(--gl-violet)));
    box-shadow: 0 0 10px rgb(var(--glow, var(--gl-violet)) / 0.9);
  }
  .m-cell__fig {
    min-block-size: 86px;
    display: grid;
    align-items: center;
    justify-items: start;
  }
  .m-cell__fig svg {
    max-inline-size: 100%;
    inline-size: 100%;
    block-size: auto;
    max-block-size: 150px;
  }

  /* Bills — a month of dots. */
  .m-dots {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
    gap: 8px;
    inline-size: min(100%, 240px);
  }
  .m-dots i {
    aspect-ratio: 1;
    border-radius: 50%;
    background: rgb(255 255 255 / 0.1);
  }
  .m-dots i[data-paid] {
    background: rgb(var(--gl-mint) / 0.85);
    box-shadow: 0 0 10px rgb(var(--gl-mint) / 0.5);
  }
  .m-dots i[data-due] {
    background: rgb(var(--gl-amber));
    box-shadow: 0 0 0 4px rgb(var(--gl-amber) / 0.18), 0 0 14px rgb(var(--gl-amber) / 0.7);
  }

  /* Repeats / to-dos — mini ledger rows outside a phone: --u is 1px here. */
  .m-rows {
    --u: 1px;
    display: grid;
    gap: 8px;
    align-content: center;
  }
  .m-rows .ph-row {
    background: rgb(255 255 255 / 0.04);
    border: 1px solid rgb(255 255 255 / 0.07);
    border-radius: 12px;
    padding: 9px 12px;
  }
  .m-rows__stamp {
    justify-self: start;
    font-size: 0.6875rem;
    font-weight: 800;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--p-teal);
    border: 1.5px dashed color-mix(in srgb, var(--p-teal) 55%, transparent);
    border-radius: 8px;
    padding: 4px 9px;
    rotate: -2deg;
  }

  /* To-do check rows */
  .m-todo {
    display: grid;
    gap: 8px;
    align-content: center;
  }
  .m-todo div {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px 10px;
    background: rgb(255 255 255 / 0.04);
    border: 1px solid rgb(255 255 255 / 0.07);
    border-radius: 12px;
    padding: 9px 12px;
    font-size: 0.8125rem;
    font-weight: 600;
    color: #dfe1f0;
  }
  .m-todo i {
    inline-size: 16px;
    block-size: 16px;
    border-radius: 6px;
    border: 1.5px solid rgb(255 255 255 / 0.28);
    flex: none;
    display: grid;
    place-items: center;
  }
  .m-todo div[data-done] i {
    background: var(--p-teal);
    border-color: var(--p-teal);
  }
  .m-todo div[data-done] i::before {
    content: "";
    inline-size: 8px;
    block-size: 5px;
    border-inline-start: 2px solid #071018;
    border-block-end: 2px solid #071018;
    rotate: -45deg;
    translate: 0 -1px;
  }
  .m-todo div[data-done] {
    /* No line-through: text-decoration paints through inline children, so it
       would strike the link-tag chip too. The check + dim carry "done". */
    color: var(--text-3);
  }
  .m-todo span {
    margin-inline-start: auto;
    font-size: 0.65rem;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--p-sky);
    background: rgb(var(--gl-sky) / 0.1);
    border: 1px solid rgb(var(--gl-sky) / 0.3);
    border-radius: var(--r-pill);
    padding: 2px 8px;
    white-space: nowrap;
  }

  /* Reports — a pure-CSS donut. Shares from the worked example. */
  .m-donut {
    inline-size: 96px;
    aspect-ratio: 1;
    border-radius: 50%;
    background: conic-gradient(
      #fdba74 0 197deg,
      #7dd3fc 197deg 261deg,
      #fcd34d 261deg 298deg,
      #c4b5fd 298deg 326deg,
      #f9a8d4 326deg 345deg,
      #86efac 345deg 360deg
    );
    -webkit-mask: radial-gradient(closest-side, transparent 60%, #000 61%);
    mask: radial-gradient(closest-side, transparent 60%, #000 61%);
  }

  /* Calculators + currencies — the numerals ARE the figure. */
  .m-big {
    font-size: clamp(2.5rem, 2rem + 1.6vw, 3.5rem);
    line-height: 1;
    letter-spacing: -0.02em;
    color: #fff;
  }
  .m-big + small {
    display: block;
    font-size: var(--t-meta);
    font-weight: 600;
    color: var(--text-2);
    margin-block-start: 4px;
  }

  /* ── Privacy scene pieces ────────────────────────────────────────────── */
  .m-taglist {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-wrap: wrap;
    gap: 7px;
  }
  .m-taglist li {
    font-size: 0.8125rem;
    font-weight: 600;
    color: #d9f5e3;
    background: rgb(var(--gl-mint) / 0.07);
    border: 1px solid rgb(var(--gl-mint) / 0.22);
    border-radius: var(--r-pill);
    padding: 6px 12px;
  }
  .m-privrows {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
  }
  .m-privrows li {
    display: grid;
    gap: 3px;
    padding-block: 12px;
    border-block-end: 1px solid rgb(255 255 255 / 0.06);
  }
  .m-privrows li:last-child {
    border-block-end: 0;
  }
  .m-privrows b {
    font-size: 0.9375rem;
    font-weight: 700;
    color: #eceef8;
  }
  .m-privrows span {
    font-size: 0.875rem;
    color: var(--text-2);
    line-height: 1.5;
  }

  /* ── Tax card ────────────────────────────────────────────────────────── */
  .m-taxcard {
    --u: 1px; /* ph-micro is sized in app points; outside a phone 1pt = 1px */
    inline-size: min(100%, 430px);
    margin-inline: auto;
    padding: clamp(22px, 2.2vw, 32px);
    display: grid;
    gap: 10px;
    justify-items: center;
    text-align: center;
  }
  .m-taxcard__amt {
    font-family: var(--font-disp);
    font-weight: 700;
    font-synthesis: none;
    font-size: clamp(3rem, 2.4rem + 2.6vw, 4.5rem);
    line-height: 1;
    color: #fff;
    text-shadow: 0 0 44px rgb(var(--gl-amber) / 0.4);
  }
  .m-taxcard__chips {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 6px;
  }
  .m-taxcard__chips span {
    font-size: var(--t-micro);
    font-weight: 700;
    color: #ffd88a;
    background: rgb(var(--gl-amber) / 0.09);
    border: 1px solid rgb(var(--gl-amber) / 0.28);
    border-radius: var(--r-pill);
    padding: 4px 10px;
  }
  .m-taxcard__bands {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 6px;
    padding-block-start: 8px;
    border-block-start: 1px solid rgb(255 255 255 / 0.07);
  }
  .m-taxcard__bands span {
    font-size: var(--t-micro);
    font-weight: 600;
    color: var(--text-2);
    background: rgb(255 255 255 / 0.045);
    border: 1px solid rgb(255 255 255 / 0.09);
    border-radius: var(--r-pill);
    padding: 4px 10px;
  }
}

/* ── Chrome re-skin: nav pill + reused kit on the new ground ─────────────── */
@layer components {
  .c-nav {
    background: transparent;
    border-block-end: 0;
  }
  .c-nav__inner {
    margin-block-start: 10px;
    border-radius: var(--r-pill);
    border: 1px solid rgb(255 255 255 / 0.08);
    background: rgb(8 9 24 / 0.55);
    -webkit-backdrop-filter: blur(16px) saturate(1.4);
    backdrop-filter: blur(16px) saturate(1.4);
    padding-inline: clamp(14px, 2vw, 22px);
    min-block-size: 58px;
    transition: border-color var(--d-micro) var(--ease-out), background-color var(--d-micro) var(--ease-out);
  }
  html.is-scrolled .c-nav__inner {
    border-color: rgb(255 255 255 / 0.12);
    background: rgb(8 9 24 / 0.78);
  }
  .c-nav__links a {
    color: #c6c7e2;
  }
  .c-nav__links a:hover {
    color: #fff;
  }

  /* Chips reused by the console demo. */
  .t-mid .s-chip {
    background: rgb(255 255 255 / 0.04);
    border-color: rgb(255 255 255 / 0.1);
  }
  .t-mid .s-chip[aria-pressed="true"] {
    background: rgb(var(--gl-sky) / 0.12);
    border-color: rgb(var(--gl-sky) / 0.5);
    color: #d9f1ff;
  }

  /* Pricing kit on glass. */
  .t-mid .c-toggle {
    background: rgb(255 255 255 / 0.045);
    border: 1px solid rgb(255 255 255 / 0.1);
  }
  .t-mid .c-plan {
    border-radius: var(--r-card);
    background: linear-gradient(180deg, rgb(255 255 255 / 0.05), rgb(255 255 255 / 0.016) 55%),
      var(--mid-2);
    border: 1px solid rgb(255 255 255 / 0.09);
    box-shadow: 0 28px 70px -32px rgb(0 0 0 / 0.85), inset 0 1px 0 rgb(255 255 255 / 0.08);
  }
  .t-mid .c-plan--feature {
    border-color: color-mix(in srgb, rgb(var(--gl-violet)) 45%, rgb(255 255 255 / 0.12));
    box-shadow: 0 28px 70px -32px rgb(0 0 0 / 0.85), 0 0 74px -16px rgb(var(--gl-violet) / 0.45),
      inset 0 1px 0 rgb(255 255 255 / 0.1);
  }
  .t-mid .c-plan__price {
    letter-spacing: -0.02em;
  }

  /* FAQ as a quiet divider list — no boxes. The cards crowded their own
     text; here the air IS the design. */
  .t-mid .c-faq {
    border-block-start: 1px solid rgb(255 255 255 / 0.1);
  }
  .t-mid .c-faq details {
    border: 0;
    border-radius: 0;
    background: none;
    border-block-end: 1px solid rgb(255 255 255 / 0.1);
  }
  .t-mid .c-faq summary {
    padding-block: clamp(20px, 2.4vw, 27px);
    padding-inline: 2px;
    font-size: clamp(1.0625rem, 1rem + 0.35vw, 1.25rem);
    font-weight: 700;
    letter-spacing: -0.01em;
    color: #dfe0f0;
    transition: color var(--d-micro) var(--ease-out);
  }
  .t-mid .c-faq summary:hover {
    color: #fff;
  }
  .t-mid .c-faq details[open] summary {
    color: #fff;
    padding-block-end: 12px;
  }
  .t-mid .c-faq summary::after {
    border-color: var(--text-3);
  }
  .t-mid .c-faq details[open] summary::after {
    border-color: var(--brand-soft);
  }
  .t-mid .c-faq .c-faq__a {
    padding-block: 0 28px;
    padding-inline: 2px;
    max-inline-size: 66ch;
    color: #a5a6c5;
    line-height: 1.65;
  }

  /* ── Quokka cameos — the mascot, cropped from one render ─────────────── */
  .m-cameo {
    margin: 0;
    /* Explicit block-size, not aspect-ratio: the image's intrinsic height
       would stretch an aspect-ratio box into an oval. */
    inline-size: var(--sz, 72px);
    block-size: var(--sz, 72px);
    border-radius: 50%;
    overflow: clip;
    border: 2px solid rgb(255 255 255 / 0.16);
    box-shadow: 0 8px 28px -8px rgb(0 0 0 / 0.7),
      0 0 34px -8px rgb(var(--ring, var(--gl-violet)) / 0.55);
    background: radial-gradient(120% 120% at 50% 20%, #3a1c6e, #1b0d33);
    flex: none;
  }
  .m-cameo img {
    inline-size: 100%;
    block-size: 100%;
    object-fit: cover;
    object-position: 52% 30%;
    scale: 1.4;
    transform-origin: 52% 30%;
  }
  .m-cameo figcaption {
    position: absolute;
    inset-block-start: 100%;
    inset-inline-start: 50%;
    translate: -50% 8px;
    font-size: var(--t-micro);
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--text-3);
    white-space: nowrap;
  }

  /* Stat band re-skin. */
  .t-mid .c-stat {
    border-block-start: 1px solid rgb(255 255 255 / 0.12);
    padding-block-start: 22px;
  }
  .t-mid .c-stat__v {
    font-size: clamp(2.75rem, 1.9rem + 3.6vw, 5rem);
    letter-spacing: -0.025em;
    line-height: 1;
  }
  .t-mid .c-stat__tag {
    display: inline-block;
    font-size: var(--t-micro);
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--text-3);
    border: 1px solid rgb(255 255 255 / 0.14);
    border-radius: var(--r-pill);
    padding: 4px 10px;
  }

  /* The action field as a constellation of dots. */
  .t-mid .c-field {
    justify-items: center;
  }
  .t-mid .c-field i {
    border-radius: 50%;
    inline-size: clamp(12px, 1.4vw, 20px);
    block-size: auto;
    aspect-ratio: 1;
  }
  .t-mid .c-field__key i {
    border-radius: 50%;
  }
}

/* ── Scenes ──────────────────────────────────────────────────────────────── */
@layer sections {
  /* Shared glow-field primitives: pre-painted radials, softness in stops. */
  .m-halo {
    position: absolute;
    pointer-events: none;
  }

  /* ═══ 1 · Hero — the overture ═══
     No grain, no starfield. The opening is a LIVING aurora field: four
     pre-painted colour blobs drifting on the compositor (translate/scale
     only, softness baked into the stops — the c-mesh discipline at hero
     scale), light rays turning slowly behind the lead device, and the brand
     line landing in three staged beats. Every entrance is a from-only
     keyframe with `backwards` fill, so the finished frame IS the author
     state — parallax, tilt and floats resume untouched after the overture,
     and with JS off the page simply opens settled (M1). */
  .m-hero {
    position: relative;
    overflow: clip;
    min-block-size: 100svh;
    display: grid;
    grid-template-rows: 1fr auto;
    justify-items: center;
    padding-block-start: clamp(104px, 13svh, 144px);
    background: radial-gradient(130% 100% at 50% -24%, #171441, var(--mid) 64%);
  }
  /* The mascot stands among the devices — real character, not ambient glow.
     The render's rectangular ground is feathered away by the mask; the float
     loop lives on the img so it can never fight the figure's positioning
     translate. */
  .m-hero__q {
    /* Floats in the open air to the right of the headline — anchored to the
       SECTION, beside the copy block. Hidden below 1280px, where it would
       collide with the lead paragraph. */
    position: absolute;
    z-index: 1;
    margin: 0;
    inset-block-start: clamp(170px, 24svh, 250px);
    inset-inline-end: clamp(24px, 6vw, 120px);
    inline-size: clamp(180px, 15vw, 245px);
    rotate: 4deg;
    pointer-events: none;
    -webkit-mask-image: radial-gradient(ellipse 66% 78% at 50% 52%, #000 42%, transparent 70%);
    mask-image: radial-gradient(ellipse 66% 78% at 50% 52%, #000 42%, transparent 70%);
    animation: m-q-in 800ms var(--ease-reveal) backwards 820ms;
  }
  .m-hero__q img {
    inline-size: 100%;
    block-size: auto;
  }
  @keyframes m-q-in {
    from {
      opacity: 0;
    }
  }
  [data-on] .m-hero__q img {
    animation: m-float 8s var(--ease-in-out) -2.5s infinite alternate;
  }
  @media (max-width: 1279px) {
    .m-hero__q {
      display: none;
    }
  }

  .m-hero__copy {
    position: relative;
    z-index: 1;
    display: grid;
    justify-items: center;
    gap: clamp(16px, 2svh, 24px);
    text-align: center;
    padding-inline: var(--gutter);
    max-inline-size: 960px;
    align-content: center;
  }
  .m-hero__kick {
    display: inline-flex;
    align-items: center;
    gap: 9px;
    font-size: var(--t-meta);
    font-weight: 700;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: #cdcdea;
    padding: 8px 16px;
    border-radius: var(--r-pill);
    background: rgb(var(--gl-violet) / 0.12);
    border: 1px solid rgb(var(--gl-violet) / 0.34);
    box-shadow: 0 0 26px -6px rgb(var(--gl-violet) / 0.5);
  }
  .m-hero__kick::before {
    content: "";
    inline-size: 6px;
    block-size: 6px;
    border-radius: 50%;
    background: rgb(var(--gl-lilac));
    box-shadow: 0 0 10px rgb(var(--gl-lilac));
  }
  /* The three-beat brand line. Each word rises out of its own clipped slot;
     the padding/negative-margin pair keeps descenders inside the clip. */
  .m-hero h1 {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    column-gap: 0.26em;
    line-height: 1;
  }
  .hw {
    display: inline-block;
    overflow: clip;
    padding-block: 0.14em;
    margin-block: -0.14em;
  }
  .hw i {
    display: inline-block;
    font-style: normal;
    animation: m-word 760ms var(--ease-reveal) backwards;
    animation-delay: calc(180ms + var(--hd, 0) * 140ms);
  }
  @keyframes m-word {
    from {
      translate: 0 120%;
    }
  }
  .hw--g i {
    background: linear-gradient(100deg, #a5b4fc 5%, #7dd3fc 52%, #86efac 100%);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
    padding-inline: 0.08em;
    margin-inline: -0.08em;
  }
  .m-hero__lead {
    font-size: var(--t-lead);
    line-height: 1.55;
    color: #a9aaca;
    max-inline-size: 54ch;
    text-wrap: pretty;
  }
  .m-hero__actions {
    display: grid;
    justify-items: center;
    gap: 14px;
  }
  .m-hero__note {
    font-size: var(--t-meta);
    color: var(--text-3);
  }

  /* The stage: three real screens over a pool of light. */
  .m-hero__stage {
    position: relative;
    z-index: 1;
    inline-size: 100%;
    block-size: clamp(340px, 46svh, 500px);
    margin-block-start: clamp(20px, 3svh, 44px);
  }
  .m-hero__stage::before {
    content: "";
    position: absolute;
    inset: auto -14% -46% -14%;
    block-size: 120%;
    pointer-events: none;
    background: radial-gradient(
        52% 68% at 50% 100%,
        rgb(var(--gl-violet) / 0.5),
        rgb(var(--gl-violet) / 0.15) 46%,
        transparent 72%
      ),
      radial-gradient(30% 42% at 50% 100%, rgb(var(--gl-lilac) / 0.55), transparent 68%),
      radial-gradient(76% 96% at 50% 106%, rgb(var(--gl-sky) / 0.12), transparent 74%);
  }
  .m-hero__dev {
    /* Anchored to the stage TOP and growing downward, so a tall phone can
       only ever sink below the fold (section clip crops it) — it can never
       climb up into the copy. */
    position: absolute;
    inset-block-start: 0;
    inset-inline-start: 50%;
    translate: 0 calc((var(--p, 0.32) - 0.32) * var(--depth, 0) * -120px);
    will-change: translate;
  }
  .m-hero__pos {
    translate: calc(-50% + var(--ox, 0px)) var(--oy, 0px);
    rotate: var(--tilt, 0deg);
    animation: m-dev-in 900ms var(--ease-reveal) backwards;
    animation-delay: var(--dd, 420ms);
  }
  /* from-only: interpolates INTO the author transform, so the entrance can
     never pin the parallax/positioning values it animates through. */
  @keyframes m-dev-in {
    from {
      opacity: 0;
      translate: calc(-50% + var(--ox, 0px)) calc(var(--oy, 0px) + 72px);
    }
  }
  .m-hero__dev--lead {
    z-index: 3;
    --depth: 0.5;
  }
  .m-hero__dev--lead .m-hero__pos {
    --oy: 0px;
    --dd: 640ms;
  }
  .m-hero__dev--lead .c-phone {
    --pw: clamp(224px, 24vw, 300px);
    box-shadow: var(--sh-phone), 0 0 90px -12px rgb(var(--gl-violet) / 0.55),
      inset 0 1px 0 rgb(255 255 255 / 0.22);
  }
  .m-hero__dev--l,
  .m-hero__dev--r {
    z-index: 2;
  }
  .m-hero__dev--l {
    --depth: 0.95;
  }
  .m-hero__dev--r {
    --depth: 0.72;
  }
  .m-hero__dev--l .m-hero__pos {
    --ox: clamp(-330px, -24vw, -230px);
    --oy: 12%;
    --tilt: -9deg;
    --dd: 380ms;
  }
  .m-hero__dev--r .m-hero__pos {
    --ox: clamp(230px, 24vw, 330px);
    --oy: 15%;
    --tilt: 9deg;
    --dd: 500ms;
  }
  .m-hero__dev--l .c-phone,
  .m-hero__dev--r .c-phone {
    --pw: clamp(158px, 17vw, 226px);
    filter: brightness(0.62) saturate(0.92);
  }
  /* The lead leans toward the pointer (initTilt writes --tx/--ty). */
  .m-hero__dev--lead .c-phone {
    transform: perspective(1100px) rotateY(var(--tx, 0deg)) rotateX(var(--ty, 0deg));
  }
  /* Idle float, gated: only while the hero is on screen. */
  [data-on] .m-hero__dev--lead .c-phone {
    animation: m-float 7s var(--ease-in-out) infinite alternate;
  }
  [data-on] .m-hero__dev--l .c-phone {
    animation: m-float 9s var(--ease-in-out) -3s infinite alternate;
  }
  [data-on] .m-hero__dev--r .c-phone {
    animation: m-float 8s var(--ease-in-out) -5s infinite alternate;
  }
  @keyframes m-float {
    from {
      translate: 0 0;
    }
    to {
      translate: 0 -10px;
    }
  }

  /* Floating data chips: a positioned wrapper carries parallax; the chip
     itself carries entrance + endless bob (sequential, so no property war). */
  .m-hero__chip {
    position: absolute;
    z-index: 4;
    translate: 0 calc((var(--p, 0.32) - 0.32) * var(--depth, 0.6) * -90px);
  }
  .m-hero__chip .m-float {
    position: static;
    translate: none;
  }
  [data-on] .m-hero__chip .m-float {
    animation: m-chip-in 700ms var(--ease-reveal) backwards var(--cd, 1.15s),
      m-bob var(--bd, 5.4s) var(--ease-in-out) calc(var(--cd, 1.15s) + 700ms) infinite;
  }
  @keyframes m-chip-in {
    from {
      opacity: 0;
      translate: 0 18px;
    }
  }
  @keyframes m-bob {
    0%,
    100% {
      translate: 0 0;
    }
    50% {
      translate: 0 -7px;
    }
  }
  @media (max-width: 767px) {
    .m-hero__chip {
      display: none;
    }
  }

  .m-hero__cue {
    position: absolute;
    z-index: 4;
    inset-block-end: 18px;
    inset-inline-start: 50%;
    translate: -50% 0;
    display: inline-flex;
    align-items: center;
    gap: 8px;
    font-size: var(--t-micro);
    font-weight: 700;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: #b9bada;
    opacity: clamp(0, 1 - var(--p, 0) * 9, 1);
  }
  .m-hero__cue svg {
    inline-size: 12px;
    block-size: 12px;
  }
  [data-on] .m-hero__cue svg {
    animation: m-cue 1.6s var(--ease-in-out) infinite;
  }
  @keyframes m-cue {
    0%,
    100% {
      translate: 0 0;
      opacity: 1;
    }
    50% {
      translate: 0 4px;
      opacity: 0.5;
    }
  }

  @media (max-width: 767px) {
    .m-hero__dev--l .m-hero__pos {
      --ox: -31vw;
      --oy: 16%;
    }
    .m-hero__dev--r .m-hero__pos {
      --ox: 31vw;
      --oy: 18%;
    }
    .m-hero__dev--l .c-phone,
    .m-hero__dev--r .c-phone {
      --pw: 128px;
    }
    .m-hero__dev--lead .c-phone {
      --pw: 224px;
    }
  }


  /* ═══ 2 · The statement — words ignite as you pass ═══ */
  .m-said {
    position: relative;
    padding-block: var(--section-y-tall);
    background: var(--mid);
  }
  .m-said h2 {
    max-inline-size: 24ch;
    margin-inline: auto;
    text-align: center;
    font-size: var(--t-scene);
    line-height: 1.14;
    text-wrap: balance;
  }
  .m-said__b {
    display: block; /* the answer gets its own lines */
    margin-block-start: 0.25em;
  }
  .m-said .w {
    opacity: calc(0.13 + 0.87 * clamp(0, (var(--p, 1) - (0.12 + var(--wi, 0) * 0.026)) * 14, 1));
    transition: opacity 80ms linear;
  }
  .m-said__b .w {
    background: linear-gradient(100deg, #b5c0ff 0%, #8fd8ff 100%);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
    padding: 0.06em 0.08em;
    margin: -0.06em -0.08em;
  }
  .m-said__coda {
    text-align: start;
    font-size: var(--t-lead);
    color: var(--text-2);
    max-inline-size: 34ch;
  }
  .m-said__after {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 18px;
    margin-block-start: clamp(28px, 4vw, 44px);
  }

  /* ═══ 3 · How the number is made — the pin ═══ */
  .m-make {
    position: relative;
    background: var(--mid);
  }
  .m-make__track {
    position: relative;
  }
  .m-make__stage {
    position: relative;
    padding-block: var(--section-y);
  }
  /* The scene's light: a low violet pool behind the ledger. */
  .m-make__stage::before {
    content: "";
    position: absolute;
    inset: 10% -10% 0 30%;
    pointer-events: none;
    background: radial-gradient(46% 42% at 68% 52%, rgb(var(--gl-violet) / 0.17), transparent 72%);
  }
  .m-make__grid {
    position: relative;
    display: grid;
    gap: clamp(28px, 4vw, 72px);
    align-items: center;
  }
  .m-make__caps {
    display: grid;
    gap: 18px;
    align-content: start;
  }
  .m-make__cap {
    padding-block: 4px;
  }
  .m-make__cap b {
    display: block;
    font-size: 1.125rem;
    font-weight: 800;
    letter-spacing: -0.01em;
    margin-block-end: 6px;
  }
  .m-make__cap p {
    font-size: var(--t-body);
    color: var(--text-2);
    max-inline-size: 38ch;
  }
  .m-make__run {
    display: inline-block;
    margin-block-start: 10px;
    font-size: var(--t-meta);
    font-weight: 800;
    font-variant-numeric: tabular-nums;
    color: color-mix(in srgb, var(--lc, var(--brand-soft)) 82%, #fff);
    background: color-mix(in srgb, var(--lc, var(--brand-soft)) 10%, transparent);
    border: 1px solid color-mix(in srgb, var(--lc, var(--brand-soft)) 34%, transparent);
    border-radius: var(--r-pill);
    padding: 5px 12px;
  }

  /* Ledger build: rows above the current beat hide ONLY while the pin is
     driving (data-beat present) — the no-JS and mobile default is the full
     ledger (M1). */
  [data-scrub][data-beat] .m-ledger :is(.m-ledger__row, .m-ledger__div, .m-ledger__res) {
    transition: opacity 460ms var(--ease-reveal), translate 460ms var(--ease-reveal);
  }
  [data-scrub][data-beat="0"] .m-ledger :is([data-b="1"], [data-b="2"], [data-b="3"], [data-b="4"]),
  [data-scrub][data-beat="1"] .m-ledger :is([data-b="2"], [data-b="3"], [data-b="4"]),
  [data-scrub][data-beat="2"] .m-ledger :is([data-b="3"], [data-b="4"]),
  [data-scrub][data-beat="3"] .m-ledger [data-b="4"] {
    opacity: 0;
    translate: 0 18px;
  }

  /* Captions: stacked and all visible by default; while pinned they occupy
     one cell and crossfade. */
  @media (min-width: 900px) and (min-height: 640px) {
    html.js .m-make__track {
      block-size: calc((var(--beats, 5) - 1) * 92svh + 100svh);
    }
    html.js .m-make__stage {
      position: sticky;
      inset-block-start: 0;
      min-block-size: 100svh;
      display: grid;
      align-content: center;
    }
    html.js .m-make__grid {
      grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.1fr);
    }
    html.js [data-scrub][data-beat] .m-make__caps {
      display: grid;
    }
    html.js [data-scrub][data-beat] .m-make__cap {
      grid-area: 1 / 1;
      opacity: 0;
      translate: 0 12px;
      transition: opacity 420ms var(--ease-reveal), translate 420ms var(--ease-reveal);
    }
    html.js [data-scrub][data-beat] .m-make__cap.is-on {
      opacity: 1;
      translate: 0 0;
      transition-delay: 60ms;
    }
  }

  /* ═══ 4 · Product tours ═══ */
  .m-tour {
    position: relative;
    overflow: clip;
    padding-block: clamp(56px, 6.5vw, 104px);
    background: var(--mid);
  }
  .m-tour__grid {
    display: grid;
    gap: clamp(32px, 5vw, 84px);
    align-items: center;
  }
  @media (min-width: 900px) {
    .m-tour__grid {
      grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    }
    .m-tour--flip .m-tour__grid > :first-child {
      order: 2;
    }
  }
  .m-tour h2 {
    font-size: var(--t-scene);
    max-inline-size: 16ch;
    margin-block: 10px 16px;
  }
  .m-tour__copy p {
    max-inline-size: 52ch;
    color: var(--text-2);
  }
  .m-tour__media {
    position: relative;
    display: grid;
    justify-items: center;
    padding-block: 26px;
  }
  .m-tour__media::before {
    content: "";
    position: absolute;
    inset: -6% -18%;
    pointer-events: none;
    background: radial-gradient(
      44% 52% at 50% 52%,
      rgb(var(--tc, var(--gl-violet)) / 0.24),
      rgb(var(--tc, var(--gl-violet)) / 0.07) 52%,
      transparent 74%
    );
  }
  .m-tour__media .c-phone {
    --pw: clamp(230px, 24vw, 296px);
    rotate: var(--lean, -3deg);
    box-shadow: var(--sh-phone), 0 0 80px -16px rgb(var(--tc, var(--gl-violet)) / 0.5),
      inset 0 1px 0 rgb(255 255 255 / 0.22);
  }
  .m-tour--flip .m-tour__media .c-phone {
    --lean: 3deg;
  }

  /* ═══ 5 · Bento — the toolset ═══ */
  .m-bento {
    position: relative;
    padding-block: var(--section-y);
    background: var(--mid);
  }
  .m-bento__grid {
    display: grid;
    grid-template-columns: repeat(12, minmax(0, 1fr));
    gap: 14px;
    margin-block-start: clamp(30px, 4vw, 52px);
  }
  .m-cell {
    grid-column: span 12;
  }
  @media (min-width: 640px) {
    .m-cell {
      grid-column: span 6;
    }
    .m-cell--w {
      grid-column: span 12;
    }
  }
  @media (min-width: 1024px) {
    .m-cell {
      grid-column: span 3; /* 8 cells → two even rows of four */
    }
  }
  /* The supervisor: peeking in from the header's far right — anchored to the
     WIDE container, not the centred text block, so it can never sit on the
     headline. */
  .m-bento > .l-container {
    position: relative;
  }
  .m-bento__peek {
    position: absolute;
    inset-block-start: 6px;
    inset-inline-end: clamp(8px, 3vw, 48px);
    rotate: 6deg;
    --ring: var(--gl-lilac);
  }
  @media (max-width: 1023px) {
    .m-bento__peek {
      display: none;
    }
  }

  /* ═══ 6 · Ask Qashly ═══ */
  .m-ai {
    position: relative;
    overflow: clip;
    padding-block: var(--section-y);
    background: var(--mid);
  }
  .m-ai::before {
    content: "";
    position: absolute;
    inset: -6% -20% auto 30%;
    block-size: 70%;
    pointer-events: none;
    background: radial-gradient(44% 46% at 62% 38%, rgb(var(--gl-sky) / 0.2), transparent 70%),
      radial-gradient(30% 40% at 78% 60%, rgb(var(--gl-violet) / 0.24), transparent 72%);
  }
  .m-ai .s-console {
    position: relative;
  }
  .m-ai .s-console .c-phone {
    box-shadow: var(--sh-phone), 0 0 84px -16px rgb(var(--gl-sky) / 0.45),
      inset 0 1px 0 rgb(255 255 255 / 0.22);
  }

  /* ═══ 7 · Privacy ═══ */
  .m-priv {
    position: relative;
    overflow: clip;
    padding-block: var(--section-y);
    background: var(--mid);
  }
  .m-priv::before {
    content: "";
    position: absolute;
    inset: 12% -14% auto -14%;
    block-size: 56%;
    pointer-events: none;
    background: radial-gradient(46% 44% at 50% 40%, rgb(var(--gl-mint) / 0.1), transparent 72%);
  }
  .m-priv__guard {
    position: relative;
    margin-inline: auto;
    margin-block-start: clamp(26px, 3.5vw, 44px);
    border-color: rgb(var(--gl-mint) / 0.38);
    --ring: var(--gl-mint);
  }
  .m-priv__guard + .m-priv__claim {
    margin-block-start: 16px;
  }
  .m-priv__claim {
    position: relative;
    text-align: center;
    font-family: var(--font-disp);
    font-weight: 700;
    font-synthesis: none;
    font-size: var(--t-scene);
    letter-spacing: -0.02em;
    line-height: 1.05;
    max-inline-size: 18ch;
    margin-inline: auto;
    margin-block: clamp(30px, 4vw, 52px);
    text-wrap: balance;
  }
  .m-priv__claim em {
    font-style: normal;
    background: linear-gradient(100deg, #9ff0bd, #7dd3fc);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
    padding: 0.06em 0.1em;
    margin: -0.06em -0.1em;
  }
  .m-priv__cols {
    position: relative;
    display: grid;
    gap: 14px;
  }
  @media (min-width: 900px) {
    .m-priv__cols {
      grid-template-columns: minmax(0, 1fr) minmax(0, 1.15fr);
      align-items: stretch;
    }
  }
  .m-priv__col {
    padding: clamp(20px, 2.2vw, 30px);
    display: grid;
    gap: 16px;
    align-content: start;
  }
  /* The tag-cloud card is naturally shorter — let it be, rather than
     stretching to match its neighbour with a slab of empty glass. */
  .m-priv__col--on {
    align-self: start;
  }
  .m-priv__col h3 {
    font-size: var(--t-micro);
    font-weight: 700;
    letter-spacing: 0.11em;
    text-transform: uppercase;
    display: flex;
    align-items: center;
    gap: 9px;
  }
  .m-priv__col--on h3 {
    color: #a9f0c4;
  }
  .m-priv__col--out h3 {
    color: #a8d9f8;
  }
  .m-priv__col h3 svg {
    inline-size: 15px;
    block-size: 15px;
    flex: none;
  }
  .m-priv__foot {
    font-size: 0.875rem;
    color: var(--text-3);
  }
  .m-priv__notes {
    max-inline-size: 70ch;
    margin-inline: auto;
    margin-block-start: clamp(28px, 4vw, 44px);
    display: grid;
    gap: 14px;
    text-align: center;
  }
  .m-priv__notes p {
    color: var(--text-2);
    font-size: var(--t-body);
  }

  /* ═══ 8 · UK tax ═══ */
  .m-tax {
    position: relative;
    overflow: clip;
    padding-block: var(--section-y);
    background: var(--mid);
  }
  .m-tax__grid {
    display: grid;
    gap: clamp(32px, 5vw, 84px);
    align-items: center;
  }
  @media (min-width: 900px) {
    .m-tax__grid {
      grid-template-columns: minmax(0, 1.05fr) minmax(0, 0.95fr);
    }
  }
  .m-tax h2 {
    font-size: var(--t-scene);
    max-inline-size: 16ch;
    margin-block: 10px 16px;
  }
  .m-tax__copy p {
    max-inline-size: 54ch;
    color: var(--text-2);
  }
  .m-tax__kick {
    display: inline-block;
    margin-block-start: 18px;
    font-size: var(--t-body);
    font-weight: 800;
    color: #ffd88a;
    background: rgb(var(--gl-amber) / 0.09);
    border: 1px solid rgb(var(--gl-amber) / 0.32);
    border-radius: var(--r-pill);
    padding: 9px 16px;
    box-shadow: 0 0 34px -8px rgb(var(--gl-amber) / 0.5);
  }
  .m-tax__micro {
    margin-block-start: 12px;
    font-size: var(--t-meta);
    color: var(--text-3);
  }
  .m-tax__stage {
    position: relative;
    padding-block: 30px;
  }
  /* The desk lamp: a warm cone from above the card. */
  .m-tax__stage::before {
    content: "";
    position: absolute;
    inset: -18% -12% 0 -12%;
    pointer-events: none;
    background: radial-gradient(40% 50% at 50% 30%, rgb(var(--gl-amber) / 0.16), transparent 70%);
  }

  /* ═══ 9 · Stats ═══ */
  .m-stats {
    padding-block: var(--section-y-tight);
    background: var(--mid);
  }
  .m-stats__grid {
    display: grid;
    gap: clamp(22px, 3vw, 40px);
  }
  @media (min-width: 640px) {
    .m-stats__grid {
      grid-template-columns: repeat(2, minmax(0, 1fr));
    }
  }
  @media (min-width: 1024px) {
    .m-stats__grid {
      grid-template-columns: repeat(4, minmax(0, 1fr));
    }
  }

  /* ═══ 10 · Pricing / 11 · FAQ ═══ */
  .m-price {
    position: relative;
    overflow: clip;
    padding-block: var(--section-y);
    background: var(--mid);
  }
  .m-price::before {
    content: "";
    position: absolute;
    inset: 6% -16% auto -16%;
    block-size: 50%;
    pointer-events: none;
    background: radial-gradient(42% 44% at 50% 30%, rgb(var(--gl-violet) / 0.13), transparent 72%);
  }
  .m-faqs {
    padding-block: var(--section-y-tight) var(--section-y);
    background: var(--mid);
  }

  /* ═══ 12 · The close ═══ */
  .m-end {
    position: relative;
    overflow: clip;
    /* Tight on top and a snug anchor offset: "Get Qashly" (#get) must land
       with the WHOLE closer framed — quokka, headline, badges — not a
       viewport of padding with the mascot's ears at the fold. */
    padding-block: clamp(32px, 4svh, 52px) var(--section-y);
    /* html's scroll-padding-top (88px) already clears the nav; the default
       96px :target margin on top of it is what buried the closer. */
    scroll-margin-top: 0;
    background: var(--mid);
    text-align: center;
  }
  /* Dawn: the one warm flood on the page, rising from the footer line. */
  .m-end::before {
    content: "";
    position: absolute;
    inset: auto -20% -34% -20%;
    block-size: 80%;
    pointer-events: none;
    background: radial-gradient(
        50% 62% at 50% 100%,
        rgb(var(--gl-ember) / 0.34),
        rgb(var(--gl-ember) / 0.1) 48%,
        transparent 72%
      ),
      radial-gradient(70% 80% at 50% 108%, rgb(var(--gl-violet) / 0.22), transparent 74%);
  }
  .m-end__stack {
    position: relative;
    display: grid;
    justify-items: center;
    gap: 18px;
  }
  .m-end h2 {
    font-size: clamp(2.25rem, 1.6rem + 2.8vw, 4rem);
    max-inline-size: 16ch;
    text-wrap: balance;
  }
  .m-end__quokka {
    inline-size: min(58vw, 300px);
    margin-block-start: 8px;
    -webkit-mask-image: radial-gradient(ellipse 68% 80% at 50% 54%, #000 44%, transparent 70%);
    mask-image: radial-gradient(ellipse 68% 80% at 50% 54%, #000 44%, transparent 70%);
  }
  .m-end__quokka img {
    inline-size: 100%;
    block-size: auto;
  }
}

/* ── Reduced motion for the new loops ────────────────────────────────────── */
@layer motion {
  @media (prefers-reduced-motion: reduce) {
    [data-on] :is(.m-hero__dev--lead, .m-hero__dev--l, .m-hero__dev--r) .c-phone,
    [data-on] .m-hero__cue svg,
    [data-on] .m-hero__q img,
    [data-on] .m-hero__chip .m-float,
    .m-hero__pos,
    .m-hero__q,
    .hw i {
      animation: none !important;
    }
    .m-said .w {
      transition: none;
    }
    .m-float {
      translate: 0 0;
    }
    /* The pin stays (scroll-driven is user-driven), but beat changes become
       instant swaps rather than crossfades. */
    [data-scrub][data-beat] .m-ledger :is(.m-ledger__row, .m-ledger__div, .m-ledger__res),
    html.js [data-scrub][data-beat] .m-make__cap {
      transition: none !important;
    }
    /* Parallax and pointer-tilt retire to their resting frame. */
    .m-hero__dev {
      translate: 0 0;
    }
    .m-hero__dev--lead .c-phone {
      transform: none;
    }
  }

  /* The JS-mirrored attribute must agree with the OS preference
     (motion.js re-syncs live). */
  html[data-motion="reduce"] [data-on] :is(.m-hero__dev--lead, .m-hero__dev--l, .m-hero__dev--r) .c-phone,
  html[data-motion="reduce"] [data-on] .m-hero__cue svg,
  html[data-motion="reduce"] [data-on] .m-hero__q img,
  html[data-motion="reduce"] [data-on] .m-hero__chip .m-float,
  html[data-motion="reduce"] .m-hero__pos,
  html[data-motion="reduce"] .m-hero__q,
  html[data-motion="reduce"] .hw i {
    animation: none !important;
  }
  html[data-motion="reduce"] .m-hero__dev {
    translate: 0 0;
  }
  html[data-motion="reduce"] .m-hero__dev--lead .c-phone {
    transform: none;
  }
  html[data-motion="reduce"] .m-said .w {
    transition: none;
  }
  html[data-motion="reduce"] .m-float {
    translate: 0 0;
  }
  html[data-motion="reduce"] [data-scrub][data-beat] .m-ledger :is(.m-ledger__row, .m-ledger__div, .m-ledger__res),
  html.js[data-motion="reduce"] [data-scrub][data-beat] .m-make__cap {
    transition: none !important;
  }
}

/* ── The console's undo affordance (re-homed from the retired act layer) ── */
@layer components {
  .s-console__undo h3 {
    font-size: var(--t-h3);
    margin-block-end: 6px;
  }
  .s-console__undo p {
    color: var(--text-2);
    max-width: 52ch;
  }
  .ph-action__undo {
    cursor: pointer;
    user-select: none;
  }
  .ph-action[data-undone] {
    opacity: 0.62;
  }
  .ph-action[data-undone] .ph-action__row {
    text-decoration: line-through;
    text-decoration-color: rgb(255 255 255 / 0.5);
  }
  .ph-action__undone {
    display: flex;
    align-items: center;
    gap: calc(7 * var(--u));
    font-size: calc(12 * var(--u));
    font-weight: 700;
    color: var(--income);
  }
  /* The quokka sticker: the mascot's one appearance outside the finale. */
  .ph-action__undone::before {
    content: "";
    inline-size: calc(22 * var(--u));
    aspect-ratio: 1;
    border-radius: 50%;
    background: url("/assets/img/hero/quokka-320.webp") 54% 12% / 300% no-repeat;
    border: 1.5px solid rgb(134 239 172 / 0.5);
  }
}
