/* FolioLab marketing site.
   Aesthetic direction: clean editorial, closely modeled on popcorn.space
   (measured from its live CSS) — warm neutral canvas, centered serif display
   headlines at weight 400 in a warm charcoal (#38332e), no section eyebrows
   (just headline + gray subhead), charcoal pill buttons, very pale pastel
   cards each holding a white "mockup tile" with a centered serif heading, a
   split light/dark CTA band, and an oversized wordmark bleeding off the footer.
   The palette is essentially monochrome charcoal + neutral gray; the pastel
   cards carry the color and orange is reserved for a small brand nod (logo,
   the "coming soon" dot, the pricing flag).
   Serif display uses the system ui-serif stack (New York on Apple, Georgia
   elsewhere) so there are zero external font requests. */

:root {
  color-scheme: light dark;

  --bg: #f5f5f3;
  --card: #ffffff;
  --card-2: #fafaf8;
  --charcoal: #38332e;   /* serif display + buttons (popcorn's warm charcoal) */
  --ink: #2a2723;        /* primary text */
  --muted: #6f6a63;      /* warm gray */
  --faint: #736e66;      /* 0307: lightest grey that holds AA 4.5:1 on bg/card/card-2 */

  --accent: #c85a27;     /* brand orange, used only as a small nod */
  --accent-strong: #b45123; /* 0307: orange that carries text (fills under --bg text, small orange text), AA */
  --accent-soft: #f4ebe3;
  --link: #38332e;       /* links are charcoal + underline, not orange */

  --brand-orange: #e78a63;
  --brand-green: #57c1a0;
  --brand-blue: #6f93e6;

  /* very pale card tints (popcorn keeps these barely-there) */
  --tint-blue: #edf0f8;
  --tint-green: #ecf3ee;
  --tint-orange: #f8efe6;
  --tint-lav: #f0edf4;

  --bearish: #b83333;
  --cautious: #cf6a20;
  --neutral: #c48a1c;
  --constructive: #7f9128;
  --bullish: #2e8c45;

  --panel: #1a1b1f;       /* dark CTA card + footer */
  --panel-ink: #f3f2ec;

  --line: rgba(25, 26, 30, 0.1);
  --line-2: rgba(25, 26, 30, 0.16);
  --shadow-sm: 0 2px 10px rgba(25, 26, 30, 0.05);
  --shadow: 0 20px 50px -18px rgba(25, 26, 30, 0.22);
  --shadow-lg: 0 40px 90px -30px rgba(25, 26, 30, 0.34);
  --shadow-lift: 6px 6px 14px -3px rgba(25, 26, 30, 0.18);     /* 0291 rider 5: cards sit up, bottom-right */
  --shadow-lift-lg: 9px 9px 22px -4px rgba(25, 26, 30, 0.24);
  --shadow-text: 0.03em 0.045em 0.12em rgba(25, 26, 30, 0.14);  /* 0305 rider: display headings, same light; em scales it with the type */

  --radius: 18px;
  --radius-lg: 30px;

  /* One font family across the whole site: the system sans, used bold for
     display (wordmark, headlines, card titles) — matches the App Store
     screenshots. No external font requests. */
  --sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
    "Helvetica Neue", Arial, sans-serif;

  --maxw: 1120px;
  --glow: 0.6;

  /* 0236: the site's first motion tokens. Entrances and expansions use
     the strong ease-out; hovers keep the built-in `ease` (correct for
     hover per the 2026-08-27 motion review). interpolate-size opts
     height:auto into transitions (FAQ accordion) — it only affects
     properties that already declare a transition. */
  --ease-out: cubic-bezier(0.23, 1, 0.32, 1);
  /* 0254: the hero pop's curve, text entrance only (bars/expansions
     stay on --ease-out). Second pass (operator: "a little smoother"):
     overshoot pulled 1.4 -> 1.12, a whisper of pop on a silky decel. */
  --ease-pop: cubic-bezier(0.3, 1.12, 0.5, 1);
  interpolate-size: allow-keywords;
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #14130f;
    --card: #201e1a;
    --card-2: #1b1a16;
    --charcoal: #f0ede4;   /* serif + buttons invert to warm cream on dark */
    --ink: #f0ede4;
    --muted: #a8a29a;
    --faint: #8c877d;

    --accent: #e88a58;
    --accent-strong: #e88a58;
    --accent-soft: #2a211b;
    --link: #f0ede4;

    --tint-blue: #1b2130;
    --tint-green: #182420;
    --tint-orange: #291f18;
    --tint-lav: #221d2b;

    --bearish: #d25858;
    --cautious: #de8340;
    --neutral: #d7a238;
    --constructive: #9fb244;
    --bullish: #4caf6a;

    --panel: #0d0e10;
    --panel-ink: #f0efe9;

    --line: rgba(240, 239, 233, 0.12);
    --line-2: rgba(240, 239, 233, 0.2);
    --shadow-sm: 0 2px 10px rgba(0, 0, 0, 0.4);
    --shadow: 0 20px 50px -18px rgba(0, 0, 0, 0.55);
    --shadow-lg: 0 40px 90px -30px rgba(0, 0, 0, 0.7);
    --shadow-lift: 7px 7px 16px -3px rgba(0, 0, 0, 0.75);
    --shadow-lift-lg: 10px 10px 24px -4px rgba(0, 0, 0, 0.85);
    --shadow-text: 0.03em 0.045em 0.14em rgba(0, 0, 0, 0.6);
    --glow: 0.28;
  }
}

* { box-sizing: border-box; }

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

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 1.0625rem;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

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

a { color: var(--link); text-underline-offset: 3px; text-decoration-thickness: 1px; }
a:hover { text-decoration-thickness: 2px; }

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

.skip-link {
  position: absolute; left: -9999px; top: 0; z-index: 100;
  background: var(--card); color: var(--ink); padding: 0.6rem 1rem; border-radius: 8px;
}
.skip-link:focus { left: 0.75rem; top: 0.75rem; }

.wrap { max-width: var(--maxw); margin: 0 auto; padding: 0 clamp(1.25rem, 4vw, 2.25rem); }

section { padding: clamp(3.75rem, 8vw, 6.5rem) 0; }
[id] { scroll-margin-top: 84px; }

/* ---------- type ---------- */

h1, h2 {
  font-family: var(--sans);
  font-weight: 550;
  letter-spacing: -0.03em;
  line-height: 1.05;
  margin: 0;
  color: var(--charcoal);
  text-wrap: balance;
}
/* 0305 rider: the hero and section headlines sit up off the page, lit like
   the cards (bottom-right, 0291 rider 5). Prose and card titles stay flat. */
.hero h1, .head h1, .head h2, .show h2, .band h2 { text-shadow: var(--shadow-text); }

/* ---------- header ---------- */

.site-header {
  position: sticky; top: 0; z-index: 50;
  background: color-mix(in srgb, var(--bg) 80%, transparent);
  -webkit-backdrop-filter: blur(18px) saturate(1.3);
  backdrop-filter: blur(18px) saturate(1.3);
  border-bottom: 1px solid var(--line);
}
/* logo + links grouped left (popcorn), CTA pushed right */
.site-header .wrap { display: flex; align-items: center; gap: 2.2rem; min-height: 66px; }

.brand { display: inline-flex; align-items: center; gap: 0.6rem; text-decoration: none; color: var(--charcoal); }
/* 0286 rider: the white icon needs an edge on the light page to read as the
   logo tile; a hairline in the line colour, drawn inside the box so nothing
   shifts. */
.brand img { width: 32px; height: 32px; border-radius: 9px; box-shadow: inset 0 0 0 1px var(--line-2); }
.brand b { font-family: var(--sans); font-weight: 550; font-size: 1.35rem; letter-spacing: -0.02em; }

.site-nav { display: flex; align-items: center; gap: 1.7rem; }
.site-nav a { color: var(--muted); text-decoration: none; font-size: 0.95rem; font-weight: 500; }
.site-nav a:hover { color: var(--charcoal); }
.nav-cta { margin-left: auto; white-space: nowrap; }
/* 0229 rider: the always-visible nav CTA ("Get the app" since 0275) carries the brand orange
   (the token pair mirrors the app's AccentColor, light and dark). 0307: the
   operator kept it on --accent over --accent-strong; the light label is 3.9:1,
   a known AA miss (dark mode is 7.3:1). */
.btn.nav-cta { background: var(--accent); }
@media (max-width: 820px) { .site-nav a { display: none; } }
/* A longer label (the pre-launch "Join the waitlist") needed a compact pill on
   phones to stay one line beside the brand down to 320px. .btn.nav-cta beats
   .btn in the cascade (.btn alone would win on order); the header gap also
   shrinks because the hidden .site-nav still spends two gap slots. */
@media (max-width: 430px) {
  .site-header .wrap { gap: 0.75rem; }
  .btn.nav-cta { padding: 0.6rem 0.85rem; font-size: 0.88rem; min-height: 44px; } /* 0307: 44px tap target */
}

/* ---------- buttons, pills ---------- */

.btn {
  display: inline-flex; align-items: center; gap: 0.5rem;
  background: var(--charcoal); color: var(--bg) !important;
  font-weight: 600; font-size: 1rem; text-decoration: none;
  padding: 0.8rem 1.7rem; border-radius: 999px; border: none;
  transition: transform 0.18s ease, opacity 0.18s ease;
}
/* .btn:hover moved to the pointer-gated motion block (0236), where
   .btn:active follows it in source order so press beats hover. */

.status {
  display: inline-flex; align-items: center; gap: 0.6rem;
  background: var(--card); border: 1px solid var(--line-2); color: var(--ink);
  font-weight: 600; font-size: 0.98rem; padding: 0.68rem 1.4rem; border-radius: 999px;
  box-shadow: var(--shadow-sm);
}
.status .dot {
  width: 8px; height: 8px; border-radius: 50%; background: var(--accent);
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--accent) 20%, transparent);
}

/* ---------- centered section header ---------- */

.head { text-align: center; max-width: 42rem; margin: 0 auto clamp(2.75rem, 5vw, 3.75rem); }
/* 0283: content pages built from landing sections (careers): a page head
   with a status pill and an h1 in place of the h2. */
.head h1 { font-size: clamp(2.1rem, 4.6vw, 3.5rem); margin: 0; }
.head .status { margin-bottom: 1.75rem; }
.page-head { padding-bottom: 0; }
.page-head .head { margin-bottom: 0; }
.band > .light:only-child { grid-column: 1 / -1; }
.head h2 { font-size: clamp(2.1rem, 4.6vw, 3.5rem); }
.head p { color: var(--muted); font-size: 1.12rem; margin: 1rem auto 0; max-width: 34rem; text-wrap: pretty; }

/* ---------- hero ---------- */

/* 0254 rider 2: x-only clip. The glows bleed past the viewport edges
   (x-scroll guard, must stay clipped), but the bottom green glow needs
   to fade PAST the section edge — a y-clip sliced it into a hard
   horizontal cut, and shrinking it inside the hero read too high and
   too small (operator, two passes). `clip` allows per-axis clipping;
   the pseudo's box extends below the section so the fade completes
   there, behind the next section's transparent background. */
.hero { position: relative; overflow-x: clip; overflow-y: visible; text-align: center; padding-top: clamp(3rem, 6vw, 5rem); }
.hero::before {
  content: ""; position: absolute; inset: 0 0 -16rem 0; z-index: 0; pointer-events: none;
  /* Positions in lengths, not %, since the pseudo's box is taller
     than the hero: tops anchored to the top edge, green anchored to
     the HERO's bottom edge (100% - 16rem extension - 6rem lift), full
     size matching its siblings — its lower falloff fades out in the
     extended box below the section line. */
  background:
    radial-gradient(42rem 26rem at 22% 6rem, color-mix(in srgb, var(--brand-orange) calc(42% * var(--glow)), transparent), transparent 60%),
    radial-gradient(44rem 30rem at 82% 3rem, color-mix(in srgb, var(--brand-blue) calc(46% * var(--glow)), transparent), transparent 62%),
    radial-gradient(44rem 28rem at 50% calc(100% - 22rem), color-mix(in srgb, var(--brand-green) calc(30% * var(--glow)), transparent), transparent 60%);
}
.hero .wrap { position: relative; z-index: 1; }

/* 0307: the hero eyebrow (0204, quieted in 0229, the offer link since 0291
   rider 2) is gone; the headline opens the hero and the offer lives in the
   fine print's orange "2 scans". Revert = these rules in git history. */
.hero h1 { font-size: clamp(2.3rem, 5.6vw, 4.4rem); line-height: 1.05; overflow-wrap: break-word; text-wrap: balance; }
/* 0305 rider: the setup sentence steps back in muted ink so the eye lands
   on the punchline; the lede ends on the reader's decision. */
.hero h1 .h1-setup { color: var(--muted); }
.hero .lede .lede-end { color: var(--ink); font-weight: 550; white-space: nowrap; }
/* 0272: both headline lines in the display sans again (operator); the
   0236 rider's serif-italic second line (.h1-script) retired with the
   new copy. Revert = the rule in git history at 0236 / 0271. */
.hero .lede { color: var(--muted); font-size: clamp(1rem, 2vw, 1.28rem); max-width: 35rem; margin: 1.4rem auto 0; text-wrap: pretty; }
/* 0272: the form (and its no-JS pill) sits under the fanned phone pair;
   the gap is sized so the visible space below the phones' dipped corners
   matches the space above the pair (~40px desktop, ~30px phones). */
.hero .cta-row { display: flex; justify-content: center; align-items: center; gap: 1rem; flex-wrap: wrap; margin-top: clamp(3rem, 5vw, 3.75rem); }
/* 0307 (operator): the whole offer line is bold and a step larger, with
   "2 scans" in orange below. */
.hero .fine { color: var(--faint); font-size: 1rem; font-weight: 650; margin: 1.1rem 0 0; }

/* two phones, side by side, gently fanned out (clean product pair).
   0272: the pair sits between the lede and the hero form (the 0271
   variant-B position, promoted), so its top gap is the short one; the
   form below carries the larger gap, since the 5deg fan drops the
   phones' outer bottom corners ~20px below this box. */
.phones { display: flex; justify-content: center; align-items: flex-end; width: min(600px, 90vw); margin: clamp(1.75rem, 3.5vw, 2.5rem) auto 0; }
/* 0255: the theme-aware <picture> wrappers vanish from layout so the
   imgs stay direct flex/frame children — every rule below (fan
   classes, widths, the 0254 entrance) keeps matching untouched. */
.phones picture, .device picture { display: contents; }
.phones img {
  width: 47%; border-radius: 30px;
  border: 1px solid var(--line); box-shadow: var(--shadow-lg); background: var(--card);
}
/* 0254 third pass: the rest-pose rotations moved from `transform` to
   the individual `rotate` property (visually identical) so the load
   entrance below can animate the fan open without touching a shared
   transform. */
.phones .left { rotate: -5deg; transform-origin: bottom right; margin-right: -6%; margin-bottom: 14px; z-index: 1; }
.phones .right { rotate: 5deg; transform-origin: bottom left; z-index: 2; }

@media (max-width: 520px) { .phones { width: 100%; } .phones img { width: 50%; } }

/* 0254: hero load entrance (operator: the popcorn.space open the
   eyebrow pattern already borrowed). Text pops in on a small rise +
   scale with a hint of overshoot, staggered top-to-bottom, then the
   phone pair settles up into place. Pure CSS, plays on every load,
   needs no JS gate (a CSS animation is its own progressive
   enhancement). The phones animate the individual `translate`
   property ONLY: it composes with their rotate() fan and never
   touches opacity, so the LCP image paints at full strength from the
   first frame — the 0236 review rejected an opacity-based phones
   entrance for exactly that reason; this is the recorded reversal
   with that mitigation (see changes/0254). fill-mode backwards holds
   each element at its from-state through its delay. */
@keyframes hero-pop {
  from { opacity: 0; transform: translateY(14px) scale(0.98); }
}
/* 0254 third pass (operator): the phones open like a hand of cards —
   they start upright (rotate 0) and pulled toward each other (the
   x-offsets below), slightly low, then drift apart + rotate + rise
   into the rest fan in ONE synchronized bloom (a stagger here read
   lopsided; symmetry is the gesture). translate percentages resolve
   against each phone's own width. Sequencing is deliberate (operator):
   the fan LANDS a beat after the text finishes — text is done at
   ~1.03s (0.28s delay + 0.75s), phones at 0.3s + 1s end ~1.3s. */
@keyframes hero-fan-left {
  from { translate: 9% 24px; rotate: 0deg; }
}
@keyframes hero-fan-right {
  from { translate: -9% 24px; rotate: 0deg; }
}
.hero h1,
.hero .lede,
.hero .cta-row,
.hero .proof { animation: hero-pop 0.75s var(--ease-pop) backwards; }
.hero h1 { animation-delay: 0.08s; }
.hero .lede { animation-delay: 0.18s; }
.hero .cta-row { animation-delay: 0.28s; }
.hero .proof { animation-delay: 0.36s; }  /* 0265: after the ask it supports */
.phones .left { animation: hero-fan-left 1s var(--ease-out) 0.3s backwards; }
.phones .right { animation: hero-fan-right 1s var(--ease-out) 0.3s backwards; }

/* ---------- hero proof strip (change 0265) ---------- */

/* Three trims of the #reviews quotes right under the ask (conversion
   review C4: proof at the point of decision). Desktop: one static row.
   Phones: a snap-aligned strip the reader swipes; no auto-scroll, so no
   WCAG 2.2.2 pause control is needed and Reduce Motion has nothing to
   switch off beyond the shared hero entrance. */
.proof { margin-top: clamp(1.6rem, 3vw, 2.2rem); }
.proof-row {
  list-style: none; margin: 0; padding: 0;
  display: flex; justify-content: center; align-items: stretch; gap: 0.9rem;
}
.proof-row li {
  display: grid; grid-template-columns: 28px 1fr; column-gap: 0.7rem; align-items: center;
  text-align: left; background: var(--card); border: 1px solid var(--line);
  border-radius: var(--radius); box-shadow: var(--shadow-lift);
  padding: 0.75rem 1rem; max-width: 17rem; font-size: 0.95rem; line-height: 1.4;
}
.proof-row .ring {
  grid-row: 1 / span 2; width: 28px; height: 28px; border-radius: 50%;
  display: grid; place-items: center; border: 1px solid var(--line-2);
  color: var(--charcoal); font-weight: 600; font-size: 0.8rem;
}
.proof-row q { grid-column: 2; quotes: "\201C" "\201D"; color: var(--ink); text-wrap: pretty; }
.proof-row q::before, .proof-row q::after { color: var(--accent); font-weight: 600; }
.proof-row .who { grid-column: 2; color: var(--muted); font-size: 0.85rem; line-height: 1.3; margin-top: 0.15rem; }
/* the paid-tester disclosure: --muted, not --faint (the 0262 rule) */
.proof-note { color: var(--muted); font-size: 0.88rem; margin: 0.8rem auto 0; max-width: 42rem; }
.proof-note a { color: inherit; text-decoration: underline; text-underline-offset: 2px; }
.proof-note a:hover { color: var(--ink); }
@media (max-width: 800px) {
  /* Bleed to the page gutter so the first card starts flush with the
     text and the next one peeks in; the strip scrolls, the page never
     does (overscroll-behavior keeps the gesture inside it). */
  .proof-row {
    justify-content: flex-start; overflow-x: auto;
    scroll-snap-type: x mandatory; scroll-padding-inline: clamp(1.25rem, 4vw, 2.25rem);
    margin: 0 calc(-1 * clamp(1.25rem, 4vw, 2.25rem));
    padding: 0.15rem clamp(1.25rem, 4vw, 2.25rem) 0.45rem;
    overscroll-behavior-x: contain; scrollbar-width: none; -webkit-overflow-scrolling: touch;
  }
  .proof-row::-webkit-scrollbar { display: none; }
  /* 82% of the row on phones (one card plus a peek of the next); capped at
     20rem so tablets show two cards and a peek instead of one wide box. */
  .proof-row li { flex: 0 0 clamp(15rem, 82%, 20rem); max-width: none; scroll-snap-align: center; }
}

/* ---------- steps ---------- */

.steps { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1.1rem; }
@media (max-width: 800px) { .steps { grid-template-columns: 1fr; } }
.step {
  position: relative; overflow: hidden;
  background: var(--card); border: 1px solid var(--line); border-radius: var(--radius);
  padding: 2.1rem 1.6rem 1.7rem; box-shadow: var(--shadow-lift);
  transition: transform 0.22s ease, box-shadow 0.22s ease;
}
/* giant step number as a soft background, anchored to the right of the
   card. Hover rules live in the pointer-gated motion block below. */
.step .n {
  position: absolute; inset: 0; z-index: 0;
  display: grid; place-items: center end; padding-right: 0.6rem;
  font-family: var(--sans); font-size: 13rem; font-weight: 800; line-height: 1;
  letter-spacing: -0.02em; color: color-mix(in srgb, var(--accent) 18%, transparent);
  pointer-events: none; user-select: none; transition: color 0.25s ease;
}
.step h3 { position: relative; z-index: 1; margin: 0 0 0.4rem; font-family: var(--sans); font-size: 1.22rem; font-weight: 550; letter-spacing: -0.02em; }
.step p { position: relative; z-index: 1; margin: 0; color: var(--muted); font-size: 1rem; }

/* ---------- pastel angle cards ---------- */

.cards { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1.25rem; }
@media (max-width: 800px) { .cards { grid-template-columns: 1fr; } }
.tcard {
  position: relative;
  background: var(--tint); border: 1px solid color-mix(in srgb, var(--tone) 16%, transparent);
  border-radius: 22px; padding: 2.25rem 1.6rem 2rem; text-align: center; box-shadow: var(--shadow-lift);
  transition: transform 0.22s ease, box-shadow 0.22s ease;
}

/* white "mockup tile" inside each pastel card — the signature element */
.tcard .mock {
  background: var(--card); border-radius: 14px; box-shadow: var(--shadow-sm);
  padding: 1.15rem 1.25rem; margin-bottom: 1.6rem;
}
.tcard .mock-tag {
  display: block; font-size: 0.72rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.08em; color: var(--faint); text-align: left;
}
.tcard .viz { width: 100%; height: 52px; display: block; margin: 0.7rem 0 0.9rem; color: color-mix(in srgb, var(--tone) 78%, var(--ink)); }
.tcard .conf { display: flex; align-items: center; gap: 5px; }
.tcard .conf i { width: 15px; height: 5px; border-radius: 3px; background: color-mix(in srgb, var(--tone) 76%, var(--ink)); }
.tcard .conf i.off { background: color-mix(in srgb, var(--ink) 13%, transparent); }
.tcard .conf span { margin-left: 7px; font-size: 0.72rem; color: var(--faint); }
.tcard h3 { margin: 0 0 0.5rem; font-family: var(--sans); font-size: 1.4rem; font-weight: 550; letter-spacing: -0.02em; color: var(--charcoal); }
.tcard p { margin: 0; color: var(--muted); font-size: 1rem; max-width: 20rem; margin-inline: auto; }

/* ---------- report showcase ---------- */

.show { display: grid; grid-template-columns: 0.95fr 1.05fr; gap: clamp(2rem, 6vw, 5rem); align-items: center; }
@media (max-width: 860px) { .show { grid-template-columns: 1fr; } .show .show-media { order: -1; } }

.show-media { position: relative; display: flex; justify-content: center; }
.show-media::before {
  content: ""; position: absolute; inset: 8% 12%; z-index: 0; border-radius: 50%;
  background: radial-gradient(closest-side, color-mix(in srgb, var(--brand-blue) calc(38% * var(--glow)), transparent), transparent);
  filter: blur(10px);
}
.device {
  position: relative; z-index: 1; width: min(300px, 82%);
  border-radius: 32px; overflow: hidden; border: 1px solid var(--line);
  box-shadow: var(--shadow-lg); background: var(--card);
}
.device img { width: 100%; display: block; }
/* 0256: the summary-open clip overlays the static capture inside the
   frame, crossfades in, and holds its last frame (site.js drives it;
   no-JS and reduced motion never create the element). */
.device-video {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; opacity: 0; transition: opacity 0.4s ease;
}
.device-video.on { opacity: 1; }

.show h2 { font-size: clamp(1.9rem, 3.6vw, 2.6rem); }
.show .lead { color: var(--muted); font-size: 1.08rem; margin: 0.9rem 0 0; }
.checks { list-style: none; margin: 1.6rem 0 0; padding: 0; display: grid; gap: 1rem; }
.checks li { display: grid; grid-template-columns: 24px 1fr; gap: 0.75rem; align-items: start; }
.checks .mk {
  width: 22px; height: 22px; margin-top: 2px; border-radius: 50%; flex: none;
  display: grid; place-items: center; font-size: 0.72rem; font-weight: 800;
  background: color-mix(in srgb, var(--bullish) 16%, transparent); color: var(--bullish);
}
/* 0282: the mark is drawn here, never typed into the HTML, so it cannot leak
   into a search snippet ("✓No account"). */
.mk::before { content: "\2713"; }
.checks strong { display: block; font-weight: 650; letter-spacing: -0.005em; }
.checks span { color: var(--muted); font-size: 0.96rem; }

/* ---------- beta voices (0262) ---------- */

/* Four stitched beta-survey quotes in a 2x2, white cards in the .step
   idiom. The orange quotation marks are the card's only color (the
   site's "orange as a small nod" rule); attribution is a thin-ring
   initial plus a descriptor, no names, no photos, no stars. */
.voices { display: grid; grid-template-columns: repeat(2, 1fr); gap: 1.25rem; max-width: 50rem; margin: 0 auto; }
@media (max-width: 800px) { .voices { grid-template-columns: 1fr; } }
.voice {
  margin: 0; background: var(--card); border: 1px solid var(--line); border-radius: var(--radius);
  padding: 1.9rem 1.6rem 1.6rem; display: flex; flex-direction: column; gap: 1.3rem; box-shadow: var(--shadow-lift);
  transition: transform 0.22s ease, box-shadow 0.22s ease;
}
.voice blockquote { margin: 0; flex: 1; font-size: 1.1rem; line-height: 1.5; color: var(--ink); text-wrap: pretty; }
.voice blockquote::before { content: "\201C"; color: var(--accent); font-weight: 600; margin-right: 0.04em; }
.voice blockquote::after { content: "\201D"; color: var(--accent); font-weight: 600; margin-left: 0.04em; }
.voice figcaption { display: flex; align-items: center; gap: 0.85rem; border-top: 1px solid var(--line); padding-top: 0.95rem; }
.voice .ring {
  width: 38px; height: 38px; border-radius: 50%; flex: none; display: grid; place-items: center;
  border: 1px solid var(--line-2); color: var(--charcoal); font-weight: 600; font-size: 0.95rem;
}
.voice .who { display: grid; gap: 0.05rem; }
.voice .who b { font-weight: 650; letter-spacing: -0.005em; color: var(--charcoal); line-height: 1.25; }
.voice .who span { color: var(--muted); font-size: 0.9rem; }
/* the paid-tester disclosure: --muted, not --faint, so it stays legible */
.voices-note { text-align: center; color: var(--muted); font-size: 0.95rem; margin: 1.6rem auto 0; max-width: 42rem; }

/* ---------- pricing ---------- */

.prices { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1.25rem; align-items: stretch; }
@media (max-width: 820px) { .prices { grid-template-columns: 1fr; } }
.price {
  position: relative; background: var(--card); border: 1px solid var(--line);
  border-radius: var(--radius-lg); padding: 1.9rem; display: flex; flex-direction: column; box-shadow: var(--shadow-lift);
  transition: transform 0.22s ease, box-shadow 0.22s ease;
}
.price h3 { margin: 0; font-family: var(--sans); font-size: 1.15rem; font-weight: 550; letter-spacing: -0.01em; }
.price .amt { font-family: var(--sans); font-size: 2.5rem; font-weight: 550; color: var(--charcoal); margin: 0.5rem 0 0.1rem; letter-spacing: -0.03em; }
.price .per { color: var(--faint); font-size: 0.9rem; margin: 0 0 1.15rem; }
.price ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.5rem; color: var(--muted); font-size: 0.98rem; }
.price ul li { display: grid; grid-template-columns: 18px 1fr; gap: 0.55rem; }
.price ul .mk { color: var(--bullish); font-weight: 800; }
.price.featured { border-color: color-mix(in srgb, var(--accent) 55%, transparent); box-shadow: var(--shadow-lift-lg); }
.price .flag {
  position: absolute; top: -12px; left: 50%; transform: translateX(-50%);
  background: var(--accent-strong); color: var(--bg); font-size: 0.74rem; font-weight: 700; /* 0307: AA in both themes */
  padding: 0.2rem 0.85rem; border-radius: 999px; white-space: nowrap;
}
.price-note { text-align: center; color: var(--faint); font-size: 0.92rem; margin: 1.5rem auto 0; max-width: 42rem; }

/* ---------- FAQ ---------- */

.faq { max-width: 46rem; margin: 0 auto; display: grid; gap: 0.7rem; }
.faq details { background: var(--card); border: 1px solid var(--line); border-radius: 16px; padding: 0 1.35rem; }
.faq details[open] { box-shadow: var(--shadow-sm); }
.faq summary {
  cursor: pointer; list-style: none; font-weight: 600; font-size: 1.04rem;
  padding: 1.1rem 0; display: flex; justify-content: space-between; align-items: center; gap: 1rem;
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after { content: "+"; font-size: 1.5rem; font-weight: 300; color: var(--accent); line-height: 1; flex: none; transition: transform 0.2s ease; }
.faq details[open] summary::after { transform: rotate(45deg); }
.faq p { margin: -0.15rem 0 1.2rem; color: var(--muted); font-size: 1rem; }

/* 0236: the answer expands instead of snapping — pure-CSS progressive
   enhancement (interpolate-size in :root + ::details-content). Browsers
   without ::details-content support keep today's instant toggle; the
   [open] shadow now lands without its own transition, masked by the
   expansion where supported. */
.faq details::details-content {
  opacity: 0; height: 0; overflow-y: clip;
  transition: height 0.2s var(--ease-out), opacity 0.2s var(--ease-out),
    content-visibility 0.2s allow-discrete;
}
.faq details[open]::details-content { opacity: 1; height: auto; }

/* ---------- split CTA band ---------- */

.band { display: grid; grid-template-columns: 1fr 1fr; gap: 1.25rem; }
@media (max-width: 780px) { .band { grid-template-columns: 1fr; } }
.band > div { border-radius: var(--radius-lg); padding: clamp(2rem, 4vw, 3rem); }
.band .light { background: var(--tint-lav); border: 1px solid var(--line); }
.band .light h2 { font-size: clamp(1.7rem, 3vw, 2.3rem); }
.band .light p { color: var(--muted); margin: 0.85rem 0 0; }
.band .mini { list-style: none; margin: 1.4rem 0 0; padding: 0; display: grid; gap: 0.7rem; }
.band .mini li { display: grid; grid-template-columns: 22px 1fr; gap: 0.6rem; color: var(--muted); font-size: 0.98rem; }
.band .mini .mk { color: var(--bullish); font-weight: 800; }

.band .dark {
  position: relative; overflow: hidden; color: var(--panel-ink);
  background:
    radial-gradient(26rem 18rem at 88% -20%, rgba(107,147,237,0.35), transparent 62%),
    radial-gradient(22rem 16rem at 4% 120%, rgba(90,201,165,0.22), transparent 60%),
    var(--panel);
  display: flex; flex-direction: column; justify-content: center;
}
.band .dark h2 { color: #fff; font-size: clamp(1.7rem, 3vw, 2.3rem); text-shadow: 0.03em 0.045em 0.14em rgba(0, 0, 0, 0.5); }
.band .dark p { color: rgba(243,242,236,0.72); margin: 0.85rem 0 1.5rem; }
.band .dark .status { background: rgba(255,255,255,0.95); color: #2a2621; border-color: transparent; align-self: flex-start; }
.band .dark .status .dot { background: var(--accent); }

/* ---------- footer ---------- */

.site-footer { border-top: 1px solid var(--line); background: var(--card-2); padding: 3.25rem 0 2.25rem; color: var(--muted); font-size: 0.92rem; overflow: hidden; }
.foot-cols { display: grid; grid-template-columns: 1.5fr 1fr 1fr 1fr; gap: 2rem; margin-bottom: 2.25rem; }
@media (max-width: 760px) { .foot-cols { grid-template-columns: 1fr 1fr; } }
/* 0307: the column headings are h2 (were h4, which skipped a level after the
   page's h2s); weight and line height restate the old h4 look over h1, h2. */
.site-footer h2 { margin: 0 0 0.6rem; font-size: 0.78rem; font-weight: 700; line-height: inherit; text-transform: uppercase; letter-spacing: 0.11em; color: var(--faint); }
.site-footer ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.42rem; }
.site-footer a { color: var(--muted); text-decoration: none; }
.site-footer a:hover { color: var(--ink); text-decoration: underline; }
.foot-lede { color: var(--muted); margin: 0.6rem 0 0; max-width: 15rem; }
/* 0307: on phones the footer links were 17px-tall inline boxes; each one now
   fills a 44px row (Apple's minimum tap target), so the list drops its gap. */
@media (max-width: 760px) {
  .site-footer ul { gap: 0; }
  .site-footer li a, .foot-lede a { display: flex; align-items: center; width: fit-content; min-width: 44px; min-height: 44px; }
}

.foot-mark {
  font-family: var(--sans); font-weight: 550;
  font-size: clamp(4rem, 15vw, 11rem); line-height: 0.82; letter-spacing: -0.04em;
  color: color-mix(in srgb, var(--ink) 8%, transparent);
  margin: 0.5rem 0 0; user-select: none; white-space: nowrap;
}

.foot-legal { border-top: 1px solid var(--line); padding-top: 1.5rem; margin-top: 1rem; color: var(--faint); font-size: 0.85rem; display: grid; gap: 0.5rem; }

/* ---------- legal / prose pages ---------- */

.prose { max-width: 45rem; margin: 0 auto; padding: clamp(2.5rem, 5vw, 4rem) 0 4.5rem; }
.prose h1 { font-size: clamp(2.1rem, 5vw, 3rem); margin: 0 0 0.4rem; }
.prose .updated { color: var(--faint); font-size: 0.92rem; margin: 0 0 2rem; }
.prose h2 { font-family: var(--sans); font-weight: 550; font-size: 1.4rem; letter-spacing: -0.02em; margin: 2.2rem 0 0.6rem; }
.prose h3 { font-size: 1.08rem; font-weight: 650; margin: 1.5rem 0 0.4rem; }
.prose p, .prose li { color: var(--muted); }
.prose strong { color: var(--ink); }
.prose .callout {
  background: var(--tint-orange); border: 1px solid color-mix(in srgb, var(--accent) 30%, transparent);
  border-radius: 14px; padding: 1.1rem 1.3rem; color: var(--ink); font-weight: 500;
}
.prose .placeholder { background: var(--accent-soft); color: var(--accent); border-radius: 5px; padding: 0 0.35rem; font-weight: 600; }

/* ---------- blog (0303) ---------- */

.post-index { padding: clamp(1.75rem, 4vw, 2.75rem) 0 4.5rem; }
.post-list { list-style: none; margin: 0 auto; padding: 0; max-width: 45rem; display: grid; gap: 1rem; }
.post-list a {
  display: block; text-decoration: none; color: inherit; background: var(--card);
  border: 1px solid var(--line); border-radius: var(--radius); padding: 1.4rem 1.5rem; box-shadow: var(--shadow-lift);
}
.post-list h2 { font-family: var(--sans); font-weight: 550; font-size: 1.35rem; letter-spacing: -0.02em; color: var(--ink); margin: 0.35rem 0 0.5rem; }
.post-list p { color: var(--muted); margin: 0 0 0.8rem; }
.post-more { color: var(--accent); font-weight: 600; font-size: 0.95rem; }
.post-meta { color: var(--faint); font-size: 0.92rem; }
.prose .post-meta { margin: 0 0 1.75rem; }
.prose .post-crumb { margin: 0 0 0.75rem; font-size: 0.92rem; }
.post-crumb a { color: var(--muted); text-decoration: none; }
.post-crumb a::before { content: "\2190\00a0"; }
.prose .post-lede { color: var(--ink); font-size: 1.15rem; }
.stages { list-style: none; padding: 0; margin: 1rem 0 1.25rem; display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 0.75rem; }
.stages li {
  background: var(--card); border: 1px solid var(--line); border-radius: 14px; box-shadow: var(--shadow-sm);
  padding: 0.95rem 1rem 1rem; display: flex; flex-direction: column; gap: 0.3rem;
}
.stage-n {
  width: 1.7rem; height: 1.7rem; border-radius: 50%; display: grid; place-items: center; margin-bottom: 0.25rem;
  background: var(--accent-soft); color: var(--accent); font-weight: 650; font-size: 0.9rem;
}
.stages strong { font-size: 0.98rem; line-height: 1.3; }
.stages li > span:last-child { font-size: 0.92rem; line-height: 1.45; }
@media (max-width: 720px) { .stages { grid-template-columns: 1fr 1fr; } }
@media (max-width: 420px) { .stages { grid-template-columns: 1fr; } }
.score-bands { list-style: none; padding: 0; margin: 0.75rem 0 1.25rem; display: flex; flex-wrap: wrap; gap: 0.5rem 1.1rem; }
.score-bands li { display: inline-flex; align-items: center; gap: 0.45rem; }
.band-dot { width: 0.7rem; height: 0.7rem; border-radius: 50%; background: var(--c); flex: none; }
.prose .post-cta { margin-top: 2.5rem; padding: 1.1rem 1.3rem; background: var(--tint-orange); border-radius: 14px; color: var(--ink); }
.post-cta a { color: var(--accent); font-weight: 600; }
.prose .note { background: var(--card-2); border: 1px solid var(--line); border-radius: 14px; padding: 0.9rem 1.1rem; font-size: 0.95rem; }
.compare-wrap {
  overflow-x: auto; margin: 1rem 0 1.5rem; background: var(--card);
  border: 1px solid var(--line); border-radius: 14px; box-shadow: var(--shadow-sm);
}
.compare { width: 100%; min-width: 34rem; border-collapse: collapse; font-size: 0.95rem; line-height: 1.45; }
.compare th, .compare td { text-align: left; vertical-align: top; padding: 0.75rem 0.9rem; border-bottom: 1px solid var(--line); }
.compare tbody tr:last-child th, .compare tbody tr:last-child td { border-bottom: 0; }
.compare thead th { font-weight: 650; color: var(--ink); background: var(--card-2); }
.compare tbody th { font-weight: 600; color: var(--ink); width: 9.5rem; }
.compare td { color: var(--muted); }
/* phones: each row stacks, its cells labelled from data-label, so both products show without a sideways swipe */
@media (max-width: 560px) {
  .compare { min-width: 0; }
  .compare thead { display: none; }
  .compare, .compare tbody, .compare tr, .compare th, .compare td { display: block; width: auto; }
  .compare tbody tr { padding: 0.85rem 0.95rem; border-bottom: 1px solid var(--line); }
  .compare tbody tr:last-child { border-bottom: 0; }
  .compare tbody th, .compare td { padding: 0; border: 0; }
  .compare tbody th { margin-bottom: 0.35rem; }
  .compare td + td { margin-top: 0.3rem; }
  .compare td::before { content: attr(data-label) ": "; font-weight: 600; color: var(--ink); }
}
.steps-more { text-align: center; margin: 2rem 0 0; }
.steps-more a { color: var(--muted); }

/* ---------- motion (0236) ---------- */

/* Hover motion only where hover is real. Ungated, a touch tap fires a
   sticky false hover — the card lifts and STAYS lifted (motion review
   2026-08-27). Links keep their ungated color/underline hovers: color
   on tap is harmless, motion is not.
   The shadow transitions as plain box-shadow ON the card, on purpose:
   0236 first shipped it as a pre-rendered ::after layer fading opacity,
   and compositing that layer's ~90px blur over the neighboring cards'
   text made THEIR text shimmer on every hover (operator report, same
   day — worse than the per-frame paint it optimized away). Reverted;
   the paint cost is accepted and documented in the change doc. */
@media (hover: hover) and (pointer: fine) {
  .btn:hover { transform: translateY(-1px); opacity: 0.9; }
  .step:hover, .tcard:hover, .price:hover, .voice:hover {
    transform: translateY(-5px) scale(1.03); box-shadow: var(--shadow-lg); z-index: 2;
  }
  .step:hover .n { color: color-mix(in srgb, var(--accent) 28%, transparent); }
}

/* Press feedback — the touch counterpart to the hover lift. Sits after
   .btn:hover in source order so press wins while both states apply. */
.btn:active { transform: translateY(0) scale(0.97); }

/* Analyst-card visualizations draw in once, as their card reveals: the
   technical line draws itself (pathLength="1" on the polyline makes the
   dash math unit-length), the fundamental bars rise, the sentiment fill
   sweeps and its marker lands. Static without JS (html.js gate mirrors
   .reveal) and under reduced motion (block at end of file). */
.tcard .viz polyline { stroke-dasharray: 1; }
html.js .tcard.reveal .viz-line polyline { stroke-dashoffset: 1; }
html.js .tcard.reveal.on .viz-line polyline {
  stroke-dashoffset: 0;
  transition: stroke-dashoffset 0.7s var(--ease-out) 0.45s;
}
html.js .tcard.reveal .viz-bars rect {
  transform-box: fill-box; transform-origin: 50% 100%; transform: scaleY(0);
}
html.js .tcard.reveal.on .viz-bars rect {
  transform: none; transition: transform 0.5s var(--ease-out);
}
html.js .tcard.reveal.on .viz-bars rect:nth-of-type(1) { transition-delay: 0.45s; }
html.js .tcard.reveal.on .viz-bars rect:nth-of-type(2) { transition-delay: 0.53s; }
html.js .tcard.reveal.on .viz-bars rect:nth-of-type(3) { transition-delay: 0.61s; }
html.js .tcard.reveal.on .viz-bars rect:nth-of-type(4) { transition-delay: 0.69s; }
html.js .tcard.reveal .viz-gauge rect:nth-of-type(2) {
  transform-box: fill-box; transform-origin: 0 50%; transform: scaleX(0);
}
html.js .tcard.reveal.on .viz-gauge rect:nth-of-type(2) {
  transform: none; transition: transform 0.6s var(--ease-out) 0.45s;
}
html.js .tcard.reveal .viz-gauge circle { opacity: 0; }
html.js .tcard.reveal.on .viz-gauge circle {
  opacity: 1; transition: opacity 0.3s var(--ease-out) 0.9s;
}

/* The step cards' giant background numerals join the same choreography
   (operator direction, same day as the viz draw-ins): each 1/2/3 fades
   and rises in just after its card lands — same curve, same 0.45s beat
   as the analyst visualizations, so the two sections read as one
   system. The color term keeps the hover tint transition alive after
   the entrance settles (the .on transition list would otherwise
   override it). Static without JS and under reduced motion. */
html.js .step.reveal .n { opacity: 0; transform: translateY(14px); }
html.js .step.reveal.on .n {
  opacity: 1; transform: none;
  transition: opacity 0.6s var(--ease-out) 0.45s,
    transform 0.6s var(--ease-out) 0.45s, color 0.25s ease;
}
/* Operator direction: the numerals count in — 1, then 2, then 3
   (0.2s apart, overlapping like a cascade). Desktop-only (the .steps
   grid is side-by-side above 800px, so all three reveal together and
   the sequence is visible); on stacked phones each card reveals
   individually as it scrolls in, where an extra delay would only read
   as lag. Delay lists map to (opacity, transform, color) — color
   keeps its zero delay so the hover tint stays instant-ish. */
@media (min-width: 801px) {
  html.js .step.reveal.d1.on .n { transition-delay: 0.65s, 0.65s, 0s; }
  html.js .step.reveal.d2.on .n { transition-delay: 0.85s, 0.85s, 0s; }
}

/* ---------- reveal ---------- */

html.js .reveal { opacity: 0; transform: translateY(20px); transition: opacity 0.5s var(--ease-out), transform 0.5s var(--ease-out); }
html.js .reveal.on { opacity: 1; transform: none; }
.d1 { transition-delay: 0.07s; }
.d2 { transition-delay: 0.14s; }
.d3 { transition-delay: 0.21s; }
/* prefers-reduced-motion block moved to the end of the file (0236) so it
   paints last over every motion rule. */

/* ---------- utilities ---------- */

.sr-only {
  position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
/* ---------- App Store badge (change 0275) ---------- */

/* Apple's badge artwork, self-hosted in assets/img (the privacy page
   promises no third-party resources; the CSP allows img-src 'self'
   only). 50px tall, above Apple's 40px minimum. The hero row reuses
   .cta-row for centering and the 0254 entrance; the dark #get card
   gets the white variant. */
.store-badge { display: inline-block; line-height: 0; border-radius: 8px; }
.store-badge picture { display: contents; }
.store-badge img { height: 50px; width: auto; display: block; }
.store-badge:focus-visible { outline: 2px solid var(--ink); outline-offset: 4px; }
.dark .store-cta { margin: 1.4rem 0 0; }
.dark .store-note { color: rgba(243,242,236,0.6); font-size: 0.88rem; margin: 0.9rem 0 0; }
.dark .store-note a { color: rgba(243,242,236,0.85); }
/* 0251's incentive clause. 0307: in the text-safe orange, so the offer stays
   visible now that the hero eyebrow is gone (4.6:1 light, 7.3:1 dark). */
.hero .store-note strong { color: var(--accent-strong); font-weight: 650; }

/* ---------- reduced motion (0236: relocated to file end so it wins the
   cascade over every motion rule above at equal specificity) ---------- */

@media (prefers-reduced-motion: reduce) {
  html.js .reveal { opacity: 1; transform: none; transition: none; }
  /* 0254: hero load entrance renders settled. */
  .hero h1, .hero .lede,
  .hero .cta-row, .hero .proof, .phones .left, .phones .right { animation: none; }
  .step, .tcard, .price, .voice, .btn { transition: none; }
  .faq summary::after { transition: none; }
  .faq details::details-content { transition: none; }
  html.js .tcard.reveal .viz-line polyline,
  html.js .tcard.reveal.on .viz-line polyline { stroke-dashoffset: 0; transition: none; }
  html.js .tcard.reveal .viz-bars rect,
  html.js .tcard.reveal.on .viz-bars rect,
  html.js .tcard.reveal .viz-gauge rect:nth-of-type(2),
  html.js .tcard.reveal.on .viz-gauge rect:nth-of-type(2) { transform: none; transition: none; }
  html.js .tcard.reveal .viz-gauge circle,
  html.js .tcard.reveal.on .viz-gauge circle { opacity: 1; transition: none; }
  html.js .step.reveal .n,
  html.js .step.reveal.on .n { opacity: 1; transform: none; transition: none; }
}
