/* ==========================================================================
   STAKKR MARKETING SITE — DESIGN TOKENS
   Single source of truth for color, spacing, radius, and type.
   Nothing outside this section should hard-code a color, px spacing value,
   corner radius, or font size — reference a variable instead. If a new
   value is needed, add it here first.
   ========================================================================== */

:root {

  /* --- Color — brand -----------------------------------------------------
     Matches the 4 named color styles in the Figma file exactly. */
  --color-black: #000000;
  --color-white: #ffffff;
  --color-yellow: #f7fb26;
  --color-pink: #ff00d0;

  /* --- Color — neutrals (used for panel/card backgrounds) --------------- */
  --color-cream: #e0d7cf;      /* "Why This Exists" panel background */
  --color-gray-light: #d9d9d9; /* Support / Privacy Policy card background */

  /* --- Color — overlays --------------------------------------------------
     One consolidated translucent dark bar used for the sticky nav and the
     footer bottom bar (confirmed same value on both, both breakpoints). */
  --color-overlay-dark: rgb(0 0 0 / 80%);

  /* --- Motion — scroll-linked effects -------------------------------------
     Adjust freely. Controls the "Why This Exists" body paragraph's
     word-by-word scroll reveal (see js/motion.js) — this is how dim each
     word sits before it's scrolled into its "revealed" position. 1 would
     mean no dimming at all; 0 would mean fully invisible until revealed. */
  --why-body-word-inactive-opacity: 0.3;

  /* --- Gradients -----------------------------------------------------
     Fades the hero photo to black at the bottom. Desktop and mobile use
     different fade points/opacities in Figma — confirmed intentional by
     Andreas (not unified), so this is overridden (not just resized) in
     the mobile media query below.
     Fixed PIXEL stops, not percentages: Figma's hero mockup is a fixed
     646px (desktop) / 844px (mobile) tall frame, and percentage stops
     re-scale to whatever height the hero actually renders at — which is
     essentially always taller than Figma's mockup on a real browser
     (and now deliberately much taller on mobile for the viewport-height
     fix), making the fade look far more gradual than intended. Pixel
     stops keep the fade completing at the same distance from the top
     regardless of how tall the section actually renders — any extra
     height below that point is just solid black, which is correct for
     off-screen bleed area. (Trello: "linear gradient on backgd image")
     3 stops, not 2: the original 2-stop version (0% at 332px → 100% at
     646px) left the vertically-centered headline/subtext/CTA sitting
     over a barely-darkened, visually busy part of the photo, which read
     as harder to read than the Figma mockup. Compared side-by-side in a
     local sandbox (not committed — see /sandbox, gitignored) against
     five other overlay approaches (blend modes, radial vignettes, a
     text-scoped backdrop); this 3-stop version — reaching meaningful
     darkening by the section's vertical middle instead of only by the
     very bottom — is the one Andreas picked. */
  --gradient-hero-fade: linear-gradient(to bottom, rgb(0 0 0 / 0%) 0px, rgb(0 0 0 / 55%) 280px, rgb(0 0 0 / 88%) 646px);

  /* --- Corner radius -------------------------------------------------
     Only one radius value exists anywhere in the design — every button is
     a full pill regardless of its height. Everything else is square. */
  --radius-pill: 40px;

  /* --- Spacing scale ---------------------------------------------------
     Rounded scale used to build every gap/padding/margin on the site.
     Gaps measured off Figma get rounded to the nearest step below rather
     than reproduced pixel-for-pixel — ask before picking a step if a gap
     genuinely sits ambiguously between two of these. */
  --space-1: 8px;
  --space-2: 16px;
  --space-3: 24px;
  --space-4: 32px;
  --space-5: 48px;
  --space-6: 64px;
  --space-7: 96px;

  /* --- Spacing exceptions -------------------------------------------------
     Component-specific exact values Andreas confirmed should be kept as
     measured rather than rounded to the scale above. */
  --hero-subtext-gap-desktop: 87px; /* gap between hero headline and subtext, desktop only */

  /* --- Fonts -------------------------------------------------------------
     Good Brush is hand-drawn/display-only and only has capital letterforms
     — see --font-display usage rule below. Roboto Mono is loaded via
     Google Fonts (self-hosted files weren't provided for it; ask Andreas
     if self-hosting is preferred later).
     Fallback stack is Helvetica/Arial/sans-serif, not cursive — a cursive
     generic keyword resolves to a system script/handwriting face on most
     platforms, which has enough character of its own to read as a glitch
     during the loading flash rather than a neutral placeholder. (Trello:
     "Fix font flash on page load (FOUT)") */
  --font-display: 'Good Brush', Helvetica, Arial, sans-serif;
  --font-body: 'Roboto Mono', ui-monospace, 'SFMono-Regular', Menlo, Consolas, monospace;

  /* --- Type scale — headlines (Good Brush) --------------------------------
     Desktop values shown here; overridden for mobile in the media query
     below. Role names match how each size is actually used in the design,
     not arbitrary t-shirt sizes. */
  --text-hero: 90px;              /* Hero headline; footer "Your collection is waiting"; Support/Privacy page title */
  --text-section-heading: 64px;   /* "Why This Exists"; "Scan · Stack · Send" step labels */
  --text-subheading: 56px;        /* "You know who you are" */
  --text-eyebrow-heading: 32px;   /* Trust-signal sub-headlines: "Powered by Discogs" etc. — does not scale down on mobile */

  /* --- Type scale — body & labels (Roboto Mono) --------------------------- */
  --text-body: 18px;                 --text-body-line: 18px;      /* Paragraph copy */
  --text-body-loose: 18px;           --text-body-loose-line: 21.6px; /* Body copy that uses the 1.2x ratio instead of flat — confirmed on both "Who This Is For" body and "How It Works" intro body via direct per-node checks (not assumed to be the same until independently verified each time) */
  --text-eyebrow-label: 18px;        --text-eyebrow-label-line: 21.6px; /* "Why this exists" / "How it works" eyebrow labels */
  --text-nav-link: 16px;             --text-nav-link-line: 16px;  /* Footer nav links: Privacy policy / Support */
  --text-fine-print: 14px;           --text-fine-print-line: 14px; /* Footer copyright line */
  --text-caption-chip: 14px;         --text-caption-chip-line: 14px; /* Captions overlaid on Scan/Stack/Send photos */
  --text-caption: 14px;              --text-caption-line: 19.6px; /* Trust-signal card captions — 140% line-height (Andreas, direct instruction). This was previously flat 14px on desktop only, corrected earlier from an assumed 19.6px on both breakpoints — that "flat on desktop" reading is superseded now; desktop and mobile use the same 140%/19.6px value again, so the mobile-specific override that used to exist for this token is gone. */
  --text-tagline: 18px;              --text-tagline-line: 25.2px; /* Trust Signals closing line ("Pay once...") — Andreas: the desktop node reads flat 18px because it happens to render as a single line there (line-height doesn't show on one line), not because a different ratio was intended, so one value covers both breakpoints. */
  --text-button-large: 18px;         --text-button-large-line: 18px; /* Large CTA button label, Medium (corrected from SemiBold) — Andreas rebuilt this button since the first token pass (was 16px); line-height normalized to match the flat 1.0x ratio every other button uses (Figma had it at 23.74px on the hero instance, which looked like a stray override — flag if you want that loose spacing kept) */
  --text-hero-subtext: 18px;         --text-hero-subtext-line: 21.6px; /* Hero subtext — confirmed fixed at this size on BOTH breakpoints, does not follow the general --text-body mobile scale-down */
  --text-button-small: 14px;         --text-button-small-line: 14px; /* Small nav button label, Regular */
  --text-page-heading: 24px;         --text-page-heading-line: 28.8px; /* Support/Privacy page heading, SemiBold */
  --text-page-subheading: 18px;      --text-page-subheading-line: 21.6px; /* Support/Privacy subheadings, SemiBold */
  --text-page-body: 16px;            --text-page-body-line: 19.2px; /* Support/Privacy body paragraphs, Regular */
}

/* Mobile overrides — same variable names, smaller viewport values.
   Kept in this same tokens section (not scattered per-component) so the
   full scale is still visible in one place. Breakpoint matches the rest
   of the site's responsive rules. */
@media (max-width: 767px) {
  :root {
    --text-hero: 60px;              /* re-measured directly off current Figma; was recorded as 62px in the first token pass */
    --text-section-heading: 42px;
    --text-subheading: 42px;
    /* --text-eyebrow-heading stays 32px — confirmed intentional, doesn't scale */

    --text-body: 16px;              --text-body-line: 22.4px;
    --text-body-loose: 16px;        --text-body-loose-line: 22.4px; /* confirmed matches --text-body-line's mobile value here, but kept as its own token since desktop diverges */
    --text-eyebrow-label: 16px;     --text-eyebrow-label-line: 19.2px;
    --text-nav-link: 14px;          --text-nav-link-line: 19.6px;
    --text-fine-print: 14px;        --text-fine-print-line: 19.6px;
    /* --text-caption-chip stays 14px/14px — confirmed reused as-is on mobile */
    /* --text-caption-line no longer needs a mobile override — desktop's
       base value is 19.6px (140%) now too, same as mobile. */
    /* --text-button-large / --text-button-small stay the same on mobile */
    /* --text-page-* tokens stay the same on mobile — Support/Privacy pages don't scale type */

    /* Mobile hero photo fade — confirmed intentionally different from desktop (stays translucent, never reaches full black, caps at 80% not 100%). Fixed px stops for the same reason as the desktop version above.
       Extended to the same 3-stop, earlier-darkening shape as desktop
       (Trello/sandbox pick, see desktop token above), scaled
       proportionally to mobile's 844px frame and kept under its
       existing 80% cap: middle stop at 366px (844 × the same 0.43 ratio
       used for desktop's 280/646) and 50% (the same ~0.63 fraction of
       the final 80%, matching desktop's 55%-of-88% ratio). This
       specific mobile version was NOT itself shown in the sandbox
       comparison — that only covered desktop viewport widths — so it's
       a proportional extrapolation of the picked desktop treatment,
       not something Andreas directly confirmed on a phone yet. Worth a
       real device check. */
    --gradient-hero-fade: linear-gradient(to bottom, rgb(0 0 0 / 0%) 0px, rgb(0 0 0 / 50%) 366px, rgb(0 0 0 / 80%) 844px);
  }
}


/* ==========================================================================
   FONT LOADING
   ========================================================================== */

@font-face {
  font-family: 'Good Brush';
  src: url('../assets/fonts/good-brush/good-brush.woff2') format('woff2'),
       url('../assets/fonts/good-brush/good-brush.woff') format('woff'),
       url('../assets/fonts/good-brush/good-brush.ttf') format('truetype');
  font-weight: 400;
  font-style: normal;
  /* block, not swap: swap shows the fallback font immediately and swaps
     in Good Brush once it loads, which is exactly the flash this is
     fixing. block keeps text invisible for a short browser-defined
     window (~3s) instead, falling back to the fallback font only if
     Good Brush still hasn't loaded by then. (Trello: "Fix font flash on
     page load (FOUT)") */
  font-display: block;
}

/* Roboto Mono is loaded via a Google Fonts <link> in the <head> of each
   page (weights 400 and 600) — see index.html. */


/* ==========================================================================
   BASE / RESET
   Minimal — enough for the skeleton to render sanely. Section-specific
   styling is added page-section by page-section, not here.
   ========================================================================== */

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

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

body {
  margin: 0;
  font-family: var(--font-body);
  font-size: var(--text-body);
  line-height: var(--text-body-line);
  color: var(--color-black);
  background: var(--color-white);
  -webkit-font-smoothing: antialiased;
}

/* On pages with a hero, fall back to black instead of white behind it.
   Mobile browser chrome (Safari's toolbar, address bar, etc.) can leave a
   sliver of the page's background showing beneath full-bleed sections in
   ways that vary by device/browser/settings and can't be predicted or
   fully eliminated from the page side (see Trello "Mobile: Full screen
   default viewport."). The hero's own gradient already fades to
   near-black at its bottom edge, so a black fallback blends in here
   regardless of the exact leftover gap size — a robust fix that doesn't
   depend on any specific device. Scoped with :has() so it doesn't affect
   the white-background Privacy Policy / Support pages. */
body:has(.hero) {
  background: var(--color-black);
}

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

a {
  color: inherit;
  text-decoration: none;
}

button {
  font: inherit;
  color: inherit;
  background: none;
  border: 0;
  cursor: pointer;
}

ul, ol {
  margin: 0;
  padding: 0;
  list-style: none;
}

h1, h2, h3, h4, p {
  margin: 0;
}

/* Every headline in the design uses Good Brush, which Figma always renders
   in capitals regardless of how the text is typed. Any element using
   --font-display MUST go through this class (or apply the same rule) so
   the built site matches Figma even if a browser's font-matching doesn't
   force caps on its own. */
.font-display {
  font-family: var(--font-display);
  font-weight: bold;
  text-transform: uppercase;
}


/* ==========================================================================
   BUTTONS
   Shared pill button used in the nav (small) and as the hero/footer CTA
   (large). Icon + label, no fixed width — sized to content.
   ========================================================================== */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-1);
  background: var(--color-white);
  color: var(--color-black);
  border-radius: var(--radius-pill);
  font-family: var(--font-body);
  white-space: nowrap;
}

.btn img {
  height: 0.9em;
  width: auto;
}

.btn--sm {
  height: 32px;
  padding: 0 var(--space-2);
  font-size: var(--text-button-small);
  line-height: var(--text-button-small-line);
  font-weight: 400;
}

.btn--lg {
  height: 54px;
  /* 42px, confirmed exact — not on the spacing scale and not within
     2px of a step, deliberate exception. (Trello: "hero cta desktop
     changes") */
  padding: 0 42px;
  font-size: var(--text-button-large);
  line-height: var(--text-button-large-line);
  /* Medium, not SemiBold — Andreas corrected this after the button
     was rebuilt with 600 in an earlier pass. */
  font-weight: 500;
}

/* Nav button shows a longer label on desktop, a short one on mobile —
   these are genuinely different copy (not a truncation), so both live in
   the DOM and CSS shows/hides the right one per breakpoint. */
.btn__label--mobile {
  display: none;
}


/* ==========================================================================
   NAV
   ========================================================================== */

/* Figma has the nav floating directly on top of the hero photo (same
   coordinates, translucent background) rather than pushing it down — so
   this is fixed/overlaid, not a normal-flow bar. Hero accounts for the
   nav's height itself via its own content offset, not via body padding. */
.nav {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 10;
  background: var(--color-overlay-dark);
}

/* Grid, not flex + space-between: with flex, the tagline (the middle
   item) sits centered in whatever space is left between the logo and
   CTA button, which isn't the true center of the bar since those two
   have different widths. Two equal 1fr side columns force the middle
   column to sit on the bar's actual center regardless of how wide the
   logo/button are. (Trello: "top text position") */
.nav__inner {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3);
}

/* Explicit grid-column, not just DOM order: when the tagline is
   display: none (hidden on mobile and on landscape phones), it's
   removed from grid placement entirely, not just visually — without
   an explicit column assigned, auto-placement shifts the CTA button
   into the tagline's own middle column instead of the third one,
   leaving it short of the right edge instead of flush against it.
   (Trello: "sticky top: mobile: alignment") */
.nav__logo {
  grid-column: 1;
  justify-self: start;
}

.nav__tagline {
  grid-column: 2;
  justify-self: center;
}

.nav__cta {
  grid-column: 3;
  justify-self: end;
}

.nav__logo img {
  display: block;
  height: 36px;
  width: auto;
  /* Exported SVG is solid black; logo should read white on the dark nav
     bar (Trello: "stakkr logo white"). Invert rather than re-export/edit
     the SVG, same approach as the hero chevron. */
  filter: invert(1);
}

.nav__tagline {
  color: var(--color-white);
  font-size: var(--text-nav-link);
  line-height: var(--text-nav-link-line);
  text-align: center;
}

/* A phone held sideways is often wider than the 767px mobile
   breakpoint, so without this it falls back to the desktop nav (with
   the tagline) even though the screen is a short phone landscape, not
   a real desktop window — cramped and unnecessary there. Targeting by
   height (not width) is what actually distinguishes a landscape phone
   from a real desktop browser, which is normally much taller even
   when narrow. (Trello: "top on horizontal phones") */
@media (orientation: landscape) and (max-height: 500px) {
  .nav__tagline {
    display: none;
  }
}


/* ==========================================================================
   HERO
   ========================================================================== */

.hero {
  position: relative;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  justify-content: center;
  min-height: 646px; /* Andreas: no dynamic viewport height on desktop — match Figma's fixed 646px hero frame exactly, not the browser window height (Trello: "linear gradient on backgd image"). This also makes the fixed-pixel gradient stops line up exactly with Figma, since the section is now the same height Figma assumes. */
  padding: var(--space-6) var(--space-3);
  color: var(--color-white);
  /* Pinned via GSAP ScrollTrigger in js/motion.js (desktop/tablet only) so
     the sections below slide up and bury it. This z-index only matters
     once the hero becomes position:fixed at runtime — with no JS it has
     no visible effect on normal document flow. */
  z-index: 0;
}

.hero__photo {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
  z-index: 0;
}

.hero::after {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--gradient-hero-fade);
  z-index: 1;
  pointer-events: none;
}

.hero__content {
  position: relative;
  z-index: 2;
  text-align: center;
}

.hero__headline {
  color: var(--color-yellow);
  font-size: var(--text-hero);
  line-height: 1;
  margin: 0 auto;
  /* Confirmed via SVG filter export (feOffset/feColorMatrix): dy 8px, no
     dx, no blur, black at 85% opacity — identical on 90px desktop and
     60px mobile. This is specific to the hero headline, NOT a universal
     Good Brush effect — it was wrongly applied to the shared
     .font-display class before, which made it bleed onto the "You know
     who you are" headline that has no shadow at all in Figma. Check
     each headline instance individually; don't assume one confirmed
     effect applies to every use of the font. (Trello: "headline no drop
     shadow") */
  text-shadow: 0 8px 0 rgb(0 0 0 / 85%);
}

.hero__subtext {
  margin-top: var(--hero-subtext-gap-desktop);
  font-size: var(--text-hero-subtext);
  line-height: var(--text-hero-subtext-line);
}

/* Desktop and mobile now have genuinely different copy in Figma (not
   just a size/wrap difference), confirmed by Andreas — match each
   breakpoint's own text exactly rather than sharing one string.
   (Trello: "new copy under headline") */
.hero__subtext--mobile {
  display: none;
}

.hero__cta {
  margin-top: var(--space-3);
}

.hero__chevron {
  position: relative;
  z-index: 2;
  align-self: center;
  margin-top: var(--space-6);
  display: none; /* mobile-only element — see mobile override below */
}

.hero__chevron img {
  display: block;
  width: 134px;
  height: 93px;
  /* Exported SVG is solid black; Figma shows this icon in white against
     the dark hero photo. Invert rather than re-export/re-edit the SVG. */
  filter: invert(1);
}


/* ==========================================================================
   WHO THIS IS FOR
   Plain text on the default page background — no panel, no shadow, no
   interactive elements (confirmed by full Figma inspection, not assumed).
   ========================================================================== */

.who-this-is-for {
  /* Explicit white background, not inherited from body — body's own
     background is black as a fallback specifically for the hero's
     toolbar-gap issue (body:has(.hero) above), which would otherwise
     make this section's black text invisible. Every section after the
     hero needs to set its own background rather than relying on body's
     color; this one is plain white per Figma (no panel). */
  background: var(--color-white);
  padding: var(--space-6) var(--space-3);
  text-align: center;
  /* position + z-index so this section paints above the pinned hero
     once js/motion.js fixes it in place — see the .hero comment above.
     No visual effect without JS. */
  position: relative;
  z-index: 1;
}

.who-this-is-for__headline {
  font-size: var(--text-subheading);
  line-height: 1;
}

/* Desktop and mobile headlines differ in more than size: mobile has a
   manual line break baked into the Figma text itself ("You know who" /
   "you are"), not just natural reflow, and the alignment flips from
   centered to left — so these are two separate strings, not one that
   reflows. */
.who-this-is-for__headline--mobile {
  display: none;
}

.who-this-is-for__body {
  margin-top: var(--space-3);
  /* Uses the "loose" (1.2x) body token, not the flat --text-body-line —
     confirmed via get_node_info on this exact node (21.6px desktop,
     not 18px flat). Later found the same 18/21.6 desktop, 16/22.4
     mobile pattern on the How It Works intro body too, so this got
     promoted to its own --text-body-loose token pair instead of being
     a one-off hardcoded value. (Trello: "line height on text under
     headline is wrong, should be 120%") */
  font-size: var(--text-body-loose);
  line-height: var(--text-body-loose-line);
  /* Desktop only — Figma constrains this text to 1198px within the
     1438px section, not full width (confirmed by checking the text
     node's own bounding box, not just its parent frame's). Missed on
     the first pass by only checking the parent frame's width and
     assuming the text filled it. (Trello: "max width on the text
     under the headline") */
  max-width: 1198px;
  margin-left: auto;
  margin-right: auto;
}


/* ==========================================================================
   HOW IT WORKS
   Confirmed with Andreas: the photo background is only sized to cover
   the intro text and part of the screenshots below it (not the full
   section) — the caption area below sits on plain white, same as the
   rest of the site. Fixed heights below (841px desktop / 614px mobile)
   match Figma's own background frame size exactly, per breakpoint,
   using the same source photo cropped differently (same approach as
   the hero image).
   No gradient overlay on this photo — confirmed directly by Andreas
   there is no gradient in the Figma file here. An earlier build added
   one anyway (a solid, fully-opaque gray gradient with no
   transparency), which is why the photo never rendered in ANY browser,
   not just Safari — it wasn't a rendering bug, the gradient was simply
   an opaque layer sitting on top of the photo, hiding it completely.
   (Trello: "background image is just a gradient not a image") */
.how-it-works {
  position: relative;
  background: var(--color-white);
  /* Paints above the pinned hero at runtime — see .hero comment. No
     visual effect without JS (the z-index below, 1, matches every other
     top-level section after the hero for this same reason). */
  z-index: 1;
}

.how-it-works__bg {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 841px;
  overflow: hidden;
  z-index: 0;
  background-image: url('../assets/images/scan-stack-send-bg.jpg');
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
}

.how-it-works__intro {
  position: relative;
  z-index: 1;
  padding: var(--space-4) var(--space-3) 0;
  text-align: center;
  color: var(--color-white);
}

.how-it-works__eyebrow {
  font-size: var(--text-eyebrow-label);
  line-height: var(--text-eyebrow-label-line);
}

.how-it-works__headline {
  font-size: var(--text-section-heading);
  line-height: 1;
  /* 12px, not on the spacing scale and not within 2px of a step —
     confirmed as a deliberate recurring exception (same value appears
     between eyebrow→headline, headline→body, and label→captions,
     desktop and mobile, at multiple font sizes) rather than a one-off
     to round away. */
  margin-top: 12px;
}

.how-it-works__body {
  font-size: var(--text-body-loose);
  line-height: var(--text-body-loose-line);
  margin-top: 12px; /* same recurring exception as above */
}

.how-it-works__grid {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  column-gap: 64px; /* measured 68.73px — rounds to the nearest scale step. Confirmed by Andreas this must be a true fixed gap, not fluid, so the container itself is now capped (see max-width) rather than letting 1fr columns stretch the gap on wide screens */
  max-width: 1214px; /* 362px × 3 images + 64px × 2 gaps — once the container hits this width, each 1fr column resolves to exactly 362px, so images and captions both fill the same column width and align flush on the left, with a genuinely fixed gap between them. Below this width (tablet range), everything scales down together proportionally instead of overflowing. */
  margin: 0 auto;
  padding: var(--space-6) var(--space-3) var(--space-6);
}

/* One item = one screenshot + its own caption, stacked. On mobile this
   whole unit becomes a single carousel "slide" that scrolls as one
   piece (see mobile override below) — that's what makes the caption
   move together with its screenshot instead of being a separately
   toggled block. (Trello: "horizontal scroll effect screen images") */
.how-it-works__item {
  display: flex;
  flex-direction: column;
}

.how-it-works__screenshot {
  /* No max-width/centering needed here — .how-it-works__grid's own
     max-width caps each 1fr column at exactly 362px, so this just
     fills its column, and the caption below (same column) lines up
     flush with it automatically. (Trello: captions not aligned with
     their images, gap not truly fixed) */
  width: 100%;
  height: auto;
  aspect-ratio: 362 / 738;
  object-fit: cover;
}

.how-it-works__caption {
  margin-top: var(--space-4); /* 32px, image → step label, clean scale match */
  /* Andreas re-measured: 16px left inset (his first pass at 50px was a
     mistake) — matches --space-2 exactly, no rounding needed. Desktop
     only; see mobile override below. */
  padding-left: var(--space-2);
}

.how-it-works__step-label {
  color: var(--color-pink);
  font-size: var(--text-section-heading);
  line-height: 1;
  text-align: left;
}

.how-it-works__chips {
  display: flex;
  flex-direction: column;
  gap: 4px; /* measured exactly, consistent across every step and both breakpoints — deliberate exception, not on the spacing scale */
  margin-top: 12px; /* same recurring exception as the intro block above */
}

.how-it-works__chip {
  background: var(--color-black);
  color: var(--color-white);
  font-size: var(--text-caption-chip);
  line-height: var(--text-caption-chip-line);
  padding: var(--space-1); /* measured ~10px/8px — within 2px of --space-1 (8px), rounded per the spacing rule */
  width: fit-content;
}

/* width: fit-content on a chip that needs to WRAP doesn't hug the
   actual wrapped lines the way it does for single-line chips — when
   content is too wide to fit on one line, fit-content/shrink-to-fit
   falls back to the full available width instead of the width of the
   longest wrapped line, so the black box renders wider than the text
   it contains (Andreas: "the black container... should not be bigger
   in width than the text inside"). Checked directly in the browser:
   these same 3 chips wrap on desktop too, at the exact same point —
   the desktop column (362px) isn't actually wider than what these
   specific chips need, an earlier assumption here that turned out to
   be wrong. Same hard-break approach already used for the hero
   headline to guarantee a specific wrap point browsers can't second-
   guess, applied on both breakpoints now, not just mobile. */


/* ==========================================================================
   WHY THIS EXISTS
   Desktop: full-bleed two-column split, photo fixed at 500px on the
   left, cream panel filling the rest on the right, content vertically
   centered in that panel. The photo stays a fixed 500px rather than
   scaling with viewport width beyond Figma's 1440px reference — there's
   no signal in Figma for what should happen wider than that, and a
   fixed photo width matches how the hero photo behaves (fills its own
   box, doesn't need to grow arbitrarily large). Mobile stacks instead
   (see mobile override): intro, then the photo full-width, then the
   body copy, all on one continuous cream background — the `.why-this-
   exists__panel` wrapper (needed on desktop to group intro+body into
   one flex column next to the photo) becomes `display: contents` on
   mobile so its children can be reordered as flat siblings alongside
   the photo.
   ========================================================================== */

.why-this-exists {
  display: flex;
  background: var(--color-cream);
  /* Paints above the pinned hero at runtime — see .hero comment. No
     visual effect without JS. */
  position: relative;
  z-index: 1;
}

.why-this-exists__photo {
  flex: 0 0 500px;
  width: 500px;
  height: 615px; /* Figma's exact frame height for this photo, same fixed-height pattern already used for the hero and How It Works background photos */
  object-fit: cover;
}

.why-this-exists__panel {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: center;
  background: var(--color-cream);
  padding: var(--space-6) var(--space-4);
}

.why-this-exists__eyebrow {
  font-size: var(--text-eyebrow-label);
  line-height: var(--text-eyebrow-label-line);
}

.why-this-exists__headline {
  color: var(--color-pink);
  font-size: var(--text-section-heading);
  line-height: 1;
  margin-top: 12px; /* same recurring exception as How It Works — measured consistently at this value, not on the spacing scale */
}

.why-this-exists__body {
  margin-top: 12px; /* same recurring exception as above */
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

.why-this-exists__body p {
  font-size: var(--text-body);
  line-height: var(--text-body-line);
  /* Confirmed via the text node's own bounding box: 608px, narrower
     than the 876px content column it sits in, with no visual cue
     pointing to it — the exact kind of miss this project has been
     burned by before. */
  max-width: 608px;
}

/* Applied only by js/motion.js, only once GSAP has loaded, wrapping each
   word of the body paragraphs above for the scroll-linked reveal. Without
   JS, no spans exist and this class never applies — the paragraphs render
   as plain full-opacity text, same as any other browsing-without-JS
   fallback on this site. */
.why-this-exists__word {
  opacity: var(--why-body-word-inactive-opacity);
}


/* ==========================================================================
   TRUST SIGNALS
   Sits on the plain white page background, not a continuation of Why
   This Exists' cream panel — checked the actual Figma parent/child
   structure rather than assuming it continued, since the two visually
   sit right next to each other. Desktop: a row of 3 fixed-width
   columns (344px) with a fixed 136px gap between them — capped at the
   exact total width (1304px) and centered, same "cap the container"
   pattern as the How It Works screenshot row, since 136px isn't a
   fluid value either. Mobile: the same 3 columns stacked vertically
   instead, full width.
   ========================================================================== */

.trust-signals {
  background: var(--color-white);
  padding: var(--space-6) var(--space-3);
  /* Paints above the pinned hero at runtime — see .hero comment. No
     visual effect without JS. */
  position: relative;
  z-index: 1;
}

.trust-signals__row {
  display: flex;
  justify-content: center;
  gap: 136px; /* measured exactly between fixed 344px columns — not on the spacing scale and not within 2px of a step, deliberate exception */
  max-width: 1304px; /* 344px × 3 columns + 136px × 2 gaps */
  margin: 0 auto;
}

.trust-signals__item {
  display: flex;
  flex-direction: column;
  align-items: center;
  width: 344px;
  text-align: center;
}

.trust-signals__icon {
  height: 98px;
  width: auto;
}

.trust-signals__headline {
  color: var(--color-pink);
  font-size: var(--text-eyebrow-heading);
  line-height: 1;
  margin-top: var(--space-2); /* 16px, icon → headline, confirmed exact against Figma */
}

.trust-signals__caption {
  font-size: var(--text-caption);
  line-height: var(--text-caption-line);
  margin-top: 12px; /* same recurring exception used elsewhere (Why This Exists, How It Works) */
}

.trust-signals__tagline {
  color: var(--color-pink);
  font-size: var(--text-tagline);
  line-height: var(--text-tagline-line);
  text-align: center;
  margin-top: 42px; /* measured exactly from the row's tallest column to the tagline — not on the spacing scale and not within 2px of a step, deliberate exception */
}


/* ==========================================================================
   FINAL CTA + FOOTER
   Both live inside one continuous patterned frame in Figma ("footer"),
   which is why they're one <footer> landmark in the HTML rather than a
   section handing off to a footer — a shared background here also
   avoids a visible seam where two independently-tiled backgrounds
   would otherwise meet. Andreas provided the pattern as an already-
   exported PNG (his own note in Figma said this pattern already exists
   in the separate app project's codebase, which this website has no
   access to) — tile size is a first pass, worth a visual check against
   Figma since the exact pattern density wasn't independently measured.
   ========================================================================== */

.site-footer {
  position: relative;
  background-image: url('../assets/images/background-pattern.png');
  background-repeat: repeat;
  background-size: 78px 78px;
  /* Pattern stays put in the viewport as the footer content scrolls over
     it, rather than scrolling with the section. iOS Safari doesn't
     support this CSS property and silently falls back to scrolling with
     the section there — see .fixed-pattern-overlay below for how that
     gap is covered. */
  background-attachment: fixed;
  color: var(--color-white);
  /* Paints above the pinned hero at runtime, on index.html — see .hero
     comment. No visual effect without JS, and harmless on the
     Privacy/Support pages, which have no pinned hero to sit above. */
  z-index: 1;
}

/* JS-only (see js/main.js): suppresses .site-footer's own background-image
   once the shared .fixed-pattern-overlay below has been inserted, so the
   two don't double up. Without this, on iOS the footer's own (broken,
   scrolling) copy would paint in front of the working fixed layer below
   it and hide the fix. */
.site-footer--js-pattern-active {
  background-image: none;
}

/* iOS Safari fallback for .site-footer's background-attachment: fixed
   above, which it doesn't support. A position: fixed element genuinely
   IS honored on iOS, but nesting it inside .site-footer and relying on
   that element's own overflow: hidden to clip it does NOT work — overflow
   only clips a fixed descendant if the ancestor also has a transform (or
   similar), and adding one of those would turn "fixed" back into
   "absolute", undoing the exact effect this is meant to create. (Learned
   the hard way — a first attempt at this shipped a broken version that
   covered the whole page, not just the footer, before this was caught.)
   The approach that actually works: one single fixed layer covering the
   whole viewport, inserted as the first child of <body> so it paints
   behind everything (z-index -1, below every section's own z-index of
   0 or 1), never clipped to any one element's box at all. Every section
   above the footer already has its own fully opaque background, so this
   layer is invisible everywhere except the one place nothing opaque is
   drawn in front of it: the footer, once .site-footer--js-pattern-active
   above has switched its own background off. */
.fixed-pattern-overlay {
  position: fixed;
  inset: 0;
  z-index: -1;
  background-image: url('../assets/images/background-pattern.png');
  background-repeat: repeat;
  background-size: 78px 78px;
  pointer-events: none;
}

/* On the Privacy Policy / Support pages, the SAME pattern actually
   covers the entire page in Figma, not just the footer — checked the
   page frame's own fill directly rather than assuming it was scoped
   to the footer sub-frame the way I first read it. Applied to the
   whole page (via <body class="pattern-page">) rather than just
   .site-footer there, and .site-footer's own copy of the same pattern
   is switched off on these pages so two separately-tiled layers don't
   create a visible seam where the page content hands off to the
   footer. (Andreas: "the whole background... should be the same
   pattern as in the footer") */
.pattern-page {
  background-image: url('../assets/images/background-pattern.png');
  background-repeat: repeat;
  background-size: 78px 78px;
}

.pattern-page .site-footer {
  background-image: none;
}

.final-cta {
  text-align: center;
  /* 52px measured from the section's own top edge to the subtitle —
     not on the spacing scale and not within 2px of a step, deliberate
     exception, like several other exact values in this section. */
  padding: 52px var(--space-3) 44px;
}

.final-cta__subtitle {
  font-size: var(--text-body);
  line-height: var(--text-body-line);
}

.final-cta__subtitle--mobile {
  display: none;
}

.final-cta__headline {
  color: var(--color-yellow);
  font-size: var(--text-hero);
  line-height: 1;
  /* 15px measured, within 2px of --space-2 (16px) — rounded per the
     spacing rule. */
  margin-top: var(--space-2);
}

.final-cta__button {
  /* 44px, measured exactly from the headline's own bottom — not on the
     spacing scale and not within 2px of a step, deliberate exception
     (same value recurs below, between this button and the footer bar). */
  margin-top: 44px;
}

.footer__bar {
  background: rgb(0 0 0 / 80%);
}

.footer__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 72px;
  padding: 0 var(--space-3); /* 24px both sides, confirmed exact — Andreas corrected an earlier asymmetric measurement (29px/39px) directly in Figma */
}

.footer__logo img {
  display: block;
  height: 36px;
  width: auto;
  /* Exported SVG is solid black; same white-on-dark treatment as the
     nav logo. */
  filter: invert(1);
}

.footer__meta {
  display: flex;
  align-items: center;
  gap: var(--space-3); /* 24px, confirmed exact, matches the links row's own internal gaps */
}

.footer__links {
  display: flex;
  align-items: center;
  gap: var(--space-3); /* 24px, confirmed exact between "Privacy policy", "|", and "Support" */
}

.footer__links a {
  color: var(--color-white);
  font-size: var(--text-nav-link);
  line-height: var(--text-nav-link-line);
}

.footer__copyright {
  font-size: var(--text-fine-print);
  line-height: var(--text-fine-print-line);
  text-align: center;
}

.footer__copyright--mobile {
  display: none;
}


/* ==========================================================================
   PRIVACY POLICY / SUPPORT PAGES
   Shared structure on both pages: a centered yellow headline right
   below the nav, then a gray content card. The nav is a fixed overlay
   with no hero underneath it here, so the headline needs explicit
   clearance padding to sit below it (84px nav height + 64px confirmed
   gap, both from Figma).
   ========================================================================== */

.page-hero {
  text-align: center;
  padding: 148px var(--space-3) 0;
}

.page-hero__headline {
  color: var(--color-yellow);
  font-size: var(--text-hero);
  line-height: 1;
}

.page-card {
  max-width: 1024px;
  margin: var(--space-3) auto 0; /* 24px gap from headline, confirmed exact */
  background: var(--color-gray-light);
  padding: var(--space-3) var(--space-3) var(--space-6); /* 24px top/sides, 64px bottom, confirmed exact */
  color: var(--color-black);
}

.page-card__heading {
  font-size: var(--text-page-heading);
  line-height: var(--text-page-heading-line);
  font-weight: 600;
}

.page-card__intro {
  font-size: var(--text-page-body);
  line-height: var(--text-page-body-line);
  margin-top: var(--space-1); /* 8px, confirmed exact, heading → body gap used consistently throughout both pages */
}

.page-card__divider {
  border: none;
  height: 1px;
  background: var(--color-black);
  margin: var(--space-3) 0 0; /* 24px above, confirmed exact */
}

/* Every other section-to-section gap on both pages is 32px, confirmed
   exact and consistent — this one specific case (divider → first
   section) is 24px instead, also confirmed exact, not a scaling
   error. */
.page-card__divider + .page-card__section {
  margin-top: var(--space-3);
}

.page-card__section {
  margin-top: var(--space-4); /* 32px, confirmed exact, consistent between every section on both pages */
}

.page-card__subheading {
  font-size: var(--text-page-body);
  line-height: var(--text-page-body-line);
  font-weight: 600;
  margin-bottom: var(--space-1); /* 8px, confirmed exact, consistent heading → body gap throughout */
}

/* "Contact", "Common questions", and "About Stakkr" use the larger
   subheading tier (18px) — the other FAQ questions under "Common
   questions" use the smaller 16px tier (same size as regular body
   text, just bold). Confirmed per-node in Figma, not assumed. */
.page-card__subheading--lg {
  font-size: var(--text-page-subheading);
  line-height: var(--text-page-subheading-line);
}

.page-card__section p,
.page-card__faq p {
  font-size: var(--text-page-body);
  line-height: var(--text-page-body-line);
}

.page-card__section p + p {
  margin-top: var(--space-1); /* consistent small gap, matches the heading → body pattern used throughout */
}

/* A couple of body paragraphs on the Support page are SemiBold in
   Figma, not Regular like most. The Privacy Policy "Third party
   services" paragraphs read this way too in Figma's raw text style,
   but Andreas corrected that specifically — those should be Regular
   with a bullet-style indent instead (see .page-card__bullet below),
   not bold. */
.page-card__body--strong {
  font-weight: 600;
}

/* Regular weight, indented to read as a bullet-style sub-item under
   the line introducing them — Andreas's correction for the "Discogs"
   / "Apple MusicKit" lines under Third party services, which Figma's
   raw text style alone would have suggested should be bold instead. */
.page-card__bullet {
  margin-left: var(--space-3);
}

.page-card__faq {
  margin-top: var(--space-4); /* 32px, confirmed exact, same as the general section-to-section gap */
}

/* 16px, confirmed exact (measured 18px, within 2px of the step) —
   distinct from the general 32px section gap since this is a
   subheading handing off to a list of FAQ items, not two full
   sections. */
.page-card__subheading--lg + .page-card__faq {
  margin-top: var(--space-2);
}

.page-card a {
  color: inherit;
  text-decoration: underline;
}


/* ==========================================================================
   MOBILE OVERRIDES — NAV & HERO
   ========================================================================== */

@media (max-width: 767px) {

  .btn__label--mobile {
    display: inline;
  }

  .btn__label--desktop {
    display: none;
  }

  .nav__inner {
    padding: calc(var(--space-2) + env(safe-area-inset-top)) var(--space-2) var(--space-2);
  }

  .nav__tagline {
    display: none;
  }

  /* Andreas's approach (Trello: "Mobile: Full screen default viewport."):
     make the hero cover the longest height a phone screen can actually
     be. 100lvh was originally used alone on the theory that it already
     equals that value by definition — but checking on an actual
     iPhone 13 Pro showed a real, measured ~58px shortfall, so a small
     buffer is back on top of it after all (Andreas: "the hero section
     needs to be approximately 58px higher to cover my iphone13 pro
     viewport"). Not as large as the old "100dvh + 300px" guess that
     felt too tall — this one's based on an actual measurement, not a
     guess, though it may need adjusting again for other devices.
     All content flows from the TOP (padding-top + normal stacking),
     never anchored to the bottom, so on devices where the visible
     area is shorter than this, the small remainder just extends past
     the fold — clipped or scrolled away, never seen — without
     dragging any content out of position (that's what broke the
     chevron in an earlier version of this fix, and why the chevron's
     own scroll target is calculated live from the DOM rather than
     assuming any fixed hero height). The black fallback
     (body:has(.hero), in the base styles) covers anything still left
     over on devices/browsers where even this undershoots. */
  .hero {
    justify-content: flex-start;
    min-height: 100vh;
    min-height: calc(100lvh + 58px);
    padding: var(--space-7) var(--space-2) var(--space-5);
  }

  .hero__subtext {
    margin-top: var(--space-4);
  }

  .hero__subtext--desktop {
    display: none;
  }

  .hero__subtext--mobile {
    display: inline;
  }

  .hero__cta {
    display: none; /* confirmed: mobile hero has no CTA button, only the nav's */
  }

  .hero__chevron {
    display: flex;
    justify-content: center;
    margin-top: var(--space-6); /* fixed gap below the subtext, not pinned to the section's bottom edge — stays put regardless of how tall the hero ends up being */
  }

}


/* ==========================================================================
   MOBILE OVERRIDES — WHO THIS IS FOR
   ========================================================================== */

@media (max-width: 767px) {

  .who-this-is-for {
    padding: var(--space-4) var(--space-2);
    text-align: left;
  }

  .who-this-is-for__headline--desktop {
    display: none;
  }

  .who-this-is-for__headline--mobile {
    display: inline;
  }

  .who-this-is-for__body {
    margin-top: var(--space-4);
  }

}


/* ==========================================================================
   MOBILE OVERRIDES — HOW IT WORKS
   Mobile has a fundamentally different layout here, not just resized:
   each screenshot + its own caption become one carousel "slide" that
   scrolls as a single piece (confirmed against Figma's own frame —
   it's 729px wide inside a 390px viewport, unambiguously meant to
   scroll). js/main.js adds one enhancement on top of the plain
   scroll-snap strip: the caption of whichever slide is centered fades
   in continuously as it approaches center, others fade out to fully
   0% (Andreas: "make the text that is not in focus 0% opacity"). No
   loop — the strip just ends after the third slide, nothing visible
   beyond it (Andreas: "do not loop, just end it with the third
   image"). Without JS, this is a plain scroll-snap carousel with
   every caption at full opacity — a lower-fidelity version of the
   same content, nothing missing.
   (Trello: "horizontal scroll effect screen images")
   ========================================================================== */

@media (max-width: 767px) {

  .how-it-works__bg {
    height: 614px; /* Figma's mobile bg frame height, same source photo as desktop, different crop via object-fit */
  }

  .how-it-works__intro {
    padding: var(--space-4) var(--space-2) 0;
    text-align: left;
  }

  .how-it-works__grid {
    display: flex;
    gap: 42px; /* measured exactly, consistent across the row — not on the spacing scale and not within 2px of a step, deliberate exception */
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    -webkit-overflow-scrolling: touch;
    /* Top gap (intro text → screenshot row) is 24px, confirmed exact
       against Figma (intro block bottom at y=3195, screenshot row top
       at y=3219). (Trello: "how it works on mobile- space") */
    padding-top: var(--space-3);
    /* Andreas clarified after checking on device: the 32px gap is
       measured from the tallest caption's own bottom to where the
       cream background of Why This Exists actually starts — not to
       that section's own text (which has its own separate 32px
       inset before its content, on top of this). (Trello: "how it
       works mobile space") */
    padding-bottom: var(--space-4);
    /* Same 16px inset as the rest of the site's mobile sections — the
       screen images are left-aligned, matching Figma, not centered in
       the viewport. Andreas: "I don't want the screen images to be
       centered, I want them to be left aligned as in the figma
       design." A left-aligned item never runs out of room to its
       right the way a centered one could, which is what was causing
       the caption clipping in the centered version — this isn't a
       workaround, it's the actual design. */
    padding-left: var(--space-2);
    /* For the last slide to rest at the exact same left inset as the
       first two, there has to be enough room after it to actually
       scroll that far — otherwise the browser stops short and it
       settles slightly further right than the others (confirmed:
       Andreas — "the 3rd screen image has not the same position as 1
       and 2"). The needed amount is exactly (viewport width − item
       width − left inset): that's how much space has to exist after
       the last item for scrolling it flush to the left inset to be
       physically possible, regardless of how wide its own caption
       happens to be. Expressed with calc() (not a fixed px value) so
       it stays correct across different phone widths. */
    padding-right: calc(100% - 215px - var(--space-2));
    /* Without this, scroll-snap-align: start scrolls straight past the
       track's own padding-left when it snaps item 1 into place,
       landing its screenshot flush at the very edge of the phone
       screen with no visible inset at all. scroll-padding tells the
       browser to treat that padding as space to preserve during
       snapping, not just decorative content padding. */
    scroll-padding-left: var(--space-2);
    scroll-padding-right: var(--space-2);
  }

  .how-it-works__item {
    flex: 0 0 215px;
    width: 215px;
    scroll-snap-align: start;
    /* Without this, a fast/hard swipe can fling straight past the
       middle slide to the last one in a single gesture — mandatory
       snap only guarantees landing ON a snap point, not stopping at
       every one along the way. This forces one slide per scroll
       gesture. Andreas: "you should not be able to scroll more than
       one image at a time." */
    scroll-snap-stop: always;
  }

  .how-it-works__screenshot {
    /* Explicit width, not just flex-basis — the base rule's width:100%
       and max-width:362px were left in place and could conflict with
       flex-basis in how different browsers resolve flex sizing
       combined with aspect-ratio. Overriding all of them directly
       removes the ambiguity instead of relying on cascade precedence.
       (Trello: "screen images size and composition is not correct") */
    width: 215px;
    max-width: none;
  }

  .how-it-works__caption {
    /* Figma measures 24px here, but the screenshot asset has a drop
       shadow baked into the image itself, which eats into the visible
       gap and makes the real on-screen spacing read as bigger than
       coded. Andreas: settled on 8px after comparing against the
       actual asset (started at 24px, tried 12px, landed here). */
    margin-top: var(--space-1);
    /* Andreas: 12px left inset for the caption (headline + chips)
       specifically, on top of the track's own section-level padding —
       same recurring 12px exception used elsewhere on this site, not
       on the spacing scale and not within 2px of a step. */
    padding-left: 12px;
    /* Andreas: the screenshot and the caption text should share the
       same left edge. align-self overrides the flex item's default
       cross-axis stretch (which was otherwise forcing the caption to
       the screenshot's own 215px width). Figma's actual caption box
       is 344px — wider than the screenshot, which is exactly why it
       needs its own explicit width instead of just fit-content: a
       flex item's fit-content is still bounded by its ancestor's own
       width (215px) regardless of align-self, so without this, chip
       text was wrapping tighter than Figma shows. Genuinely long chip
       text (like Stack's) still wraps naturally within this 344px box
       — Andreas: "let them have two rows so you can see all the text
       without scrolling" — it just no longer wraps too early the way
       a bare 215px box forced it to. */
    align-self: flex-start;
    width: 344px;
  }

}


/* ==========================================================================
   MOBILE OVERRIDES — WHY THIS EXISTS
   Stacked layout: intro, then the full-width photo, then the body
   copy, all on one continuous cream background. The desktop-only
   `.why-this-exists__panel` wrapper becomes display: contents here so
   its children (intro, body) act as flat siblings of the photo and can
   be reordered around it. Text gets its own 16px inset; the photo
   stays full-bleed (no side padding) — same pattern as the How It
   Works background photo breaking out of its section's padding.
   ========================================================================== */

@media (max-width: 767px) {

  .why-this-exists {
    display: flex;
    flex-direction: column;
    padding: var(--space-4) 0;
  }

  .why-this-exists__panel {
    display: contents;
  }

  .why-this-exists__intro {
    order: 1;
    padding: 0 var(--space-2);
  }

  .why-this-exists__photo {
    order: 2;
    flex: none;
    width: 100%;
    height: 233px; /* Figma's exact mobile frame height for this photo */
    margin-top: var(--space-2); /* 16px, headline → photo, confirmed exact against Figma */
  }

  .why-this-exists__body {
    order: 3;
    padding: 0 var(--space-2);
    margin-top: var(--space-2); /* 16px, photo → body, confirmed exact against Figma */
  }

  .why-this-exists__headline {
    /* 16px on mobile, confirmed exact — distinct from desktop's 12px
       exception for the same eyebrow → headline gap, not a scaling
       error, just a different hand-placed value at this breakpoint. */
    margin-top: var(--space-2);
  }

}


/* ==========================================================================
   MOBILE OVERRIDES — TRUST SIGNALS
   Same 3 items, stacked instead of in a row, each full width.
   ========================================================================== */

@media (max-width: 767px) {

  .trust-signals {
    padding: var(--space-4) var(--space-2);
  }

  .trust-signals__row {
    flex-direction: column;
    align-items: center;
    gap: var(--space-4); /* 32px between columns, confirmed exact against Figma */
    max-width: none;
  }

  .trust-signals__item {
    width: 100%;
  }

  .trust-signals__caption {
    /* 8px on mobile, confirmed exact — distinct from desktop's 12px
       recurring exception for the same headline → caption gap. */
    margin-top: var(--space-1);
  }

  .trust-signals__tagline {
    margin-top: var(--space-4); /* 32px, confirmed exact — distinct from desktop's 42px exception */
  }

}


/* ==========================================================================
   MOBILE OVERRIDES — FINAL CTA + FOOTER
   ========================================================================== */

@media (max-width: 767px) {

  .final-cta {
    /* 42px, measured exactly from the section's own top edge to the
       subtitle, and reused again below between the button and the
       footer bar — not on the spacing scale and not within 2px of a
       step, deliberate exception on both counts (and distinct from
       desktop's own 52px/44px equivalents, not a scaling error). */
    padding: 42px var(--space-2) 42px;
  }

  .final-cta__subtitle--desktop {
    display: none;
  }

  .final-cta__subtitle--mobile {
    display: inline;
  }

  .final-cta__button {
    /* 16px, confirmed exact, distinct from desktop's 44px equivalent. */
    margin-top: var(--space-2);
  }

  .btn--lg {
    /* Mobile's footer CTA button is a distinct size from desktop's —
       confirmed via Figma (SemiBold, 52px tall, 40px horizontal
       padding), not a scaled-down version of the desktop button. The
       hero's own CTA is hidden on mobile entirely, so this only
       affects the footer button. */
    height: 52px;
    padding: 0 40px;
    font-size: 16px;
    line-height: 16px;
    font-weight: 600;
  }

  .footer__bar {
    /* 70%, confirmed exact — distinct from desktop's 80%, not a
       mistake. */
    background: rgb(0 0 0 / 70%);
  }

  .footer__inner {
    flex-direction: column;
    align-items: center;
    height: auto;
    padding: var(--space-4) var(--space-2);
  }

  .footer__meta {
    flex-direction: column;
    gap: var(--space-2); /* 16px, confirmed exact, logo → links row */
    margin-top: var(--space-2);
  }

  .footer__links {
    gap: var(--space-1); /* 10px measured, within 2px of --space-1 (8px) — rounded per the spacing rule */
  }

  .footer__copyright {
    margin-top: 12px; /* same recurring 12px exception used elsewhere on this site */
    text-align: center;
  }

  .footer__copyright--desktop {
    display: none;
  }

  .footer__copyright--mobile {
    display: inline;
  }

}


/* ==========================================================================
   MOBILE OVERRIDES — PRIVACY POLICY / SUPPORT PAGES
   ========================================================================== */

@media (max-width: 767px) {

  .page-hero {
    /* 68px mobile nav height + 42px gap — confirmed exact by Andreas
       directly (the earlier 64px guess, carried over from desktop
       since Figma's own mobile mockup nav didn't match this site's
       actual nav, was wrong). */
    padding: 110px var(--space-2) 0;
  }

  .page-card {
    /* 16px left/right margin so the patterned page background shows
       at the edges — the card isn't full-bleed on mobile, confirmed
       by Andreas. */
    margin-left: var(--space-2);
    margin-right: var(--space-2);
    /* 74px total, confirmed exact by Andreas, from the card's own
       bottom edge to the Final CTA subtitle text below it. Final
       CTA's own top padding (42px) already accounts for part of
       that, so only the remainder goes here — keeping the whole gap
       in one place would double it up. */
    margin-bottom: 32px;
    padding: var(--space-3) var(--space-2) var(--space-4); /* 24px top (same as desktop), 16px sides, 32px bottom — all confirmed exact, distinct from desktop's 24px sides / 64px bottom */
  }

}
