/* =============================================================================================
   capso — landing page.

   ⭐ EVERY VALUE HERE COMES FROM THE DESIGN HANDOFF SPEC, not from looking at a screenshot and
   guessing. Hexes, sizes, weights, letter-spacing, radii, shadow recipes and transition durations
   are the intended ones. Where a value looks arbitrary it is quoted from the spec — do not "tidy" it.

   ⚠️ THE PROTOTYPE USED INLINE STYLES because its authoring environment forced that. The spec says
   explicitly not to carry that constraint over, so this is normal CSS.

   ⚠️ SECTIONS ALTERNATE BACKGROUNDS — `--bg` then `--deep`, every other one, each with a 1px top
   border. That banding is what gives the page its rhythm; a page of uniform black reads flat and was
   the single biggest thing missing from the first attempt.

   ⚠️ NO `prefers-color-scheme` QUERY EXISTS IN THIS FILE, deliberately. Dark is the finished design
   and every visitor gets it first. The switch above the hero phone sets `data-theme`; nothing else
   ever changes it.
   ============================================================================================= */

/* ── Tokens ─────────────────────────────────────────────────────────────────────────────────── */
:root {
  /* Evidence types, ordered weakest→strongest as the weight chart needs. `--ink*` is the darkened
     variant used for TEXT in light mode only — tints, borders and glows keep the bright hex. */
  --t0: #9D6CE6; --t1: #0A84FF; --t2: #14B894; --t3: #FF9F43;
  --t4: #FF6FA5; --t5: #5AC8FA; --t6: #FF6F71;
  --ink0: #6B3FBF; --ink1: #0060DF; --ink2: #0A7057; --ink3: #8F5200;
  --ink4: #B82C63; --ink5: #0A6E97; --ink6: #C4353A;

  /* The app's selection purple: ALL primary CTAs and focus rings, and nothing decorative. */
  --purple: #7C3AED;
  --yellow: #FFD479;

  --maxw: 1200px;
  --pad-x: clamp(20px, 5vw, 56px);
}

:root, [data-theme="dark"] {
  --bg: #000000;
  --deep: #0B1014;
  --text: #F5F5F5;   /* deliberately not pure white — it over-contrasts on black */
  --text2: #A6A6A6;
  --surface: rgba(255, 255, 255, 0.045);
  --surface2: rgba(255, 255, 255, 0.075);
  --line: rgba(255, 255, 255, 0.13);
  --hair: rgba(255, 255, 255, 0.2);
  --shade: rgba(0, 0, 0, 0.5);
  --skel: rgba(255, 255, 255, 0.11);
  --on-solid: #0B1014;  /* text on a SOLID evidence chip */
  --g1: #FF6FA5; --g2: #FF9F43; --g3: #FFD479;
  --hl-glow: rgba(255, 255, 255, 0.4);   /* inline highlight bloom */

  /* ▶▶ GLOW STRENGTH — see the block below the light palette. */
  --glow-k: 1.2;      /* alpha multiplier for TRANSLUCENT glows */
  --glow-x: 1px;    /* extra spread for SOLID-color glows (dots, chips, bars) */
  --glow-xx: 3px;   /* extra spread for the big soft panel glows */
}

[data-theme="light"] {
  --bg: #F6F6F7;
  --deep: #FFFFFF;
  --text: #0B1014;
  --text2: #5C6268;
  --surface: rgba(11, 16, 20, 0.035);
  --surface2: rgba(11, 16, 20, 0.06);
  --line: rgba(11, 16, 20, 0.1);
  --hair: rgba(11, 16, 20, 0.14);
  --shade: rgba(11, 16, 20, 0.09);
  --skel: rgba(11, 16, 20, 0.11);
  --on-solid: #FFFFFF;
  /* ⚠️ TRYING THE DARK-MODE GRADIENT IN LIGHT MODE (Lucas, 2026-08-28). The darkened set was
     `--g1: #C0326A; --g2: #A85A00; --g3: #8A6612;` — keep it here to revert in one line.
     The yellow end is the problem: #FFD479 on #F6F6F7 measures about 1.3:1, which is not a
     contrast failure so much as an invisibility. Judge it on screen. */
  --g1: #FF6FA5; --g2: #FF9F43; --g3: #FFD479;
  --hl-glow: rgba(10, 132, 255, 0.55);   /* the "In Capso" blue — see the note on .hl */
}

/* ⚠️ LIGHT MODE ONLY. In dark the highlight bloom is white and stays white — there is no blue to
   swap. Here it follows the card, so the whole section moves to purple together. */
[data-theme="light"][data-level="technical"] {
  --hl-glow: rgba(157, 108, 230, 0.55);

  --glow-k: 1.45;
  --glow-x: 1px;
  --glow-xx: 6px;
}

/* ▶▶ ADJUST EVERY GLOW ON THE PAGE HERE ────────────────────────────────────────────
   ⚠️ A GLOW THAT READS ON BLACK DISAPPEARS ON WHITE, and it is not one problem but two — which is
   why there are two knobs, not a single "brightness":

     · TRANSLUCENT glows (rgba) fade because the color is diluted toward the ground it sits on.
       `--glow-k` multiplies their alpha. Values above 1 are fine; alpha clamps at 1.

     · SOLID-color glows (the type chips, the dots, the weight bars) are already at full alpha, so
       raising it does nothing. What they lose on white is CONTRAST, and the fix is to show MORE of
       the color: `--glow-x` / `--glow-xx` add spread, pushing the bloom out past the element
       instead of hiding it under one. `--glow-xx` is for the big soft panels, which need much more
       before anything shows.

   The app hits the same wall and solves it the same way — `SUBMIT_GLOW` in SuggestSheet.jsx carries
   0.5 dark / 0.65 light, and `favGlowStrength` in theme/tokens.js exists for exactly this reason.

   The join button has its OWN four-state block further down; it is the page's one CTA and wanted
   independent hover values. Everything else is driven from here. */

/* ── Inter, self-hosted ─────────────────────────────────────────────────────────────────────
   ⭐ SELF-HOSTED ON PURPOSE, AND IT IS A PRIVACY DECISION, NOT A PERFORMANCE ONE. Loading these
   from fonts.googleapis.com sends every visitor's IP address to Google before they have agreed to
   anything — on a page whose own privacy line reads "Never tracked, never shared", a few inches
   above the form. It also had to be disclosed in three separate sections of the privacy policy.
   Serving the files ourselves deletes the transfer, so there is nothing to disclose.
   ⚠️ DO NOT PUT THE GOOGLE FONTS <link> BACK. It is the obvious "fix" if a font ever looks wrong,
   and it silently reintroduces the disclosure the policy was written without.

   ⚠️ THESE ARE VARIABLE FONTS. One file covers the whole 400–800 range, which is why five weights
   cost one download instead of five. `font-weight: 400 800` is a RANGE, not a typo — narrowing it
   to a single value makes the browser synthesize the others and they come out wrong. Italic stops
   at 700 because that is what Inter ships; the page never asks for heavier italic.

   ⚠️ `unicode-range` IS WHAT KEEPS THIS CHEAP, so keep the subsets split. Every Portuguese and
   Spanish accent this page uses — ã á à â ç é ê í ó ô õ ú ü ñ ¡ ¿ — is inside `latin` (U+0000-00FF),
   so `latin-ext` is 273 KB that a browser will almost certainly never request. It is disk weight,
   not bandwidth. Merging the two into one file would make it bandwidth.

   Inter is SIL Open Font License 1.1 — self-hosting is expressly permitted. See assets/fonts/OFL.txt. */
@font-face {
  font-family: 'Inter'; font-style: normal; font-weight: 400 800; font-display: swap;
  src: url('assets/fonts/inter-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Inter'; font-style: normal; font-weight: 400 800; font-display: swap;
  src: url('assets/fonts/inter-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Inter'; font-style: italic; font-weight: 400 700; font-display: swap;
  src: url('assets/fonts/inter-latin-it.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Inter'; font-style: italic; font-weight: 400 700; font-display: swap;
  src: url('assets/fonts/inter-latin-ext-it.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* ── Reset ──────────────────────────────────────────────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
  margin: 0; background: var(--bg); color: var(--text);
  font-family: Inter, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  font-weight: 400; -webkit-font-smoothing: antialiased; overflow-x: clip;
}
h1, h2, h3, p, ul, ol { margin: 0; padding: 0; }
ul, ol { list-style: none; }
button, input { font-family: inherit; }
img { display: block; max-width: 100%; }
/* ⚠️ Declared explicitly — without this, any added link renders browser-default blue. */
a { color: #5AC8FA; text-decoration: none; }
a:hover { color: #0A84FF; text-decoration: underline; }

.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
.skip { position: absolute; left: -9999px; top: 8px; z-index: 99; background: var(--deep); color: var(--text); padding: 10px 16px; border-radius: 10px; }
.skip:focus { left: 16px; }
:focus-visible { outline: 2px solid var(--purple); outline-offset: 3px; border-radius: 8px; }

@keyframes capsoIn { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }

/* The status dot breathes — the page's only idle motion above the fold, and the one thing saying
   this project is alive rather than abandoned. ⚠️ The GLOW breathes, not the dot: scaling a 7px
   circle inside a flex row draws the eye to a jitter instead of a pulse. Alpha follows `--glow-k`
   like every other glow, so it survives light mode. */
@keyframes dotBreathe {
  from { box-shadow: 0 0 5px var(--glow-x) rgba(20, 184, 148, calc(0.4 * var(--glow-k))); }
  to   { box-shadow: 0 0 17px calc(2px + var(--glow-x)) rgba(20, 184, 148, calc(0.95 * var(--glow-k))); }
}

/* ── Section shell ──────────────────────────────────────────────────────────────────────────
   Full-bleed, 1px top border, alternating fill. Inner content capped and centered. */
section { border-top: 1px solid var(--line); }
section.alt { background: var(--deep); }
.wrap { max-width: var(--maxw); margin: 0 auto; padding: clamp(56px, 8vw, 100px) var(--pad-x); }
.two-col { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: clamp(40px, 6vw, 76px); align-items: center; }

/* ⭐ THE TWO PHONES SHARE AN EDGE. The hero pins its phone column to a fixed 318px
   (`.hero { grid-template-columns: minmax(0, 1fr) 318px }`), so the phone sits hard against the
   right of the content box. A plain `.two-col` splits into equal halves instead, which left the
   swipe phone centred in a ~530px column — floating well inside the hero phone's line and reading
   as a misalignment down the page.
   ⚠️ THIS ONLY WORKS BECAUSE THE TWO CONTENT BOXES ARE IDENTICAL: `.hero` and `.wrap` both take
   `max-width: var(--maxw)` with `padding: … var(--pad-x)` and the same `gap`, so matching the column
   width is enough — no offsets, and it stays true when either variable changes.
   ⚠️ A MODIFIER, NOT A CHANGE TO `.two-col`. The depth section uses the same class for a SHEET, not
   a phone, and 318px would crush it. Same breakpoint as the hero's, so both fall back to one column
   together. */
@media (min-width: 1000px) {
  .two-col--phone { grid-template-columns: minmax(0, 1fr) 318px; }
}

h2 { font-size: clamp(30px, 3.8vw, 50px); font-weight: 800; letter-spacing: -0.04em; line-height: 1.02; text-wrap: balance; }
h2.narrow { font-size: clamp(30px, 3.6vw, 48px); }
/* ⭐ The one inline highlight on the page: full-strength text plus a soft bloom, so a single phrase
   lifts out of a gray paragraph without becoming a different color.
   ⚠️ A WHITE GLOW IS INVISIBLE IN LIGHT MODE. Light instead borrows the #0A84FF already blooming
   around the "In Capso" card two blocks below — so the emphasis still reads as LIGHT rather than as
   a shadow, and the page gains a link between the sentence that names the audience and the card
   that shows them what they get. A dark bloom was the first attempt and read as smudge. */
.hl { color: var(--text); font-weight: 500; text-shadow: 0 0 18px var(--hl-glow); }

/* ⚠️ THESE TWO OVERRIDE THE GLOW COLOR AND DO NOT FOLLOW THE TOGGLE. Everywhere else on the page a
   highlight glows in whatever the current reading level is; here the words ARE the levels, so each
   one keeps its own color in both themes — blue for Regular, purple for Technical, the same pair the
   "In Capso" card switches between. The text stays --text; only the light changes. */
.hl-regular { --hl-glow: rgba(10, 132, 255, calc(0.6 * var(--glow-k))); }
.hl-technical { --hl-glow: rgba(157, 108, 230, calc(0.6 * var(--glow-k))); }

.body-copy { font-size: 16.5px; line-height: 1.6; color: var(--text2); text-wrap: pretty; }
.uplabel { font-size: 12px; font-weight: 700; letter-spacing: 0.09em; text-transform: uppercase; color: var(--text2); }

/* ── Pill switchers (language + theme) ──────────────────────────────────────────────────────── */
.pillbar { display: inline-flex; align-items: center; gap: 3px; padding: 4px; border-radius: 999px; background: var(--surface); border: 1px solid var(--line); }
.pillbar button {
  font-size: 12.5px; font-weight: 700; letter-spacing: -0.01em; padding: 8px 14px;
  border-radius: 999px; border: none; cursor: pointer; color: var(--text2); background: transparent;
  transition: background 130ms ease, color 130ms ease;
}
/* ⚠️ `:not([aria-pressed="true"])` IS LOAD-BEARING, not tidiness. A bare `.pillbar button:hover`
   has the SAME specificity as the pressed rule, so it would also fire on the ACTIVE tab and pull it
   off #FFFFFF onto --text. Excluding the pressed state keeps hover to the tabs that can actually be
   clicked to somewhere new.
   ⚠️ And the target is var(--text), NOT a literal white — in light mode "brighter" means darker.
   The token already knows which way that goes. */
.pillbar button:not([aria-pressed="true"]):hover { color: var(--text); }
.pillbar button[aria-pressed="true"] { background: var(--purple); color: #FFFFFF; }

/* ── 0 · Header ─────────────────────────────────────────────────────────────────────────────── */
.topbar { position: relative; z-index: 2; max-width: var(--maxw); margin: 0 auto; padding: 26px var(--pad-x); display: flex; align-items: center; justify-content: space-between; gap: 20px; flex-wrap: wrap; }
.wordmark { font-size: 27px; font-weight: 800; letter-spacing: -0.05em; line-height: 1; }
.wordmark.small { font-size: 20px; }

/* ── 1 · Hero ───────────────────────────────────────────────────────────────────────────────
   Two ambient blobs behind the header AND the hero — a purple one high and centered, a pink one
   off the right edge. They are what keep the top of the page from reading as flat black. */
/* ⚠️ `overflow-x: clip` HERE BECAUSE `.blob-2` HANGS 160px PAST THE RIGHT EDGE. The blobs are
   decorative and deliberately oversized, and the pink one is positioned `right: -160px` on a shell
   that spans the full page — so it widens the DOCUMENT by 160px. `body { overflow-x: clip }` is
   meant to catch that, but body overflow only takes effect once it propagates to the viewport, and
   anything measuring layout overflow instead (a full-page screenshot, most obviously) still sees the
   wider box. The symptom is the whole page reading as shifted left with a black band down the right:
   every section background stops at the body's width while the capture is 160px wider.

   ⚠️ `clip`, NOT `hidden`. `hidden` would make this a scroll container, which changes what `position:
   sticky` and scroll anchoring do inside it. `clip` just clips.
   ⚠️ x ONLY. Both blobs are also placed with a negative `top` and are meant to bleed upward. */
.hero-shell { position: relative; border-top: 0; overflow-x: clip; }
.blob { position: absolute; pointer-events: none; filter: blur(20px); z-index: 0; }
.blob-1 { top: -240px; left: 50%; transform: translateX(-40%); width: 1100px; height: 700px; background: radial-gradient(closest-side, rgba(124, 58, 237, 0.3), transparent 70%); }
.blob-2 { top: -140px; right: -160px; width: 720px; height: 620px; background: radial-gradient(closest-side, rgba(255, 111, 165, 0.2), transparent 70%); }

.hero {
  position: relative; z-index: 1; max-width: var(--maxw); margin: 0 auto;
  padding: clamp(16px, 3vw, 40px) var(--pad-x) clamp(60px, 8vw, 110px);
  display: grid; grid-template-columns: 1fr;
  gap: clamp(40px, 6vw, 76px); align-items: center;
}
/* ⚠️ THE PHONE COLUMN IS PINNED TO EXACTLY THE 318px THE PHONE NEEDS, not an equal half it cannot
   use — an even split handed it ~190px of dead space while the headline was squeezed. Two columns
   only above 1000px; below that the headline would have to shrink to share the row, so the phone
   goes underneath and the copy takes the full width. */
@media (min-width: 1000px) {
  .hero { grid-template-columns: minmax(0, 1fr) 318px; }
}

.status-badge {
  display: inline-flex; align-items: center; gap: 9px;
  font-size: 12.5px; font-weight: 600; letter-spacing: 0.02em;
  background: var(--surface); border: 1px solid var(--line); border-radius: 999px;
  padding: 7px 14px 7px 11px;
}
/* ▶▶ 2.6s is a slow breath — fast enough to notice, slow enough not to nag while someone reads.
   `alternate` means the two keyframes are the ends of one breath, so it eases both ways. */
.status-badge .dot {
  width: 7px; height: 7px; border-radius: 50%; background: #14B894; flex: 0 0 auto;
  animation: dotBreathe 2.6s ease-in-out infinite alternate;
}

/* ⭐ TWO LINES, AT THE SPEC'S SIZE. `clamp(42px, 5.8vw, 76px)` with `text-wrap: balance` is exactly
   what the prototype uses — the headline was never the problem. THE COLUMN WAS.

   ⚠️ Measured against the shipped Inter ExtraBold at this tracking, the longest of the six strings
   is the ENGLISH one, and it is what caps the size for every language:

       en  "You, up-to-date"   7.327 em   ← the binding constraint
       pt  "em 5 segundos."    7.235 em
       es  "en 5 segundos."    6.943 em
       en  "in 5 seconds."     5.992 em
       pt  "Você, em dia"      5.872 em
       es  "Tú, al día"        4.033 em

   At the full 76px that needs 557px. Two EQUAL hero columns give only 506px — so it wrapped. The
   fix is the column, not the type: see the phone-column rule below.

   The `13.1cqw` term is a CEILING (100 / 7.327 em, less headroom for the variable Google Fonts
   build), and it exists only for narrow phones — below about a 330px viewport even 42px cannot fit.
   ⚠️ cqw, NOT vw: it has to track the COLUMN, not the window. Above ~330px it never binds and the
   spec's value wins everywhere. */
.hero-copy { container-type: inline-size; }
h1 {
  margin-top: 24px; font-size: clamp(30px, min(13.1cqw, max(5.8vw, 42px)), 76px);
  font-weight: 800; letter-spacing: -0.045em; line-height: 0.94; text-wrap: balance;
}
/* ⚠️ THE PADDING/MARGIN PAIR IS A FIX, NOT SPACING — do not "clean it up".
   `letter-spacing: -0.045em` is applied after EVERY character, the last one included, so the inline
   box ends 0.045em inside where the final glyph actually draws. `background-clip: text` can only
   paint within that box, so the overhanging sliver gets no gradient and the period looks cropped.
   The padding grows the background box; the equal negative margin takes the space back, so nothing
   moves. Any headline ending in a narrow glyph (a period, a comma) hits this. */
.grad {
  background: linear-gradient(100deg, var(--g1), var(--g2) 45%, var(--g3));
  -webkit-background-clip: text; background-clip: text; color: transparent;
  padding-right: 0.08em; margin-right: -0.08em;
}
/* ⚠️ `drop-shadow`, NOT `text-shadow`, and the difference is not cosmetic here. On a
   `background-clip: text` element the background is painted BEFORE the text shadow, so a
   `text-shadow` lands ON TOP of the gradient and mutes the very colors it is meant to lift.
   `filter: drop-shadow` works on the finished, already-clipped render and puts the shadow behind it.

   It exists to hold the yellow end of the gradient, which measures about 1.3:1 against the light page
   and would otherwise fade out right where "5 seconds" sits.

   ⭐ THE OTHER HALF GETS THE SAME GLOW IN THE OTHER MODE. `.solid` is "You, up-to-date" — flat white
   on dark — and it needs holding for a different reason: the headline does not sit on the page color
   at all. `.blob-1` and `.blob-2` bloom purple and pink directly behind it, and white type over the
   bright part of a blob loses its edge.
   ⚠️ `drop-shadow` on `.solid` too, even though that half has no `background-clip` to fight. Matching
   the declaration exactly is the point — `text-shadow` blurs on a slightly different curve, and the
   two halves of one headline must not be lifted differently.

   ▶▶ ADJUST THE HEADLINE GLOW HERE — ONE KNOB PER MODE, and they are independent ───────
      Each value is a whole `drop-shadow()` argument: OFFSET-X OFFSET-Y BLUR COLOR
        0 0                 centered, so it reads as a glow rather than a cast shadow
        2px                 the bloom. 1px is a hairline edge; 4px starts to look like fog
        rgba(0,0,0,0.52)    raise the alpha to bite harder, lower it to soften

      ⚠️ DELIBERATELY TWO TOKENS, NOT ONE. They hold the same value today and they are NOT the same
      knob: the light one holds the yellow end of a gradient against a near-white page, the dark one
      holds white type against the purple and pink blobs behind it. Those are different jobs on
      different grounds, so tuning one must never move the other.
      ⚠️ LIGHT IS SETTLED — leave `--h1-glow-light` alone unless the light headline itself is what is
      being judged. */
:root {
  --h1-glow-light: 0 0 2px rgba(0, 0, 0, 0.52);  /* `.grad` on white — settled, do not tune */
  --h1-glow-dark:  0 0 4px rgba(0, 0, 0, 0.82);  /* `.solid` on the blobs — the one to tune */
}
[data-theme="light"] .grad { filter: drop-shadow(var(--h1-glow-light)); }
[data-theme="dark"] .solid { filter: drop-shadow(var(--h1-glow-dark)); }
/* This line exists to explain the product's name — it stays at full weight and full text color. */
.tagline { margin-top: 20px; max-width: 24em; font-size: clamp(19px, 1.8vw, 23px); font-weight: 600; letter-spacing: -0.022em; line-height: 1.3; color: var(--text); text-wrap: balance; }
.hero-sub { margin-top: 14px; max-width: 33em; font-size: clamp(16px, 1.4vw, 18px); font-weight: 400; line-height: 1.55; color: var(--text2); text-wrap: pretty; }

/* ── The form ───────────────────────────────────────────────────────────────────────────────── */
.signup { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 34px; max-width: 540px; }
.signup input {
  flex: 1 1 230px; min-width: 0; font-size: 16px; font-weight: 500; color: var(--text);
  background: var(--surface); border: 1px solid var(--hair); border-radius: 16px;
  padding: 17px 18px; outline: none; transition: border-color 130ms ease, box-shadow 130ms ease;
}
.signup input::placeholder { color: var(--text2); opacity: 0.85; }
/* ⚠️ The focus ring is the JOIN BUTTON'S PINK, not the site purple — the field and the button are
   one control, and a purple ring beside a sunset-stroked button reads as a stray system accent.
   #FF6FA5 is the first stop of that gradient. Alpha follows `--glow-k` like every other glow. */
.signup input:focus { border-color: #FF6FA5; box-shadow: 0 0 0 4px rgba(255, 111, 165, calc(0.25 * var(--glow-k))); }
/* ⭐ THE JOIN BUTTON IS THE APP'S "HOLD TO SUBMIT" TREATMENT, ported from components/SuggestSheet.jsx
   — navy fill, a sunset gradient stroke, and a sunset bloom behind it. It is the one thing on the
   page we actually want a visitor to do, and the site purple is spent on switch tabs and segmented
   controls; sharing it made the CTA look like another toggle.

   ⚠️ THE NAVY IS THE SAME IN BOTH THEMES. `submitBg` is #0D0D17 in the app's light palette too —
   the button is meant to read as its own object, not as a themed surface.

   ⚠️ THE STROKE IS A GRADIENT, SO IT CANNOT BE A `border-color`. Two backgrounds do it: the navy
   clipped to padding-box, the sunset to border-box, with a transparent 2px border between them.

   ⚠️ AND THE GLOW IS A GRADIENT TOO — the app bakes it to a PNG (scripts/bake-submit-glow.js)
   because a gradient bloom is not a shadow. Here it is THREE offset colored shadows, pink on the
   left through yellow on the right, which reads as one sunset bloom without needing a stacking
   context (a blurred pseudo-element at z-index:-1 disappears behind any ancestor that paints a
   background). */
.signup button {
  flex: 0 0 auto; font-size: 16px; font-weight: 700; letter-spacing: -0.01em; color: #F5F5F5;
  border: 2px solid transparent; border-radius: 16px; padding: 15px 28px; cursor: pointer;
  background:
    linear-gradient(#0D0D17, #0D0D17) padding-box,
    linear-gradient(to top right, #FF6FA5, #FF9F43 50%, #FFD479) border-box;
  /* Declared ONCE — every knob below is a variable, so the four states never drift apart. */
  box-shadow:
    calc(-1 * var(--glow-off)) 0 var(--glow-blur) var(--glow-tighten) rgba(255, 111, 165, var(--glow-a)),
    0 0 var(--glow-blur) var(--glow-tighten) rgba(255, 159, 67, var(--glow-a)),
    var(--glow-off) 0 var(--glow-blur) var(--glow-tighten) rgba(255, 212, 121, var(--glow-a));
  transition: transform 130ms ease, box-shadow 180ms ease;
}
.signup button:active { transform: translateY(1px); }

/* ▶▶ ADJUST THE JOIN BUTTON'S GLOW HERE ──────────────────────────────────────────────
   Four states, each with its own alpha, and they are the only things you should need to touch.
   Mirrors `SUBMIT_GLOW` in components/SuggestSheet.jsx, which likewise carries per-mode values
   (dark 0.5 / light 0.65) because the bloom reads FAINTER on a light ground and needs more.

     --glow-a        overall strength, 0..1
     --glow-blur     softness — how far the bloom feathers out
     --glow-off      how far the pink and yellow sit apart; bigger = wider, more obviously a sunset
     --glow-tighten  negative spread, pulls the bloom back toward the button

   ⚠️ ORDER IN THIS BLOCK IS LOAD-BEARING. `[data-theme="light"] .signup button` and
   `.signup button:hover` have EQUAL specificity, so whichever comes last wins. Light-base must sit
   after dark-hover, and light-hover last of all. Getting this wrong is what made light mode have no
   hover at all — the light rule was silently overwriting it. */
.signup button                            { --glow-a: 0.45;  --glow-blur: 26px; --glow-off: 11px; --glow-tighten: -8px; }
.signup button:hover                      { --glow-a: 0.55;  --glow-blur: 32px; }
[data-theme="light"] .signup button       { --glow-a: 0.75; --glow-blur: 26px; }
[data-theme="light"] .signup button:hover { --glow-a: 0.99; --glow-blur: 34px; }

/* ── THE SHARE BUTTONS ──────────────────────────────────────────────────────────────────────
   Three of them, one glyph (`#ic-share`, traced from the app). They differ only in how they are
   colored, and each `<use>` inherits its stroke from here — see the note in index.html on why the
   symbol's paths carry no `stroke` attribute of their own.

   ⭐ THE SQUARE IS THE JOIN BUTTON INVERTED. The CTA is a navy fill inside a sunset stroke; this is
   the sunset as the FILL with the navy as the mark. Same radius, same bloom.
   ⚠️ IT INHERITS THE GLOW BY BEING `.signup button`. Every `--glow-*` knob above already applies to
   it, including the two light-mode overrides, so the pair can never drift out of sync — which is
   exactly what a second hand-copied box-shadow would eventually do. Only what DIFFERS is set below. */
/* ── The referral field ────────────────────────────────────────────────────────────────────────
   ⚠️ EVERY RULE IS `.signup .referral-*`, NEVER `.referral-*` ALONE. `.signup button` sets the
   gradient pill on every button inside the form, and a bare `.referral-toggle` loses to it on
   specificity — which is exactly how `.share-square` once came out navy and the wrong size. */
/* The referral ask on the confirmed card. Quieter than the body it follows — it is an invitation,
   not the news, and the news is that they are on the list. */
.form-status .st-ask { margin: 10px 0 0; font-size: 13.5px; line-height: 1.5; color: var(--text2); }
/* Closes the card. Same voice as the line that opens the ask, so they read as one passage. */
.form-status .st-thanks { margin: 12px 0 0; font-size: 14px; line-height: 1.5; color: var(--text2); }

.signup .referral { flex: 1 0 100%; margin-top: -2px; }

.signup .referral-toggle {
  background: none; border: 0; border-radius: 0; padding: 0; box-shadow: none;
  font-size: 13px; font-weight: 500; letter-spacing: 0; color: var(--text2);
  text-decoration: underline; text-underline-offset: 3px; cursor: pointer;
  transition: color 130ms ease;
}
.signup .referral-toggle:hover { color: var(--text); }
.signup .referral-toggle:active { transform: none; }

/* Collapsed by default; the toggle swaps itself out for the field. */
.signup .referral-field { display: none; }
.signup .referral[data-open] .referral-field { display: block; }
.signup .referral[data-open] .referral-toggle { display: none; }

/* ⭐ PURPLE, NOT THE PINK THE EMAIL FIELD USES. Both are on screen together and they are not the
   same kind of thing — one is required and submits the form, the other is optional. Identical
   fields stacked on each other read as one control split in two.
   ⚠️ This does NOT contradict the note above about the join field's pink ring: that reasoning is
   about the field the CTA belongs to, and this is the other one. #7A46E1 is the site purple. */
/* ⭐ THE MAX-WIDTH LIVES ON THE FIELD, NOT THE INPUT, so the hint underneath shares the box's
   edges and can be centred with it as one unit in the closing section. */
.signup .referral-field { max-width: 340px; }
.signup .referral-input-wrap { position: relative; }
.signup .referral-input {
  width: 100%; font-size: 15px; padding: 12px 16px;
  border-color: #7A46E1;
}

/* ⭐ A SECOND "SEND IT", INSIDE THE BOX. The one on the status card is a long way from the field
   the reader has just been sent to fill in — by the time the cursor is in the input, the card can
   be most of a screen away. Same action, arm's reach.
   ⚠️ `.signup .referral-send`, never `.referral-send` alone: `.signup button` paints the gradient
   pill on every button in the form and wins on specificity otherwise. */
.signup .referral-send {
  display: none; position: absolute; right: 14px; top: 50%; transform: translateY(-50%);
  background: none; border: 0; border-radius: 0; padding: 0; box-shadow: none;
  font-size: 13px; font-weight: 600; letter-spacing: 0; color: #7A46E1;
  text-decoration: underline; text-underline-offset: 3px; cursor: pointer;
}
/* ⚠️ `.signup button:active` nudges every button down 1px, which would fight the centring
   transform and make this jump. It keeps its own. */
.signup .referral-send:active { transform: translateY(-50%); }
.signup .referral[data-send] .referral-send { display: block; }
.signup .referral[data-send] .referral-input { padding-right: 76px; }
.signup .referral-input:focus {
  border-color: #7A46E1;
  box-shadow: 0 0 0 4px rgba(122, 70, 225, calc(0.25 * var(--glow-k)));
}
.signup .referral-hint { margin: 7px 0 0; font-size: 12px; line-height: 1.45; color: var(--text2); }

.signup-actions { display: flex; align-items: center; gap: 10px; flex: 0 0 auto; }

/* ⭐ THE SQUARE IS THE JOIN BUTTON, SQUARE. It is deliberately NOT restyled — no background, no
   border, no radius, no box-shadow here — so `.signup button` above supplies the navy fill, the
   sunset stroke, the radius and all four glow states, and the two can never diverge. Everything in
   this rule is geometry.
   A sunset FILL was tried and it shouted: at 56×56 beside the CTA it read as the primary action and
   the actual primary action read as secondary. The pair works because they are the same object.

   🚨 `.signup .share-square`, NOT `.share-square`. Anything that does need to override
   `.signup button` — which is (0,1,1) — must use this two-class form; a lone class is (0,1,0) and
   silently loses. That already cost one round: `padding: 0` lost to `15px 28px`, so the square came
   out oversized, and a `background` override lost too. */
.signup .share-square {
  /* 56×56 matches the CTA's rendered height: 2px border + 15px padding + ~22px line box, twice over
     (box-sizing is border-box, so the border is inside this number).
     ⚠️ If the CTA's font-size or padding changes, this goes stale silently — the buttons simply stop
     lining up. It is stated rather than derived because `aspect-ratio` on a stretched flex item
     resolved against the wrong axis. */
  width: 56px; height: 56px; padding: 0; flex: 0 0 auto;
  display: grid; place-items: center;
}
/* `currentColor` — the mark is the CTA's own label color, inherited, not a second copy of #F5F5F5. */
.signup .share-square svg { width: 18px; height: 20px; fill: currentColor; }

/* The topbar mark: the glyph alone, no chrome, sitting with the wordmark rather than beside the
   language pills — so the two must travel together as one flex item. */
/* ⚠️ 20px, not the 10px it started at. At the tighter gap the glyph read as part of the wordmark —
   a piece of the logo rather than a control sitting next to it. The gap is what separates them. */
.brand { display: flex; align-items: center; gap: 20px; }
.share-bare {
  display: grid; place-items: center; padding: 4px; border: 0; background: none; cursor: pointer;
  color: var(--text2); transition: color 130ms ease, transform 130ms ease;
}
/* ⚠️ `--text2` IS the brief: light grey on dark, dark grey on light. It is already defined per mode,
   so a literal pair of hexes here would be a second source of truth for the same intent. */
/* ⚠️ 15 × 16 IS THIS GLYPH'S NATIVE SIZE — it uses `#ic-share-sm`, a separate drawing, not the
   18×20 scaled down. Scaling that one to 16 high cropped it. Do not "simplify" the two symbols
   into one: at 27px beside the wordmark the large drawing sits too heavy and reads as a second
   logo mark, which is the whole reason a small cut exists. */
.share-bare svg { width: 15px; height: 16px; fill: currentColor; }
.share-bare:hover { color: var(--text); }
.share-bare:active { transform: translateY(1px); }
/* ── end of the glow knobs ──────────────────────────────────────────────────────── */

/* ⚠️ `pre-line` IS WHAT MAKES THE LINE BREAK IN i18n.js WORK. The copy carries a real newline
   between the two sentences, and `data-i18n` writes it with textContent — where HTML would
   otherwise collapse it to a space. Everything else still wraps normally.
   ⚠️ SO KEEP THE SPAN AND THE LINK ON ONE SOURCE LINE. Under `pre-line` a newline in the
   MARKUP renders too, and putting the anchor on its own line would push "Privacy Policy" onto
   a third line by accident. */
/* ⚠️ 44em IS NOT ARBITRARY — it is `.hero-sub`'s width expressed in THIS font size. That rule is
   `max-width: 33em` at `clamp(16px, 1.4vw, 18px)`, so on desktop it renders 33 x 18 = 594px wide.
   This text is 13.5px, and 594 / 13.5 = 44em. Without a cap the fine print had no max-width at
   all and ran the full column, overhanging the paragraph above it by a visibly ragged margin —
   worst in Portuguese and Spanish, whose sentences are the longest.
   ⚠️ `em` on two different font sizes is why the numbers differ. If `.hero-sub` ever changes its
   max-width or its clamp, recompute: (its em x its px) / 13.5. */
.privacy { margin-top: 12px; font-size: 13.5px; line-height: 1.5; color: var(--text2); white-space: pre-line; max-width: 44em; }
/* Underlined, unlike the footer links: this one sits inside a sentence rather than in a list of
   controls, so colour alone would not read as a link at 13.5px. */
.privacy-link { color: var(--text2); text-decoration: underline; text-underline-offset: 2px; }
.privacy-link:hover { color: #5AC8FA; }

.form-status {
  margin-top: 16px; padding: 16px 18px; border-radius: 18px; background: var(--surface);
  animation: capsoIn 240ms ease both; max-width: 540px;
}
/* ⚠️ PENDING IS ORANGE, AND ONLY CONFIRMED IS GREEN. The "we sent you a link" card used to be
   green, which said "done" about a step the reader still has to take — the one card on the page
   whose whole job is to send someone somewhere else. Orange is the sunset's own #FF9F43, so it
   reads as attention rather than as a warning. */
.form-status[data-state="pending"] { border: 1px solid rgba(255, 159, 67, 0.55); box-shadow: 0 0 30px -12px rgba(255, 159, 67, calc(0.55 * var(--glow-k))); }
.form-status[data-state="confirmed"] { border: 1px solid rgba(20, 184, 148, 0.55); box-shadow: 0 0 30px -12px rgba(20, 184, 148, calc(0.55 * var(--glow-k))); }

/* ⭐ THE HIGHLIGHT GLOWS IN THE CARD'S OWN COLOR, and only in light mode. `.hl` elsewhere on the
   page follows the reading level; inside a status card the words being lit belong to the card, so
   the glow is keyed to `data-state` instead.
   ⚠️ WHITE IN DARK MODE, COLORED IN LIGHT. On the dark ground a white bloom is what separates the
   lit words from the body text around them; a colored one there just muddies against a border that
   is already that color. On the near-white ground the reverse is true — white glows on white are
   invisible, so the card's own hue does the work. Same reasoning as `--h1-glow-light`. */
.form-status .hl { --hl-glow: rgba(255, 255, 255, calc(0.55 * var(--glow-k))); }
[data-theme="light"] .form-status[data-state="pending"] .hl { --hl-glow: rgba(255, 159, 67, calc(0.85 * var(--glow-k))); }
[data-theme="light"] .form-status[data-state="confirmed"] .hl { --hl-glow: rgba(20, 184, 148, calc(0.85 * var(--glow-k))); }

/* The spam hint and its resend. Quieter than the body — it is for the minority whose email did not
   arrive, and it must not compete with the instruction above it. */
.form-status .st-hint { margin-top: 10px; font-size: 13px; line-height: 1.5; color: var(--text2); }
.st-resend {
  border: 0; background: none; padding: 0; cursor: pointer; font: inherit;
  color: var(--text2); text-decoration: underline; text-underline-offset: 2px;
  transition: color 130ms ease;
}
.st-resend:hover { color: #FF9F43; }
.st-resent { color: var(--text); font-weight: 600; }
.form-status[data-state="error"] { border: 1px solid rgba(255, 111, 113, 0.55); box-shadow: 0 0 30px -12px rgba(255, 111, 113, calc(0.55 * var(--glow-k))); }
.form-status .st-title { font-size: 15px; font-weight: 700; }
.form-status .st-body { margin-top: 4px; font-size: 14px; line-height: 1.5; color: var(--text2); }
/* The share ask is its own beat — a blank line after the confirmation, not a third sentence glued
   to it. Same muted color as the body: it is a request, and it should not shout over the good news. */
.form-status .st-share { margin-top: 12px; font-size: 14px; line-height: 1.5; color: var(--text2); }
/* The success card's button keeps its grey shape — it is a quiet ask sitting under a confirmation,
   not a second CTA competing with the one the reader just pressed. Only the LABEL is lit. */
.share-btn {
  /* 16px — the CTA's size. It reads as a real button rather than a footnote under one. */
  margin-top: 12px; font-size: 16px; font-weight: 700;
  background: var(--surface2); border: 1px solid var(--hair); border-radius: 12px;
  padding: 10px 15px; cursor: pointer; transition: border-color 130ms ease;
  display: inline-flex; align-items: center; gap: 8px;
}
/* ⚠️ THE GRADIENT GOES ON THE LABEL SPAN, NOT THE BUTTON. `background-clip: text` paints the
   element's own background into its glyphs — put it on the button and the grey fill is what gets
   clipped away, so the shape vanishes and the label is what remains. The span carries the gradient;
   the button keeps its surface. Same technique as `.grad` on the h1, same three stops. */
.share-btn .share-label {
  background: linear-gradient(100deg, var(--g1), var(--g2) 45%, var(--g3));
  -webkit-background-clip: text; background-clip: text; color: transparent;
  padding-right: 0.08em; margin-right: -0.08em; /* see `.grad` — the clipped-glyph fix */
}
.share-btn svg { width: 18px; height: 20px; fill: url(#sunsetTail); }
/* ⚠️ The hover was the app green, which belongs to Trial capsules and has nothing to do with this
   button. Dark mode gets the sunset's last stop — the yellow the label ends on.
   ⚠️ LIGHT MODE TAKES THE ORANGE INSTEAD, and for the same reason the glow below exists: #FFD479 on
   a near-white surface is barely a state change at all. `--g2` is the gradient's middle stop, so the
   hover still comes out of the sunset — it is a different stop, not a different palette. */
.share-btn:hover { border-color: #FFD479; }
[data-theme="light"] .share-btn:hover { border-color: var(--g2); }

/* ⭐ LIGHT MODE ONLY, and it is the SAME problem the headline has: the sunset's yellow end measures
   about 1.3:1 against a near-white surface, so the last letters and the glyph dissolve into the
   button. A tight black drop-shadow holds them.

   ▶▶ ADJUST THE SHARE BUTTON'S GLOW HERE — TWO STACKED LAYERS ────────────────────────
      Each token is one whole `drop-shadow()` argument: OFFSET-X OFFSET-Y BLUR COLOR
        0 0                 centered, so it reads as a glow rather than a cast shadow
        1px                 the bloom. 1px is a hairline edge; 4px starts to look like fog
        rgba(0,0,0,.5)      raise the alpha to bite harder, lower it to soften

      ⭐ WHY TWO LAYERS. Chained filters each run on the PREVIOUS one's output, so layer 2 shadows
      the already-shadowed glyph — layer 1's shadow included. That COMPOUNDS rather than adds, and
      it is the only way past the ceiling one shadow hits: at alpha 1 a single `drop-shadow` is as
      dark as a single `drop-shadow` gets, and on a near-white surface that was still not enough.
      Two layers at a moderate alpha also read better than one at maximum — density builds while the
      edge stays soft, instead of going hard and outlined.
        · darker    → raise both alphas together, in steps of ~0.1
        · tighter   → leave layer 1 at 1px and give layer 2 the same; widen only if it looks thin
        · layer off → set it to `0 0 0 transparent`; do not delete the token or the filter breaks

      ⚠️ ITS OWN TOKEN PAIR, NOT `--h1-glow-light`. The headline is 76px of display type on the page
      background; this is 16px of UI type on a raised surface. Same problem, different sizes and
      different grounds — so they are separate knobs, and editing these must never move
      "in 5 seconds". Same reasoning as the two headline tokens above.
      ⚠️ There is deliberately NO dark counterpart. On the dark surface the sunset already clears
      contrast comfortably; a black glow there would do nothing but muddy the gradient.

      🚨 IF TWO LAYERS STILL IS NOT ENOUGH, STOP STACKING AND FIX THE COLOR. Past this point the
      shadows are compensating for a gradient whose yellow end is simply too light for this ground,
      and a third layer buys legibility by making the type look outlined. Line 71 already holds a
      prepared darker light-mode ramp — `--g1: #C0326A; --g2: #A85A00; --g3: #8A6612;` — which fixes
      it at the source in one edit. That is the right next move, not a third shadow. */
:root {
  --share-glow-light:  0 0 9px rgba(0, 0, 0, .4);
  --share-glow-light2: 0 0 2px rgba(0, 0, 0, .75);
}

/* ⚠️ `drop-shadow`, NOT `text-shadow`. On a `background-clip: text` element the background paints
   BEFORE the text shadow, so a text-shadow lands on top of the gradient and mutes the very colors
   it is there to lift. Same reason `.grad` uses a filter.
   ⚠️ The filter goes on the label and the glyph SEPARATELY, never on the button — on the button it
   would shadow the surface and the 1px border too, drawing a halo around the whole shape. */
[data-theme="light"] .share-btn .share-label,
[data-theme="light"] .share-btn svg {
  filter: drop-shadow(var(--share-glow-light)) drop-shadow(var(--share-glow-light2));
}

/* ── The phone ──────────────────────────────────────────────────────────────────────────────── */
.hero-device, .split-device { display: flex; flex-direction: column; align-items: center; gap: 18px; }
.phone-wrap { position: relative; width: 318px; max-width: 100%; }
.deck-glow {
  position: absolute; inset: 6% -18% -6% -18%; pointer-events: none;
  background: radial-gradient(closest-side, rgba(10, 132, 255, calc(0.35 * var(--glow-k))), transparent 72%);
  filter: blur(24px); transition: background 700ms ease;
}
.phone { position: relative; border-radius: 46px; background: #0A0A0A; padding: 9px; border: 1px solid var(--hair); box-shadow: 0 40px 80px -30px var(--shade); }
/* ⚠️ 9/17.5 CROPS THE IMAGE BOTTOM ON PURPOSE — the cut-off next row of tiles is what says
   "there is more". Do not "fix" it by fitting the whole screenshot. */
.phone-screen { position: relative; border-radius: 38px; overflow: hidden; aspect-ratio: 9 / 17.5; background: #000; }

/* ⭐ THE DYNAMIC ISLAND. A screenshot never contains it — iOS captures the framebuffer, and the
   cutout is physical — so the top of the screen reads as a flat strip with a stray clock and battery
   floating in it. Drawing the pill back in is what makes the frame read as a phone.

   Sized from the real thing and scaled to this screen: the island is ~125×37pt sitting ~12pt down,
   and the screen is 300px standing in for 393pt (×0.763). It lands between the clock and the battery
   in the screenshot, which is exactly where the hardware sits.

   ⚠️ Always pure black, in both themes. It is a hole in the display, not a surface — when the
   light-mode screenshots arrive it should stay black against the white status bar, not invert. */
.phone-screen::before {
  content: ""; position: absolute; z-index: 5;
  top: 9px; left: 50%; transform: translateX(-50%);
  width: 95px; height: 28px; border-radius: 999px;
  background: #000;
}
.phone-screen > img { width: 100%; display: block; }

/* ── The hero phone: a photograph of a device, not a CSS one ────────────────────────────────────
   The shots come out of the app already inside an iPhone 14 Pro frame, so none of `.phone` or
   `.phone-screen` applies — no bezel, no rounded screen mask, and no drawn-in Dynamic Island,
   because the frame in the PNG has a real one.

   ⭐ SIZED SO THE DEVICE MATCHES, NOT THE FILE. The CSS phone it replaces stood 599.44px tall (a
   298px screen at 9/17.5, plus 9px padding and a 1px border each side), and the new shot has to land
   on that exactly or the section's vertical rhythm moves.

   ⚠️ THE PNG IS NOT ALL PHONE. The canvas is 664×1348 but the DEVICE inside it is 643×1313 at offset
   (9, 18) — measured, not guessed; alpha goes 8→250 within one pixel, so that margin is hard canvas
   padding rather than a soft shadow. Setting the FILE to 599.44 tall therefore draws a phone 583.87
   tall, ~16px short of the one it replaces and visibly smaller beside it. So the canvas is scaled to
   615.52 (= 599.44 × 1348/1313), which puts the device at 599.44 and the file's own width at 303.19.

   The width goes on the WRAP because the pins are positioned as percentages of it. `max-width: 100%`
   on `.phone-wrap` still governs the narrow-screen case.

   ⚠️ `drop-shadow`, NOT `box-shadow`. The PNG is RGBA with transparent corners outside the device's
   rounded frame; a box-shadow traces the image's rectangular BOX and leaves dark wedges sticking out
   at every corner. drop-shadow follows the alpha. It takes no spread, so the CSS phone's `-30px` is
   folded into a shorter offset and a tighter blur. */
.phone-wrap--shot { width: 303.19px; }
.phone-shot {
  display: block;
  width: 100%;
  height: auto;
  filter: drop-shadow(0 28px 44px var(--shade));
}

.shot-caption { max-width: 300px; text-align: center; font-size: 13px; line-height: 1.55; color: var(--text2); }

/* ── 2 · Papers are public ──────────────────────────────────────────────────────────────────── */
.head-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 16px clamp(32px, 5vw, 72px); align-items: end; }
.head-grid .body-copy { max-width: 34em; }
.compare { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: clamp(18px, 2.5vw, 28px); margin-top: clamp(36px, 5vw, 56px); }
.compare-col { border: 1px solid var(--line); border-radius: 26px; background: var(--surface); padding: 24px 24px 26px; display: flex; flex-direction: column; gap: 18px; }
.compare-body { font-size: 14.5px; line-height: 1.55; color: var(--text2); text-wrap: pretty; }

/* Deliberately abstract — no fabricated paper titles, just the shape of an undifferentiated wall. */
.skel-rows { display: flex; flex-direction: column; gap: 12px; }
.skel-row { display: flex; align-items: center; gap: 12px; }
.skel-row:nth-child(4) { opacity: 0.5; }
.skel-sq { flex: 0 0 auto; width: 30px; height: 30px; border-radius: 8px; background: var(--skel); }
.skel-bar { flex: 1 1 auto; height: 9px; border-radius: 999px; background: var(--skel); }
.skel-row:nth-child(2) .skel-bar { max-width: 78%; }
.skel-row:nth-child(3) .skel-bar { max-width: 90%; }
.skel-row:nth-child(4) .skel-bar { max-width: 62%; }

/* ⭐ THE CARD TAKES THE COLOR OF THE READING LEVEL. Regular is the review blue; Technical switches
   the whole card — border, bloom and label — to the meta-analysis purple. The toggle stops being a
   text swap and becomes a change of mode you can see from across the room.

   ⚠️ #9D6CE6, NOT the site purple #7C3AED. That one is reserved for things you can act on (the CTA,
   focus rings); spending it here would make a display card look clickable.

   One rgb triplet drives all three, so they cannot drift apart. `--us-text` is the label on dark,
   `--us-ink` the darkened variant it needs on white. */
.compare-col.us { --us-rgb: 10, 132, 255; --us-text: #5AC8FA; --us-ink: #0060DF; }
[data-level="technical"] .compare-col.us { --us-rgb: 157, 108, 230; --us-text: #CBB0F5; --us-ink: #7B2BA8; }

.compare-col.us {
  border-color: rgba(var(--us-rgb), 0.4);
  box-shadow: 0 0 56px calc(-22px + var(--glow-xx)) rgba(var(--us-rgb), calc(0.75 * var(--glow-k)));
  transition: border-color 220ms ease, box-shadow 220ms ease;
}
.compare-col.us .uplabel { color: var(--us-text); transition: color 220ms ease; }
[data-theme="light"] .compare-col.us .uplabel { color: var(--us-ink); }
/* Both cards hold one box of the same shape, so the eye compares CONTENTS, not containers.
   ⚠️ `flex: 1` MATCHES THEIR HEIGHTS. The grid already stretches the two outer cards to the taller
   one, but the inner boxes size to their content — and the right-hand one is always taller (a toggle
   plus a three-line title reserve). Letting each box absorb its card's leftover space makes them
   equal, so the comparison reads as two versions of the same thing rather than a big claim next to
   a small one. */
.paper, .mini-capsule {
  flex: 1 1 auto;
  padding: 18px; border-radius: 20px; background: var(--deep); border: 1px solid var(--line);
}

/* ⚠️ THE PAPER TITLE IS DELIBERATELY SET AS BODY TEXT — small, dense, 400 weight, full measure. It
   is the only place on the page where type is allowed to look like WORK. Styling it prettier would
   quietly lose the argument the section is making. */
.paper-title { font-size: 14.5px; line-height: 1.5; font-weight: 400; color: var(--text); }
.paper-meta { margin-top: 12px; font-size: 12.5px; line-height: 1.4; color: var(--text2); }
/* ⭐ FINE PRINT, AND IT HAS TO BE THERE. In Portuguese and Spanish the title above is OUR translation
   of the published English, not what the journal printed — this is the page saying so. Without it the
   column claims to show a paper as published and shows something else.
   ⚠️ `:empty` HIDES IT IN ENGLISH, where the title above IS the original. `t()` returns '' for a key
   a language does not carry, so the element renders but stays empty — and an empty <p> with a margin
   still pushes the card taller in one language and not the others. */
.paper-note { margin-top: 10px; font-size: 11.5px; line-height: 1.4; font-style: italic; color: var(--text2); opacity: 0.72; }
.paper-note:empty { display: none; }

.mini-capsule .seg { margin-bottom: 14px; }
/* The payoff, and it has to be visibly EASIER: bigger, heavier, shorter.
   ⚠️ `min-height` reserves three lines so switching Regular/Technical cannot make the card jump —
   the two strings differ in length in every language, most of all in Portuguese. */
.capsule-title {
  font-size: 17.5px; font-weight: 600; letter-spacing: -0.02em; line-height: 1.32;
  min-height: 3.96em; text-wrap: pretty;
}

/* The explainer for the toggle the visitor has just used. */
/* ⚠️ CENTERED, and that is the fix rather than moving it. The note describes the toggle in the
   right card, but it belongs to BOTH — it is what the whole comparison was for. Left-aligned under a
   two-column block it tipped the section to one side; parked in the right column it left the left
   half empty. Centered, it closes the section instead of hanging off it. */
.levels-note { margin-top: clamp(36px, 5vw, 56px); padding-top: 28px; border-top: 1px solid var(--line); text-align: center; }
.levels-note h3 { font-size: clamp(22px, 2.2vw, 28px); font-weight: 800; letter-spacing: -0.03em; line-height: 1.1; }
.levels-note .body-copy { margin: 12px auto 0; max-width: 46em; }

/* Staged for the NEXT section (the capsule body and its caveats) — unused right now. */
.mini-caveat { display: flex; align-items: center; gap: 9px; }
.mini-caveat-chip {
  font-size: 11.5px; font-weight: 700; letter-spacing: 0.05em; text-transform: uppercase;
  color: #FF9F43; background: rgba(255, 159, 67, 0.14); border: 1px solid rgba(255, 159, 67, 0.4);
  border-radius: 999px; padding: 4px 10px;
}
[data-theme="light"] .mini-caveat-chip { color: #8F5200; }
.mini-caveat-bar { flex: 1 1 auto; height: 8px; border-radius: 999px; background: rgba(255, 159, 67, 0.28); }

/* ── 3 · The swipe ──────────────────────────────────────────────────────────────────────────── */
.split-copy .body-copy { margin-top: 18px; max-width: 32em; }
.anatomy { display: flex; flex-direction: column; gap: 16px; margin-top: 30px; }
.anatomy li { display: flex; align-items: flex-start; gap: 13px; }
.anatomy .dot { flex: 0 0 auto; width: 10px; height: 10px; border-radius: 50%; margin-top: 6px; }
/* ⚠️ THE FIFTH DOT MUST BE ORANGE — the line it labels literally says "caveats in orange", and the
   app really does draw caveat bullets in that color. The other four are just variety. */
.anatomy li:nth-child(1) .dot { background: var(--t1); box-shadow: 0 0 12px var(--glow-x) var(--t1); }
.anatomy li:nth-child(2) .dot { background: var(--t0); box-shadow: 0 0 12px var(--glow-x) var(--t0); }
.anatomy li:nth-child(3) .dot { background: var(--t5); box-shadow: 0 0 12px var(--glow-x) var(--t5); }
.anatomy li:nth-child(4) .dot { background: var(--t2); box-shadow: 0 0 12px var(--glow-x) var(--t2); }
/* ⭐ THE CAVEAT DOT IS A RING, NOT A DISC — it matches the app, where a caveat bullet is drawn as an
   unfilled circle so a limitation never looks like a finding. Same 10px outer size (the reset
   makes every box `border-box`, so the border eats inward instead of growing it). */
.anatomy li:nth-child(5) .dot {
  /* 2px on a 10px circle — the same ring-to-diameter ratio as the app's 1.5px on 8px. */
  background: none; border: 2px solid var(--t3);
  box-shadow: 0 0 12px var(--glow-x) var(--t3);
}
.anatomy strong { display: block; font-size: 15.5px; font-weight: 700; letter-spacing: -0.015em; }
.anatomy .txt { display: block; margin-top: 3px; font-size: 14.5px; line-height: 1.5; color: var(--text2); text-wrap: pretty; }

/* The exit from the deck. Set apart by a rule because it is a different beat: everything above is
   what you get in five seconds, this is what happens when five seconds is not enough. */
.swipe-more {
  margin-top: 30px; padding-top: 24px; border-top: 1px solid var(--line);
  font-size: 15px; line-height: 1.6; color: var(--text2); max-width: 40em; text-wrap: pretty;
}

/* Legend chips under the phone — a colored arrow glyph leads each one. */
/* ⚠️ A GRID, NOT WRAPPED FLEX, so all three chips are EXACTLY the same width — flex sized each to
   its own text, which is what made the 2-then-1 break look accidental rather than chosen.
   Two on top (left, right), the third centered beneath at one column's width: the horizontal pair
   mirrors the horizontal swipes, and the odd one out is the vertical one.
   ⚠️ Two rows, not three. Stacking all three made this column noticeably taller than the copy beside
   it, and since the section centers its two halves, the phone floated up out of alignment. */
/* Two on top, the third centred beneath, and all three exactly the same width — the third takes
   `50% - 4px`, which is what one of the top pair occupies once the 8px gap is removed.

   ⭐ `grid-auto-rows: 1fr` IS WHAT MAKES ALL THREE THE SAME HEIGHT. The top pair share a row, so grid
   already stretches them to match each other; the third sits in a row of its own and used to size to
   its own single line while the two above it wrapped to two. Equal auto rows level them.
   ⚠️ It costs nothing in English, where all three are one line and the rows are already equal. It is
   Portuguese and Spanish that need it — there the longest labels wrap and the short one did not. */
.gestures { display: grid; grid-template-columns: 1fr 1fr; grid-auto-rows: 1fr; gap: 8px; width: 100%; max-width: 430px; }
.gesture:nth-child(3) { grid-column: 1 / -1; justify-self: center; width: calc(50% - 4px); }

/* ⭐ THE PILL ROW IS WIDER THAN THE PHONE COLUMN, ON PURPOSE. Pinning this column to the phone's
   318px (`.two-col--phone`, so the two phones line up) leaves (318 − 8) / 2 = 155px per pill, and the
   longest English label needs 204px — so every one of them broke onto two lines. There is no font
   size that fixes that; the row simply needs more width than the phone does.
   So it keeps the 430px it had when this sat in an equal half of the section — 211px per pill, 7px
   more than the widest English label — and overhangs the column by 56px a side. It stays centred on
   the phone, which is what it was before.
   ⚠️ THE RIGHT OVERHANG EATS INTO `--pad-x`, and that is the one thing to watch. Above 1120px there
   is always at least 56px of page padding to absorb it. Between 1000 and 1120 the padding is a few
   pixels short, so the outer pill can come up to ~6px past the viewport — `body { overflow-x: clip }`
   trims that, and 6px of a rounded corner in blank margin is not something anyone will see.

   ⚠️⚠️ AND IT IS SCOPED TO THE SAME BREAKPOINT AS THE COLUMN IT COMPENSATES FOR. It was written at
   top level once, which put a desktop-only override on every phone: a fixed 430px inside a ~364px
   content box, overhanging ~33px a side, so the two pills ran off both edges of the screen. Below
   1000px there is no narrow column to escape — the section is one full-width stack — so the base
   rule above (100% wide, capped at 430) is already right and the pills sit inside `--pad-x` with
   every other element on the page. */
@media (min-width: 1000px) {
  .two-col--phone .gestures { width: 430px; max-width: none; }
}
/* ⚠️ NO `white-space: nowrap` HERE, deliberately. The pills are a fixed width now, so nowrap would
   push the text OUT of the pill rather than keep it on one line — and it would do it in Portuguese,
   where the longest label wants 287px against the 211px a pill gets. Wrapping is the graceful end of
   that, and it is what this did before. English fits on one line with ~7px to spare. */
.gesture { display: flex; align-items: center; justify-content: center; gap: 8px; font-size: 12.5px; font-weight: 600; color: var(--text2); background: var(--surface); border: 1px solid var(--line); border-radius: 999px; padding: 8px 12px; text-align: center; }
/* ⚠️ ONE GLYPH, ROTATED. All three arrows are ↑; ← and → are separate designs in most fonts and
   sat at a different weight beside it. `inline-block` is required or the rotation is ignored. */
.gesture .arrow { display: inline-block; font-weight: 700; font-size: 13.5px; line-height: 1; }
.gesture-left .arrow { color: #FF6F71; transform: rotate(-90deg); }
.gesture-up .arrow { color: #5AC8FA; }
.gesture-right .arrow { color: #14B894; transform: rotate(90deg); }
/* The right-hand swipe puts its arrow AFTER the text, so the chip points the way the thumb goes. */
.gesture-right { flex-direction: row-reverse; }

/* Each chip lends its own color to the highlighted action inside it — `.hl` reads `--hl-glow` from
   whatever it is nested in, so the word "Discard" glows red and "Read Later" glows green without
   any extra markup. */
.gesture-left { --hl-glow: rgba(255, 111, 113, calc(0.7 * var(--glow-k))); }
.gesture-up { --hl-glow: rgba(90, 200, 250, calc(0.7 * var(--glow-k))); }
.gesture-right { --hl-glow: rgba(20, 184, 148, calc(0.7 * var(--glow-k))); }
/* The chips run at 600; the page-wide `.hl` weight of 500 would make the emphasis LIGHTER here. */
.gesture .hl { font-weight: 700; }

/* ── 4 · The types ──────────────────────────────────────────────────────────────────────────── */
.types-body { margin-top: 18px; max-width: 36em; }
.type-chips { display: flex; flex-wrap: wrap; gap: 9px; margin-top: 30px; }
.type-chip {
  font-size: 13.5px; font-weight: 700; letter-spacing: -0.01em; cursor: pointer;
  border-radius: 999px; padding: 9px 15px;
  color: var(--c); background: color-mix(in srgb, var(--c) 12%, transparent);
  border: 1px solid color-mix(in srgb, var(--c) 35%, transparent);
  transition: background 140ms ease, color 140ms ease, box-shadow 140ms ease;
}
[data-theme="light"] .type-chip { color: var(--ink); }
.type-chip[aria-pressed="true"] { color: var(--on-solid); background: var(--c); border-color: var(--c); box-shadow: 0 0 26px calc(-6px + var(--glow-x)) var(--c); }

.type-panel {
  display: flex; flex-wrap: wrap; gap: 28px 48px; justify-content: space-between; align-items: center;
  margin-top: 26px; padding: 30px 30px 32px; border-radius: 26px; background: var(--surface);
  border: 1px solid color-mix(in srgb, var(--c) 40%, transparent);
  box-shadow: 0 0 70px calc(-30px + var(--glow-xx)) var(--c);
  transition: border-color 200ms ease, box-shadow 200ms ease;
}
.type-panel-main { display: flex; flex-direction: column; gap: 12px; max-width: 34em; }
.type-name { font-size: clamp(24px, 2.6vw, 34px); font-weight: 800; letter-spacing: -0.035em; line-height: 1.05; color: var(--c); }
[data-theme="light"] .type-name { color: var(--ink); }
/* ⚠️ FOUR LINES ARE RESERVED so the panel does not resize as you click through the chips. Press has
   the longest definition and sets the height; everything shorter now sits in a box of the same size
   instead of the whole section jumping. In `em`, so it follows the clamped font size rather than
   pinning a pixel height that would clip in Portuguese. */
.type-def {
  font-size: clamp(17px, 1.7vw, 21px); font-weight: 500; letter-spacing: -0.02em; line-height: 1.42;
  color: var(--text); text-wrap: pretty; min-height: 5.68em;
}
/* The label, the bars and the word are one object: centred on each other with the bars as the
   anchor, and the whole block middle-aligned against the text by `align-items: center` above. */
.type-strength { display: flex; flex-direction: column; gap: 10px; align-items: center; text-align: center; }
.type-strength-label { font-size: 11.5px; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; color: var(--text2); }
/* ⚠️ A ranked bar chart is a SCIENTIFIC CLAIM on a page that promises not to overstate findings,
   and "meta-analysis = strongest" is contested. Drawn faithfully to the approved design; flagged as
   unresolved content in the i18n.js header. */
.type-bars { display: flex; align-items: flex-end; gap: 5px; height: 44px; }
.type-bars span { width: 12px; border-radius: 4px; background: var(--skel); transition: background 200ms ease; }
.type-bars span.on { background: var(--c); box-shadow: 0 0 14px calc(-2px + var(--glow-x)) var(--c); }
.type-bars span:nth-child(1) { height: 12px; }
.type-bars span:nth-child(2) { height: 20px; }
.type-bars span:nth-child(3) { height: 28px; }
.type-bars span:nth-child(4) { height: 36px; }
.type-bars span:nth-child(5) { height: 44px; }
.type-strength-word { font-size: 13px; font-weight: 600; color: var(--text2); }

/* ── Reading-level toggle (now inside section 2's "In Capso" card) ─────────────────────── */
.seg { display: flex; gap: 3px; padding: 4px; border-radius: 999px; background: var(--surface2); }
.seg button { flex: 1 1 0; font-size: 14px; font-weight: 700; letter-spacing: -0.01em; padding: 11px 16px; border-radius: 999px; border: none; cursor: pointer; color: var(--text2); background: transparent; transition: background 130ms ease, color 130ms ease; }
.seg button:not([aria-pressed="true"]):hover { color: var(--text); }
.seg button[aria-pressed="true"] { background: var(--deep); color: var(--text); box-shadow: 0 2px 10px -4px var(--shade); }

/* ── 6 · Depth ──────────────────────────────────────────────────────────────────────────────── */
.depth-copy .body-copy { max-width: 30em; }
/* ⚠️ `pre-line` is what turns the newlines in `depthNote` into a paragraph break. */
.depth-note { margin-top: 20px; font-size: 14px; line-height: 1.55; color: var(--text2); max-width: 34em; white-space: pre-line; }
.depth-sheet { width: 100%; max-width: 420px; border: 1px solid var(--hair); border-radius: 26px; background: var(--surface); padding: 10px 10px 14px; box-shadow: 0 30px 60px -34px var(--shade); }
.grabber { width: 40px; height: 4px; border-radius: 99px; background: var(--hair); margin: 6px auto 12px; }
.depth-sheet li { display: flex; align-items: center; justify-content: space-between; gap: 16px; padding: 15px 14px; border-top: 1px solid var(--line); font-size: 15px; font-weight: 600; letter-spacing: -0.015em; }
.depth-cta { padding: 16px 4px 2px; }
/* ⚠️ The app's BLUE, not the site's purple — this mirrors the real in-app button. */
.depth-button { width: 100%; background: #0A84FF; color: #FFFFFF; font-size: 15.5px; font-weight: 700; letter-spacing: -0.015em; border: 0; border-radius: 15px; padding: 15px 18px; cursor: default; box-shadow: 0 0 34px -8px rgba(10, 132, 255, calc(0.7 * var(--glow-k))); }

/* ── 8 · FAQ ──────────────────────────────────────────────────────────────────
   ⭐ EVERY ROW WEARS AN EVIDENCE COLOR (`--c`, set per item in app.js). The closed list is a column
   of colored dots — the same signature as the capsule wall and the type chips — so the section reads
   as part of this product rather than a generic accordion bolted on the end. Opening a row commits
   to that color: tinted fill, colored edge, and the same bloom the type panel uses. */
/* ⚠️ CENTERED AS A BLOCK, NOT AS TEXT. The heading centers and the column of rows is centered in the
   wrap by `margin: auto` — but the questions and answers inside stay LEFT aligned, because centered
   body copy is hard to read once it runs past a line. Capped at 62em, which was already there; it
   was the missing `auto` margin that left the whole thing hanging off to the left. */
.faq h2 { text-align: center; }
.faq-list { margin: 34px auto 0; display: flex; flex-direction: column; gap: 10px; max-width: 62em; }
.faq-item {
  border: 1px solid var(--line); border-radius: 18px; background: var(--surface);
  transition: border-color 200ms ease, background 200ms ease, box-shadow 200ms ease;
}
/* ⚠️ NO TINT IN THE FILL — it stays `--surface`, exactly like `.type-panel` in the evidence section.
   Mixing the type color into the background made these rows read as heavier, tinted boxes next to a
   clean one, which is very visible in light mode where the mix darkens rather than lightens. The
   color belongs in the EDGE and the BLOOM; the fill is the same everywhere on the page. Border at
   40% matches the panel too. */
.faq-item[open] {
  border-color: color-mix(in srgb, var(--c) 40%, transparent);
  box-shadow: 0 0 44px calc(-26px + var(--glow-xx)) var(--c);
}

.faq-q {
  display: flex; align-items: center; gap: 14px;
  padding: 17px 20px; cursor: pointer; list-style: none;
  font-size: 16px; font-weight: 600; letter-spacing: -0.015em;
  color: var(--text2); transition: color 160ms ease;
}
/* Both are needed: WebKit uses the pseudo-element, everyone else honors `list-style: none`. */
.faq-q::-webkit-details-marker { display: none; }
.faq-q:hover, .faq-item[open] .faq-q { color: var(--text); }
.faq-q-text { flex: 1 1 auto; }

/* The dot — lit even when the row is shut, so the collapsed list still carries the palette. */
.faq-q::before {
  content: ""; flex: 0 0 auto; width: 9px; height: 9px; border-radius: 50%;
  background: var(--c); box-shadow: 0 0 12px var(--glow-x) var(--c);
  opacity: 0.5; transition: opacity 160ms ease;
}
.faq-q:hover::before, .faq-item[open] .faq-q::before { opacity: 1; }

/* A chevron drawn from two borders — no icon font, no SVG — taking the row's color when open. */
.faq-q::after {
  content: ""; flex: 0 0 auto; width: 9px; height: 9px; margin-right: 4px;
  border-right: 2px solid currentColor; border-bottom: 2px solid currentColor;
  transform: rotate(45deg) translate(-2px, -2px);
  transition: transform 200ms ease, border-color 200ms ease;
}
.faq-item[open] .faq-q::after {
  transform: rotate(-135deg) translate(-2px, -2px);
  border-right-color: var(--c); border-bottom-color: var(--c);
}

/* ⚠️ The ANSWER fades in rather than the row animating its height. Height interpolation on
   <details> needs `interpolate-size` / `::details-content`, which is not reliable everywhere yet —
   and a half-supported open animation looks worse than none. */
.faq-a {
  padding: 0 20px 20px 43px; max-width: 58em;
  font-size: 15.5px; line-height: 1.6; color: var(--text2); text-wrap: pretty;
  animation: capsoIn 220ms ease both;
}

/* ── 7 · Final CTA ──────────────────────────────────────────────────────────────────────────── */
/* ── 6 · Insight capsules ───────────────────────────────────────────────────────────────────────
   Two stills side by side, centred, with the copy above them. No grid template and no fixed column:
   the phones are a known 303.19px each (`.phone-wrap--shot`), so a centred flex row sizes itself and
   drops to one column on its own when the pair no longer fits.

   ⚠️ NOT `.two-col`. That is a copy-beside-device layout and this section is copy-ABOVE-devices —
   the two shots are a pair being compared with each other, so splitting them across a text column
   would break the comparison they exist to make. */
/* Centred, like the closing section — the two phones below are a symmetric pair, and a left-ragged
   heading above a centred pair reads as a mistake. `text-align` on the wrap carries the h2 as well;
   the flex row and the figcaptions below set their own alignment and are unaffected.
   ⚠️ THE MEASURE TIGHTENS WHEN TEXT CENTRES. 46em was fine ragged-right; centred, both edges move on
   every line and a long measure gets hard to track back. 42em is about 75 characters. */
.insight-wrap { text-align: center; }
.insight-body { max-width: 42em; margin: 18px auto 0; }
.insight-pair {
  display: flex; flex-wrap: wrap; justify-content: center;
  gap: clamp(28px, 5vw, 64px);
  margin-top: clamp(40px, 6vw, 64px);
}
.insight-shot { margin: 0; display: flex; flex-direction: column; align-items: center; gap: 16px; }
/* The face toggle. Narrower than the section's other `.seg` (that one is a full-width control in a
   text column; this sits under a 303px phone and only carries two short words), and sized so the two
   halves stay equal in Portuguese and Spanish, where "Frente / Reverso" is the long pair. */
.insight-seg { width: 200px; }
.insight-seg button { font-size: 13px; padding: 8px 12px; }
/* The caption names what the READER is looking at, so it is tied to the shot rather than floating
   under the pair — a caption under both would have to describe two different things at once. */
.insight-shot figcaption {
  max-width: 303px; text-align: center;
  font-size: 13px; line-height: 1.55; color: var(--text2);
}

.closing .wrap { max-width: 760px; text-align: center; padding: clamp(56px, 8vw, 104px) var(--pad-x); }
.closing h2 { font-size: clamp(32px, 4.4vw, 56px); letter-spacing: -0.045em; line-height: 1; }
.closing .body-copy { max-width: 34em; margin: 18px auto 0; }
.closing .signup { max-width: 480px; margin-left: auto; margin-right: auto; justify-content: center; }
/* ⚠️ `.closing .wrap` centres TEXT, and it reaches inside the form — so the typed address, the
   placeholder and the referral hint were all centred while the boxes around them were not. Centre
   the BOXES (auto margins) and hand the text back its left edge, so these read exactly like the
   pair in the hero. */
.closing .signup input { text-align: left; }
.closing .signup .referral-field { margin-left: auto; margin-right: auto; text-align: left; }
/* 🚨 THE BUTTON ROW GETS ITS OWN LINE HERE, ALWAYS. The form wraps on width, so the row sat below
   the email field only because "Join the waiting list" was too wide to fit beside it — and the
   moment the label shortened to "Joining" it fitted, jumped up onto the field's line, and the
   layout lurched at the exact moment the reader was waiting on it. Pinning it to 100% means the
   label can say anything without moving. */
.closing .signup-actions { flex: 1 0 100%; justify-content: center; }
/* ⚠️ `text-align` centres the TEXT; the BOX still sits at the left of the column once it has a
   max-width, which reads as off-centre. Both centred cases need the auto margins. */
.closing .privacy { text-align: center; margin-left: auto; margin-right: auto; }
/* 🚨 THE STATUS CARD IS THE SAME TRAP AS .privacy ABOVE, and it caught us anyway. It carries a
   max-width, so `text-align: center` on the column centred the WORDS INSIDE it while the box itself
   stayed pinned to the left edge of the content area — off-centre card, centred text, exactly
   backwards. Auto margins centre the box; `text-align: left` hands the text back its edge.
   ⚠️ 480px, not the default 540, so its edges line up with the form above it rather than
   overhanging it by 30px a side. */
.closing .form-status { max-width: 480px; margin-left: auto; margin-right: auto; text-align: left; }

/* ── 8 · Footer ─────────────────────────────────────────────────────────────────────────────── */
footer { background: var(--deep); border-top: 1px solid var(--line); }
footer .inner { max-width: var(--maxw); margin: 0 auto; padding: 40px var(--pad-x) 56px; display: flex; flex-wrap: wrap; justify-content: space-between; align-items: flex-start; gap: 20px 40px; }
.disclaimer { margin-top: 12px; font-size: 13px; line-height: 1.6; color: var(--text2); max-width: 46em; }
.foot-right { display: flex; flex-direction: column; gap: 8px; font-size: 13px; color: var(--text2); }
/* Buttons that read as links — they change state, so they are not <a>. */
.foot-langs { display: flex; flex-wrap: wrap; align-items: center; gap: 7px; }
.foot-lang {
  border: 0; background: none; padding: 0; cursor: pointer;
  font-size: 13px; color: var(--text2); transition: color 130ms ease;
}
.foot-lang:hover { color: #5AC8FA; text-decoration: underline; }
.foot-lang[aria-pressed="true"] { color: var(--text); }
/* ⚠️ MATCHES .foot-lang, NOT the global link color. The policy link sits directly above the
   language row, and at 13px a blue link beside grey ones reads as the ONE thing worth clicking
   down there. It is a legal document — findable, not advertised. Same grey, same blue on hover,
   so the whole footer behaves as one set of controls.
   ⚠️ Scoped by class rather than .foot-right a, which would also recolor the email address. */
.foot-legal { color: var(--text2); transition: color 130ms ease; }
/* Quieter than everything above it — this is attribution, not a control. */
.foot-copy { font-size: 12px; color: var(--text2); opacity: 0.72; }
.foot-legal:hover { color: #5AC8FA; text-decoration: underline; }

/* ⚠️ `invert(1)` IS LOAD-BEARING IN LIGHT MODE — the file is white artwork on transparency, which
   is nothing at all on a white footer. Inverting turns it black. That works because the mark is
   monochrome; if a colored version ever replaces it, invert will shift the hues and it needs a
   second file instead.
   ⚠️ Full opacity, and no hover state — it is not interactive. Loreworx has no site to link to, so
   the mark is a signature rather than a button, and dimming something unclickable only reads as an
   affordance that does not work. */
.foot-logo { margin-top: 14px; }
.foot-logo img { width: 142px; height: auto; }
[data-theme="light"] .foot-logo img { filter: invert(1); }

/* ── Phone only ─────────────────────────────────────────────────────────────────────────────────
   ⚠️ THE ONLY `max-width` QUERY IN THIS FILE, and it is 999.98 rather than 999 on purpose. Every
   other query here is `min-width: 1000px`; pairing that with `max-width: 999px` leaves a gap, because
   a viewport can land on a fractional width (browser zoom, a scrollbar, a 1.5× device ratio) and
   999.5px would match NEITHER. 999.98 closes it.

   These are layout choices that only make sense once the hero has stacked and everything is one
   narrow column — they are not "small-screen versions" of the desktop rules, which is why they live
   together here rather than being scattered through the sections above. */
@media (max-width: 999.98px) {
  /* The form reads as one centred object under a full-width headline. On desktop it sits at the left
     edge of a text column that has a phone beside it, and centring it there would break that edge. */
  .signup { justify-content: center; }
  .privacy { text-align: center; margin-left: auto; margin-right: auto; }

  /* 🚨 THE BUTTON ROW GETS ITS OWN LINE, IN BOTH SECTIONS. `.signup` wraps on width, so the row
     sat under the email field only because the CTA label was too wide to fit beside it — and the
     moment it shortened to "Joining" it fitted, jumped up onto the field's line, and the layout
     lurched at the exact moment the reader was waiting on it. Pinning to 100% means the label can
     say anything without moving anything. (`.closing` does this at every width; this is the hero.) */
  .signup-actions { flex: 1 0 100%; justify-content: center; }

  /* ⚠️ THE REFERRAL BLOCK DID NOT FOLLOW THE REST OF THE FORM. Everything here centres on mobile,
     and this was still sitting where the desktop text column left it — the toggle hard against the
     left edge under a centred button row.
     ⭐ Centre the BOX, not the words in it: the toggle is inline so `text-align` moves it, while the
     field needs auto margins because it has a max-width. Same split as `.closing` — a centred box
     whose contents keep their left edge, so the hint lines up with the input above it. */
  .signup .referral { text-align: center; }
  .signup .referral-field { margin-left: auto; margin-right: auto; text-align: left; }

  /* ⭐ `margin-left: auto`, NOT a `justify-content` or `text-align` change. `.type-panel` is a wrapped
     flex ROW with `justify-content: space-between`: once the strength block drops to its own line it
     is the only item on that line, and `space-between` puts a lone item at the START — which is the
     empty half of the panel. An auto margin absorbs the free space instead and pushes it to the right
     edge, so the panel reads as two corners rather than one column with a hole beside it.
     ⚠️ The block keeps its own internal centring (`.type-strength` is `align-items: center`) — the
     label, the bars and the word stay stacked on each other; it is the BLOCK that moves. */
  .type-strength { margin-left: auto; }
}

@media (prefers-reduced-motion: reduce) {
  /* ⚠️ `animation-iteration-count: 1` IS NOT OPTIONAL HERE. Killing only the DURATION on an
     `infinite alternate` animation does not stop it — it makes it repeat every 0.01ms, turning a
     calm pulse into a strobe. That is worse than the motion it was meant to remove, and for a
     photosensitive visitor it is actively harmful. */
  *, *::before, *::after {
    transition-duration: 0.01ms !important;
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
  }
  /* The dot keeps its lit end-state rather than freezing mid-breath. */
  .status-badge .dot { animation: none; box-shadow: 0 0 12px var(--glow-x) rgba(20, 184, 148, calc(0.9 * var(--glow-k))); }
}
