/* Neto — landing page styles.
   Every colour, radius and duration below resolves to a token from tokens.css, which is a
   generated mirror of the Figma source. The only exceptions are quarantined in §0 and are
   pending Design (docs/inbox/design/Q-2026-09-12-la-escala-de-marketing-no-existe.md). */

/* ── Fonts ──────────────────────────────────────────────────────────────────────────── */
@font-face {
  font-family: 'Rethink Sans Variable';
  font-style: normal;
  font-display: swap;
  font-weight: 400 800;
  src: url('/landing/fonts/rethink-sans-latin-wght-normal.woff2') format('woff2-variations');
  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: 'Rethink Sans Variable';
  font-style: normal;
  font-display: swap;
  font-weight: 400 800;
  src: url('/landing/fonts/rethink-sans-latin-ext-wght-normal.woff2') format('woff2-variations');
  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;
}

/* ── §0. What the page owns, now that the system owns the rest ────────────────────────
   The quarantine block that used to sit here is gone. All seven gaps came back coined from
   Figma the same day (docs/inbox/web/done/A-2026-09-12-las-siete-brechas-*): the display
   scale, the section rhythm, the reveal duration and its stagger, the blur ramp, the four
   decorative-cyan tokens and the marketing panel. Not one of them is a value invented here.

   What is left below is genuinely the page's own, and Design said so: a token is a STOP, and
   the interpolation between stops is the landing's business — the design system has no
   opinion about the viewport and should not grow one. Every clamp's two extremes are coined
   stops; only the curve between them is local. */
:root {
  /* ts-display-md (48) → ts-display-xl (72). Tracking is the tokens' own −1px at every stop. */
  --display-xl: clamp(48px, 6vw, 72px);
  /* ts-heading-display (28) → ts-display-md (48) */
  --display-lg: clamp(28px, 4vw, 48px);
  /* ts-heading-section (24) → ts-heading-display (28) */
  --display-md: clamp(24px, 2.4vw, 28px);
  /* ts-body-base (16) → ts-heading-subsection (20) */
  --lead-size: clamp(16px, 1.5vw, 20px);

  /* Section rhythm. Both extremes are the tokens themselves, not their values retyped:
     spacing/64 is the product's ceiling, spacing/128 the marketing rung coined for this. */
  --section-y: clamp(var(--spacing-64), 9vw, var(--spacing-128));
  --block-gap: clamp(var(--spacing-32), 5vw, var(--spacing-64));

  /* Column geometry. Not tokens and not pretending to be: no component measures itself
     against the width of a marketing page. */
  --gutter: clamp(20px, 4vw, 32px);
  --container: 1120px;
  --container-narrow: 760px;
}

/* ── §1. Reset & base ───────────────────────────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; }
html {
  --font-sans: 'Rethink Sans Variable', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
}
body {
  margin: 0;
  font-family: var(--font-sans);
  background: var(--bg-surface-canvas);
  color: var(--fg-default);
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}
h1, h2, h3, p, figure, ul, ol { margin: 0; }
ul, ol { padding: 0; list-style: none; }
img, svg { display: block; max-width: 100%; }
/* `svg { display: block }` above outranks the UA stylesheet's [hidden] rule — author
   styles beat UA styles — so anything marked hidden needs saying again, louder. */
[hidden] { display: none !important; }
a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; }

:where(a, button, input, summary):focus-visible {
  outline: var(--border-width-focus-default) solid var(--shadow-focus-inner);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

.skip {
  position: absolute;
  left: var(--spacing-8);
  top: calc(var(--spacing-8) * -8);
  z-index: 100;
  padding: var(--spacing-8) var(--spacing-16);
  border-radius: var(--radius-full);
  background: var(--bg-brand);
  color: var(--fg-on-brand);
  transition: top var(--motion-duration-fast) var(--motion-easing-enter);
}
.skip:focus { top: var(--spacing-8); }

.wrap {
  width: 100%;
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: var(--gutter);
}
.wrap--narrow { max-width: var(--container-narrow); }
.section { padding-block: var(--section-y); }
/* The same near-full-bleed rounded band as the panels, per Alfredo: 12px inset, 16px corners
   (see .panel for the measurement behind that shape). Only its ground is the tint instead of a
   dark panel, and it follows the theme. */
.section--tint {
  margin: var(--spacing-12);
  border-radius: var(--radius-2xl);
  background: var(--bg-surface-default);
}

/* Type. The .ts-* classes from tokens.css cover everything at and below 28px; §0 covers
   what sits above it. Never write a raw font-size outside those two. */
.display-xl {
  font-weight: 700;
  font-size: var(--display-xl);
  line-height: 1.06;
  letter-spacing: -1px;
  text-wrap: balance;
}
.display-lg {
  font-weight: 700;
  font-size: var(--display-lg);
  line-height: 1.08;
  letter-spacing: -1px;
  text-wrap: balance;
}
.display-md {
  font-weight: 700;
  font-size: var(--display-md);
  line-height: 1.15;
  letter-spacing: -0.25px;
}
.lead {
  font-size: var(--lead-size);
  line-height: 1.55;
  color: var(--fg-subtle);
  text-wrap: pretty;
}
.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: var(--spacing-6);
  color: var(--fg-brand);
  text-transform: uppercase;
}
.muted { color: var(--fg-subtle); }

/* ── §2. Buttons — the pill shape and sizes come from the Button component spec ─────── */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--spacing-8);
  border-radius: var(--radius-full);
  border: var(--border-width-default) solid transparent;
  cursor: pointer;
  white-space: nowrap;
  transition:
    background-color var(--motion-duration-fast) var(--motion-easing-move),
    border-color var(--motion-duration-fast) var(--motion-easing-move),
    color var(--motion-duration-fast) var(--motion-easing-move),
    transform var(--motion-duration-instant) var(--motion-easing-move);
}
.btn:active { transform: scale(0.97); }
.btn--md {
  height: var(--button-size-lg-height);
  padding-inline: var(--button-size-lg-padding-x);
  font-weight: 500;
  font-size: var(--button-text-lg-size);
  letter-spacing: 0.5px;
}
.btn--lg {
  height: var(--button-size-xl-height);
  padding-inline: var(--button-size-xl-padding-x);
  font-weight: 600;
  font-size: var(--button-text-xl-size);
  letter-spacing: 0.5px;
}
.btn--filled {
  background: var(--button-filled-background-default);
  color: var(--button-filled-foreground);
}
.btn--filled:hover { background: var(--button-filled-background-hover); }
.btn--outline {
  border-color: var(--button-outline-border);
  color: var(--button-outline-foreground);
}
/* Hover is neutral on purpose: cyan is reserved for selected state and the focus ring
   (design.md §Principles #4). Two cyan states side by side read as two selections. */
.btn--outline:hover { background: var(--button-outline-background-hover); }
.btn--ghost { color: var(--button-ghost-foreground); }
.btn--ghost:hover { background: var(--button-ghost-background-hover); }

.cta-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--spacing-12);
  align-items: center;
}
.cta-note {
  display: flex;
  flex-wrap: wrap;
  gap: var(--spacing-8) var(--spacing-16);
  color: var(--fg-subtle);
}
.cta-note li { display: flex; align-items: center; gap: var(--spacing-6); }
.cta-note svg { color: var(--fg-provision); flex: none; }

/* On a phone every content CTA is full width, per Alfredo: one button per line, edge to edge of
   the column — the biggest target a thumb can get, and two stacked buttons of equal width instead
   of two ragged ones. (Before, they sat centred at their own width.)
   - In the hero, .cta-row is a child of a column with `align-items: flex-start`, so the row has to
     stretch to the column first or its buttons have no width to fill.
   - The footer button is a single button in a column flex container; it stretches with align-self.
   Scoped to content: the nav's actions are pinned to the end of the bar and stay their size. */
@media (max-width: 759px) {
  .cta-row { align-self: stretch; }
  .cta-row > .btn { flex: 1 1 100%; }
  .footer__pitch > .btn { align-self: stretch; }
  /* The footer's line sits centred over its full-width button, per Alfredo, balanced so a centred
     two-line heading does not leave one word alone on the second line. */
  .footer__pitch > h2 { text-align: center; text-wrap: balance; }
  .footer__pitch > .footer__brand { align-self: center; }
}

/* ── §3. Announcement bar + nav ─────────────────────────────────────────────────────── */
.announce {
  background: var(--bg-surface-inverse);
  color: var(--fg-on-inverse);
  text-align: center;
  padding: var(--spacing-10) var(--gutter);
}
.announce a {
  color: inherit;
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-color: var(--fg-on-inverse-subtle);
}

.nav {
  position: sticky;
  top: 0;
  z-index: 50;
  background: color-mix(in oklch, var(--bg-surface-canvas) 82%, transparent);
  border-bottom: var(--border-width-default) solid transparent;
  transition:
    background-color var(--motion-duration-fast) var(--motion-easing-move),
    border-color var(--motion-duration-fast) var(--motion-easing-move);
}
@supports (backdrop-filter: blur(1px)) {
  .nav { backdrop-filter: saturate(180%) blur(14px); }
}
.nav[data-stuck='true'] {
  background: color-mix(in oklch, var(--bg-surface-canvas) 92%, transparent);
  border-bottom-color: var(--border-default);
}
.nav__inner {
  display: flex;
  align-items: center;
  gap: var(--spacing-24);
  height: 64px;
  transition: height var(--motion-duration-moderate) var(--motion-easing-move);
}
.nav[data-stuck='true'] .nav__inner { height: 56px; }
.brand { display: flex; align-items: center; gap: var(--spacing-10); font-weight: 700; }
.brand img { width: 28px; height: 28px; border-radius: var(--radius-lg); }
.nav__links { display: none; gap: var(--spacing-20); margin-inline-start: auto; }
.nav__links a { color: var(--fg-subtle); transition: color var(--motion-duration-fast) var(--motion-easing-move); }
.nav__links a:hover { color: var(--fg-default); }
/* The page you are on. The calculator pages share this nav; there the link to them is marked
   aria-current and reads as the active one — full foreground and weight, no cyan (the nav is not a
   selection control). */
.nav__links a[aria-current='page'] { color: var(--fg-default); font-weight: 600; }
/* `margin-inline-start: auto` is what pins the actions to the end of the bar. It used to be
   overridden by `.nav__links + .nav__actions { margin-inline-start: 24px }` sitting outside
   any media query — and below 860px the links are `display: none`, so that rule replaced the
   auto with a fixed gap against nothing and the whole group bunched up against the logo with
   half the bar empty to its right. An adjacent-sibling selector still matches a hidden
   sibling; `display: none` removes the box, not the element. The fixed gap now lives in the
   same media query as the links it is separating from. */
.nav__actions { display: flex; align-items: center; gap: var(--spacing-8); margin-inline-start: auto; }
.icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  border: 0;
  border-radius: var(--radius-full);
  background: transparent;
  cursor: pointer;
  color: var(--fg-subtle);
  transition: background-color var(--motion-duration-fast) var(--motion-easing-move);
}
.icon-btn:hover { background: var(--bg-hover); }
/* The theme icon is swapped in CSS, not in JS. `hidden` is an HTMLElement property:
   assigning svg.hidden = true sets a JS expando and never reaches the attribute, so the
   JS version silently showed the wrong glyph. The class on <html> is already the truth. */
html:not(.dark) [data-icon='sun'] { display: none; }
html.dark [data-icon='moon'] { display: none; }
/* Keyed off what the link IS, not what it looks like. It used to be `.nav .btn--ghost`,
   which meant restyling "Entrar" to outlined would have silently freed it from both rules
   and shown it at every width — including 320px, where the bar has 24px to spare. A layout
   rule that depends on a visual variant breaks the first time someone changes the variant. */
.nav__signin { display: none; }
@media (min-width: 860px) {
  .nav__links { display: flex; }
  .nav__links + .nav__actions { margin-inline-start: var(--spacing-24); }
  .nav__signin { display: inline-flex; }
}

/* ── §4. Hero ───────────────────────────────────────────────────────────────────────── */
.hero { padding-block: clamp(48px, 7vw, 96px) var(--section-y); }
.hero__grid {
  display: grid;
  gap: var(--block-gap);
  align-items: center;
}
@media (min-width: 960px) {
  .hero__grid { grid-template-columns: minmax(0, 1fr) minmax(0, 0.92fr); gap: clamp(40px, 5vw, 72px); }
}
.hero__copy { display: flex; flex-direction: column; gap: var(--spacing-24); align-items: flex-start; }
.hero__copy .lead { max-width: 54ch; }
/* The eyebrow sits inside the h1 (for what the h1 says, not how it looks): a block of its own
   above the headline, the 24px the column gap used to give it, and its own type — letter-spacing,
   weight and size all come from .ts-label-micro, so the headline's -1px tracking does not leak in. */
.hero__title .eyebrow { display: block; margin-bottom: var(--spacing-24); }
/* Inline, not a flex item: when the line wraps on a phone the dot stays with the first word
   instead of hanging on its own at the left of a centred two-line block. */
.hero__title .eyebrow svg { display: inline-block; vertical-align: middle; margin-inline-end: var(--spacing-6); }

/* The hero bloom. --marketing-accent-glow was coined to go with blur-lg/blur-xl, and this
   is the pair in use: a decorative cyan that is explicitly allowed on marketing surfaces
   (Design, 2026-09-12 §6) while inside the product cyan stays reserved for selected and
   focus. Pseudo-element and pointer-events:none — it is paint, never a target. */
/* `overflow: clip` and not `hidden`: hidden would make the hero a scroll container, and the
   glow bleeds past its box either way. Clipping it here — rather than leaning on the body's
   overflow-x — is what keeps documentElement.scrollWidth equal to the viewport.

   The glow is a RADIAL GRADIENT, not a solid disc behind a blur. The solid version left a
   straight vertical edge where the clip met it — a blurred disc is still fully opaque at its
   centre, so the clip was cutting through colour that was still there. A gradient that
   reaches zero alpha at 70% of its own radius is transparent before the container ends, and
   the clip has nothing left to cut. The blur only softens what is by then already faint. */
.hero { position: relative; isolation: isolate; overflow: clip; }
.hero::before {
  content: '';
  position: absolute;
  z-index: -1;
  pointer-events: none;
  inset-block-start: -8%;
  inset-inline-end: 0;
  width: min(640px, 70%);
  aspect-ratio: 1;
  background: radial-gradient(closest-side, var(--marketing-accent-glow), transparent 70%);
  filter: blur(var(--blur-lg));
  opacity: 0.85;
}
/* ── §5. The flow — the hero's animated distribution of one month's gross ───────────── */
.flow {
  background: var(--bg-surface-subtle);
  border: var(--border-width-default) solid var(--border-default);
  border-radius: var(--radius-2xl);
  padding: var(--spacing-20);
  box-shadow:
    0 var(--elevation-floating-key-offset-y) var(--elevation-floating-key-blur)
      var(--elevation-floating-key-spread) var(--shadow-key),
    0 var(--elevation-floating-ambient-offset-y) var(--elevation-floating-ambient-blur)
      var(--elevation-floating-ambient-spread) var(--shadow-ambient);
}
.flow__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--spacing-12);
  flex-wrap: wrap;
  margin-bottom: var(--spacing-16);
}
.flow__gross { font-variant-numeric: tabular-nums; }
.flow__bar {
  display: flex;
  height: var(--spacing-12);
  border-radius: var(--radius-full);
  overflow: hidden;
  background: var(--bg-surface-default);
  gap: 2px;
}
.flow__seg {
  width: 0;
  transition: width var(--motion-duration-reveal) var(--motion-easing-enter);
}
.flow__seg[data-kind='tax'] { background: var(--bg-tax); }
.flow__seg[data-kind='provision'] { background: var(--fg-provision); }
.flow__seg[data-kind='expense'] { background: var(--fg-expense); }
.flow__seg[data-kind='net'] { background: var(--bg-net); }
.flow__rows { margin-top: var(--spacing-20); display: grid; gap: var(--spacing-2); }
.flow__row {
  display: grid;
  grid-template-columns: 10px minmax(0, 1fr) auto;
  align-items: center;
  gap: var(--spacing-10);
  padding: var(--spacing-8) var(--spacing-6);
  border-radius: var(--radius-md);
}
.flow__row[data-kind='net'] { background: var(--bg-net-subtle); }
.flow__dot { width: 10px; height: 10px; border-radius: var(--radius-full); }
.flow__row[data-kind='tax'] .flow__dot { background: var(--bg-tax); }
.flow__row[data-kind='provision'] .flow__dot { background: var(--fg-provision); }
.flow__row[data-kind='expense'] .flow__dot { background: var(--fg-expense); }
.flow__row[data-kind='net'] .flow__dot { background: var(--bg-net); }
.flow__amount { font-variant-numeric: tabular-nums; }
.flow__row[data-kind='net'] .flow__label,
.flow__row[data-kind='net'] .flow__amount { color: var(--fg-net); font-weight: 700; }
.flow__foot {
  margin-top: var(--spacing-16);
  padding-top: var(--spacing-12);
  border-top: var(--border-width-default) solid var(--border-default);
  color: var(--fg-subtle);
}

/* ── §6. Intent router — the dark panel ────────────────────────────────────────────────
   An inset marketing panel, not a full-bleed band: the page keeps its margin around it, the
   way the reference does. Everything inside uses only the four --marketing-panel-* tokens.
   That is not shorthand — they are the ONLY values in the system measured against an
   always-dark surface. The semantic accents (fg/income, fg/tax…) flip with the theme, so on
   a panel that never flips, half of them would land unreadable in one mode. */
/* Near full-bleed with rounded corners — measured off Deel, not guessed.

   This took three tries, and the history is the useful part. The first version was a card
   trapped inside .wrap's 1120px. Asked for "full width like Deel", the second made it a
   flat band running off both edges — and dropped the radius along the way, which nobody had
   asked for. Deel's block, measured: 12px from each viewport edge, 20px radius. The page's
   own ground shows as a thin frame and in the four corners. Full width AND rounded; "full
   width" never meant "square".

   Radius is --radius-2xl (16px), not Deel's 20: the system has no 20 rung, and inventing
   one to match a reference four pixels more closely is exactly the drift the tokens exist
   to stop. The frame is --spacing-12, which does exist.

   .section still owns the vertical padding and .wrap still bounds the content; the panel
   contributes only its inset, its corners and its ground. */
.panel {
  position: relative;
  isolation: isolate;
  overflow: clip;
  margin: var(--spacing-12);
  border-radius: var(--radius-2xl);
  background: var(--marketing-panel-background);
  color: var(--marketing-panel-foreground);
}
.panel::before {
  content: '';
  position: absolute;
  z-index: -1;
  pointer-events: none;
  inset-block-start: -25%;
  inset-inline-start: 25%;
  width: min(680px, 80%);
  aspect-ratio: 1;
  background: radial-gradient(closest-side, var(--marketing-accent-glow), transparent 70%);
  filter: blur(var(--blur-lg));
  opacity: 0.6;
}
.panel h2 { color: var(--marketing-panel-foreground); }
.panel .lead { color: var(--marketing-panel-foreground-subtle); }
/* No width cap, per Alfredo: the lead reads on one line from ~620px up (a phone wraps it). */
.panel__head { margin-bottom: var(--block-gap); }
.panel__head .lead { margin-top: var(--spacing-16); }

.router {
  display: grid;
  gap: var(--spacing-12);
  margin-bottom: var(--spacing-24);
}
@media (min-width: 800px) { .router { grid-template-columns: repeat(3, 1fr); gap: var(--spacing-20); } }

.router__opt {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-8);
  align-items: flex-start;
  text-align: start;
  padding: var(--spacing-20);
  /* A subtle edge at every width, per Alfredo — the pills' hairline rim. Borderless, the three read
     as a list of text rather than three buttons (worst on a phone, with no hover to say so).
     Selected swaps it for the accent line. */
  border: var(--border-width-default) solid color-mix(in oklch, var(--marketing-panel-foreground) 14%, transparent);
  border-radius: var(--radius-xl);
  background: transparent;
  color: var(--marketing-panel-foreground-subtle);
  cursor: pointer;
  transition:
    background-color var(--motion-duration-fast) var(--motion-easing-move),
    border-color var(--motion-duration-fast) var(--motion-easing-move),
    color var(--motion-duration-fast) var(--motion-easing-move);
}
.router__opt strong { color: var(--marketing-panel-foreground); font-weight: 600; }
/* Hover lifts with the panel's own foreground at low alpha, never with the brand: cyan on
   this panel means selected, exactly as it does inside the product. */
.router__opt:hover { background: color-mix(in oklch, var(--marketing-panel-foreground) 7%, transparent); }
.router__opt[aria-pressed='true'] {
  background: color-mix(in oklch, var(--marketing-panel-foreground) 10%, transparent);
  border-color: var(--marketing-accent-line);
}
.router__more {
  display: inline-flex;
  align-items: center;
  gap: var(--spacing-6);
  margin-top: var(--spacing-4);
  color: var(--marketing-panel-foreground-subtle);
  transition: color var(--motion-duration-fast) var(--motion-easing-move);
}
.router__opt[aria-pressed='true'] .router__more { color: var(--marketing-accent-on-panel); }
.router__more svg { transition: transform var(--motion-duration-fast) var(--motion-easing-move); }
.router__opt:hover .router__more svg { transform: translateX(2px); }
/* The icon is the cards' own .card__icon, as in the other sections. Neutral while unchosen — the
   three are peers, and giving each a money colour would claim a meaning they do not have — and
   on the selected card it takes the selected state, the one use of cyan design.md allows. */
.router__icon { margin-bottom: var(--spacing-4); transition: background-color var(--motion-duration-fast) var(--motion-easing-move), color var(--motion-duration-fast) var(--motion-easing-move); }
.router__opt[aria-pressed='true'] .router__icon { background: var(--bg-selected); color: var(--fg-brand); }

/* Below 800px the three cards are a swipe slider, per Alfredo. Stacked they ran 128px each and
   the answer and photo they change landed under the fold. In a row, one card and the edge of the
   next fill the width — the peek is what says "swipe" — and the result sits right under them.
   Swiping to a card chooses it (main.js); a tap still does, and still scrolls the result into view.
   The row bleeds to the panel's edge (negative gutter, padded back) so the peek is cut by the panel,
   not by an invisible margin. "Ver qué cambia" is hidden: on a phone the card is the target. */
.router__dots { display: none; }
@media (max-width: 799px) {
  .router {
    display: flex;
    gap: var(--spacing-12);
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    overscroll-behavior-x: contain;
    scrollbar-width: none;
    margin-inline: calc(-1 * var(--gutter));
    margin-bottom: var(--spacing-12);
  }
  .router::-webkit-scrollbar { display: none; }
  /* Centred on the page's axis: the card in front sits in the middle with both neighbours peeking
     equally. The spacers are flex items, so their % resolves against the row like the cards' does:
     9% + 82% + 9%, less the gap the spacer brings with it. */
  .router::before,
  .router::after { content: ''; flex: 0 0 calc(9% - var(--spacing-12)); }
  .router__opt {
    flex: 0 0 82%;
    scroll-snap-align: center;
    /* One swipe, one card: a quick flick otherwise flew past Empleado straight to Ambos. */
    scroll-snap-stop: always;
    padding: var(--spacing-16);
    gap: var(--spacing-2);
    /* Icon beside the text, not above it, so the row stays one card tall. */
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    column-gap: var(--spacing-12);
    align-items: center;
  }
  .router__opt > :not(.router__icon) { grid-column: 2; }
  .router__icon { grid-row: 1 / span 2; margin-bottom: 0; }
  .router__more { display: none; }

  /* Where you are in the row. Position, not selection — before any choice it still says which card
     is in front — so it wears the panel foreground, not the selected cyan. */
  .router__dots {
    display: flex;
    justify-content: center;
    gap: var(--spacing-6);
    margin-bottom: var(--spacing-24);
  }
  .router__dots span {
    width: var(--spacing-6);
    height: var(--spacing-6);
    border-radius: var(--radius-full);
    background: color-mix(in oklch, var(--marketing-panel-foreground) 28%, transparent);
    transition:
      width var(--motion-duration-moderate) var(--motion-easing-move),
      background-color var(--motion-duration-moderate) var(--motion-easing-move);
  }
  .router__dots span[data-on='true'] { width: var(--spacing-16); background: var(--marketing-panel-foreground); }

  .router__media { margin-top: var(--spacing-24); }
}

/* Three static answers for readers without JS (and for crawlers); with JS they hide and the live
   paragraph shows the chosen one. The head script removes .no-js before first paint. */
html:not(.no-js) .router__answers { display: none; }
.no-js [data-router-answer] { display: none; }
.router__answers { display: grid; gap: var(--spacing-12); }

.router__answer {
  border-inline-start: var(--border-width-strong) solid var(--marketing-accent-line);
  padding-inline-start: var(--spacing-16);
  color: var(--marketing-panel-foreground-subtle);
  min-height: var(--spacing-48);
  max-width: 78ch;
}

/* ── The media ─────────────────────────────────────────────────────────────────────────
   The photograph is the ground and the pills float on it, as in the reference. The image
   is a background layer on purpose: it is decoration, it carries no information the text
   does not, and this way the block renders complete before the file exists and stays
   complete if it ever 404s — the gradient underneath is not a fallback bolted on, it is
   the base layer. Drop a file at public/landing/images/ and it appears.
   The scrim is NOT optional: it is what guarantees the pills stay legible over a
   photograph nobody has measured yet. */
.router__media {
  position: relative;
  margin: var(--block-gap) 0 0;
  padding: clamp(var(--spacing-20), 3vw, var(--spacing-40));
  border-radius: var(--radius-xl);
  overflow: clip;
  min-height: 320px;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: var(--spacing-24);
  background-color: color-mix(in oklch, var(--marketing-panel-foreground) 6%, transparent);
  background-image: url('/landing/images/como-trabajas.webp');
  background-size: cover;
  background-position: center;
}
/* One photograph per profile, swapped as a choice is made. Each rule lists the profile's image
   FIRST and the default photo SECOND: background layers paint top to bottom, and a layer whose
   file fails to load is skipped. So the three can ship before their images exist — until a file
   lands, its profile keeps showing the shared desk. Drop the file in and it appears. The set is
   specified in docs/landing-images.md so the three read as one series when they swap. */
.router__media[data-profile='independiente'] {
  background-image: url('/landing/images/como-trabajas-independiente.webp'), url('/landing/images/como-trabajas.webp');
}
.router__media[data-profile='empleado'] {
  background-image: url('/landing/images/como-trabajas-empleado.webp'), url('/landing/images/como-trabajas.webp');
}
.router__media[data-profile='ambos'] {
  background-image: url('/landing/images/como-trabajas-ambos.webp'), url('/landing/images/como-trabajas.webp');
}
/* The profile photos carry a person in the right half, face in the upper third. `cover` on a wide,
   short box crops top and bottom, and centred it cut foreheads; on a phone the box is taller than
   wide and crops the sides, and centred it cut the person in half. So the crop is anchored where
   the person is. The default photo, a desk with no one at it, keeps its centre. */
.router__media[data-profile] { background-position: 72% 22%, center; }
.router__media::before {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  /* A light veil, only on the left where the pills sit. The pills carry their own glass ground,
     so no text on this photo depends on the veil for contrast — it was a full-width darkening
     plus a bottom scrim, and the photos behind them barely showed. */
  background: linear-gradient(
    100deg,
    color-mix(in oklch, var(--marketing-panel-background) 72%, transparent) 0%,
    color-mix(in oklch, var(--marketing-panel-background) 40%, transparent) 35%,
    transparent 65%
  );
}
.router__media > * { position: relative; }

.pills { display: grid; gap: var(--spacing-8); justify-items: start; }
.pill {
  display: inline-flex;
  align-items: center;
  gap: var(--spacing-10);
  padding: var(--spacing-8) var(--spacing-16) var(--spacing-8) var(--spacing-8);
  border-radius: var(--radius-full);
  border: var(--border-width-default) solid color-mix(in oklch, var(--marketing-panel-foreground) 14%, transparent);
  background: color-mix(in oklch, var(--marketing-panel-background) 78%, transparent);
  color: var(--marketing-panel-foreground-subtle);
  transition:
    opacity var(--motion-duration-moderate) var(--motion-easing-move),
    color var(--motion-duration-moderate) var(--motion-easing-move),
    border-color var(--motion-duration-moderate) var(--motion-easing-move),
    transform var(--motion-duration-moderate) var(--motion-easing-move);
}
@supports (backdrop-filter: blur(1px)) {
  .pill { backdrop-filter: blur(var(--blur-sm)); }
}
/* The organic stagger of the reference: each pill sits a little further in than the last. */
.pill:nth-child(2) { transform: translateX(var(--spacing-8)); }
.pill:nth-child(3) { transform: translateX(var(--spacing-16)); }
.pill:nth-child(4) { transform: translateX(var(--spacing-8)); }
/* Each tab wears the colour the app gives that kind of money, so the five read as five
   things and not as one list repeated. The values come from the dark scale the panel
   declares — the product's own semantics, not decoration invented for a landing.

   Movimientos stays neutral, which is also the app's answer: a transfer between your own
   accounts is neither income nor expense, it only moves money, and MovimientosCard gives
   its icon no colour either.

   The colour says WHICH tab. The check says whether you have it. Two meanings, two
   encodings — the accent never doubles as the on/off signal, or a colour-blind reader
   would be reading one channel for both. */
.pill[data-tab='ingresos'] { --pill-accent: var(--fg-income); }
.pill[data-tab='gastos'] { --pill-accent: var(--fg-expense); }
.pill[data-tab='movimientos'] { --pill-accent: var(--fg-neutral); }
.pill[data-tab='tributarias'] { --pill-accent: var(--fg-tax); }
.pill[data-tab='provisiones'] { --pill-accent: var(--fg-provision); }

.pill__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--spacing-28);
  height: var(--spacing-28);
  border-radius: var(--radius-full);
  background: color-mix(in oklch, var(--pill-accent, var(--marketing-panel-foreground)) 18%, transparent);
  color: var(--pill-accent, var(--marketing-panel-foreground));
  flex: none;
}
.pill__label { font-weight: 500; }
.pill__check {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--spacing-20);
  height: var(--spacing-20);
  border-radius: var(--radius-full);
  border: var(--border-width-default) solid color-mix(in oklch, var(--marketing-panel-foreground) 20%, transparent);
  color: transparent;
  flex: none;
  transition:
    color var(--motion-duration-moderate) var(--motion-easing-move),
    background-color var(--motion-duration-moderate) var(--motion-easing-move),
    border-color var(--motion-duration-moderate) var(--motion-easing-move);
}
/* Three states, not two. Before you choose, a tab is neither on nor off for you — it just
   exists. Painting them all "on" by default would answer for the independiente profile
   without being asked. */
.pill[data-on='true'] {
  color: var(--marketing-panel-foreground);
  border-color: color-mix(in oklch, var(--pill-accent, var(--marketing-accent-on-panel)) 40%, transparent);
}
.pill[data-on='true'] .pill__check {
  color: var(--marketing-panel-background);
  background: var(--marketing-accent-on-panel);
  border-color: var(--marketing-accent-on-panel);
}
.pill[data-on='false'] { opacity: 0.4; }


@media (min-width: 900px) {
  .router__media { min-height: 380px; }
}

/* ── §7. Cards & feature blocks ─────────────────────────────────────────────────────── */
.grid-3 { display: grid; gap: var(--spacing-16); }
@media (min-width: 760px) { .grid-3 { grid-template-columns: repeat(3, 1fr); } }
.card {
  background: var(--bg-surface-subtle);
  border: var(--border-width-default) solid var(--border-default);
  border-radius: var(--radius-xl);
  padding: var(--spacing-20);
  display: flex;
  flex-direction: column;
  gap: var(--spacing-10);
}
.card__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--spacing-40);
  height: var(--spacing-40);
  border-radius: var(--radius-lg);
  background: var(--bg-neutral-subtle);
  color: var(--fg-neutral);
  flex: none;
}
.card__icon[data-tone='tax'] { background: var(--bg-tax-subtle); color: var(--fg-tax); }
.card__icon[data-tone='income'] { background: var(--bg-income-subtle); color: var(--fg-income); }
.card__icon[data-tone='provision'] { background: var(--bg-provision-subtle); color: var(--fg-provision); }

.section-head { display: flex; flex-direction: column; gap: var(--spacing-12); margin-bottom: var(--block-gap); }
.section-head .lead { max-width: 60ch; }

.section-head--center { align-items: center; text-align: center; }

/* Features — Deel's tabs-horizontal. A centred tab bar over two rounded boxes: the copy on
   the left, the capture on the right.

   The capture box has ONE shape for all six. The captures come at six aspect ratios, from a
   3.5:1 KPI strip to a near-square annual chart; each is contained inside the box rather than
   sized by it, so switching tabs never moves the page under the bar — and the six read as a
   set instead of six images at six sizes. */
.showcase { display: grid; gap: var(--block-gap); }
/* With JS the bar is shown and only the selected panel is; the gap is for the no-JS stack. */
.showcase:has(> .showcase__tabs:not([hidden])) { gap: var(--spacing-32); }

/* Scrolls sideways when six labels do not fit (every phone). Centring a scrolling flex row
   with justify-content clips its start; auto margins on the ends centre it when it fits and
   collapse to zero when it does not. */
.showcase__tabs {
  display: flex;
  gap: var(--spacing-24);
  overflow-x: auto;
  scrollbar-width: none;
  border-bottom: var(--border-width-default) solid var(--border-default);
}
.showcase__tabs::-webkit-scrollbar { display: none; }
.showcase__tab:first-child { margin-inline-start: auto; }
.showcase__tab:last-child { margin-inline-end: auto; }
.showcase__tab {
  flex: none;
  appearance: none;
  border: 0;
  background: none;
  cursor: pointer;
  white-space: nowrap;
  padding: var(--spacing-12) var(--spacing-4);
  color: var(--fg-subtle);
  /* The indicator is an inset shadow, not a border, so selecting a tab never changes its box. */
  box-shadow: inset 0 calc(-1 * var(--border-width-strongest)) 0 transparent;
  transition:
    color var(--motion-duration-fast) var(--motion-easing-move),
    box-shadow var(--motion-duration-fast) var(--motion-easing-move);
}
.showcase__tab:hover { color: var(--fg-default); }
/* Numbered, per Alfredo: six options read as a sequence to go through, not a menu. The number is
   hidden from assistive tech (each tab's name stays "Aportes", not "02 Aportes"); on the selected
   tab it takes the indicator's cyan. No opacity to make it quieter: at 70% it measured 4.21:1 in
   light, under the 4.5 its 16px needs. */
.showcase__num {
  margin-inline-end: var(--spacing-6);
  font-variant-numeric: tabular-nums;
}
.showcase__tab[aria-selected='true'] .showcase__num { color: var(--fg-brand); }
/* Cyan is the selected state — the one place design.md §Principios #4 reserves it for. */
.showcase__tab[aria-selected='true'] {
  color: var(--fg-default);
  box-shadow: inset 0 calc(-1 * var(--border-width-strongest)) 0 var(--border-brand-strong);
}
.showcase__tab:focus-visible { outline-offset: calc(-1 * var(--border-width-focus-default)); }

.showcase__panel {
  display: grid;
  gap: var(--spacing-16);
}
@media (min-width: 900px) {
  .showcase__panel { grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); gap: var(--spacing-24); }
}
.showcase__panel[data-entering] {
  animation: showcase-in var(--motion-duration-slow) var(--motion-easing-enter);
}
@keyframes showcase-in {
  from { opacity: 0; transform: translateY(var(--spacing-8)); }
  to { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  .showcase__panel[data-entering] { animation: none; }
}

.showcase__copy {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: flex-start;
  gap: var(--spacing-12);
  padding: clamp(var(--spacing-24), 4vw, var(--spacing-48));
  border-radius: var(--radius-2xl);
  background: var(--bg-surface-subtle);
  border: var(--border-width-default) solid var(--border-default);
}
.feature__list { display: grid; gap: var(--spacing-8); color: var(--fg-subtle); margin-top: var(--spacing-4); }
.feature__list li { display: flex; gap: var(--spacing-8); }
.feature__list svg { color: var(--fg-net); flex: none; margin-top: 3px; }

/* The privacy stage's ground — the surface warmed toward brand — so the page has one
   "a piece of the app sits here" backdrop, not two.

   The capture fills the box, per Alfredo. Every capture is 3:2 (scripts/landing-captures.mjs), so
   it takes the box's full inner width and the box never changes shape between tabs. It runs to the
   box's bottom edge and fades there: a fixed-ratio frame cannot snap to the end of a row, and the
   fade makes the cut read as the view continuing below rather than as a slice through a line. */
.showcase__media {
  margin: 0;
  overflow: hidden;
  padding: clamp(var(--spacing-16), 4vw, var(--spacing-40)) clamp(var(--spacing-16), 4vw, var(--spacing-40)) 0;
  border-radius: var(--radius-2xl);
  border: var(--border-width-default) solid var(--border-default);
  background: linear-gradient(
    135deg,
    var(--bg-surface-default) 0%,
    color-mix(in oklch, var(--bg-surface-default), var(--bg-brand) 16%) 100%
  );
}
.showcase__media img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 3 / 2;
  /* Never stretched: an image of another ratio (an old capture still in a browser's cache) crops
     from the top instead of distorting. */
  object-fit: cover;
  object-position: top;
  border-radius: var(--radius-xl) var(--radius-xl) 0 0;
  border: var(--border-width-default) solid var(--border-default);
  border-bottom: 0;
  box-shadow:
    0 var(--elevation-floating-key-offset-y) var(--elevation-floating-key-blur)
      var(--elevation-floating-key-spread) var(--shadow-key),
    0 var(--elevation-floating-ambient-offset-y) var(--elevation-floating-ambient-blur)
      var(--elevation-floating-ambient-spread) var(--shadow-ambient);
  -webkit-mask-image: linear-gradient(to bottom, black 78%, transparent);
  mask-image: linear-gradient(to bottom, black 78%, transparent);
}

/* ── §8. Privacy — the stage and three tall cards ────────────────────────────────────
   Littio's shape. Light, and following the page theme: every colour here is a surface or
   foreground token that flips on its own, so dark mode needs no second rule — and never gets a
   light slab, the failure this section was once rebuilt to avoid.

   It was the dark marketing panel. With the closing block now light as well, dark is left to
   the two pieces that frame the page, and the panel's tokens stay in use there. */

/* The stage. The gradient is the surface leaning toward the brand, mixed from tokens rather
   than picked: Littio's lavender-to-blue, in Neto's own cyan. --marketing-accent-wash was the
   obvious candidate and is deliberately not used — it holds the same value in both themes, so
   on the dark page it would be a pale block. */
.privacy__stage {
  container-type: inline-size;
  position: relative;
  overflow: clip;
  margin: 0 0 var(--spacing-16);
  display: flex;
  justify-content: center;
  padding-top: clamp(var(--spacing-32), 5vw, var(--spacing-64));
  border-radius: var(--radius-2xl);
  background: linear-gradient(
    135deg,
    var(--bg-surface-default) 0%,
    color-mix(in oklch, var(--bg-surface-default), var(--bg-brand) 16%) 100%
  );
}

/* The phone runs off the bottom of the stage with no lower bezel, as in the reference. Where
   it stops is decided by the capture, cropped just past "No acepto" — not by the stage's
   height, which would move the cut at every width as the phone scales. */
/* The status bar scales with the PHONE, not the page. In fixed px it was sized for the 320px
   desktop phone and at 390px viewport, where the phone clamps to 230px, the 74px island ran
   straight into the signal and wifi glyphs — 74 of island plus ~65 of icons into 172 of room.
   `container-type` makes the phone the unit, so every part of the bar is a share of its width
   and the proportions hold at every size. */
/* Sized against the STAGE, not the viewport. It was `clamp(230px, 26vw, 320px)` — and 26vw
   is a share of the window, so from 320px up to ~885px the floor won and the phone sat at a
   fixed 230px while the stage grew from 280 to 820 around it: 82% of the stage at 320, 33% at
   768, with its copy at 8.7px across that whole range. The stage is the container now, so the
   phone is 80% of it until it reaches 320px, where it stops and the stage keeps growing — back
   to the reference's ~30% on desktop.
   Bezel and radius are shares of the phone's own width too. Fixed at 9px and 46px they read as
   a heavier, rounder device the smaller it got. */
.phone {
  --phone-w: min(80cqi, 320px);
  container-type: inline-size;
  width: var(--phone-w);
  border: calc(var(--phone-w) * 0.028) solid var(--bg-surface-chrome);
  border-bottom: 0;
  border-radius: calc(var(--phone-w) * 0.145) calc(var(--phone-w) * 0.145) 0 0;
  background: var(--bg-surface-canvas);
  overflow: hidden;
  box-shadow:
    0 var(--elevation-floating-key-offset-y) var(--elevation-floating-key-blur)
      var(--elevation-floating-key-spread) var(--shadow-key),
    0 var(--elevation-floating-ambient-offset-y) var(--elevation-floating-ambient-blur)
      var(--elevation-floating-ambient-spread) var(--shadow-ambient);
}
.phone img { display: block; width: 100%; height: auto; }
.phone__status {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  height: 12.5cqi;
  padding-inline: 6.5cqi;
  color: var(--fg-default);
  background: var(--bg-surface-canvas);
}
.phone__time { font-weight: 600; font-size: 4.4cqi; font-variant-numeric: tabular-nums; }
/* The island is hardware, and hardware does not change with the theme: black in both. Not
   --bg-surface-inverse, which is "the opposite of the page" and so turns WHITE on the dark
   page. The marketing panel ground is the one token that holds a dark value in both themes. */
.phone__island {
  width: 24cqi;
  height: 7.2cqi;
  border-radius: var(--radius-full);
  background: var(--marketing-panel-background);
}
.phone__icons { display: flex; align-items: center; justify-content: flex-end; gap: 1.3cqi; }
.phone__icons svg { height: 3.6cqi; width: auto; }

/* Three tall cards: icon at the top, words at the bottom, air in between. The height is what
   makes it read as a grid of objects rather than a list; `justify-content: space-between`
   is what puts the text on the floor of the card whatever its length. */
.privacy__cards { display: grid; gap: var(--spacing-16); }
@media (min-width: 760px) { .privacy__cards { grid-template-columns: repeat(3, 1fr); } }
/* Centred on both axes, with the icon held close to its text. It used to push the text to the
   card's floor — `space-between` over a 230px min-height — which is Littio's layout, but with
   the text centred as well it left the icon floating alone at the top, ~90px from the words it
   labels. Alfredo asked for the gap to close. The grid still stretches the three cards to one
   height, so they stay level; the group now sits in the middle of each. */
.privacy__card {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  justify-content: center;
  gap: var(--spacing-16);
  padding: var(--spacing-32) var(--spacing-24);
  border-radius: var(--radius-2xl);
  background: var(--bg-surface-default);
}
.privacy__icon { color: var(--fg-subtle); }
.privacy__card h3 { margin-bottom: var(--spacing-8); }
.privacy__card p { color: var(--fg-subtle); }

/* ── §9. Calculator — a protagonist block ─────────────────────────────────────────────
   A warm panel so it stands apart from the cool privacy stage above it: the surface leaning
   toward --bg-tax, mixed from tokens so it flips with the theme. The result card inside is
   built from the landing's own tokens rather than a screenshot of the calculator page, whose
   hand-copied tokens still render it in a different typeface. */
.calc {
  display: grid;
  gap: var(--block-gap);
  align-items: center;
  padding: clamp(var(--spacing-24), 5vw, var(--spacing-64));
  border-radius: var(--radius-2xl);
  /* `in oklab`, not `in oklch`. oklch interpolates HUE, and in dark mode the surface is a
     blue slate with real chroma (hue ~260°) while the amber sits near 95° — the short way
     round between them runs through cyan, so the "warm" panel rendered teal in dark and
     stopped standing apart from the cyan stage above it. In light the surface is nearly
     achromatic, its hue barely weighs, and the bug was invisible. oklab mixes on the a/b
     axes with no hue arc, so the tint stays amber in both themes. */
  background: linear-gradient(
    135deg,
    var(--bg-surface-default) 0%,
    color-mix(in oklab, var(--bg-surface-default), var(--bg-tax) 22%) 100%
  );
}
@media (min-width: 900px) { .calc { grid-template-columns: minmax(0, 1fr) minmax(0, 0.9fr); } }
.calc__copy { display: flex; flex-direction: column; align-items: flex-start; gap: var(--spacing-16); }
.calc__copy .lead { max-width: 40ch; }
.calc__copy .cta-row { margin-top: var(--spacing-8); }

.calc__card {
  margin: 0;
  display: grid;
  gap: var(--spacing-20);
  padding: var(--spacing-24);
  border: var(--border-width-default) solid var(--border-default);
  border-radius: var(--radius-2xl);
  background: var(--bg-surface-subtle);
  color: var(--fg-subtle);
  box-shadow:
    0 var(--elevation-floating-key-offset-y) var(--elevation-floating-key-blur)
      var(--elevation-floating-key-spread) var(--shadow-key),
    0 var(--elevation-floating-ambient-offset-y) var(--elevation-floating-ambient-blur)
      var(--elevation-floating-ambient-spread) var(--shadow-ambient);
}
.calc__inputs { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: var(--spacing-12); }
.calc__field {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-4);
  padding: var(--spacing-12) var(--spacing-16);
  border-radius: var(--radius-lg);
  background: var(--bg-surface-default);
}
.calc__field .ts-amount-large { color: var(--fg-default); }
.calc__result {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-4);
  padding-top: var(--spacing-16);
  border-top: var(--border-width-default) solid var(--border-default);
}
/* The total is the only figure on this card that should pull the eye. --fg-tax on the card
   measures 4.97:1 in light and 10.15:1 in dark — large text needs 3. */
.calc__total {
  font-weight: 700;
  font-size: var(--display-lg);
  line-height: 1.05;
  letter-spacing: -1px;
  font-variant-numeric: tabular-nums;
  color: var(--fg-tax);
}

/* One hue in three strengths, not three hues. The three parts are all social security — one
   kind of money — and --bg-tax and --fg-tax happen to share a value in dark mode, so two
   separate tokens would have merged pensión and salud into one colour there. An alpha ramp of
   one token stays distinct in both themes. Colour is never the only carrier: every row names
   its part. */
.calc__bar {
  display: flex;
  gap: 2px;
  height: var(--spacing-12);
  border-radius: var(--radius-full);
  overflow: hidden;
}
.calc__seg { width: var(--pct); }
.calc [data-part='pension'] { --part: var(--fg-tax); }
.calc [data-part='salud'] { --part: color-mix(in oklch, var(--fg-tax) 62%, transparent); }
.calc [data-part='arl'] { --part: color-mix(in oklch, var(--fg-tax) 32%, transparent); }
.calc__seg, .calc__dot { background: var(--part); }

.calc__rows { display: grid; gap: var(--spacing-8); }
.calc__rows li {
  display: grid;
  grid-template-columns: 10px minmax(0, 1fr) auto;
  align-items: center;
  gap: var(--spacing-10);
  color: var(--fg-default);
}
.calc__dot { width: 10px; height: 10px; border-radius: var(--radius-full); }
.calc__rate { color: var(--fg-subtle); }

/* ── §10. Pro — a brand mesh in the page's bounding-box panel ─────────────────────────────
   Same near-full-bleed rounded geometry as the other panels (see .panel), dark in both themes,
   centred. The ground is a mesh: four radial gradients anchored at the corners over the
   marketing panel ground, every colour a brand token — the brand cyan, the on-panel accent,
   and two deep teals mixed from them. No hue picked by eye.

   The colour lives at the EDGES and the middle stays near the panel ground, on purpose. Under
   data-theme="dark" the filled button is #22d3ee — the very value of --marketing-accent-on-panel,
   the brightest blob. A bright blob behind the button would make the button vanish into it, so
   the mesh frames the centre instead of filling it.

   The four blobs mix `in oklab`, not oklch: see .calc for the hue arc that turned a warm panel
   teal. Here every stop is already cyan, but a teal mixed toward slate in oklch would still swing
   its hue on the way. */
.pro-panel {
  position: relative;
  isolation: isolate;
  overflow: clip;
  margin: var(--spacing-12);
  border-radius: var(--radius-2xl);
  color: var(--marketing-panel-foreground);
  background-color: var(--marketing-panel-background);
  background-image:
    radial-gradient(at 0% 0%, color-mix(in oklab, var(--bg-brand) 62%, transparent) 0, transparent 46%),
    radial-gradient(at 100% 0%, color-mix(in oklab, var(--bg-brand), var(--marketing-panel-background) 58%) 0, transparent 52%),
    radial-gradient(at 100% 100%, color-mix(in oklab, var(--marketing-accent-on-panel) 48%, transparent) 0, transparent 44%),
    radial-gradient(at 0% 100%, color-mix(in oklab, var(--bg-brand), var(--marketing-panel-background) 42%) 0, transparent 50%);
}
/* The texture: grain from an inline SVG turbulence filter, desaturated so it adds light and
   shadow and no hue of its own. A data URI, so the page still makes no third-party request.
   It sits under the content (z-index -1 inside the isolated panel) and never takes a click. */
.pro-panel::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  opacity: 0.22;
  mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='220' height='220'><filter id='g'><feTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/><feColorMatrix type='saturate' values='0'/></filter><rect width='100%' height='100%' filter='url(%23g)'/></svg>");
}
.pro {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: var(--spacing-16);
}
.pro h2 { color: var(--marketing-panel-foreground); }
/* One line, per Alfredo: the narrow column fits its 64 characters from ~620px up; a phone wraps it. */
.pro .lead { max-width: none; }
.pro .cta-row { justify-content: center; margin-top: var(--spacing-8); }
/* The status chip. It was the system Badge — #1e293b on a panel that is itself near #0f172a, so it
   all but disappeared into the mesh. Brand cyan now, on the only tokens measured for this ground:
   a tinted fill and rim from --marketing-accent-on-panel, the word Pro in that accent, the rest in
   the panel foreground.

   The dot is cyan and NOT --bg-live. That token exists and is green, and it is what the header's
   "TRM en vivo" dot wears — it means live data. "En construcción" is not live data, and borrowing
   the status colour would claim a state that is not true.

   It pulses once every two seconds: a cycle, not a transition, so its period comes off
   --motion-duration-spin like the Spinner's. Reduced motion keeps the dot and drops the pulse —
   less movement, same information. */
.pro-chip {
  display: inline-flex;
  align-items: center;
  gap: var(--spacing-8);
  height: var(--spacing-32);
  padding-inline: var(--spacing-12) var(--spacing-16);
  border-radius: var(--radius-full);
  border: var(--border-width-default) solid color-mix(in oklab, var(--marketing-accent-on-panel) 55%, transparent);
  /* OPAQUE, mixed into the panel ground — not a translucent tint. Translucent, the chip let the
     mesh through, and on a 375px panel the corner blobs crowd the centre: the word Pro, cyan on a
     cyan-washed backdrop, measured 4.20:1 against the 4.5 its 14px needs (4.96 at 1280). An opaque
     fill makes the chip's contrast independent of wherever the blobs happen to land. */
  background: color-mix(in oklab, var(--marketing-panel-background), var(--marketing-accent-on-panel) 14%);
  color: var(--marketing-panel-foreground);
}
.pro-chip strong { color: var(--marketing-accent-on-panel); font-weight: 600; }
.pro-chip__dot {
  position: relative;
  width: var(--spacing-8);
  height: var(--spacing-8);
  border-radius: var(--radius-full);
  background: var(--marketing-accent-on-panel);
}
.pro-chip__dot::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: var(--marketing-accent-on-panel);
  animation: pro-chip-pulse calc(var(--motion-duration-spin) * 2) var(--motion-easing-enter) infinite;
}
@keyframes pro-chip-pulse {
  0% { transform: scale(1); opacity: 0.6; }
  70%, 100% { transform: scale(2.6); opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .pro-chip__dot::after { animation: none; opacity: 0; }
}

/* ── §11. FAQ ───────────────────────────────────────────────────────────────────────── */
.faq { display: grid; gap: var(--spacing-2); max-width: var(--container-narrow); }
/* Centred as a COLUMN, not as text. The questions are accordion rows with the toggle pinned
   to the right; centring their text would break the row, so each question stays left inside
   a column that sits in the middle of the page. Scoped to this section: .section-head is
   shared by blocks that stay left-aligned. */
.section--faq .faq { margin-inline: auto; }
.section--faq .section-head { align-items: center; text-align: center; }
.faq details {
  border-bottom: var(--border-width-default) solid var(--border-default);
}
.faq summary {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--spacing-16);
  padding: var(--spacing-20) 0;
  cursor: pointer;
  list-style: none;
  font-weight: 600;
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary svg {
  flex: none;
  color: var(--fg-subtle);
  transition: transform var(--motion-duration-fast) var(--motion-easing-move);
}
.faq details[open] summary svg { transform: rotate(45deg); }
.faq p { padding-bottom: var(--spacing-20); color: var(--fg-subtle); max-width: 68ch; }
.faq p a { color: var(--fg-brand); text-decoration: underline; text-underline-offset: 2px; }
.faq p a:hover { color: var(--fg-default); }

/* ── §11bis. Final CTA — photograph under an overlay ───────────────────────────────────
   The overlay is the contract, not the decoration: it is what lets display-sized text sit on
   a photograph nobody has measured. Contrast is measured against the real photo on its worst
   pixel (see docs/landing-images.md), in both themes, not assumed.

   Same near-full-bleed rounded geometry as the profile block and the footer (see .panel).
   `overflow: clip` rounds the photograph and the scrim together.

   It is the one panel that follows the page theme, on purpose. Stacked on the footer — dark in
   both themes — a dark closing block ran the two together into one mass. So in light mode it
   is light, which is what most visitors see; in dark mode it follows the page rather than
   becoming a light bomb, and the photograph against the footer's flat ground carries the
   separation there. The surface tokens flip on their own, so there is no second rule for dark. */
.cta-final {
  position: relative;
  isolation: isolate;
  overflow: clip;
  margin: var(--spacing-12);
  border-radius: var(--radius-2xl);
  text-align: center;
  color: var(--fg-default);
  background-color: var(--bg-surface-default);
  background-image: url('/landing/images/cta-final.webp');
  background-size: cover;
  background-position: center 40%;
}
/* Strongest through the middle, where the text is. The first dark version thinned out exactly
   where the lead and the note sit and measured 3.87 / 3.47 against 4.5. Light turns the risk
   around: the danger is no longer the blown-out window but the DARK parts of the frame — her
   hair, the shadowed room — under dark text. Same four-stop shape, re-measured for it. */
.cta-final::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background: linear-gradient(
    to bottom,
    color-mix(in oklch, var(--bg-surface-default) 80%, transparent) 0%,
    color-mix(in oklch, var(--bg-surface-default) 92%, transparent) 45%,
    color-mix(in oklch, var(--bg-surface-default) 92%, transparent) 78%,
    color-mix(in oklch, var(--bg-surface-default) 78%, transparent) 100%
  );
}
.cta-final__inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--spacing-16);
}
/* No measure cap: Alfredo wants this on one line. 46ch was wrapping it in two with room to spare.
   The .wrap--narrow column still bounds it; on a phone it cannot fit 64 characters and wraps. */
.cta-final .lead { color: var(--fg-subtle); max-width: none; }
.cta-final .cta-row { justify-content: center; margin-top: var(--spacing-8); }

/* ── §12. Footer ────────────────────────────────────────────────────────────────────────
   A marketing panel, so it carries data-theme="dark" like the others: dark whatever the page
   is doing, with its subtree resolving against the scale built for a dark ground.

   The same near-full-bleed rounded shape as the profile block, for the same measured reason
   (see .panel). The hairline that used to sit on top is gone: it existed because this band
   touched the closing block with no gap, and two dark grounds ran together. With the inset,
   the page's own ground separates them — a boundary that no longer depends on anything
   having loaded. */
.footer {
  margin: var(--spacing-12);
  border-radius: var(--radius-2xl);
  overflow: clip;
  background: var(--marketing-panel-background);
  color: var(--marketing-panel-foreground-subtle);
  padding-block: var(--section-y);
}
.footer__top {
  display: grid;
  gap: var(--block-gap);
  margin-bottom: var(--spacing-48);
}
@media (min-width: 860px) {
  .footer__top { grid-template-columns: minmax(0, 1fr) minmax(0, 1.2fr); gap: clamp(32px, 5vw, 80px); }
}
.footer__pitch { display: flex; flex-direction: column; align-items: flex-start; gap: var(--spacing-16); max-width: 42ch; }
.footer__pitch h2 { color: var(--marketing-panel-foreground); }
/* The mark signs the page, per Alfredo — the nav's own brand lockup, in the panel's foreground. */
.footer__brand { color: var(--marketing-panel-foreground); }

.footer__nav { display: grid; gap: var(--spacing-24); }
@media (min-width: 560px) { .footer__nav { grid-template-columns: repeat(3, 1fr); } }
.footer__nav h3 {
  color: var(--marketing-panel-foreground);
  text-transform: uppercase;
  margin-bottom: var(--spacing-12);
}
.footer__nav li + li { margin-top: var(--spacing-8); }
.footer__nav a { transition: color var(--motion-duration-fast) var(--motion-easing-move); }
.footer__nav a:hover { color: var(--marketing-panel-foreground); }
.footer__nav a[aria-current='page'] { color: var(--marketing-panel-foreground); }

.footer__legal {
  display: flex;
  flex-wrap: wrap;
  gap: var(--spacing-12) var(--spacing-24);
  justify-content: space-between;
  padding-top: var(--spacing-20);
  border-top: var(--border-width-default) solid color-mix(in oklch, var(--marketing-panel-foreground) 12%, transparent);
}


/* ── §12b. Phone: centred ──────────────────────────────────────────────────────────────────
   Per Alfredo, below 760px the page reads down a centred axis, text above all — the buttons
   were already full width, and left-aligned text over full-width buttons read as two layouts.

   Centred: every heading block, the hero copy and its three notes, the profile answer, the
   problem cards, the feature copy, the calculator copy, the footer's links and legal lines.
   NOT centred, on purpose — these are structures read across, not text read down:
   - the hero's flow card and the calculator's breakdown (a label on the left, an amount on the
     right; centring breaks the pairing),
   - the pills on the photo (they sit in the veil on the left, the photo's quiet third),
   - the FAQ accordion (the toggle is pinned to the right of each question).
   Paragraph caps (max-width in ch) get auto margins, or a capped paragraph centres its text inside
   a box that is still pinned left. */
@media (max-width: 759px) {
  .hero__copy,
  .calc__copy,
  .showcase__copy,
  .section-head,
  .panel__head { align-items: center; text-align: center; }
  .panel__head,
  .hero__copy .lead,
  .calc__copy .lead,
  .section-head .lead { margin-inline: auto; }
  .panel__head { display: flex; flex-direction: column; }

  .cta-note { justify-content: center; }

  /* The answer's left rule is a quotation mark for a left-aligned paragraph; centred, it would
     hang off one side of nothing. */
  .router__answer {
    text-align: center;
    border-inline-start: 0;
    padding-inline-start: 0;
    margin-inline: auto;
  }

  .card { align-items: center; text-align: center; }

  /* A checklist does not centre line by line: each item centred on its own put the check marks at
     different x positions, so the list stopped reading as one list (Alfredo). The LIST is centred
     as a block — it shrinks to its widest item — and inside it every item starts at the same edge,
     checks in one column. */
  .feature__list { justify-items: start; text-align: start; }

  .footer__nav { text-align: center; }
  .footer__legal { flex-direction: column; align-items: center; text-align: center; }
}

/* ── §13. Reveal ────────────────────────────────────────────────────────────────────────
   Opacity + 16px of travel, staggered between siblings. What leaves goes one step faster
   than what arrived (design-system/docs/15-motion.md §1) — nothing here leaves, so only
   the enter curve is used. */
[data-reveal] {
  opacity: 0;
  transform: translateY(16px);
  transition:
    opacity var(--motion-duration-reveal) var(--motion-easing-enter),
    transform var(--motion-duration-reveal) var(--motion-easing-enter);
  transition-delay: var(--reveal-delay, 0ms);
}
[data-reveal][data-shown='true'] { opacity: 1; transform: none; }

/* Less movement never means less information (PRODUCT.md §4.9): everything is visible, the
   travel is what goes. */
@media (prefers-reduced-motion: reduce) {
  [data-reveal] { opacity: 1; transform: none; transition: none; }
  .flow__seg { transition: none; }
  .btn:active { transform: none; }
  .nav__inner, .nav { transition: none; }
}

/* Anything that never reaches the observer (no JS, print, crawlers) must still be readable. */
.no-js [data-reveal] { opacity: 1; transform: none; }
