/* ===========================================================================
   Pesito — Marketing site, single source of truth for styles.

   Palette (2026-07-16, Lotería): traditional Mexican Lotería card
   language — flat, bright, highly saturated hues (verde, amarillo,
   azul cobalto, rosa mexicano, coral), every colored surface outlined
   in near-black ink, on warm cream card stock. Supersedes the
   2026-07-11 mono-verde rationalization; the editorial panel system
   stays, recolored. Rojo survives ONLY as the semantic negative
   (errors, legal notices, comparison "cons"); festive warmth comes
   from coral/rosa/amarillo instead, so error-red keeps its meaning.

   Source of truth: docs/design-system/MASTER.md (contrast-validated).
   Design tokens started from docs/website-prompt.md §3–§4.
   =========================================================================== */

:root {
  /* ═════════════════════════════════════════════════════════════════
     THEMING MODEL — 2026-08 (dark mode + high contrast).

     Every neutral is declared twice: a plain light value first, then
     the same token restated with `light-dark()`. Engines that don't
     know `light-dark()` (pre-2024) drop the second declaration and
     keep the light site exactly as it shipped; everything else
     resolves against the element's `color-scheme`, which `html`
     sets to `light dark` and the `data-theme` attribute pins.

     The dark theme is not a separate skin — it is the SAME Lotería
     grammar inverted: flat saturated fills inside a BONE frame on
     warm ink card stock, instead of inside a black frame on warm
     cream card stock. That is why the saturated hues below carry a
     single value: verde, amarillo, cobalto, rosa and coral are the
     deck, and the deck does not change color when you turn the
     lights off. Only the paper and the ink swap.
     ═════════════════════════════════════════════════════════════ */

  /* Color tokens — the verde ladder */
  --verde-900: #003D26;
  --verde-700: #006847;
  --verde-500: #00A651;
  --verde-300: #6FCF97;
  /* The two lightest rungs are TINTS (surfaces), not fills, so they
     invert with the paper. */
  --verde-100: #DFF2E7;
  --verde-100: light-dark(#DFF2E7, #0A2B1A);
  --verde-050: #F4FAF6;
  --verde-050: light-dark(#F4FAF6, #0A2015);

  /* Semantic negative only — never decorative. 700/500 are type,
     200/100/050 are surfaces; both ends invert. */
  --rojo-700: #A50F20;
  --rojo-700: light-dark(#A50F20, #FDA19B);
  --rojo-500: #CE1126;
  --rojo-500: light-dark(#CE1126, #F66D67);
  --rojo-200: #F7C9CE;
  --rojo-200: light-dark(#F7C9CE, #4B1D1B);
  --rojo-100: #FBD8DC;
  --rojo-100: light-dark(#FBD8DC, #3B1816);
  --rojo-050: #FDECEE;
  --rojo-050: light-dark(#FDECEE, #2B1311);

  /* `--blanco` is the RAISED surface (cards, open FAQ rows, the
     calculator), one step brighter than the canvas — not "the color
     white". In dark it is the one step lighter than the ink stock. */
  --blanco: #FFFFFF;
  --blanco: light-dark(#FFFFFF, #2E2921);
  /* Canvas + band neutrals: warm cream — Lotería card stock. */
  --hueso: #FAF7F2;
  --hueso: light-dark(#FAF7F2, #14100A);
  /* Slightly deeper-than-hueso backgrounds for legal callouts and
     inline sidebars. Sits between `--hueso` and `--crema` so it reads
     as "a card on the hueso canvas" rather than a muddy second tone. */
  --hueso-100: #F6F3EB;
  --hueso-100: light-dark(#F6F3EB, #201C16);
  --hueso-200: #F2EEE0;
  --hueso-200: light-dark(#F2EEE0, #2E2921);
  --crema: #F2EEE6;
  --crema: light-dark(#F2EEE6, #211C14);
  /* Ink ramp — neutral; tinta-900 doubles as the Lotería outline.
     Dark values are warm bone, not pure white: a #FFF frame on near
     -black halates against the saturated fills. */
  --tinta-900: #0B0B0B;
  --tinta-900: light-dark(#0B0B0B, #F7F1E6);
  --tinta-700: #2A2A2E;
  --tinta-700: light-dark(#2A2A2E, #DCD7CD);
  --tinta-500: #55555C;
  --tinta-500: light-dark(#55555C, #B3ADA3);
  /* `--tinta-400` fills the gap between `--tinta-500` (body copy) and
     `--tinta-300` (hairlines). Used for dimmed metadata such as
     "Vigente a partir de …" on legal pages. Held at ≥4.5:1 on hueso. */
  --tinta-400: #62626A;
  --tinta-400: light-dark(#62626A, #A49E94);
  --tinta-300: #C7C7CC;
  --tinta-300: light-dark(#C7C7CC, #413C35);

  /* Lotería accents — flat, saturated, always inside an ink outline.
     Contrast pairs validated in docs/design-system/MASTER.md:
     ink type on amarillo/coral/verde-500; white type on azul-700/
     verde-700/rosa. Rosa-600 is the body-copy-safe rosa. */
  --amarillo-500: #FFC400;
  --azul-700: #0A45A6;
  --rosa-500: #E4007C;
  --rosa-600: #C4006B;
  --coral-500: #FF5A36;
  --lila-300: #C7B9F2;

  --app-green: #00D632;

  /* The green period that closes the hero headline. Deep verde reads
     as ink-adjacent on cream but sinks into a near-black canvas, so
     dark mode steps it up to the app green. */
  --brand-dot: #006847;
  --brand-dot: light-dark(#006847, #00CC64);

  /* Brand green wherever it has to behave like TYPE or an ICON rather
     than a fill: the wordmark, and every stroked glyph on a card.
     `--verde-700` is 5.3:1 on cream but only 1.9:1 on ink stock, which
     is what made the logo look washed out in dark mode. Mint is the
     same hue family read at the other end of the ramp — 10.7:1 there.
     Calmer than `--brand-dot` on purpose: the dot is punctuation and
     wants to pop, a row of six icons does not. */
  --accent-verde: #006847;
  --accent-verde: light-dark(#006847, #6FCF97);
  /* Hover/emphasis step for accent-verde text: darker in light,
     lighter in dark. */
  --accent-verde-strong: light-dark(#003D26, #9ADFB8);

  /* Environment fills for LARGE surfaces (panels, bars) — dark mode
     pulls the three loud Lotería fields down a step so a full-width
     card is festive, not glaring, on the near-black canvas. Small
     pops (chips, rail cards, footer cells, buttons) keep the bright
     values: tiny areas carry brightness well; big fields don't.
     Ink stays >=4.5:1 on every deepened fill (verde 4.9, amarillo
     9.4); white reads 10.4:1 on the deep cobalt. */
  /* Plain hex first, light-dark() second: browsers without
     light-dark() (Safari ≤16 — real devices in the remittance-sender
     demographic) drop the second declaration and keep the light hex,
     so the big editorial panels never lose their fill under their
     fixed-white ink. The dark values are restated in the dark-theme
     blocks at the bottom of this file for those same engines. */
  --fill-verde-vivo: #00A651;
  --fill-amarillo: #FFC400;
  --fill-azul: #0A45A6;
  --fill-verde-vivo: light-dark(#00A651, #0C9247);
  --fill-amarillo: light-dark(#FFC400, #E7B41E);
  --fill-azul: light-dark(#0A45A6, #05378C);

  /* ---- Theme-invariant ink pair -------------------------------
     The dark theme flips the neutral ramp (`--tinta-*` becomes
     light, `--hueso`/`--blanco` become dark) so 4k lines of
     surface + outline rules retheme with no markup surgery. The
     ONE thing that must not flip is type that sits on a saturated
     Lotería fill: verde-500 and amarillo-500 stay bright in both
     themes, so their label stays near-black; azul-700, verde-700
     and rosa stay deep, so their label stays white.

     Rule of thumb when adding CSS:
       • foreground on a BRIGHT fill  → var(--ink-fixed)
       • foreground on a DEEP fill    → var(--paper-fixed)
       • everything else              → var(--tinta-*) / var(--hueso) */
  --ink-fixed: #0B0B0B;
  --paper-fixed: #FFFFFF;

  /* ---- Hairlines ----------------------------------------------
     Sub-outline dividers (table rows, sheet links, card edges).
     Declared as tokens because a literal rgba(0,0,0,0.06) is
     invisible on a dark canvas. The 2px Lotería outline is
     `--panel-border`; these are the quiet rules underneath it. */
  --hairline: rgba(11, 11, 11, 0.09);
  --hairline: light-dark(rgba(11, 11, 11, 0.09), rgba(246, 242, 232, 0.14));
  --hairline-strong: rgba(11, 11, 11, 0.16);
  --hairline-strong: light-dark(rgba(11, 11, 11, 0.16), rgba(246, 242, 232, 0.24));

  /* ---- Canvas -------------------------------------------------
     The page ground, built from color tokens so the gradient itself
     needs no theme-specific restatement. */
  --canvas-top: #FBF9F4;
  --canvas-top: light-dark(#FBF9F4, #19150F);
  --canvas-bottom: #F4EFE6;
  --canvas-bottom: light-dark(#F4EFE6, #0E0B06);
  --canvas: linear-gradient(180deg, var(--canvas-top) 0%, var(--hueso) 40%, var(--canvas-bottom) 100%);
  /* Grain is a blend mode + opacity, not a color, so it is the one
     thing `light-dark()` can't carry — the theme block near the
     bottom of this file restates it. */
  --grain-opacity: 0.55;
  --grain-blend: overlay;

  /* Typography. Apple's macOS 26 / iOS 26 Liquid Glass aesthetic is
     inseparable from SF Pro's optical sizing — so on Apple devices we
     let the platform serve the real family (`-apple-system`, which
     resolves to SF Pro Display above ~20pt and SF Pro Text below).
     Inter is the self-hosted fallback for Windows, Linux, and Android;
     it shares the same width, weight axis, and x-height as SF, so
     switches between platforms feel continuous.

     `ui-rounded` first-in-stack gives Apple hardware (iOS 13+ / macOS
     Big Sur+) the SF Rounded variant at display sizes, which is what
     iOS 26's "Dynamic Island" and Control Center glass panels use.
     Falls through silently to SF Pro / Inter elsewhere. */
  --font-body: ui-rounded, -apple-system, BlinkMacSystemFont,
               'SF Pro Display', 'SF Pro Text',
               'Inter', 'Segoe UI Variable', 'Segoe UI',
               Roboto, 'Helvetica Neue', sans-serif;
  --font-display: ui-rounded, -apple-system, BlinkMacSystemFont,
                  'SF Pro Display', 'Inter Display', 'Inter',
                  'Segoe UI Variable', 'Segoe UI',
                  Roboto, 'Helvetica Neue', sans-serif;
  --font-mono: ui-monospace, 'SF Mono', 'JetBrains Mono', Menlo, Consolas,
               'Liberation Mono', monospace;

  /* Fluid type scale — min / fluid / max via clamp().
     Display ceiling per accessibility/typography guidance: ≤ ~92px. */
  --fs-display-xxl: clamp(44px, 6.2vw, 92px);
  --fs-display-xl: clamp(38px, 4.9vw, 72px);
  --fs-display-l:  clamp(32px, 4vw, 58px);
  --fs-display-m:  clamp(26px, 3vw, 42px);
  --fs-title:      clamp(26px, 2.6vw, 40px);
  --fs-headline: 24px;
  --fs-body-l: 18px;
  --fs-body: 16px;
  --fs-body-s: 14px;
  --fs-caption: 13px;
  --fs-eyebrow: 12px;

  /* Layout */
  --container-max: 1200px;
  --container-inner: 1040px;
  --reading-max: 640px;
  --container-pad: 24px;
  /* Fluid section rhythm (2026-08-26): min at 375px, max at ~1430px,
     one clamp instead of a @media step - no 56px cliff at 768px. The
     ed-layer's --panel-pad/--panel-gap set the precedent; these carry
     the legacy shells. */
  --space-section: clamp(72px, 9vw, 128px);
  --space-section-head: clamp(48px, 5vw, 72px);

  /* Measure for type that sits outside the reading column. Capped in
     `ch` so each cap tracks its own type size, and not shared between
     the two roles: a 58px poster headline and 13px fine print reach the
     same physical column at very different character counts, so one
     value would leave the headline in three-word lines or the fine print
     at 120 characters. Running body copy uses `--reading-max` instead —
     it shares a column with images and cards, so it needs to agree with
     them in pixels rather than characters. */
  --measure-display: 26ch;   /* poster headline; text-wrap: balance does the rest */
  --measure-note: 84ch;      /* fine print, ≈ the prose column physically */

  /* Radius */
  --radius-sm: 8px;
  --radius-md: 12px;
  --radius-lg: 16px;
  --radius-xl: 24px;
  --radius-pill: 9999px;

  /* Shadow */
  --shadow-card: 0 2px 12px rgba(11, 11, 11, 0.06);
  --shadow-hover: 0 12px 32px rgba(0, 61, 38, 0.12);
  --shadow-modal: 0 24px 64px rgba(11, 11, 11, 0.24);

  /* Motion */
  --dur-instant: 100ms;
  --dur-fast: 180ms;
  --dur-base: 240ms;
  --dur-slow: 360ms;
  --dur-deliberate: 520ms;
  --dur-entrance: 620ms;

  --ease-standard: cubic-bezier(0.2, 0, 0, 1);
  --ease-decelerate: cubic-bezier(0, 0, 0, 1);
  --ease-accelerate: cubic-bezier(0.3, 0, 1, 1);
  --ease-emphasized: cubic-bezier(0.2, 0, 0, 1.2);
  --ease-entrance: cubic-bezier(0.16, 1, 0.3, 1);

  /* ═════════════════════════════════════════════════════════════════
     Liquid Glass — iOS 26 / macOS 26 "Tahoe" material language.

     Real refraction is done at the platform level (CanvasKit shaders
     on the Flutter side, display compositor on Apple). On the web,
     we approximate with a layered stack:

       1. backdrop-filter: blur + saturate + brightness  (the physics)
       2. surface tint                                    (the color)
       3. inset top-highlight + bottom-shade             (edge lighting)
       4. a thin gradient stroke + outer soft shadow     (the ring)
       5. optional SVG feTurbulence/feDisplacementMap     (true warp)

     Tokens are declared here so the whole site + docs + in-app web
     shell can reach for the same numbers. Flutter mirrors these in
     mobile/lib/core/design/liquid_glass.dart — if you change a value
     here, match it there.
     ═════════════════════════════════════════════════════════════════ */

  /* Backdrop physics */
  --glass-blur-xs: 8px;
  --glass-blur-sm: 14px;
  --glass-blur-md: 22px;
  --glass-blur-lg: 36px;
  --glass-blur-xl: 56px;
  --glass-saturate: 1.8;
  --glass-brightness: 1.06;

  /* Nav chrome — its own blur pair so the scroll-triggered
     "materialize" moment reads as a deliberate step up in
     thickness, not a subtle tween. Matched to iOS 26 Finder chrome
     (~28px at rest) and Control Center surface (~40px). */
  --glass-blur-nav: 28px;
  --glass-blur-nav-scrolled: 40px;
  --glass-saturate-nav: 1.92;

  /* Floating nav capsule material (2026-08-26). Both themes are their
     own glass: light = hueso-tinted white with warm white edge light;
     dark = deep smoked glass with a faint cool edge. The old single
     material painted white lighting over dark content and let the
     1.92x saturation smear whatever hue scrolled beneath — dark mode
     backs saturation off to a whisper and drops the brightness lift
     entirely (brightening blacks reads as haze, not glass). */
  --nav-glass-tint: light-dark(rgba(250, 247, 242, 0.5), rgba(23, 20, 16, 0.52));
  --nav-glass-edge: light-dark(rgba(255, 255, 255, 0.65), rgba(255, 255, 255, 0.14));
  --nav-glass-edge-strong: light-dark(rgba(255, 255, 255, 0.78), rgba(255, 255, 255, 0.2));
  --nav-glass-edge-under: light-dark(rgba(255, 255, 255, 0.18), rgba(255, 255, 255, 0.04));
  --nav-glass-ring: light-dark(rgba(255, 255, 255, 0.42), rgba(255, 255, 255, 0.07));
  --nav-glass-ring-strong: light-dark(rgba(255, 255, 255, 0.56), rgba(255, 255, 255, 0.1));
  --nav-glass-specular: light-dark(rgba(255, 255, 255, 0.32), rgba(255, 255, 255, 0.07));
  --nav-glass-vignette: light-dark(rgba(11, 11, 11, 0.03), rgba(0, 0, 0, 0.18));
  --nav-glass-drop: light-dark(rgba(11, 11, 11, 0.16), rgba(0, 0, 0, 0.55));
  --nav-glass-drop-scrolled: light-dark(rgba(11, 11, 11, 0.24), rgba(0, 0, 0, 0.7));
  /* Non-color knobs can't ride light-dark(); the dark theme blocks
     near the end of this file override them. */
  --nav-glass-saturate: 1.6;
  --nav-glass-saturate-scrolled: 1.85;
  --nav-glass-brightness: 1.05;

  /* Surface tints — layered ON TOP of whatever the backdrop filter
     reveals. Hueso-biased (green-tinted white) so the glass belongs
     to the brand, not generic Apple-on-white. */
  --glass-surface-light: rgba(250, 247, 242, 0.62);
  --glass-surface-light: light-dark(rgba(250, 247, 242, 0.62), rgba(28, 24, 20, 0.66));
  --glass-surface-regular: rgba(250, 247, 242, 0.36);
  --glass-surface-regular: light-dark(rgba(250, 247, 242, 0.36), rgba(28, 24, 20, 0.44));
  --glass-surface-clear: rgba(255, 255, 255, 0.12);
  --glass-surface-chrome: rgba(250, 247, 242, 0.74);
  --glass-surface-chrome: light-dark(rgba(250, 247, 242, 0.74), rgba(21, 18, 14, 0.8));
  --glass-surface-dark: rgba(11, 11, 11, 0.42);
  --glass-surface-dark-regular: rgba(11, 11, 11, 0.58);

  /* Brand-tinted variants — glass that picks up verde.
     Use sparingly: CTAs, active nav chip, "confirm" sheets. */
  --glass-tint-verde: rgba(0, 166, 81, 0.18);
  --glass-tint-verde-strong: rgba(0, 104, 71, 0.32);

  /* Edge lighting — simulates a 1px specular band on the top edge
     and a softer return on the bottom, faking light-from-above. */
  --glass-edge-top: rgba(255, 255, 255, 0.62);
  --glass-edge-top-dark: rgba(255, 255, 255, 0.18);
  --glass-edge-bottom: rgba(255, 255, 255, 0.14);
  --glass-edge-stroke: rgba(255, 255, 255, 0.42);
  --glass-edge-stroke-dark: rgba(255, 255, 255, 0.1);
  --glass-edge-stroke-warm: rgba(250, 247, 242, 0.5);

  /* Outer shadows — softer, longer, more layers than a flat box-shadow.
     Always compose 2-3 stops: a tight 1-3px hair for crispness,
     a mid 8-14px for presence, and an ambient 24-48px for lift. */
  --glass-shadow-sm:
    0 1px 1px rgba(11, 11, 11, 0.04),
    0 2px 8px rgba(11, 11, 11, 0.06);
  --glass-shadow-md:
    0 1px 2px rgba(11, 11, 11, 0.06),
    0 6px 18px rgba(11, 11, 11, 0.08),
    0 24px 48px -12px rgba(11, 11, 11, 0.14);
  --glass-shadow-lg:
    0 1px 3px rgba(11, 11, 11, 0.08),
    0 14px 36px rgba(11, 11, 11, 0.14),
    0 36px 72px -16px rgba(11, 11, 11, 0.22);
  --glass-shadow-verde-md:
    0 1px 2px rgba(0, 0, 0, 0.08),
    0 8px 24px rgba(0, 61, 38, 0.18),
    0 24px 56px -16px rgba(0, 61, 38, 0.28);

  /* Continuous-ish corner radii — Apple's squircle is ~48% rounder
     than CSS `border-radius`, so bump nominal sizes up one step to
     visually match iOS 26 geometry. */
  --glass-radius-xs: 12px;
  --glass-radius-sm: 16px;
  --glass-radius-md: 22px;
  --glass-radius-lg: 28px;
  --glass-radius-xl: 36px;
  --glass-radius-pill: 9999px;

  /* ═════════════════════════════════════════════════════════════════
     Aurora atmosphere — v2 (2026-04-23).

     The "Liquid Glass redesign" pass. Where v1 tokens described the
     glass PANE itself (blur, tint, edge, shadow), these tokens
     describe the LIGHT that lands on the pane — the cinematic
     backdrop that makes every glass surface read as if it exists
     in a real lit room rather than a flat CSS stage.

     Three conceptual layers:

       1. Aurora blooms   — large, ultra-soft radial gradient washes
                            of brand hue, sized in vw so they scale
                            with the viewport. Use as body backdrop
                            layers or as section accents. Alpha stays
                            ≤ 14% so text contrast is never harmed.

       2. Halo shadows    — short outer-glow stacks, brand-tinted,
                            that ride alongside neutral shadows for
                            cards, CTAs, devices. Add dimension
                            without dominating the surface.

       3. Section shells  — prebuilt multi-stop background recipes
                            for sections (hero, cta-final, pricing,
                            trust) so every section shares one
                            atmospheric vocabulary.

     Designed to compose: a hero can layer [aurora-verde +
     aurora-oro + section-hero-shell] without any recipe fighting
     another — every bloom is radial, alpha-stacked, and fades out
     past 70% of its own radius.
     ═════════════════════════════════════════════════════════════════ */

  /* ---- Aurora bloom colors — raw rgba stops ---------------------
     Verde-only: light on this site is always green light. */
  --aurora-verde-core: rgba(0, 166, 81, 0.22);
  --aurora-verde-soft: rgba(0, 166, 81, 0.12);
  --aurora-verde-trace: rgba(0, 104, 71, 0.08);
  --aurora-menta-core: rgba(111, 207, 151, 0.22);
  --aurora-menta-soft: rgba(111, 207, 151, 0.12);
  --aurora-cream:      rgba(233, 242, 236, 0.35);

  /* ---- Ambient halo shadows — outer glow rings ------------------
     Layered with the existing neutral `--glass-shadow-*` stack.
     The trick: a tinted shadow at 20-40px blur, low opacity, no
     offset. Reads as "the element is emitting a soft corona",
     not "the element has a colored drop shadow". */
  --halo-verde-sm: 0 0 24px -4px rgba(0, 166, 81, 0.22);
  --halo-verde-md: 0 0 40px -8px rgba(0, 166, 81, 0.28),
                   0 0 80px -24px rgba(0, 104, 71, 0.22);
  --halo-verde-lg: 0 0 60px -12px rgba(0, 166, 81, 0.32),
                   0 0 120px -32px rgba(0, 104, 71, 0.28);

  /* ---- Luminous surface tints — brighter, more chromatic ------
     Use sparingly for hero-class surfaces that want to read as
     internally lit rather than merely translucent. The key is
     the warm white base + a faint cream overtone so the surface
     never greys out against the colored backdrop. */
  --glass-surface-luminous: rgba(250, 254, 251, 0.66);
  --glass-surface-luminous: light-dark(rgba(250, 254, 251, 0.66), rgba(32, 28, 23, 0.7));
  --glass-surface-luminous-strong: rgba(250, 254, 251, 0.82);
  --glass-surface-luminous-strong: light-dark(rgba(250, 254, 251, 0.82), rgba(32, 28, 23, 0.86));
  --glass-edge-luminous: rgba(255, 255, 255, 0.85);

  /* ---- Section atmosphere shells ------------------------------
     Copy-paste-friendly multi-stop recipes. Use as
     `background: var(--shell-aurora-warm);` on a section.
     Each stacks 3–4 radial blooms + a faint linear base. */

  /* Light aurora — soft verde top-left, menta mid-right. The default
     for hero + hueso sections. Verde-only light. */
  --shell-aurora-warm:
    radial-gradient(82vw 52vw at 8% -6%, var(--aurora-verde-soft) 0%, transparent 58%),
    radial-gradient(68vw 48vw at 96% 22%, var(--aurora-menta-soft) 0%, transparent 60%),
    linear-gradient(180deg, var(--canvas-top) 0%, var(--hueso) 50%, var(--canvas-bottom) 100%);

  /* Cool verde shell — for the pricing / CTA sections that live on
     a verde ground. Bright-green bloom + a softer verde corona. */
  --shell-aurora-verde:
    radial-gradient(95vw 58vw at 50% -12%, rgba(0, 214, 104, 0.28) 0%, transparent 60%),
    radial-gradient(70vw 48vw at 12% 92%, var(--aurora-menta-core) 0%, transparent 62%),
    radial-gradient(70vw 48vw at 88% 108%, rgba(0, 132, 85, 0.24) 0%, transparent 60%),
    linear-gradient(180deg, #004e31 0%, var(--verde-700) 50%, var(--verde-900) 100%);

  /* Deep verde shell — footer + dark sections. Same posture as
     verde shell but dimmer, with the bloom pushed to a thin top rim
     so the footer reads as a continuation of the CTA aurora rather
     than a flat cut. */
  --shell-aurora-deep:
    radial-gradient(120vw 44vw at 50% -8%, rgba(0, 132, 85, 0.4) 0%, transparent 52%),
    radial-gradient(60vw 40vw at 10% 110%, var(--aurora-verde-trace) 0%, transparent 70%),
    var(--verde-900);
}

/* Mobile scale overrides — tighter headlines + body. The clamp()
   floors already cover the worst case; these just tune the micro
   sizes that don't warrant a full fluid curve. */
@media (max-width: 768px) {
  :root {
    --fs-headline: 20px;
    --fs-body-l: 17px;
    --fs-body: 15px;
  }
}

/* ---------- Reset ---------- */

/* ═══════════════════════════════════════════════════════════════════
   Cross-Document View Transitions — the web platform's native answer
   to SwiftUI's `matchedGeometryEffect`.

   Supported in Chrome 126+, Edge 126+, Safari 18.2+; browsers without
   support silently fall back to a hard navigation. That's the whole
   progressive-enhancement story — no JS needed for the fallback.

   How it works:
     1. `@view-transition { navigation: auto }` opts the origin into
        cross-document transitions for same-origin same-type nav.
     2. Any element with a `view-transition-name` is captured on BOTH
        the outgoing and incoming page.
     3. Between unload and first paint of the new page the browser
        interpolates the shared element's position + size + opacity,
        and crossfades the remainder via the implicit `root` group.

   We tag THREE things:
     • `.lang-toggle-indicator` — the dark ES/EN pill slides from the
        old language's position to the new one's, natively, regardless
        of what JavaScript did or didn't do.
     • `.brand-mark-icon` + `.brand-mark-text` — the nav wordmark
        stays pinned across page loads so the nav reads as continuous
        chrome rather than a reloaded element.

   Animation speeds are tuned via the `::view-transition-group(…)`
   pseudo-elements below; they use the Apple sheet-enter curve
   (`cubic-bezier(0.16, 1, 0.3, 1)`) so the feel matches the
   hover-anticipation motion the pill already uses within a page.
   ═══════════════════════════════════════════════════════════════════ */

@view-transition {
  navigation: auto;
}

/* Default root transition — a soft crossfade, ~260ms, slightly
   asymmetric (old fades faster than new so the incoming content
   "arrives" rather than "swaps"). */
::view-transition-old(root) {
  animation: pesito-vt-fade-out 200ms cubic-bezier(0.3, 0, 1, 1) both;
}
::view-transition-new(root) {
  animation: pesito-vt-fade-in 280ms cubic-bezier(0, 0, 0, 1) both;
}
@keyframes pesito-vt-fade-out {
  from { opacity: 1; }
  to   { opacity: 0; }
}
@keyframes pesito-vt-fade-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}

/* The lang-toggle pill — the centerpiece shared element. Animation
   timing matches the in-page hover slide so the commit moment feels
   like a direct continuation of the anticipation motion. */
::view-transition-group(lang-indicator) {
  animation-duration: 460ms;
  animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
}

/* The brand wordmark — stays EXACTLY in place across nav. If the
   icon+text don't shift between pages (they shouldn't — the nav is
   fixed), the group animates effectively to a no-op, but it's still
   captured so the element doesn't repaint. */
::view-transition-group(brand-icon),
::view-transition-group(brand-text) {
  animation-duration: 300ms;
  animation-timing-function: cubic-bezier(0.2, 0, 0, 1);
}

/* Reduced motion: browsers should already short-circuit view
   transitions, but we set a `prefers-reduced-motion` block to be
   explicit — durations drop to near-zero so the transition still
   fires (preserves layout stability) but is imperceptibly fast. */
@media (prefers-reduced-motion: reduce) {
  ::view-transition-old(root),
  ::view-transition-new(root),
  ::view-transition-group(lang-indicator),
  ::view-transition-group(brand-icon),
  ::view-transition-group(brand-text) {
    animation-duration: 1ms !important;
  }
}

*, *::before, *::after { box-sizing: border-box; }
html {
  /* Smooth anchor scrolling is a vestibular trigger, so it is opt-in on
     the preference rather than unconditional. `main.js` already forces
     'instant' for the scrolls it drives; this covers the ones it doesn't
     (native fragment navigation, back/forward restore, no-JS). */
  scroll-behavior: smooth;
  scroll-padding-top: 80px;
  /* Drives both the UA chrome (form controls, scrollbars, the
     pre-paint canvas) AND every `light-dark()` token above. With no
     `data-theme` attribute the OS preference wins, so dark mode
     works with JavaScript disabled; the toggle pins it per-visitor
     by writing `data-theme="light|dark"` onto this element. */
  color-scheme: light dark;
  /* Never let mobile Safari inflate type on rotate — the fluid
     clamp() scale already handles small viewports, and the browser's
     own adjustment fights the 200%-zoom requirement (WCAG 1.4.4). */
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
}
/* Defensive horizontal-overflow clamp.

   `position: fixed` elements (`.nav`, `.nav-sheet`, `body::after`'s
   aurora, etc.) resolve `left: 0 / right: 0` against the initial
   containing block, which in most browsers widens to the document's
   scrollable width when ANY descendant triggers horizontal overflow.
   A single stray grid gap or oversized shape was enough to push the
   nav + sheet out to the scroll width, producing the visible
   "footer shifted left of the header" misalignment on mobile.

   `overflow-x: clip` pins the ICB to the layout-viewport width
   without introducing a horizontal scroll container (unlike
   `hidden`, which would also clobber `position: sticky` on
   descendants). Both html and body get it so that paint clipping
   kicks in regardless of which is the scroller in a given browser. */
html, body { overflow-x: clip; }
body {
  margin: 0;
  /* Base layer only — a subtle top-to-bottom hueso wash. The
     real atmospheric work lives on `body::after` (the cinematic
     aurora bloom stack) and `body::before` (paper grain).
     Splitting it across pseudo-elements lets the stack layer
     cleanly: grain in OVERLAY blend mode, blooms in normal mode
     — neither fighting the other for alpha.

     Why not a single `background:` shorthand like v1?
     The old stack used `background-attachment: fixed` on three
     radials inside one composite. That forces the browser to
     repaint the entire viewport-sized gradient on any scroll
     invalidation (even when nothing moved). By moving blooms to
     a fixed pseudo-element, only that one layer owns repaint,
     and the browser composites it once per viewport resize. */
  background: var(--canvas);
  color: var(--tinta-900);
  font-family: var(--font-body);
  font-feature-settings: "tnum" 1, "cv11" 1, "ss01" 1;
  font-weight: 500;
  font-size: var(--fs-body);
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
  /* Min-height guards against short pages (legal docs, 404) where
     the aurora layer would otherwise clip to content height. */
  min-height: 100vh;
}

/* ═══════════════════════════════════════════════════════════════════
   Cinematic aurora backdrop — v2 (2026-04-23).

   Five positioned radial blooms + a warm wash, all on one fixed
   viewport-sized layer that ONLY repaints on resize. Positioned
   so the blooms span the full scroll range without ever feeling
   repetitive:

     • verde  top-left      (8% -6%)      — primary brand, establishes
                                            the page's "morning" light
     • menta  mid-right     (96% 22%)     — lighter counterpoint, sits
                                            behind the hero CTAs
     • cream  mid-center    (52% 48%)     — a soft center bloom so the
                                            upper-fold doesn't read as
                                            empty between the blobs
     • verde  bottom-left   (-6% 84%)     — anchors the FAQ / audience
                                            sections so content reveals
                                            against a lit ground
     • verde  bottom-right  (108% 112%)   — trace echo, closes the loop

   All blooms use ≤ 22% alpha so body text contrast is never at
   risk — every pixel still has enough hueso under it to hit
   AA/AAA on tinta-900/-700 copy.                                  */
/* Lotería (2026-07-16): the aurora is retired. Folk-art flatness means
   the canvas is plain card stock — no atmospheric blooms. The grain
   overlay below stays (it reads as paper, not atmosphere). The rule is
   kept as a disabled stub so the bloom stack can be recovered from git
   if a future direction wants it. */
body::after {
  content: none;
}

/* Paper grain — a top-level fixed overlay on OVERLAY blend so it
   adds film-like tactility without shifting hue. Independent of
   `body::after` so the grain's blend-mode doesn't wrestle with
   the bloom stack's alpha. */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background-image:
    url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='240' height='240'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 0.04 0 0 0 0 0.04 0 0 0 0 0.04 0 0 0 0.05 0'/></filter><rect width='100%25' height='100%25' filter='url(%23n)'/></svg>");
  background-size: 240px 240px;
  opacity: var(--grain-opacity);
  mix-blend-mode: var(--grain-blend);
}

/* Honor prefers-reduced-transparency — users who've opted out of
   Apple's translucency get a plain hueso canvas (blooms drop out,
   grain stays for tactility). All the glass primitives have their
   own reduced-transparency fallbacks, so removing the aurora
   leaves them on a clean warm base. */
@media (prefers-reduced-transparency: reduce) {
  body::after { display: none; }
}
.money, .tabular { font-variant-numeric: tabular-nums; }
img, svg { display: block; max-width: 100%; }
a { color: inherit; text-decoration: none; }
button { font: inherit; cursor: pointer; border: none; background: none; }
ul, ol { padding: 0; margin: 0; list-style: none; }
h1, h2, h3, h4 { margin: 0; }
p { margin: 0; }

/* ---------- Inter self-host ---------- */

@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url('/assets/fonts/InterVar-latin.woff2') format('woff2-variations');
}

/* ---------- Utilities ---------- */

.container {
  width: 100%;
  max-width: var(--container-max);
  margin: 0 auto;
  padding: 0 var(--container-pad);
}
.container-inner {
  width: 100%;
  max-width: var(--container-inner);
  margin: 0 auto;
  padding: 0 var(--container-pad);
}
.reading {
  width: 100%;
  max-width: var(--reading-max);
  margin: 0 auto;
}

/* ---------- The article column ----------

   `.reading` centres a 640px column inside whatever wrapper holds it.
   On a page whose hero, related links, and footer all sit in a 1200px
   `.container`, those auto margins pushed the prose ~200px to the right
   of everything else, so one page carried three competing left edges and
   read as unrelated grids stacked on each other.

   `.article-col` keeps the 640px measure and drops the re-centring: the
   column spans its wrapper and the measure moves onto the children, so
   the prose starts on the wrapper's own text edge. `--inset` adds a
   panel's worth of padding for pages that open with a card hero, whose
   copy starts one `--panel-pad` in from the container edge.

   Cards, grids, and tables are exempt: they carry their own inner
   padding and their own text caps, and squeezing a three-column
   comparison table into a prose measure is what made those tables
   unreadable in the first place.

   `.ed-panel--prose` is the same contract for copy that lives inside a
   full-bleed card, so a legal page and a story page share one geometry
   whether or not the prose wears a frame. */
.article-col > :not(.ed-panel, .ed-grid, .ed-bar, .faq-list, table),
.ed-panel--prose > * {
  max-width: var(--reading-max);
}
/* The border counts: a card's copy starts past its frame as well as its
   padding, so dropping the 2px leaves the column a hair off the hero. */
.article-col--inset {
  padding-inline-start: calc(
    var(--container-pad) + var(--panel-border-width) + var(--panel-pad)
  );
}

/* A poster headline carries a wider deck than the copy under it. Capped
   on the display ladder only, never on a bare `h1`/`h2`: the ladder is
   always large, so a character count converts to a sane column, while a
   bare `h2` can be 24px, where the same count is a 370px ribbon. */
.article-col > :is(.display-xxl, .display-xl, .display-l, .display-m),
.ed-panel--prose > :is(.display-xxl, .display-xl, .display-l, .display-m) {
  max-width: var(--measure-display);
}

/* Prose lists. The `ul, ol` reset near the top of this file strips
   markers, padding, and margins so that nav rows and card grids can be
   lists; running copy needs all three back. Without a marker or a gap
   the items merge into one block, and the 24px indent they carry is then
   a second left edge with nothing to explain it. `en/licenses` had
   already been patched by hand with an inline `list-style: disc`, and
   this is that fix generalised. `revert` rather than `disc` so an `<ol>`
   still numbers itself. */
.article-col :is(ul, ol),
.ed-panel--prose :is(ul, ol),
.legal-body :is(ul, ol) {
  list-style: revert;
  padding-left: 24px;
}
.article-col :is(ul, ol) > li + li,
.ed-panel--prose :is(ul, ol) > li + li,
.legal-body :is(ul, ol) > li + li {
  margin-top: 8px;
}

/* A note that qualifies a row of cards is still body copy, so it starts
   on the page's copy edge rather than on the cards' frame edge. Keyed on
   both locales' class names because the two homepages carry the same
   construct under different names, and that is exactly how they drifted
   apart in the first place. */
.ed-chips + .plan-note,
.ed-chips + .trust-note {
  margin-inline-start: calc(var(--panel-border-width) + var(--panel-pad));
  max-width: var(--measure-note);
}

/* Eyebrow — the small uppercase label that sits above every section
   title. v2 promotes it to a glass chip: a translucent pill with a
   soft verde halo so it reads as a "lit nameplate" floating over
   the section, matching the rest of the Liquid Glass material
   family. Remains an inline element so existing HTML (`<span
   class="eyebrow">`) doesn't need to change.
   ─────────────────────────────────────────────────────────────── */
.eyebrow {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 6px 14px 6px 12px;
  border-radius: var(--glass-radius-pill);
  font-size: var(--fs-eyebrow);
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--verde-700);
  background: rgba(255, 255, 255, 0.55);
  border: 1px solid rgba(0, 166, 81, 0.18);
  -webkit-backdrop-filter: blur(var(--glass-blur-sm)) saturate(var(--glass-saturate));
  backdrop-filter: blur(var(--glass-blur-sm)) saturate(var(--glass-saturate));
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.78),
    inset 0 -1px 0 rgba(11, 11, 11, 0.02),
    0 1px 2px rgba(0, 61, 38, 0.06),
    0 0 24px -8px rgba(0, 166, 81, 0.22);
  font-feature-settings: "ss01" 1;
}
.eyebrow::before {
  content: "";
  display: inline-block;
  width: 6px;
  height: 6px;
  border-radius: 999px;
  background: currentColor;
  box-shadow: 0 0 0 3px rgba(0, 166, 81, 0.18);
  flex-shrink: 0;
}
/* Dark-section eyebrows (pricing, cta-final, gallery): flip the
   glass tint to a dim menta so it reads as a lit ornament on
   the verde ground rather than a bright hueso chip that
   fights the backdrop. */
.pricing .eyebrow,
.cta-final .eyebrow,
.trust .eyebrow,
.how-gallery .eyebrow {
  background: rgba(250, 247, 242, 0.1);
  border-color: rgba(111, 207, 151, 0.4);
  color: var(--verde-300);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.14),
    inset 0 -1px 0 rgba(0, 0, 0, 0.12),
    0 1px 2px rgba(0, 0, 0, 0.1),
    0 0 24px -8px rgba(0, 166, 81, 0.24);
}

/* Display classes — heroic type. Tracking and leading are paired with
   font-size on the same rung and never shared across rungs: letterforms
   read too loose as they grow and too tight as they shrink, so each step
   down the ladder loosens tracking and opens leading. The editorial layer
   swaps the family to Archivo Black but inherits this ladder, which is
   why the negative tracking is gentler than an Inter-era scale would
   want — a heavy grotesque is already tight at display sizes. */
.display-xxl { font-family: var(--font-display); font-size: var(--fs-display-xxl); font-weight: 800; letter-spacing: -0.025em; line-height: 0.98; text-wrap: balance; }
.display-xl  { font-family: var(--font-display); font-size: var(--fs-display-xl);  font-weight: 800; letter-spacing: -0.02em;  line-height: 1.0;  text-wrap: balance; }
.display-l   { font-family: var(--font-display); font-size: var(--fs-display-l);   font-weight: 800; letter-spacing: -0.015em; line-height: 1.04; text-wrap: balance; }
.display-m   { font-family: var(--font-display); font-size: var(--fs-display-m);   font-weight: 800; letter-spacing: -0.01em;  line-height: 1.08; text-wrap: balance; }
.title       { font-family: var(--font-display); font-size: var(--fs-title);       font-weight: 700; letter-spacing: -0.01em;  line-height: 1.15; color: var(--tinta-700); text-wrap: balance; }
.headline    { font-size: var(--fs-headline);    font-weight: 700; line-height: 1.25; color: var(--tinta-700); }
.body-l      { font-size: var(--fs-body-l);      font-weight: 500; line-height: 1.55; color: var(--tinta-700); }
.caption     { font-size: var(--fs-caption);     font-weight: 500; letter-spacing: 0.01em; line-height: 1.4; color: var(--tinta-500); }

/* The green dot is Pesito's signature — used sparingly on headlines
   and paragraph breaks. (Renamed from .brand-dot in the 2026-07-11
   palette rationalization: the punctuation is now brand verde.) */
.brand-dot { color: var(--brand-dot); font-style: normal; display: inline-block; transform: translateY(-0.05em); }

/* Pull-quote — Inter italic, bold weight, still distinct from body copy
   but keeps the site's sans-serif consistency. */
.pullquote {
  font-style: italic;
  font-weight: 600;
  font-size: clamp(20px, 2.2vw, 30px);
  line-height: 1.35;
  letter-spacing: -0.01em;
  color: var(--tinta-900);
  padding: 4px 0;
  max-width: 56ch;
  text-wrap: pretty;
}
.pullquote::before {
  content: "“";
  display: block;
  font-style: normal;
  font-weight: 800;
  font-size: 1.6em;
  line-height: 0.6;
  color: var(--verde-500);
  margin-bottom: 8px;
}
.pullquote cite {
  display: block;
  margin-top: 12px;
  font-style: normal;
  font-size: var(--fs-caption);
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--tinta-500);
}

.section {
  position: relative;
  padding: var(--space-section) 0;
}

/* Screen-reader-only utility — hides the visuals while keeping the
   element in the accessibility tree for form labels and aria hints. */
.visually-hidden {
  position: absolute !important;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* ---------- Buttons ---------- */

.btn {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 16px 28px;
  border-radius: var(--radius-pill);
  font-size: var(--fs-body-l);
  font-weight: 700;
  transition: transform var(--dur-fast) var(--ease-emphasized),
              background var(--dur-fast) var(--ease-standard),
              box-shadow var(--dur-fast) var(--ease-standard);
}
.btn:active { transform: scale(0.98); }

/* `.btn-primary`: Liquid Glass filled pill. The verde fill stays solid
   (this is the anchor CTA — not a translucent surface) but picks up
   the same top-edge specular, warm inner highlight, and layered
   shadow stack as the glass primitives so it reads as one material
   family. v2: added a soft verde halo at rest (so the button "emits"
   even before hover) and a brighter halo on hover. */
.btn-primary {
  position: relative;
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.18) 0%, rgba(255, 255, 255, 0) 42%),
    linear-gradient(180deg, var(--verde-500) 0%, #008A46 100%);
  color: var(--paper-fixed);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.42),
    inset 0 -1px 0 rgba(0, 0, 0, 0.14),
    0 1px 2px rgba(0, 0, 0, 0.08),
    0 8px 18px rgba(0, 61, 38, 0.22),
    0 22px 44px -14px rgba(0, 61, 38, 0.28),
    var(--halo-verde-sm);
  border: 1px solid rgba(0, 104, 71, 0.45);
  letter-spacing: -0.005em;
}
.btn-primary:hover {
  transform: translateY(-1px);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.52),
    inset 0 -1px 0 rgba(0, 0, 0, 0.14),
    0 1px 3px rgba(0, 0, 0, 0.1),
    0 12px 24px rgba(0, 61, 38, 0.28),
    0 30px 54px -14px rgba(0, 61, 38, 0.36),
    var(--halo-verde-md);
}

/* `.btn-outline`: clear glass pill. Text reads as tinta-700 on warm
   contexts, flips to hueso when the parent has `.on-dark`. */
.btn-outline {
  position: relative;
  background: var(--glass-surface-clear);
  -webkit-backdrop-filter:
    blur(var(--glass-blur-sm)) saturate(var(--glass-saturate)) brightness(var(--glass-brightness));
  backdrop-filter:
    blur(var(--glass-blur-sm)) saturate(var(--glass-saturate)) brightness(var(--glass-brightness));
  color: var(--tinta-700);
  border: 1px solid var(--glass-edge-stroke-warm);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.55),
    inset 0 -1px 0 rgba(0, 0, 0, 0.04),
    var(--glass-shadow-sm);
}
.btn-outline:hover {
  background: rgba(255, 255, 255, 0.42);
  transform: translateY(-1px);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.65),
    inset 0 -1px 0 rgba(0, 0, 0, 0.05),
    var(--glass-shadow-md);
}
/* Ghost button — text-only with a subtle glass chip on hover so it
   has a "clicked target" feel without competing with the primary
   verde-filled pill. Always text-first. */
.btn-ghost {
  color: var(--verde-700);
  padding: 12px 16px;
  border-radius: var(--glass-radius-pill);
  transition:
    color var(--dur-fast) var(--ease-standard),
    background var(--dur-fast) var(--ease-standard),
    box-shadow var(--dur-fast) var(--ease-standard);
}
.btn-ghost:hover {
  color: var(--verde-900);
  background: rgba(0, 166, 81, 0.1);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.5),
    0 0 24px -6px rgba(0, 166, 81, 0.22);
}

/* Focus rings — ALWAYS visible, restyled only. */
:focus-visible {
  outline: 2px solid var(--verde-500);
  outline-offset: 2px;
}

/* ═══════════════════════════════════════════════════════════════════
   Iridescent sheen — Pesito's "oil on warm glass" hover effect.

   Ported from the reference neumorphic-iridescent button
   (https://freefrontend.com/code/iridescent-3d-neumorphic-button-2026-03-12/,
   credit Simey / inspired by Wojciech Zieliński) but re-tinted to
   the brand's heritage spectrum — mint → verde → teal rather
   than the reference's cool cyan/magenta — so it reads as Pesito,
   not generic glassmorphism.

   Applied as a utility over ANY button that has `position: relative`
   and a non-transparent `border-radius`. Works by layering two
   pseudo-elements OVER the existing button fill:

     1. `::after`  — inner glassy glow. Brightens the bottom edge on
                     hover, sells the "lit from below" depth.
     2. `::before` — the iridescent sheen. Blurred diagonal gradient
                     that slides IN from the top-right on hover; on
                     rest it's invisible (opacity 0, translated out).

   Both layers are strictly DECORATIVE: `pointer-events: none` so
   hit-testing is unchanged; `mix-blend-mode: lighten` so they never
   make text harder to read (can only BRIGHTEN the underlying pixel,
   not darken).

   The `oklab` color-space gradient interpolates perceptually — no
   muddy greys in the middle of the spectrum. Requires Chrome 111+ /
   Safari 16.2+ / Firefox 113+; older browsers fall through to the
   base button fill with no sheen (graceful degrade).

   Applied below to `.btn-primary`, `.btn-rojo`, `.store-badge`,
   `.finale-store`, `.finale-signin-cta`, and `.cta-final
   .waitlist-submit` — every tier-1 CTA on the site. Secondary /
   ghost / outline buttons deliberately skip it so the iridescence
   stays a premium signal, not wallpaper.
   ═══════════════════════════════════════════════════════════════════ */

.iridescent {
  position: relative;
  isolation: isolate; /* contain the mix-blend to the button */
}

.iridescent::before,
.iridescent::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  transition:
    opacity 600ms cubic-bezier(0.32, 0, 0.67, 0),
    transform 600ms cubic-bezier(0.32, 0, 0.67, 0),
    filter 600ms cubic-bezier(0.32, 0, 0.67, 0);
}

/* Inner glassy glow — a soft lit bottom edge. Present (low opacity)
   at rest; intensifies on hover. */
.iridescent::after {
  opacity: 0.28;
  box-shadow:
    inset 0 -0.25em 2px 1px rgba(255, 255, 255, 0.42),
    inset 0 -0.3em 0.5em rgba(255, 255, 255, 0.25),
    inset 0 -0.3em 1em rgba(255, 255, 255, 0.18);
  mix-blend-mode: lighten;
  z-index: 2;
}

/* Iridescent sheen — the headline effect. Hidden at rest (opacity 0,
   translated off-screen to upper-right, scaled down), animates IN on
   hover from the top-right corner like a light hitting the surface.
   The mask ensures the sheen only shows on the lower-right portion
   of the button, leaving the top-left clean for legibility. */
.iridescent::before {
  opacity: 0;
  transform: translate(1.1em, 0) scale(0.85);
  background: linear-gradient(
    98deg in oklab,
    oklch(0.95 0.08 145) -5%,      /* pale mint */
    oklch(0.9 0.17 152),            /* brand verde */
    oklch(0.88 0.15 172) 65%,       /* teal-verde */
    oklch(0.92 0.08 195) 140%       /* cool aqua tail */
  );
  /* Diagonal mask: sheen fades in from top-right, absent top-left */
  -webkit-mask: linear-gradient(166deg, transparent 55%, black 95%);
          mask: linear-gradient(166deg, transparent 55%, black 95%);
  filter: blur(6px) contrast(1.25);
  mix-blend-mode: lighten;
  z-index: 3;
}

/* Hover / focus / shine states — the magic moment. */
.iridescent:hover::before,
.iridescent:focus-visible::before,
.iridescent.is-shining::before {
  opacity: 0.55;
  transform: translate(0, 0) scale(1);
  transition-duration: 320ms;
  transition-timing-function: cubic-bezier(0.33, 1, 0.68, 1);
}
.iridescent:hover::after,
.iridescent:focus-visible::after,
.iridescent.is-shining::after {
  opacity: 0.75;
  transition-duration: 320ms;
  transition-timing-function: cubic-bezier(0.33, 1, 0.68, 1);
}

/* Dark-surface variant — buttons with a dark body (finale-signin-cta,
   finale-store) need a brighter sheen to punch through the tint.
   Applied via `.iridescent--dark`. */
.iridescent--dark::before {
  background: linear-gradient(
    98deg in oklab,
    oklch(0.97 0.07 145) -5%,
    oklch(0.95 0.16 150),
    oklch(0.93 0.2 170) 65%,
    oklch(0.95 0.1 200) 140%
  );
  filter: blur(7px) contrast(1.3) brightness(1.05);
}
.iridescent--dark::after {
  box-shadow:
    inset 0 -0.25em 2px 1px rgba(255, 255, 255, 0.28),
    inset 0 -0.3em 0.5em rgba(255, 255, 255, 0.18),
    inset 0 -0.3em 1em rgba(255, 255, 255, 0.12);
}
.iridescent--dark:hover::before,
.iridescent--dark:focus-visible::before,
.iridescent--dark.is-shining::before {
  opacity: 0.42;
}

/* Reduced-motion + reduced-transparency: skip the sheen entirely.
   The base button styling is intact and still signals interactivity
   via the existing Liquid Glass hover transform. */
@media (prefers-reduced-motion: reduce), (prefers-reduced-transparency: reduce) {
  .iridescent::before,
  .iridescent::after {
    display: none;
  }
}

/* Apply the utility to every tier-1 CTA on the site. No HTML
   changes required — just CSS composition. Buttons that already
   share `position: relative` (all the primary CTAs via the `.btn`
   base, the store badges via their own rules, the finale-* via their
   own rules) inherit the sheen automatically. */
.btn-primary,
.btn-rojo,
.cta-final .waitlist-submit,
.store-badge,
.finale-signin-cta {
  position: relative;
  isolation: isolate;
}
.btn-primary::before,
.btn-primary::after,
.btn-rojo::before,
.btn-rojo::after,
.cta-final .waitlist-submit::before,
.cta-final .waitlist-submit::after,
.store-badge::before,
.store-badge::after,
.finale-signin-cta::before,
.finale-signin-cta::after,
.finale-store::before,
.finale-store::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  transition:
    opacity 600ms cubic-bezier(0.32, 0, 0.67, 0),
    transform 600ms cubic-bezier(0.32, 0, 0.67, 0),
    filter 600ms cubic-bezier(0.32, 0, 0.67, 0);
}

/* Light/brand-colored buttons — mint→verde→teal sheen. */
.btn-primary::before,
.btn-rojo::before,
.cta-final .waitlist-submit::before,
.store-badge::before {
  opacity: 0;
  transform: translate(1.1em, 0) scale(0.85);
  background: linear-gradient(
    98deg in oklab,
    oklch(0.86 0.16 30) -5%,
    oklch(0.92 0.15 85),
    oklch(0.9 0.18 165) 65%,
    oklch(0.88 0.2 280) 140%
  );
  -webkit-mask: linear-gradient(166deg, transparent 55%, black 95%);
          mask: linear-gradient(166deg, transparent 55%, black 95%);
  filter: blur(6px) contrast(1.25);
  mix-blend-mode: lighten;
  z-index: 3;
}
.btn-primary::after,
.btn-rojo::after,
.cta-final .waitlist-submit::after,
.store-badge::after {
  opacity: 0.28;
  box-shadow:
    inset 0 -0.25em 2px 1px rgba(255, 255, 255, 0.42),
    inset 0 -0.3em 0.5em rgba(255, 255, 255, 0.25),
    inset 0 -0.3em 1em rgba(255, 255, 255, 0.18);
  mix-blend-mode: lighten;
  z-index: 2;
}

/* Dark variant — jet-black or dark-translucent backgrounds get a
   brighter sheen + subtler inner glow so the iridescence still reads
   against the dark canvas. */
.finale-signin-cta::before,
.finale-store::before {
  opacity: 0;
  transform: translate(1.1em, 0) scale(0.85);
  background: linear-gradient(
    98deg in oklab,
    oklch(0.92 0.2 30) -5%,
    oklch(0.98 0.18 90),
    oklch(0.95 0.22 170) 65%,
    oklch(0.95 0.24 275) 140%
  );
  -webkit-mask: linear-gradient(166deg, transparent 55%, black 95%);
          mask: linear-gradient(166deg, transparent 55%, black 95%);
  filter: blur(7px) contrast(1.3) brightness(1.05);
  mix-blend-mode: lighten;
  z-index: 3;
}
.finale-signin-cta::after,
.finale-store::after {
  opacity: 0.22;
  box-shadow:
    inset 0 -0.25em 2px 1px rgba(255, 255, 255, 0.28),
    inset 0 -0.3em 0.5em rgba(255, 255, 255, 0.18),
    inset 0 -0.3em 1em rgba(255, 255, 255, 0.12);
  mix-blend-mode: lighten;
  z-index: 2;
}

/* Hover / focus — unify all variants through a single set of
   transitions to the same target state. */
.btn-primary:hover::before,
.btn-primary:focus-visible::before,
.btn-rojo:hover::before,
.btn-rojo:focus-visible::before,
.cta-final .waitlist-submit:hover::before,
.cta-final .waitlist-submit:focus-visible::before,
.store-badge:hover::before,
.store-badge:focus-visible::before {
  opacity: 0.58;
  transform: translate(0, 0) scale(1);
  transition-duration: 320ms;
  transition-timing-function: cubic-bezier(0.33, 1, 0.68, 1);
}
.btn-primary:hover::after,
.btn-primary:focus-visible::after,
.btn-rojo:hover::after,
.btn-rojo:focus-visible::after,
.cta-final .waitlist-submit:hover::after,
.cta-final .waitlist-submit:focus-visible::after,
.store-badge:hover::after,
.store-badge:focus-visible::after {
  opacity: 0.78;
  transition-duration: 320ms;
  transition-timing-function: cubic-bezier(0.33, 1, 0.68, 1);
}
.finale-signin-cta:hover::before,
.finale-signin-cta:focus-visible::before,
.finale-store:hover::before,
.finale-store:focus-visible::before {
  opacity: 0.46;
  transform: translate(0, 0) scale(1);
  transition-duration: 320ms;
  transition-timing-function: cubic-bezier(0.33, 1, 0.68, 1);
}
.finale-signin-cta:hover::after,
.finale-signin-cta:focus-visible::after,
.finale-store:hover::after,
.finale-store:focus-visible::after {
  opacity: 0.6;
  transition-duration: 320ms;
  transition-timing-function: cubic-bezier(0.33, 1, 0.68, 1);
}

/* One-time load shimmer — every tier-1 CTA briefly pulses on first
   paint so the user sees the iridescent material exists before any
   hover. Controlled by the `is-shining` class that `scripts/main.js`
   adds on DOMContentLoaded + removes after the shimmer completes. */
.btn-primary.is-shining::before,
.btn-rojo.is-shining::before,
.cta-final .waitlist-submit.is-shining::before,
.store-badge.is-shining::before {
  opacity: 0.52;
  transform: translate(0, 0) scale(1);
  transition-duration: 900ms;
  transition-timing-function: cubic-bezier(0.33, 1, 0.68, 1);
}
.btn-primary.is-shining::after,
.btn-rojo.is-shining::after,
.cta-final .waitlist-submit.is-shining::after,
.store-badge.is-shining::after {
  opacity: 0.7;
  transition-duration: 900ms;
  transition-timing-function: cubic-bezier(0.33, 1, 0.68, 1);
}
.finale-signin-cta.is-shining::before,
.finale-store.is-shining::before {
  opacity: 0.42;
  transform: translate(0, 0) scale(1);
  transition-duration: 900ms;
  transition-timing-function: cubic-bezier(0.33, 1, 0.68, 1);
}
.finale-signin-cta.is-shining::after,
.finale-store.is-shining::after {
  opacity: 0.55;
  transition-duration: 900ms;
  transition-timing-function: cubic-bezier(0.33, 1, 0.68, 1);
}

/* Reduced-motion + reduced-transparency: skip sheen entirely. */
@media (prefers-reduced-motion: reduce), (prefers-reduced-transparency: reduce) {
  .btn-primary::before,
  .btn-primary::after,
  .btn-rojo::before,
  .btn-rojo::after,
  .cta-final .waitlist-submit::before,
  .cta-final .waitlist-submit::after,
  .store-badge::before,
  .store-badge::after,
  .finale-signin-cta::before,
  .finale-signin-cta::after,
  .finale-store::before,
  .finale-store::after {
    display: none;
  }
}

/* ═══════════════════════════════════════════════════════════════════
   Liquid Glass — primitives

   Three things make this feel like a real iOS 26 / macOS 26 surface
   rather than "frosted glass, AI edition":

     1. The ::before top-edge specular. One hair-thin 1px gradient
        from white 0.6 down to transparent, draped over the top 40%
        of the element. This is what makes glass look "lit".
     2. The ::after bottom vignette. A subtle inverse gradient on
        the bottom edge, darker by ~8%, so the element has weight.
     3. box-shadow stacks of 2-3 layers. Flat 8px shadows always
        read as cheap; real Apple glass has a hair layer + a mid
        layer + a long ambient.

   Use `.glass` as the baseline, then compose variants (`.glass--dark`,
   `.glass--verde`, `.glass--clear`) for tone. Wrap interactive glass
   with `.glass--interactive` to get hover squeeze + shadow bloom.
   ═══════════════════════════════════════════════════════════════════ */

.glass {
  position: relative;
  background: var(--glass-surface-regular);
  -webkit-backdrop-filter:
    blur(var(--glass-blur-md))
    saturate(var(--glass-saturate))
    brightness(var(--glass-brightness));
  backdrop-filter:
    blur(var(--glass-blur-md))
    saturate(var(--glass-saturate))
    brightness(var(--glass-brightness));
  border-radius: var(--glass-radius-md);
  border: 1px solid var(--glass-edge-stroke);
  box-shadow:
    inset 0 1px 0 var(--glass-edge-top),
    inset 0 -1px 0 var(--glass-edge-bottom),
    var(--glass-shadow-md);
  isolation: isolate;
  transition:
    transform var(--dur-base) var(--ease-emphasized),
    box-shadow var(--dur-base) var(--ease-standard),
    background var(--dur-base) var(--ease-standard),
    border-color var(--dur-base) var(--ease-standard);
}

/* Top-edge specular — a lit arc across the top 42% of the card. */
.glass::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  background: linear-gradient(
    180deg,
    rgba(255, 255, 255, 0.28) 0%,
    rgba(255, 255, 255, 0.04) 42%,
    transparent 70%
  );
  mix-blend-mode: plus-lighter;
  z-index: 0;
}

/* Bottom vignette — weight. */
.glass::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  background: linear-gradient(
    180deg,
    transparent 0%,
    transparent 65%,
    rgba(11, 11, 11, 0.06) 100%
  );
  z-index: 0;
}

/* Direct children sit above the specular/vignette layers. */
.glass > * { position: relative; z-index: 1; }


/* Interactive glass — pointer state responds with a subtle lift and
   a deeper shadow + a widened specular, mirroring iOS 26's "breathing"
   button state. */
.glass--interactive {
  cursor: pointer;
  will-change: transform, box-shadow;
}
.glass--interactive:hover {
  transform: translateY(-1px);
  box-shadow:
    inset 0 1px 0 var(--glass-edge-top),
    inset 0 -1px 0 var(--glass-edge-bottom),
    var(--glass-shadow-lg);
}
.glass--interactive:active {
  transform: translateY(0) scale(0.985);
  transition-duration: var(--dur-instant);
}


/* ---- Liquid Glass "goo" container ---------------------------------
   Port of SwiftUI's `GlassEffectContainer(spacing:)`. When applied to
   a parent, any `.glass` or `.glass--pill` children inside it that
   come within ~spacing pixels of each other will PHYSICALLY MERGE
   into one continuous shape (like mercury beads fusing). As they
   pull apart, the fused shape splits.

   Implementation is an SVG filter trick:

     1. Gaussian blur softens the edges of child glass shapes.
     2. feColorMatrix thresholds alpha to near-binary, turning the
        blurred halos into hard-edged fused regions.
     3. feComposite re-overlays the original SourceGraphic so the
        interior of each shape (type, icons) stays crisp — only the
        EDGES participate in the merge.

   The trade-off: children rendered through the filter lose their
   own drop shadows and backdrop-filter effects (filter eats them).
   So `.glass-container` children use an INTERNAL tint + hair border,
   not backdrop-blur. Render the container on a parent that itself
   has glass/backdrop if you want blur-through-the-group.

   Use `.glass-container--tight` for a shorter merge reach (buttons,
   chips) and `.glass-container` for the default (cards, store
   badges). Spacing in SwiftUI lives on the container; we encode it
   in the filter's stdDeviation.                                     */

.glass-container {
  filter: url(#liquid-goo);
  /* isolate so the goo filter only composites its own subtree,
     not the surrounding page. */
  isolation: isolate;
}
.glass-container--tight {
  filter: url(#liquid-goo-tight);
  isolation: isolate;
}


/* Reduced motion / reduced transparency: keep the goo off when the
   user has disabled animations — the merge IS the animation. */
@media (prefers-reduced-motion: reduce), (prefers-reduced-transparency: reduce) {
  .glass-container,
  .glass-container--tight { filter: none; }
}

/* Older browsers / contexts without backdrop-filter support fall
   back to a heavier opaque tint so the element still has identity.
   (`.nav` fallbacks live alongside the `.nav` rule below — source
   order matters for cascade.) */
@supports not ((-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px))) {
  .glass { background: rgba(250, 247, 242, 0.92); }
}

/* Honor prefers-reduced-transparency (macOS/iOS accessibility toggle). */
@media (prefers-reduced-transparency: reduce) {
  .glass { background: var(--hueso); }
  .glass::before, .glass::after { display: none; }
}

/* ---------- Navigation ----------

   Apple macOS 26 / iOS 26 "Tahoe" Liquid Glass chrome. Two things
   make this a real pane of glass rather than a CSS frost effect:

     1. Proper blur depth. 28px rest, 40px scrolled — paired with
        1.92x saturation so the warm hueso backdrop colours punch
        through instead of bleaching to grey frost.
        ponytail: the SVG feTurbulence warp (`url(#nav-liquid-glass)`
        chained into backdrop-filter, injected by a since-deleted
        `injectNavGlassFilter` IIFE in scripts/main.js) cost 5x scroll
        frame time over the hero video — it forces software filtering
        on every scroll frame. Plain GPU-composited blur is the
        ceiling here; don't reintroduce the url() component.

     2. Layered edge lighting. Four inset shadows, ported from the
        macOS 26 chrome spec:
          • upper-left 2px specular seam         (the lit edge)
          • lower-right 2px softer return        (ambient bounce)
          • inner 3px ring glow at 0.42 alpha    (the "etched" look)
          • outer ambient drop shadow            (depth off surface)
        Together they read as a single continuous 1px bevel of
        refracted light — the detail that separates "glass" from
        "translucent box".

   Before scroll: soft lighting, ~22% hueso tint so the hero art
   shows through. After scroll: the `nav--scrolled` class (set by
   scripts/main.js when window.scrollY > 8) bumps blur, thickens the
   tint to ~72% chrome, intensifies every inset, and pops the
   hair-thin bottom divider. */
/* Floating Liquid Glass capsule (2026-08-26). The nav detaches from
   the viewport edge into a rounded pill that content scrolls UNDER —
   a floating functional layer, not a consumed strip. Every lighting
   value is theme-aware: dark mode is its own dark glass (deep tint,
   gentler saturation boost, no brightness lift, faint top edge
   light), never the light material's white film over dark content. */
.nav {
  position: fixed;
  top: max(10px, env(safe-area-inset-top, 0px));
  left: 12px;
  right: 12px;
  margin-inline: auto;
  max-width: var(--container-max);
  z-index: 100;
  height: 68px;
  border-radius: 999px;
  overflow: clip;
  background: var(--nav-glass-tint);
  -webkit-backdrop-filter:
    blur(var(--glass-blur-nav))
    saturate(var(--nav-glass-saturate))
    brightness(var(--nav-glass-brightness));
  backdrop-filter:
    blur(var(--glass-blur-nav))
    saturate(var(--nav-glass-saturate))
    brightness(var(--nav-glass-brightness));
  /* Edge-light stack: a bright top hairline (light catching the
     material), a soft inner ring (the etched lip), and an ambient
     drop that gives the capsule float. All themed. */
  box-shadow:
    inset 0 1px 0 var(--nav-glass-edge),
    inset 0 -1px 0 var(--nav-glass-edge-under),
    inset 0 0 3px 1px var(--nav-glass-ring),
    0 8px 24px -14px var(--nav-glass-drop);
  transition:
    background var(--dur-base) var(--ease-standard),
    backdrop-filter var(--dur-base) var(--ease-standard),
    -webkit-backdrop-filter var(--dur-base) var(--ease-standard),
    box-shadow var(--dur-base) var(--ease-standard);
}
/* Top-edge specular — a lit arc across the upper 55% of the capsule.
   Visible at rest so the nav always reads as lit glass, full at
   scroll for the materialize moment. */
.nav::before {
  content: "";
  position: absolute;
  inset: 0 0 auto 0;
  height: 55%;
  pointer-events: none;
  background: linear-gradient(
    180deg,
    var(--nav-glass-specular) 0%,
    transparent 100%
  );
  opacity: 0.55;
  transition: opacity var(--dur-base) var(--ease-standard);
  z-index: 0;
}
/* Bottom vignette — gives the capsule weight against what scrolls
   beneath it. */
.nav::after {
  content: "";
  position: absolute;
  inset: auto 0 0 0;
  height: 40%;
  pointer-events: none;
  background: linear-gradient(
    180deg,
    transparent 0%,
    var(--nav-glass-vignette) 100%
  );
  opacity: 0.5;
  transition: opacity var(--dur-base) var(--ease-standard);
  z-index: 0;
}
/* Scroll: the material thickens — deeper tint, more blur, heavier
   drop — so the same surface reads as gaining substance, not as a
   different bar fading in. */
.nav.nav--scrolled {
  background: var(--glass-surface-chrome);
  -webkit-backdrop-filter:
    blur(var(--glass-blur-nav-scrolled))
    saturate(var(--nav-glass-saturate-scrolled))
    brightness(var(--nav-glass-brightness));
  backdrop-filter:
    blur(var(--glass-blur-nav-scrolled))
    saturate(var(--nav-glass-saturate-scrolled))
    brightness(var(--nav-glass-brightness));
  box-shadow:
    inset 0 1px 0 var(--nav-glass-edge-strong),
    inset 0 -1px 0 var(--nav-glass-edge-under),
    inset 0 0 3px 1px var(--nav-glass-ring-strong),
    0 16px 40px -18px var(--nav-glass-drop-scrolled);
}
.nav.nav--scrolled::before { opacity: 1; }
.nav.nav--scrolled::after { opacity: 1; }

/* Nav fallbacks — placed AFTER the base `.nav` rule so source order
   lets them override. Without backdrop-filter a half tint is too
   transparent to read as chrome; lift to near-opaque. The reduced-
   transparency variant goes frosty-solid with a defined hairline —
   accessibility trumps aesthetics; the capsule shape stays. */
@supports not ((-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px))) {
  .nav {
    /* Plain color first: the engines that lack backdrop-filter are
       exactly the ones that may lack light-dark() too — without this
       line the whole declaration drops and the fallback is inert. */
    background: rgba(250, 247, 242, 0.96);
    background: light-dark(rgba(250, 247, 242, 0.96), rgba(24, 21, 17, 0.96));
    box-shadow:
      inset 0 1px 0 var(--nav-glass-edge),
      0 8px 24px -14px var(--nav-glass-drop);
  }
  .nav.nav--scrolled {
    background: var(--hueso);
  }
}
@media (prefers-reduced-transparency: reduce) {
  .nav {
    background: var(--hueso);
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
    border: 1px solid var(--hairline-strong);
    box-shadow: 0 8px 24px -14px var(--nav-glass-drop);
  }
  .nav::before,
  .nav::after { display: none; }
}

.nav-inner {
  position: relative;
  z-index: 1;
  display: flex;
  align-items: center;
  height: 100%;
  gap: 32px;
}
.nav-links {
  display: flex;
  gap: 32px;
  margin: 0 auto;
}
.nav-links a {
  font-size: var(--fs-body);
  color: var(--tinta-700);
  font-weight: 600;
  transition: color var(--dur-fast) var(--ease-standard);
}
.nav-links a:hover { color: var(--verde-700); }
.nav-actions { display: flex; align-items: center; gap: 12px; }
/* Liquid Glass segmented control — Apple `GlassEffectContainer` port.
   --------------------------------------------------------------------
   Apple's GlassEffectContainer behavior (per developer docs):
     > As shapes near one another, their paths start to blend into one
     > another. The higher the spacing, the sooner blending begins as
     > the shapes approach each other.

   In SwiftUI, each `glassEffect(...)` child in a container contributes
   a shape to one rendered material. When the ACTIVE state moves, the
   dark indicator slides between positions and — because neighbors are
   inside the goo threshold — its path literally morphs continuously.

   CSS port:
     1. `.lang-toggle-indicator` is the single animated shape — a
        dark absolute-positioned pill that JS slides over whichever
        anchor is active.
     2. Under the `#liquid-goo-tight` filter, as the indicator slides
        past the hovered anchor, the two shapes' alpha halos fuse into
        one continuous contour for the duration of the transit.
     3. Hover anticipation: pointer on the idle anchor moves the
        indicator fully over IT first, then the page load completes
        the navigation. Users see the commit before the URL changes.

   See `scripts/main.js` for the positioning logic.                   */

.lang-toggle {
  position: relative;
  display: inline-flex;
  align-items: center;
  padding: 4px;
  font-size: var(--fs-caption);
  font-weight: 600;
  isolation: isolate;
  /* Baseline toggle-container background — glass hueso pill. The goo
     filter is INTENTIONALLY not applied here: it would threshold the
     anchor glyphs into invisibility (labels are alpha-antialiased and
     don't survive the 18/-7 alpha clamp). The sliding indicator alone
     gives Apple's segmented-control feel; we keep `#liquid-goo-tight`
     available via `.glass-container--tight` for surfaces where the
     children are solid shapes without text. */
  /* Theme-aware: light = milky glass chip; dark = a recessed smoked
     well inside the capsule. The old hardcoded white chip made the
     dark theme's pale inactive label (tinta-500) sit on milky white
     at ~1.6:1 - the unreadable ES next to an active EN. */
  background: light-dark(rgba(255, 255, 255, 0.55), rgba(255, 255, 255, 0.08));
  border: 1px solid light-dark(rgba(255, 255, 255, 0.72), rgba(255, 255, 255, 0.14));
  box-shadow:
    inset 0 1px 0 light-dark(rgba(255, 255, 255, 0.8), rgba(255, 255, 255, 0.08)),
    inset 0 -1px 0 rgba(0, 0, 0, 0.04),
    0 1px 2px light-dark(rgba(0, 0, 0, 0.04), rgba(0, 0, 0, 0.3));
  border-radius: var(--glass-radius-pill);
  -webkit-backdrop-filter: blur(var(--glass-blur-sm)) saturate(var(--glass-saturate));
  backdrop-filter: blur(var(--glass-blur-sm)) saturate(var(--glass-saturate));
}

/* The animated dark indicator.

   Two animation contexts both drive this element:
     • IN-PAGE (hover preview):  CSS transition animates --x / --w
       via `scripts/main.js` mouseover.
     • CROSS-PAGE (commit):  the `view-transition-name` below tags
       the element as a shared surface. When the user clicks to
       switch language, the browser snapshots the indicator's
       position on the old page and interpolates to the new page's
       position natively — no JS bridge required.

   Both paths use the same cubic-bezier so they feel like one motion.

   The initial-paint flash we had (indicator rendering at 0,34 for
   one frame before JS corrects it) is prevented by the new
   `.lang-toggle[data-ready="false"]` rule: transitions are suppressed
   until the first rAF, then enabled. */
.lang-toggle-indicator {
  position: absolute;
  top: 4px;
  bottom: 4px;
  left: 0;
  width: var(--w, 34px);
  transform: translate3d(var(--x, 0px), 0, 0);
  background: var(--tinta-900);
  border-radius: var(--glass-radius-pill);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.14),
    inset 0 -1px 0 rgba(0, 0, 0, 0.3),
    0 2px 6px rgba(0, 0, 0, 0.22);
  pointer-events: none;
  z-index: 1;
  will-change: transform, width;
  transition:
    transform 440ms cubic-bezier(0.16, 1, 0.3, 1),
    width 440ms cubic-bezier(0.16, 1, 0.3, 1);
}
/* Only the NAV's toggle participates in the cross-document view
   transition — a `view-transition-name` must be globally unique per
   document, and the footer also contains a `.lang-toggle-indicator`.
   The nav version is above-fold, visible on every scroll depth, and
   is the one users interact with most; the footer toggle still works,
   it just doesn't do the cross-page slide. */
.nav .lang-toggle-indicator {
  view-transition-name: lang-indicator;
}
/* Suppress the initial-paint glide. The JS in main.js sets
   data-ready="true" after it has positioned the indicator over the
   active anchor; only then do in-page hover transitions activate.
   (The cross-document transition is unaffected — it uses view-
   transition snapshots, not the CSS `transition` property.) */
.lang-toggle[data-ready="false"] .lang-toggle-indicator {
  transition: none;
}

.lang-toggle a {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 4px 8px;
  border-radius: var(--glass-radius-pill);
  color: var(--tinta-500);
  z-index: 2;
  transition: color 320ms cubic-bezier(0.2, 0, 0, 1);
}
.lang-toggle a:hover { color: var(--tinta-900); }
.lang-toggle a.lang-toggle--active { color: var(--blanco); }
/* Drawn flags of the Americas (inline SVG — emoji flags render as bare
   letter pairs on Windows). A hairline keeps the white Canadian field
   visible on light surfaces and the dark canton visible on dark ones. */
.lang-flag {
  display: block;
  flex: none;
  border-radius: 2.5px;
  box-shadow: 0 0 0 0.5px light-dark(rgba(26, 18, 6, 0.22), rgba(255, 255, 255, 0.25));
}

/* Five-language dropdown for the nav (landing pages). A native
   <details>: tap toggles it with no JS (mobile), main.js adds
   hover-open on fine pointers plus outside-click and Escape to close.
   The panel materializes from its trigger (transform-origin at the
   summary) and drops the motion under prefers-reduced-motion. */
.lang-menu { position: relative; }
/* The nav capsule clips its contents (overflow: clip) to keep the glass
   edges clean — which would also clip the dropdown panel. Un-clip only
   while the menu is open; the capsule's own contents never overflow, so
   nothing else changes. */
.nav:has(details.lang-menu[open]) { overflow: visible; }
.lang-menu summary {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 7px 12px;
  font-size: var(--fs-caption);
  font-weight: 600;
  color: var(--tinta-900);
  cursor: pointer;
  list-style: none;
  background: light-dark(rgba(255, 255, 255, 0.55), rgba(255, 255, 255, 0.08));
  border: 1px solid light-dark(rgba(255, 255, 255, 0.72), rgba(255, 255, 255, 0.14));
  box-shadow:
    inset 0 1px 0 light-dark(rgba(255, 255, 255, 0.8), rgba(255, 255, 255, 0.08)),
    inset 0 -1px 0 rgba(0, 0, 0, 0.04),
    0 1px 2px light-dark(rgba(0, 0, 0, 0.04), rgba(0, 0, 0, 0.3));
  border-radius: var(--glass-radius-pill);
  -webkit-backdrop-filter: blur(var(--glass-blur-sm)) saturate(var(--glass-saturate));
  backdrop-filter: blur(var(--glass-blur-sm)) saturate(var(--glass-saturate));
}
.lang-menu summary::-webkit-details-marker { display: none; }
.lang-menu summary:active { transform: scale(0.97); }
.lang-menu .lang-menu-chev { transition: transform 180ms cubic-bezier(0.2, 0, 0, 1); }
.lang-menu[open] .lang-menu-chev { transform: rotate(180deg); }
.lang-menu ul {
  position: absolute;
  top: calc(100% + 8px);
  left: 50%;
  translate: -50% 0;
  margin: 0;
  padding: 6px;
  list-style: none;
  min-width: 176px;
  z-index: 60;
  background: light-dark(rgba(255, 252, 245, 0.92), rgba(28, 26, 23, 0.92));
  border: 1px solid light-dark(rgba(26, 18, 6, 0.12), rgba(255, 255, 255, 0.14));
  border-radius: 16px;
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.16), 0 2px 8px rgba(0, 0, 0, 0.08);
  -webkit-backdrop-filter: blur(var(--glass-blur-sm)) saturate(var(--glass-saturate));
  backdrop-filter: blur(var(--glass-blur-sm)) saturate(var(--glass-saturate));
  transform-origin: top center;
  animation: lang-menu-in 170ms cubic-bezier(0.2, 0, 0, 1);
}
@keyframes lang-menu-in {
  from { opacity: 0; transform: scale(0.94) translateY(-4px); }
  to { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  .lang-menu ul { animation: none; }
  .lang-menu .lang-menu-chev { transition: none; }
  .lang-menu summary:active { transform: none; opacity: 0.8; }
}
.lang-menu ul a {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 9px 10px;
  border-radius: 10px;
  text-decoration: none;
  color: var(--tinta-900);
  font-weight: 600;
  font-size: var(--fs-caption);
}
.lang-menu ul a:hover { background: light-dark(rgba(26, 18, 6, 0.06), rgba(255, 255, 255, 0.08)); }
.lang-menu ul a[aria-current="true"] { font-weight: 800; }
.lang-menu ul a[aria-current="true"]::after {
  content: "";
  margin-left: auto;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--verde-500);
}
/* Very small phones: the nav capsule runs out of room and starts
   clipping its own controls. The pill compacts to flag + chevron
   (the flag is the mode's identity here) and the spacing tightens. */
@media (max-width: 419.98px) {
  .lang-menu .lang-menu-code { display: none; }
  .lang-menu summary { padding: 7px 9px; }
  .nav-actions { gap: 8px; }
  .nav-inner { gap: 14px; }
}
/* Fold-class widths (Galaxy Fold cover screen is 280px): the wordmark
   text yields and the app icon carries the brand. */
@media (max-width: 339.98px) {
  .brand-mark-text { display: none; }
}
/* While the indicator is over a non-active anchor (hover anticipation),
   lighten the hovered link to show focus intent without flickering
   the non-hovered link's color. JS sets data-preview="<code>" on the
   host when a hover is in flight. */
.lang-toggle[data-preview="es"] a[data-lang="es"],
.lang-toggle[data-preview="en"] a[data-lang="en"],
.lang-toggle[data-preview="pt"] a[data-lang="pt"],
.lang-toggle[data-preview="nah"] a[data-lang="nah"],
.lang-toggle[data-preview="fr"] a[data-lang="fr"] { color: var(--blanco); }
.lang-toggle[data-preview]:not([data-preview="es"]) a[data-lang="es"].lang-toggle--active,
.lang-toggle[data-preview]:not([data-preview="en"]) a[data-lang="en"].lang-toggle--active,
.lang-toggle[data-preview]:not([data-preview="pt"]) a[data-lang="pt"].lang-toggle--active,
.lang-toggle[data-preview]:not([data-preview="nah"]) a[data-lang="nah"].lang-toggle--active,
.lang-toggle[data-preview]:not([data-preview="fr"]) a[data-lang="fr"].lang-toggle--active {
  color: var(--tinta-500);
}

/* Reduced motion: the indicator still reflects the active anchor's
   position, but without the transit animation — jumps to it. Still
   visually a glass pill, still readable, just no morph. */
@media (prefers-reduced-motion: reduce) {
  .lang-toggle-indicator { transition: none; }
}
@media (prefers-reduced-transparency: reduce) {
  .lang-toggle { filter: none; }
  .lang-toggle-indicator { box-shadow: none; }
}
.nav-hamburger {
  display: none;
  width: 40px;
  height: 40px;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-md);
  color: var(--tinta-900);
}
.nav-hamburger:hover { background: var(--verde-050); }

/* The legacy bar only ever shows its link list in the narrow band
   between the mobile hamburger (<1024px) and the rail (≥1280px).
   Five links plus the actions cluster don't fit there at the
   desktop 32px rhythm, so tighten the gap and drop the secondary
   sign-in link — it's still in the sheet, the footer and the final
   CTA. */
@media (min-width: 1024px) and (max-width: 1279.98px) {
  .nav-links { gap: 22px; }
  .nav-links a { font-size: var(--fs-body-s); }
  .nav-actions .btn-ghost { display: none; }
}

@media (max-width: 1023px) {
  .nav-links { display: none; }
  .nav-hamburger { display: flex; }
  .nav-actions .btn-primary { display: none; }
  /* Desktop relies on `.nav-links { margin: 0 auto }` to push the
     actions to the far right. With .nav-links hidden on tablet /
     mobile that margin disappears, so the actions collapse next to
     the logo. Re-anchor them to the right edge so the hamburger
     sits flush with the container. */
  .nav-actions { margin-left: auto; }
}

@media (max-width: 480px) {
  .nav-inner { gap: 16px; }
  .nav-actions { gap: 12px; }
  .nav-actions .btn-ghost { display: none; }
}

.nav-sheet {
  position: fixed;
  inset: 0;
  background: var(--hueso);
  z-index: 200;
  display: flex;
  flex-direction: column;
  padding: 88px 24px 32px;
  transform: translateY(-100%);
  /* `visibility` is what actually closes this sheet. Sliding it out
     of view with `transform` alone left its six links, two buttons
     and the close control in the tab order on EVERY page at EVERY
     viewport — keyboard users tabbed into an invisible dialog, and
     `role="dialog" aria-modal="true"` told screen readers a modal
     was permanently open. `visibility: hidden` removes it from both
     the tab order and the accessibility tree; the 0s delay equal to
     the slide duration keeps the close animation watchable instead
     of snapping the sheet away on the first frame. */
  visibility: hidden;
  transition: transform var(--dur-slow) var(--ease-decelerate),
              visibility 0s linear var(--dur-slow);
  overflow-y: auto;
}
.nav-sheet.nav-sheet--open {
  transform: translateY(0);
  visibility: visible;
  transition: transform var(--dur-slow) var(--ease-decelerate),
              visibility 0s;
}
@media (prefers-reduced-motion: reduce) {
  .nav-sheet { transition: visibility 0s; }
}
/* Direct-child selector only — the actions-row anchors inside
   .nav-sheet-actions are buttons, not menu links, and should NOT
   inherit the 22px / hairline-border list styling that would
   clobber their padding, colour, and add stray underlines. */
.nav-sheet > a {
  font-size: 22px;
  font-weight: 700;
  padding: 18px 0;
  border-bottom: 1px solid var(--hairline);
  color: var(--tinta-900);
}
.nav-sheet > a:first-of-type { border-top: 1px solid var(--hairline); }
.nav-sheet-actions {
  margin-top: 32px;
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.nav-sheet-actions .btn { justify-content: center; width: 100%; padding: 16px 24px; }

/* ---------- Hero ---------- */

/* The editorial layer owns hero layout (`.ed-grid--hero`). What survives
   here is the copy/CTA/badge typography those panels still compose with. */
.hero-sub {
  margin-top: 24px;
  color: var(--tinta-500);
  max-width: 480px;
}
.hero-ctas { margin-top: 32px; display: flex; gap: 16px; align-items: center; flex-wrap: wrap; }
.hero-store-row { display: flex; gap: 12px; margin-top: 24px; flex-wrap: wrap; }
/* Hero store badges — glass pill. Clear variant so the hero art
   behind them still reads (when present), with a hair-thin warm
   stroke so the edges define against the hueso canvas. */
.store-badge {
  position: relative;
  display: inline-flex;
  flex-direction: column;
  padding: 10px 18px;
  border-radius: var(--glass-radius-pill);
  border: 1px solid var(--glass-edge-stroke-warm);
  background: rgba(255, 255, 255, 0.55);
  -webkit-backdrop-filter:
    blur(var(--glass-blur-sm)) saturate(var(--glass-saturate)) brightness(var(--glass-brightness));
  backdrop-filter:
    blur(var(--glass-blur-sm)) saturate(var(--glass-saturate)) brightness(var(--glass-brightness));
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.7),
    inset 0 -1px 0 rgba(0, 0, 0, 0.04),
    var(--glass-shadow-sm);
  font-size: 11px;
  line-height: 1.2;
  color: var(--tinta-500);
  font-weight: 600;
  min-width: 160px;
  transition: transform var(--dur-fast) var(--ease-emphasized),
              box-shadow var(--dur-fast) var(--ease-standard),
              background var(--dur-fast) var(--ease-standard);
}
.store-badge:hover {
  transform: translateY(-1px);
  background: rgba(255, 255, 255, 0.72);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.8),
    inset 0 -1px 0 rgba(0, 0, 0, 0.04),
    var(--glass-shadow-md);
}
.store-badge strong {
  font-size: 15px;
  font-weight: 700;
  color: var(--tinta-900);
  letter-spacing: -0.005em;
}
@media (max-width: 768px) {
  .hero-sub { margin-top: 20px; }
  .hero-ctas { margin-top: 24px; gap: 12px; }
  .hero-store-row { margin-top: 20px; gap: 10px; }
  .store-badge { min-width: 0; flex: 1 1 0; padding: 10px 14px; }
}


/* ---------- Trust strip ---------- */

.trust {
  position: relative;
  background:
    radial-gradient(80vw 40vw at 50% 0%, rgba(0, 166, 81, 0.38) 0%, transparent 60%),
    radial-gradient(60vw 34vw at 10% 100%, rgba(111, 207, 151, 0.18) 0%, transparent 60%),
    linear-gradient(180deg, var(--verde-900) 0%, #002A1A 100%);
  color: var(--paper-fixed);
  overflow: hidden;
  isolation: isolate;
}
.trust::before {
  content: "";
  position: absolute;
  inset: 0;
  background-image:
    url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='240' height='240'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 0.85 0 0 0 0.05 0'/></filter><rect width='100%25' height='100%25' filter='url(%23n)'/></svg>");
  background-size: 240px 240px;
  mix-blend-mode: overlay;
  opacity: 0.4;
  pointer-events: none;
  z-index: 0;
}
.trust > * { position: relative; z-index: 1; }
/* Legacy `.trust` override (solid verde-900) — retired. */
.trust-inner {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  padding: clamp(40px, 4vw, 56px) 0;
}
.trust-value {
  color: var(--paper-fixed);
  margin-bottom: 6px;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.24);
}
.trust-label {
  color: rgba(250, 247, 242, 0.78);
  font-size: var(--fs-caption);
  letter-spacing: 0.02em;
  font-weight: 500;
}
@media (max-width: 768px) {
  .trust-inner { grid-template-columns: repeat(2, 1fr); gap: 32px; }
}

/* ---------- Generic section header ---------- */

.section-head {
  text-align: center;
  margin-bottom: var(--space-section-head);
  max-width: 760px;
  margin-left: auto;
  margin-right: auto;
  padding: 0 24px;
}
.section-head .eyebrow {
  /* Inline chips already float; just center + add a little breathing
     room above the big title. */
  margin-bottom: 20px;
}
.section-head h2 {
  color: var(--tinta-900);
  text-shadow: 0 1px 0 rgba(255, 255, 255, 0.32);
}
.section-head .kicker {
  max-width: 560px;
  margin: 18px auto 0;
  color: var(--tinta-500);
  line-height: 1.55;
}


/* ---------- How it works (home page mini-version) ---------- */

.how {
  position: relative;
  background: transparent;
}
.how::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: -1;
  background:
    radial-gradient(60vw 40vw at 50% 50%, rgba(111, 207, 151, 0.08) 0%, transparent 60%),
    radial-gradient(60vw 40vw at 0% 50%, rgba(0, 166, 81, 0.06) 0%, transparent 60%);
}
.how-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 24px;
}
.how-num {
  font-size: var(--fs-display-m);
  font-weight: 800;
  color: transparent;
  background: linear-gradient(135deg, var(--verde-500) 0%, var(--verde-900) 100%);
  -webkit-background-clip: text;
          background-clip: text;
  line-height: 1;
  letter-spacing: -0.02em;
  margin-bottom: 4px;
  filter: drop-shadow(0 2px 8px rgba(0, 104, 71, 0.18));
}
.how-title { color: var(--tinta-900); }
.how-body { color: var(--tinta-500); }
.how-device {
  width: 120px;
  aspect-ratio: 9 / 18;
  margin-top: 8px;
  border-radius: var(--radius-md);
  overflow: hidden;
  border: 1px solid var(--tinta-300);
}
.how-cta { text-align: center; margin-top: 56px; }

@media (max-width: 768px) {
  .how-grid { grid-template-columns: 1fr; }
}

/* ---------- App showcase ----------
   The `.showcase-device` rules that lived here styled hand-drawn SVG
   approximations of the app. Those are gone: the showcase now renders
   the real captures inside `.device-frame`, further down this file. */


/* ---------- Pricing ----------
   The "$0. In every column that matters" section. A verde stage
   with a gold spotlight bloom behind the headline, glass pill
   surrounds on the $0 columns, and a subtle paper grain to keep
   the gradient from reading as flat CSS.                           */

.pricing {
  position: relative;
  background:
    radial-gradient(85vw 55vw at 50% -14%, rgba(0, 214, 104, 0.28) 0%, transparent 58%),
    radial-gradient(70vw 48vw at 20% 100%, rgba(111, 207, 151, 0.22) 0%, transparent 62%),
    radial-gradient(70vw 48vw at 80% 100%, rgba(0, 132, 85, 0.24) 0%, transparent 60%),
    linear-gradient(180deg, var(--verde-900) 0%, var(--verde-700) 40%, #00583C 100%);
  color: var(--paper-fixed);
  text-align: center;
  overflow: hidden;
  isolation: isolate;
}
/* Paper grain for the pricing ground — same recipe as cta-final::before. */
.pricing::before {
  content: "";
  position: absolute;
  inset: 0;
  background-image:
    url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='240' height='240'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 0.85 0 0 0 0.05 0'/></filter><rect width='100%25' height='100%25' filter='url(%23n)'/></svg>");
  background-size: 240px 240px;
  mix-blend-mode: overlay;
  opacity: 0.5;
  pointer-events: none;
  z-index: 0;
}
/* Gold spotlight bloom centered behind the headline — mirrors the
   cta-final gold spotlight so the page's aurora language carries. */
.pricing::after {
  content: "";
  position: absolute;
  top: -10%;
  left: 50%;
  transform: translateX(-50%);
  width: 68vw;
  max-width: 720px;
  aspect-ratio: 1;
  background: radial-gradient(
    circle,
    rgba(111, 207, 151, 0.32) 0%,
    rgba(111, 207, 151, 0.14) 35%,
    transparent 72%
  );
  pointer-events: none;
  z-index: 0;
  filter: blur(1px);
}
.pricing > * { position: relative; z-index: 1; }
.pricing .eyebrow { color: var(--verde-300); }
.pricing .eyebrow::before { background: var(--verde-300); box-shadow: 0 0 0 3px rgba(111, 207, 151, 0.22); }
.pricing h2 {
  color: var(--paper-fixed);
  margin-top: 16px;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.18);
}
.pricing-amount {
  font-size: var(--fs-display-l);
  font-weight: 800;
  line-height: 1;
  letter-spacing: -0.025em;
  color: var(--paper-fixed);
  font-variant-numeric: tabular-nums;
  text-shadow: 0 2px 8px rgba(0, 0, 0, 0.22);
}
.pricing-label {
  margin-top: 10px;
  color: rgba(250, 247, 242, 0.82);
  font-size: var(--fs-body-l);
  font-weight: 500;
}
.pricing-note {
  margin-top: 48px;
  max-width: 620px;
  margin-left: auto;
  margin-right: auto;
  font-size: var(--fs-caption);
  color: rgba(230, 244, 236, 0.72);
  line-height: 1.6;
}


/* ---------- Security ---------- */

.security {
  position: relative;
  background: transparent;
}
.security-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 64px;
  align-items: start;
}
.security-body {
  margin-top: 20px;
  color: var(--tinta-500);
  max-width: 560px;
}
.security-badges {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 14px;
  /* `minmax(0, 1fr)` so the tracks may shrink below their intrinsic
     content width. Without this, the longest badge label (e.g. the
     "AES-256-GCM" run, which the browser treats as one unbreakable
     token) sets a minimum track width, and on narrow viewports the
     grid spills past the container — which on the /en/ homepage
     was dragging 2–3px of horizontal overflow into the document. */
  grid-template-columns: repeat(2, minmax(0, 1fr));
}
@media (max-width: 520px) {
  /* Stack badges on a phone. Two-wide at 375–390px cramped the text
     to ~145px/track once icon + padding + gap were accounted for;
     the resulting two-line clamp looked like a layout bug rather
     than a deliberate card. */
  .security-badges { grid-template-columns: 1fr; }
}
.security-badge {
  position: relative;
  background: var(--glass-surface-luminous);
  -webkit-backdrop-filter: blur(var(--glass-blur-md)) saturate(var(--glass-saturate)) brightness(var(--glass-brightness));
  backdrop-filter: blur(var(--glass-blur-md)) saturate(var(--glass-saturate)) brightness(var(--glass-brightness));
  border: 1px solid var(--glass-edge-luminous);
  border-radius: var(--glass-radius-md);
  padding: 20px 22px;
  display: flex;
  gap: 14px;
  align-items: flex-start;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.82),
    inset 0 -1px 0 rgba(11, 11, 11, 0.03),
    var(--glass-shadow-sm);
  isolation: isolate;
  transition: transform var(--dur-fast) var(--ease-emphasized),
              box-shadow var(--dur-fast) var(--ease-standard),
              background var(--dur-fast) var(--ease-standard);
}
.security-badge::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  background: linear-gradient(
    180deg,
    rgba(255, 255, 255, 0.3) 0%,
    rgba(255, 255, 255, 0.03) 40%,
    transparent 72%
  );
  z-index: 0;
}
.security-badge > * { position: relative; z-index: 1; }
.security-badge:hover {
  transform: translateY(-2px);
  background: var(--glass-surface-luminous-strong);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.9),
    inset 0 -1px 0 rgba(0, 0, 0, 0.04),
    var(--glass-shadow-md),
    var(--halo-verde-sm);
}
.security-badge-icon {
  color: var(--paper-fixed);
  flex-shrink: 0;
  width: 36px;
  height: 36px;
  padding: 6px;
  border-radius: 10px;
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.42) 0%, rgba(255, 255, 255, 0) 58%),
    linear-gradient(135deg, var(--verde-500) 0%, var(--verde-700) 100%);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.38),
    inset 0 -1px 0 rgba(0, 0, 0, 0.16),
    0 4px 12px -2px rgba(0, 104, 71, 0.32);
}
.security-badge-text {
  font-size: var(--fs-body);
  color: var(--tinta-900);
  font-weight: 600;
  line-height: 1.4;
  letter-spacing: -0.003em;
}

@media (max-width: 1023px) {
  .security-grid { grid-template-columns: 1fr; gap: 40px; }
}


/* ---------- FAQ ---------- */

.faq-list {
  margin-top: 48px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
/* Each FAQ is a glass pill. Closed: a tight glassy row. Open:
   expands, switches the border stroke to an oro tint, and picks
   up a warm inner highlight so the answer panel reads as lit
   from within. */
.faq-item {
  position: relative;
  background: var(--glass-surface-luminous);
  -webkit-backdrop-filter: blur(var(--glass-blur-md)) saturate(var(--glass-saturate)) brightness(var(--glass-brightness));
  backdrop-filter: blur(var(--glass-blur-md)) saturate(var(--glass-saturate)) brightness(var(--glass-brightness));
  border: 1px solid var(--glass-edge-luminous);
  border-radius: var(--glass-radius-sm);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.72),
    inset 0 -1px 0 rgba(0, 0, 0, 0.03),
    var(--glass-shadow-sm);
  overflow: hidden;
  transition:
    background var(--dur-base) var(--ease-standard),
    box-shadow var(--dur-base) var(--ease-standard),
    border-color var(--dur-base) var(--ease-standard),
    transform var(--dur-base) var(--ease-emphasized);
}
.faq-item:hover {
  transform: translateY(-1px);
  background: var(--glass-surface-luminous-strong);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.82),
    inset 0 -1px 0 rgba(0, 0, 0, 0.03),
    var(--glass-shadow-md),
    var(--halo-verde-sm);
}
.faq-item[open] {
  background: rgba(250, 254, 251, 0.88);
  border-color: rgba(111, 207, 151, 0.42);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.85),
    inset 0 -1px 0 rgba(0, 0, 0, 0.04),
    0 1px 2px rgba(0, 0, 0, 0.05),
    0 16px 36px -10px rgba(11, 11, 11, 0.12),
    var(--halo-verde-sm);
}
.faq-item[open]:hover {
  transform: none;
}
.faq-item summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 20px 24px;
  font-size: var(--fs-body-l);
  font-weight: 600;
  color: var(--tinta-900);
  cursor: pointer;
  list-style: none;
  letter-spacing: -0.005em;
}
.faq-item summary::-webkit-details-marker { display: none; }
.faq-item summary:hover { color: var(--verde-900); }
/* `summary` is a flex row, so EVERY inline child becomes its own flex
   item and both the 16px gap and `space-between` land between them. A
   question carrying region-swapped figures rendered as
   "Why 1% with a    $1    USD    cap?" for that reason. `.faq-q` keeps
   the question one item; wrap any summary whose text contains markup. */
.faq-item summary .faq-q { min-width: 0; }
.faq-chev {
  flex-shrink: 0;
  transition: transform var(--dur-slow) var(--ease-emphasized);
  color: var(--tinta-500);
}
.faq-item[open] .faq-chev { transform: rotate(180deg); color: var(--verde-700); }
.faq-answer {
  padding: 0 24px 22px;
  color: var(--tinta-500);
  line-height: 1.65;
  animation: faq-fade var(--dur-slow) var(--ease-emphasized);
}
@keyframes faq-fade {
  from { opacity: 0; transform: translateY(-4px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* ---------- FAQ hub (/preguntas, /en/faq) ----------

   Editorial-directory layout. The old hub squeezed three white boxes
   into a 640px `.container.reading` column, creating a 2+1 orphan
   grid and reading like a generic admin shell rather than Pesito
   chrome. The new hub treats the page as a book's table of contents:

     1. A wide `.container-inner` (1040px) so the three category
        cards have room to breathe side-by-side on desktop.
     2. Big display-font tabular numerals as chapter markers, inked
        in a verde-700 → oro gradient so each card feels stamped by
        the brand rather than numbered by a spreadsheet.
     3. Three glass cards (the same material family as `.pillar` and
        `.scenario`) with hover lift + oro hairline + arrow slide.
     4. An ambient verde aurora bleeding in from the top-right,
        mirroring `.how-hero`, so the two "guide" pages read as one
        chapter family.
     5. Most-asked list as a quiet companion strip beneath a single
        gold capsule ornament — no bolted-on oro-bordered box.
   ═══════════════════════════════════════════════════════════════ */

.faq-hub {
  padding: clamp(96px, 9vw, 128px) 0 clamp(64px, 7vw, 96px);
  background:
    radial-gradient(
      1200px 600px at 100% -10%,
      rgba(0, 166, 81, 0.10) 0%,
      transparent 60%
    ),
    var(--hueso);
}

/* Hero block — breadcrumb, eyebrow, h1, lede. Width-capped so the
   display title wraps at a readable 2-3 lines rather than stretching
   across the 1040px container into one long river. */
.faq-hub-head {
  max-width: 720px;
  margin-bottom: clamp(48px, 5vw, 72px);
}

.faq-hub-crumb {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: var(--fs-caption);
  font-weight: 600;
  color: var(--tinta-500);
  margin-bottom: 24px;
  transition: color var(--dur-fast) var(--ease-standard);
}
.faq-hub-crumb:hover { color: var(--verde-700); }
.faq-hub-crumb svg {
  transition: transform var(--dur-fast) var(--ease-emphasized);
}
/* Arrow slides LEFT on hover — signalling "going back". Inverse of
   the forward-arrow pattern used on primary CTAs. */
.faq-hub-crumb:hover svg { transform: translateX(-2px); }

.faq-hub-title {
  margin-top: 16px;
  color: var(--tinta-900);
  max-width: 20ch;
  text-wrap: balance;
}
.faq-hub-lede {
  margin-top: 24px;
  color: var(--tinta-700);
  max-width: 58ch;
}
.faq-hub-mail {
  color: var(--accent-verde);
  text-decoration: underline;
  text-underline-offset: 2px;
}
.faq-hub-mail:hover { color: var(--accent-verde-strong); }

/* Category grid — explicit 3-col so rhythm holds. `auto-fit` would
   drop to 2+1 on sub-1040px widths, which is the exact "weird
   orphan" bug the old layout had. Collapse to 1-col at tablet. */
.faq-hub-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 20px;
}
@media (max-width: 960px) {
  .faq-hub-grid { grid-template-columns: 1fr; gap: 16px; }
}


/* Most-asked strip — a second table of contents for visitors who
   know what they want to find. Anchored by a single oro capsule
   ornament at the top-left of the divider rule, which matches the
   `.eyebrow::before` hairline language elsewhere on the site. */
.faq-hub-most {
  margin-top: clamp(64px, 7vw, 96px);
  padding-top: clamp(36px, 3.5vw, 48px);
  border-top: 1px solid rgba(111, 207, 151, 0.35);
  position: relative;
}
.faq-hub-most::before {
  content: "";
  position: absolute;
  top: -5px;
  left: 0;
  width: 48px;
  height: 9px;
  background: var(--verde-500);
  border-radius: 9999px;
  box-shadow: 0 0 0 4px rgba(111, 207, 151, 0.14);
}
.faq-hub-most-head {
  max-width: 560px;
  margin-bottom: 32px;
}
.faq-hub-most-title {
  margin-top: 12px;
  font-size: clamp(22px, 2vw, 28px);
  font-weight: 700;
  color: var(--tinta-900);
  letter-spacing: -0.01em;
  text-wrap: balance;
}
.faq-hub-most-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 0 40px;
  list-style: none;
  padding: 0;
}
@media (max-width: 720px) {
  .faq-hub-most-grid { grid-template-columns: 1fr; gap: 0; }
}
.faq-hub-most-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 18px 4px;
  border-bottom: 1px solid var(--hairline);
  color: var(--tinta-900);
  font-weight: 600;
  font-size: var(--fs-body);
  text-decoration: none;
  transition:
    color var(--dur-fast) var(--ease-standard),
    border-color var(--dur-fast) var(--ease-standard),
    padding-left var(--dur-fast) var(--ease-emphasized);
}
/* Hanging arrow — tinta-300 at rest, verde on hover. Slides 4px
   right on hover while the item itself indents 8px left-padding,
   creating a subtle pull-toward-you effect. */
.faq-hub-most-item::after {
  content: "→";
  flex-shrink: 0;
  color: var(--tinta-300);
  font-weight: 400;
  font-size: 18px;
  transition:
    color var(--dur-fast) var(--ease-standard),
    transform var(--dur-fast) var(--ease-emphasized);
}
.faq-hub-most-item:hover {
  color: var(--verde-900);
  border-color: var(--verde-500);
  padding-left: 8px;
}
.faq-hub-most-item:hover::after {
  color: var(--verde-500);
  transform: translateX(4px);
}
.faq-hub-most-item:focus-visible {
  outline: 2px solid var(--verde-500);
  outline-offset: 2px;
  border-radius: 4px;
}


.cta-final {
  position: relative;
  background:
    radial-gradient(90vw 60vw at 50% -8%, rgba(0, 214, 104, 0.28) 0%, transparent 60%),
    radial-gradient(70vw 48vw at 14% 100%, rgba(111, 207, 151, 0.22) 0%, transparent 62%),
    radial-gradient(70vw 48vw at 86% 110%, rgba(0, 132, 85, 0.28) 0%, transparent 62%),
    linear-gradient(180deg, #005E3E 0%, var(--verde-700) 48%, var(--verde-900) 100%);
  color: var(--paper-fixed);
  padding: clamp(88px, 10vw, 140px) 0 clamp(72px, 8.5vw, 120px);
  overflow: hidden;
  isolation: isolate;
}

.cta-final::before {
  content: "";
  position: absolute;
  inset: 0;
  background-image:
    url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='240' height='240'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 0.85 0 0 0 0.06 0'/></filter><rect width='100%25' height='100%25' filter='url(%23n)'/></svg>");
  background-size: 240px 240px;
  opacity: 0.45;
  mix-blend-mode: overlay;
  pointer-events: none;
  z-index: 0;
}
/* Gold spotlight behind the headline — bigger, softer, more
   dimensional than v1. Two stacked gradients (warm oro core + a
   wider cream halo) so the bloom reads as a real projector rather
   than a flat gradient. */
.cta-final::after {
  content: "";
  position: absolute;
  top: -14%;
  left: 50%;
  width: 78vw;
  max-width: 880px;
  aspect-ratio: 1;
  transform: translateX(-50%);
  background:
    radial-gradient(circle at 50% 50%, rgba(111, 207, 151, 0.22) 0%, transparent 55%),
    radial-gradient(circle at 50% 52%, rgba(111, 207, 151, 0.34) 0%, rgba(111, 207, 151, 0.14) 32%, transparent 70%);
  pointer-events: none;
  z-index: 0;
  filter: blur(2px);
  animation: cta-spot-breathe 12s ease-in-out infinite;
}
@keyframes cta-spot-breathe {
  0%, 100% { opacity: 0.9;  transform: translateX(-50%) scale(1); }
  50%      { opacity: 1;    transform: translateX(-50%) scale(1.04); }
}
@media (prefers-reduced-motion: reduce) {
  .cta-final::after { filter: none; animation: none; }
}



.cta-final h2 {
  color: var(--paper-fixed);
  margin-top: 20px;
  max-width: 16ch;
  margin-left: auto;
  margin-right: auto;
}





/* Legacy waitlist form rules kept for /lista-espera dedicated page. */
.cta-final .waitlist-error {
  margin-top: 10px;
  padding: 10px 14px;
  border-radius: var(--radius-md);
  background: var(--rojo-050);
  color: var(--rojo-700);
  font-size: var(--fs-body-s);
  font-weight: 600;
  line-height: 1.45;
  border: 1px solid var(--rojo-200);
  text-align: left;
}
.cta-final .waitlist.is-error .waitlist-row {
  border-color: var(--rojo-500);
  box-shadow: 0 0 0 4px rgba(206, 17, 38, 0.12);
}
.cta-final .waitlist-success {
  display: none;
  margin-top: 14px;
  padding: 12px 16px;
  border-radius: var(--radius-md);
  background: var(--verde-100);
  color: var(--verde-900);
  font-size: var(--fs-body);
  font-weight: 600;
  line-height: 1.45;
  border: 1px solid rgba(0, 104, 71, 0.15);
  text-align: left;
}
.cta-final .waitlist.is-submitting .waitlist-submit { opacity: 0.6; cursor: wait; }
.cta-final .waitlist.is-success .waitlist-row,
.cta-final .waitlist.is-success .form-consent { display: none; }
/* `~` rather than `+` — a `.waitlist-error` element may sit between the
   form and the success banner, so adjacent-sibling would miss. */
.cta-final .waitlist.is-success ~ .waitlist-success { display: block; }

/* ----- Sign-in half (right) ----- */


.finale-signin-cta {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  margin-top: 22px;
  padding: 14px 18px 14px 22px;
  border-radius: var(--glass-radius-pill);
  /* Dark glass pill — tint-900 with a 60% layer and a saturate-2x
     backdrop so the verde behind the card modulates through. A 1px
     top-highlight makes it read as a "lit" jet-black surface, not
     flat paint. */
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.1) 0%, rgba(255, 255, 255, 0) 50%),
    linear-gradient(180deg, #0e0e11 0%, #050506 100%);
  color: var(--hueso);
  font-size: var(--fs-body);
  font-weight: 700;
  letter-spacing: -0.005em;
  align-self: flex-start;
  border: 1px solid rgba(255, 255, 255, 0.08);
  transition: transform var(--dur-fast) var(--ease-emphasized),
              background var(--dur-fast) var(--ease-standard),
              box-shadow var(--dur-fast) var(--ease-standard);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.2),
    inset 0 -1px 0 rgba(0, 0, 0, 0.4),
    0 1px 2px rgba(0, 0, 0, 0.1),
    0 10px 24px -6px rgba(0, 0, 0, 0.45);
}
.finale-signin-cta:hover {
  transform: translateY(-1px);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.28),
    inset 0 -1px 0 rgba(0, 0, 0, 0.4),
    0 1px 3px rgba(0, 0, 0, 0.12),
    0 18px 32px -10px rgba(0, 0, 0, 0.55);
}
.finale-signin-cta:active { transform: scale(0.98); }


/* ----- Store badges row (under the card) ----- */

.finale-store {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 12px;
  padding: 10px 20px 10px 16px;
  border-radius: var(--glass-radius-pill);
  background: rgba(11, 11, 11, 0.32);
  -webkit-backdrop-filter:
    blur(var(--glass-blur-md)) saturate(var(--glass-saturate)) brightness(var(--glass-brightness));
  backdrop-filter:
    blur(var(--glass-blur-md)) saturate(var(--glass-saturate)) brightness(var(--glass-brightness));
  border: 1px solid rgba(255, 255, 255, 0.14);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.18),
    inset 0 -1px 0 rgba(0, 0, 0, 0.2),
    0 1px 2px rgba(0, 0, 0, 0.12),
    0 10px 24px -8px rgba(0, 0, 0, 0.35);
  color: var(--hueso);
  transition: transform var(--dur-fast) var(--ease-emphasized),
              background var(--dur-fast) var(--ease-standard),
              border-color var(--dur-fast) var(--ease-standard),
              box-shadow var(--dur-fast) var(--ease-standard);
  min-width: 172px;
}
.finale-store:hover {
  transform: translateY(-1px);
  background: rgba(11, 11, 11, 0.44);
  border-color: rgba(255, 255, 255, 0.22);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.26),
    inset 0 -1px 0 rgba(0, 0, 0, 0.24),
    0 1px 3px rgba(0, 0, 0, 0.14),
    0 18px 32px -10px rgba(0, 0, 0, 0.45);
}


/* ----- Responsive collapse ----- */

@media (max-width: 860px) {
  .finale-signin-cta { width: 100%; justify-content: space-between; }
}
@media (max-width: 520px) {
  .finale-store { flex: 1 1 0; min-width: 0; padding: 10px 14px; justify-content: center; }
}

/* ---------- Footer ---------- */

.footer {
  position: relative;
  background: #000;
  color: var(--hueso);
  padding: clamp(56px, 6.2vw, 88px) 0 clamp(32px, 3vw, 40px);
  overflow: hidden;
}
.footer::after { content: none; }
.footer::before { content: none; }
.footer > * { position: relative; z-index: 1; }
@media (max-width: 768px) {
  .footer-bottom { flex-direction: column; align-items: flex-start; }
}
.footer-cols {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 32px;
}
.footer-col h4 {
  font-size: var(--fs-caption);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: rgba(250, 247, 242, 0.6);
  margin-bottom: 16px;
}
.footer-col a {
  display: block;
  padding: 6px 0;
  font-size: var(--fs-body);
  color: var(--hueso);
  transition: color var(--dur-fast) var(--ease-standard);
}
.footer-col a:hover { color: var(--verde-300); }
.footer-bottom {
  margin-top: clamp(40px, 4.5vw, 64px);
  padding-top: 32px;
  border-top: 1px solid rgba(250, 247, 242, 0.1);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  flex-wrap: wrap;
}
/* Capped: uncapped, the disclaimer ran the full container at 13px, which
   is ~180 characters a line. It is the longest single paragraph on the
   site and sat on every page. */
.footer-mouseprint {
  margin-top: 24px;
  font-size: var(--fs-caption);
  color: rgba(250, 247, 242, 0.5);
  line-height: 1.6;
  max-width: var(--measure-note);
}

@media (max-width: 1023px) {
  .footer-cols { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 640px) {
  .footer-cols { grid-template-columns: 1fr; }
}

/* ---------- Wordmark (inline SVG-free, just type) ---------- */

.wordmark {
  display: inline-flex;
  align-items: baseline;
  font-weight: 800;
  letter-spacing: -0.03em;
  color: var(--accent-verde);
}
.wordmark-dot {
  width: 6px;
  height: 6px;
  background: var(--brand-dot);
  border-radius: 50%;
  margin-left: 3px;
  align-self: flex-end;
  margin-bottom: 6px;
}
/* "On dark" means on a SATURATED dark ground (the legacy verde
   footer), not "in dark mode" — so it must not ride the neutral
   ramp, or it turns near-black on a near-black footer. */
.wordmark--on-dark { color: var(--paper-fixed); }

/* Brand mark: the app icon + Pesito wordmark lockup used in chrome
   (nav + footer). The icon has its own identity — bright grass-green
   gradient + peso-₱ glyph — so the heritage `--verde-700` of the
   wordmark needs to drop one step to avoid clashing with the brighter
   green beside it. That's what `.brand-mark-text` does. */
.brand-mark {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  text-decoration: none;
}
.brand-mark-icon {
  width: 32px;
  height: 32px;
  display: block;
  flex-shrink: 0;
  border-radius: 7px;
  box-shadow:
    0 1px 2px rgba(0, 0, 0, 0.08),
    0 6px 14px rgba(0, 104, 71, 0.18);
}
/* Only the NAV's brand icon is a shared element across navigations.
   `view-transition-name` must be globally unique per document, so we
   intentionally scope this to `.nav .brand-mark-icon`. If you ever
   render a second brand-mark inside the page body, it'll simply not
   participate in the cross-document morph (still works visually). */
.nav .brand-mark-icon {
  view-transition-name: brand-icon;
}
.brand-mark-icon--sm {
  width: 26px;
  height: 26px;
  border-radius: 6px;
}
.brand-mark-text {
  font-weight: 800;
  letter-spacing: -0.025em;
  color: var(--tinta-900);
}
.nav .brand-mark-text {
  view-transition-name: brand-text;
}
.brand-mark--on-dark .brand-mark-text { color: var(--hueso); }
@media (max-width: 480px) {
  .brand-mark-icon { width: 28px; height: 28px; border-radius: 6px; }
}

/* ---------- Reveal on scroll ----------
   Implemented as CSS scroll-driven animations in the motion layer at
   the end of this file (search "MOTION LAYER"). Content is visible by
   default — the old JS-gated `.is-revealed` transition shipped blank
   sections in headless renderers and is retired. */

/* ---------- Legal documents (privacidad, terminos, en/privacy, en/terms) ----
   Shared typography + callout vocabulary so legal pages don't each ship
   their own inline `<style>` block. Typography follows the same ladder
   the rest of the site uses (`--fs-*`), but with a tighter column
   (`--reading-max`) and a slightly wider line-height for long-form
   reading. The callout variants map to the three content types legal
   pages need: neutral placeholder, ARCO / rights box, and transfers
   table chrome.

   Anchor: `<main class="legal-main"><article class="legal-body">…`.
   ------------------------------------------------------------------ */

.legal-main {
  padding-top: clamp(96px, 9vw, 128px);
  padding-bottom: clamp(64px, 8.5vw, 120px);
}

.legal-body h2 {
  margin-top: 48px;
  margin-bottom: 12px;
  color: var(--tinta-900);
  font-size: var(--fs-headline);
}
.legal-body h3 {
  margin-top: 32px;
  margin-bottom: 8px;
  color: var(--tinta-700);
  font-size: var(--fs-body-l);
  font-weight: 600;
}
.legal-body p,
.legal-body li {
  color: var(--tinta-500);
  font-size: var(--fs-body-l);
  line-height: 1.7;
  margin-top: 12px;
}
.legal-body ul,
.legal-body ol { padding-left: 24px; margin-top: 12px; }
.legal-body a {
  color: var(--accent-verde);
  text-decoration: underline;
}
.legal-body .legal-effective {
  color: var(--tinta-400);
  margin-top: 8px;
}

/* Neutral callout: "review required", TODOs, draft notices. */
.legal-placeholder {
  margin-top: 24px;
  padding: 16px 20px;
  border: 1px solid var(--tinta-300);
  background: var(--hueso-200);
  color: var(--tinta-500);
  font-size: var(--fs-body);
  border-radius: var(--radius-sm);
}

/* Red "important" callout — legal notice, binding clause. Semantic
   red: full border + tint, never a side stripe. */
.legal-notice {
  margin-top: 24px;
  padding: 16px 20px;
  border: 1px solid var(--rojo-200);
  background: var(--rojo-050);
  color: var(--tinta-700);
  font-size: var(--fs-body);
  border-radius: var(--radius-sm);
}


@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
  html { scroll-behavior: auto; }
  [data-reveal] { opacity: 1; transform: none; }
}

/* ------------------------------------------------------------------
   FX calculator (en-US hero right column + SEO landing pages)
   --------------------------------------------------------------- */

.fx-card {
  background: var(--blanco);
  border: 1px solid var(--hairline);
  border-radius: 24px;
  box-shadow:
    0 1px 2px rgba(0, 0, 0, 0.04),
    0 24px 48px -24px rgba(0, 60, 30, 0.18);
  padding: 28px;
  max-width: 480px;
  width: 100%;
  /* The card lives in a half-width grid column on the landing pages, so
     its own width, not the viewport's, decides whether a four-column
     table fits. Everything inside it sizes against this container. */
  container: fx-card / inline-size;
}
.fx-stale {
  background: var(--hueso-100);
  border: 1px solid var(--tinta-300);
  color: var(--tinta-700);
  border-radius: 12px;
  padding: 10px 14px;
  font-size: var(--fs-caption);
  margin-bottom: 12px;
}
.fx-form {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 16px;
}
/* Two selects side by side inside a ~450px card leaves each about
   215px, which clips option labels mid-word ("OXXO cash picku"). A
   <select> cannot ellipsize gracefully, so it gets the full width. */
@container fx-card (max-width: 560px) {
  .fx-form { grid-template-columns: 1fr; }
}
.fx-field {
  display: block;
}
.fx-field:first-child {
  grid-column: 1 / -1;
}
.fx-label {
  display: block;
  font-size: var(--fs-caption);
  font-weight: 600;
  color: var(--tinta-500);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  margin-bottom: 6px;
}
.fx-input-row {
  display: flex;
  align-items: center;
  background: var(--hueso);
  border: 1px solid var(--hairline-strong);
  border-radius: 12px;
  padding: 10px 14px;
  transition: border-color var(--dur-fast) var(--ease-standard),
              box-shadow var(--dur-fast) var(--ease-standard);
  cursor: text;
}
.fx-input-row:focus-within {
  border-color: var(--verde-700);
  box-shadow: 0 0 0 3px rgba(0, 166, 81, 0.18);
}
.fx-prefix {
  font-weight: 600;
  color: var(--tinta-500);
  margin-right: 6px;
}
.fx-input-row input {
  flex: 1;
  border: none;
  background: transparent;
  font: inherit;
  font-size: 24px;
  font-weight: 700;
  color: var(--tinta-900);
  outline: none;
  width: 100%;
  -moz-appearance: textfield;
}
.fx-input-row input::-webkit-outer-spin-button,
.fx-input-row input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.fx-field select {
  width: 100%;
  background: var(--hueso);
  border: 1px solid var(--hairline-strong);
  border-radius: 12px;
  padding: 12px 14px;
  font: inherit;
  font-size: var(--fs-body);
  font-weight: 600;
  color: var(--tinta-900);
  cursor: pointer;
  appearance: none;
  -webkit-appearance: none;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%23737373' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><polyline points='6 9 12 15 18 9'/></svg>");
  background-repeat: no-repeat;
  background-position: right 14px center;
  padding-right: 36px;
  transition: border-color var(--dur-fast) var(--ease-standard),
              box-shadow var(--dur-fast) var(--ease-standard);
}
.fx-field select:focus {
  outline: none;
  border-color: var(--verde-700);
  box-shadow: 0 0 0 3px rgba(0, 166, 81, 0.18);
}
.fx-hint {
  display: block;
  font-size: var(--fs-caption);
  color: var(--tinta-500);
  margin-top: 6px;
  line-height: 1.4;
}
.fx-result {
  margin-top: 24px;
  padding: 16px 18px;
  background: linear-gradient(180deg, var(--hueso) 0%, var(--blanco) 100%);
  border: 1px solid var(--hairline);
  border-radius: 16px;
  display: grid;
  gap: 8px;
}
.fx-result-row {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  font-size: var(--fs-body-s);
  color: var(--tinta-500);
}
.fx-result-row .fx-result-val { color: var(--tinta-900); font-weight: 600; }
.fx-result-row--total {
  margin-top: 6px;
  padding-top: 10px;
  border-top: 1px solid var(--hairline);
  font-size: var(--fs-body);
}
.fx-result-row--total .fx-result-key { color: var(--tinta-700); font-weight: 600; }
.fx-result-row--total .fx-result-val {
  font-size: 22px;
  color: var(--accent-verde);
  font-weight: 800;
}
.fx-compare {
  margin-top: 24px;
  border-top: 1px solid rgba(0, 0, 0, 0.1);
  padding-top: 20px;
}
/* Mounted in its own section rather than inside the quote card, where
   the four columns finally have room. It names the same container as
   `.fx-card` so one set of rules governs the stacked fallback. */
.fx-compare-standalone {
  margin-top: 0;
  border-top: 0;
  padding-top: 0;
  container: fx-card / inline-size;
}
.fx-compare-standalone .fx-compare-table { font-size: var(--fs-body); }
.fx-compare-standalone .fx-compare-fee,
.fx-compare-standalone .fx-compare-eta { font-size: var(--fs-body-s); }
.fx-compare-standalone .fx-compare-row td { padding: 14px 8px; }
.fx-compare-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  /* The "more on Pesito" pill is a full sentence in Spanish and cannot
     shrink, so on a narrow card it has to be allowed onto its own line. */
  flex-wrap: wrap;
  gap: 8px;
  font-size: var(--fs-caption);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--tinta-500);
  margin-bottom: 12px;
}
.fx-compare-better {
  color: var(--accent-verde);
  background: rgba(0, 166, 81, 0.1);
  padding: 4px 10px;
  border-radius: 999px;
  font-weight: 700;
  text-transform: none;
  letter-spacing: 0;
  font-size: var(--fs-caption);
}
.fx-compare-table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--fs-body-s);
}
.fx-compare-row td {
  padding: 10px 0;
  border-bottom: 1px solid var(--hairline);
  vertical-align: middle;
}
.fx-compare-row:last-child td { border-bottom: none; }
/* Flat tint, not a gradient: with `border-collapse: collapse` a
   gradient repeats per cell, which reads as uneven stripes across the
   row once the table is wide. */
.fx-compare-row--self td { background: rgba(0, 166, 81, 0.08); }
.fx-compare-row--self td:first-child { border-radius: 8px 0 0 8px; }
.fx-compare-row--self td:last-child { border-radius: 0 8px 8px 0; }
.fx-compare-name {
  font-weight: 600;
  color: var(--tinta-900);
  padding-left: 8px !important;
  padding-right: 8px;
}
.fx-compare-method {
  display: block;
  font-weight: 500;
  color: var(--tinta-500);
  font-size: var(--fs-caption);
  margin-top: 2px;
}
.fx-compare-fee {
  color: var(--tinta-500);
  font-size: var(--fs-caption);
  padding: 0 8px !important;
}
.fx-compare-net {
  font-weight: 700;
  color: var(--tinta-900);
  text-align: right;
  white-space: nowrap;
  padding: 0 8px !important;
}
.fx-compare-row--self .fx-compare-net { color: var(--accent-verde); }
.fx-compare-eta {
  color: var(--tinta-500);
  font-size: var(--fs-caption);
  text-align: right;
  padding-right: 8px !important;
}
.fx-compare-foot {
  font-size: var(--fs-caption);
  color: var(--tinta-500);
  margin-top: 12px;
  line-height: 1.45;
}

/* Column headers. The table shipped without a <thead>, which made the
   four columns guessable-at-best on desktop and unlabelled once the
   layout collapses on mobile. */
.fx-compare-head-row th {
  padding: 0 8px 8px;
  text-align: left;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--tinta-500);
  border-bottom: 2px solid var(--hairline-strong);
  white-space: nowrap;
}
.fx-compare-head-row th:nth-child(3),
.fx-compare-head-row th:nth-child(4) { text-align: right; }

/* ---------- Narrow card: stop being a table ----------
   A four-column price comparison is unreadable in a container this
   size, and `overflow-x: auto` on the wrapper only hides the two
   columns that carry the argument (what it costs, what they receive)
   behind a sideways scroll most people never try.

   The trigger is the CARD's width, not the viewport's. On the landing
   pages this card sits in a half-width grid column, so a 1440px laptop
   gives it roughly 600px: wide enough to pass a viewport media query,
   far too narrow for four columns of prose.

   Each row becomes its own bordered card with the column name printed
   beside each value (from the `data-label` the renderer emits). The
   markup stays a real <table>, so it is still announced as tabular
   data and still copies out as a table. */
@container fx-card (max-width: 640px) {
  .fx-compare-head-row { display: none; }
  .fx-compare-table,
  .fx-compare-table tbody,
  .fx-compare-row,
  .fx-compare-row td { display: block; width: 100%; }
  .fx-compare-row {
    border: 2px solid var(--frame-ink);
    border-radius: var(--panel-radius-sm);
    padding: 14px 16px;
    margin-bottom: 10px;
    background: var(--blanco);
  }
  .fx-compare-row--self {
    background: var(--verde-100);
    box-shadow: var(--card-lift);
  }
  .fx-compare-row--self td { background: none; }
  .fx-compare-row td {
    padding: 6px 0 !important;
    border-bottom: 1px solid var(--hairline);
    text-align: left;
    white-space: normal;
  }
  .fx-compare-row td:last-child { border-bottom: none; }
  /* The provider name is the card's title, so it keeps no label. */
  .fx-compare-name {
    font-size: var(--fs-body);
    padding-bottom: 10px !important;
  }
  .fx-compare-row td[data-label]:not(.fx-compare-name) {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 4px 14px;
    /* Spanish labels plus a peso figure can exceed a 320px card, so the
       value drops to its own line rather than spilling out of it. */
    flex-wrap: wrap;
  }
  .fx-compare-row td[data-label]:not(.fx-compare-name)::before {
    content: attr(data-label);
    flex-shrink: 0;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    color: var(--tinta-500);
  }
  /* The received amount is the number people actually compare. */
  .fx-compare-net { font-size: var(--fs-body-l); }
}
.fx-fallback {
  padding: 24px;
  background: var(--hueso);
  border: 1px solid rgba(0, 0, 0, 0.1);
  border-radius: 12px;
  color: var(--tinta-500);
  font-size: var(--fs-body-s);
  text-align: center;
}

@media (max-width: 1023px) {
  .fx-card { max-width: 100%; padding: 24px; }
  .fx-form { grid-template-columns: 1fr; }
}
@media (max-width: 540px) {
  .fx-input-row input { font-size: 20px; }
}

/* Dropping the cost and speed columns to make the table fit was the old
   way to survive a narrow card. The stacked layout above shows every
   field with its own label, so the data stays. Engines without container
   queries never reach that layout, so they keep the old escape hatch. */
@supports not (container-type: inline-size) {
  @media (max-width: 1023px) { .fx-compare-eta { display: none; } }
  @media (max-width: 540px) { .fx-compare-fee { display: none; } }
}

/* ------------------------------------------------------------------
   Investor / team page chrome — sober, document-style cards.
   --------------------------------------------------------------- */

.investor-hero {
  padding: clamp(96px, 8.5vw, 120px) 0 clamp(40px, 4.5vw, 64px);
  background:
    radial-gradient(60vw 30vw at 50% 0%, rgba(0, 166, 81, 0.12) 0%, transparent 60%),
    var(--hueso);
}
.investor-stat-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 16px;
  margin-top: 40px;
}
.investor-stat {
  background: var(--blanco);
  border: 1px solid var(--hairline);
  border-radius: 16px;
  padding: 20px;
}
.investor-stat-value {
  font-size: 28px;
  font-weight: 800;
  color: var(--tinta-900);
  letter-spacing: -0.5px;
  margin-bottom: 4px;
}
.investor-stat-label {
  font-size: var(--fs-caption);
  color: var(--tinta-500);
  font-weight: 500;
  line-height: 1.4;
}
.investor-section {
  padding: clamp(44px, 4.5vw, 64px) 0;
  border-top: 1px solid var(--hairline);
}
.investor-section h2 { color: var(--tinta-900); margin-bottom: 12px; }
.investor-section .kicker { color: var(--tinta-500); margin-bottom: 32px; }

.team-card {
  background: var(--blanco);
  border: 1px solid var(--hairline-strong);
  border-radius: 20px;
  padding: 28px;
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.team-card .team-name { font-size: 20px; font-weight: 700; color: var(--tinta-900); }
.team-card .team-role { font-size: var(--fs-body-s); color: var(--accent-verde); font-weight: 600; text-transform: uppercase; letter-spacing: 0.06em; }
.team-card .team-bio { font-size: var(--fs-body); color: var(--tinta-700); line-height: 1.6; }

/* Profile variant: headshot beside the copy rather than above it.
   The founder cards live in a 640px `.reading` column, so a side-by-side
   grid pins them to their track minimum and the bios wrap to a ~28ch
   ribbon. Stacking the cards and laying each one out horizontally keeps
   the bio at a readable measure and stays aligned with the editorial
   column the rest of the page uses. It also degrades cleanly: a card
   with no headshot yet just gives the copy the full width instead of
   sitting lower than its neighbour. */
.team-card--profile {
  flex-direction: row;
  align-items: flex-start;
  gap: 20px;
}
.team-card--profile .team-card-body {
  display: flex;
  flex-direction: column;
  gap: 12px;
  min-width: 0;
}
/* The <picture> wrapper is the flex item, not the <img> inside it, so the
   sizing has to be pinned here or the headshot gets shrunk and squashed. */
.team-card--profile > picture {
  display: block;
  flex: 0 0 auto;
}
.team-card .team-photo {
  display: block;
  width: 128px;
  height: 128px;
  border-radius: 16px;
  border: 2px solid var(--hairline-strong);
  object-fit: cover;
  background: var(--hueso);
}

@media (max-width: 560px) {
  .team-card--profile { flex-direction: column; }
  .team-card .team-photo { width: 104px; height: 104px; }
}

@media (max-width: 1023px) {
  .investor-stat-grid { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 540px) {
  .investor-stat-grid { grid-template-columns: 1fr; }
}

/* ------------------------------------------------------------------
   Comparison-table chrome shared by /vs/ and /pesito-vs-* pages.
   --------------------------------------------------------------- */

.compare-table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--fs-body);
}
.compare-table thead tr {
  border-bottom: 1px solid var(--tinta-300);
}
.compare-table thead th {
  text-align: left;
  padding: 12px 0;
  font-weight: 700;
  color: var(--tinta-900);
}
.compare-table thead th:first-child {
  font-weight: 600;
  color: var(--tinta-500);
  font-size: var(--fs-body-s);
  text-transform: uppercase;
  letter-spacing: 0.08em;
}
.compare-table tbody td {
  padding: 16px 8px;
  border-bottom: 1px solid var(--hairline);
  vertical-align: top;
}
.compare-table tbody td:first-child { font-weight: 600; padding-left: 0; }
.compare-table .compare-pos { color: var(--accent-verde); font-weight: 600; }
.compare-table .compare-neg { color: var(--rojo-500); }

/* ---------- .compare-table--cards: stack instead of scroll ----------
   Four columns of sentence-length cells cannot shrink below their
   min-content width, so a narrow screen either scrolls sideways or
   squeezes every column into a two-word ribbon. Below 720px each row
   becomes its own card with the column name printed beside each value.

   Opt-in, because it needs `data-label` on every cell to stay readable:
   a table without labels would stack into unlabelled values. */
@media (max-width: 720px) {
  .compare-table--cards thead { display: none; }
  .compare-table--cards,
  .compare-table--cards tbody,
  .compare-table--cards tr,
  .compare-table--cards td { display: block; width: 100%; }
  .compare-table--cards tr {
    border: var(--panel-border);
    border-radius: var(--panel-radius-sm);
    padding: 4px 16px 8px;
    margin-bottom: 12px;
    background: var(--blanco);
  }
  .compare-table--cards tbody td {
    padding: 10px 0;
    border-bottom: 1px solid var(--hairline);
  }
  .compare-table--cards tbody td:last-child { border-bottom: none; }
  /* First cell is the row's title, so it carries no label. */
  .compare-table--cards tbody td:first-child {
    font-size: var(--fs-body-l);
    font-weight: 700;
    padding-top: 12px;
  }
  .compare-table--cards td[data-label]:not(:first-child)::before {
    content: attr(data-label);
    display: block;
    margin-bottom: 2px;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    color: var(--tinta-500);
  }
}

/* Related-searches footer used by SEO landing pages */
.related-searches {
  padding: 48px 0;
  background: var(--crema);
  border-top: 1px solid var(--hairline);
}
.related-searches h2 {
  font-size: var(--fs-headline);
  color: var(--tinta-900);
  margin-bottom: 16px;
}
.related-searches ul {
  list-style: none;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 8px;
}
.related-searches li a {
  display: block;
  padding: 12px 16px;
  background: var(--blanco);
  border: 1px solid var(--hairline);
  border-radius: 10px;
  color: var(--tinta-700);
  text-decoration: none;
  transition: border-color var(--dur-fast) var(--ease-standard),
              color var(--dur-fast) var(--ease-standard);
  cursor: pointer;
}
.related-searches li a:hover {
  border-color: var(--verde-700);
  color: var(--verde-700);
}



/* ═══════════════════════════════════════════════════════════════════
   Entrance Animations — page load choreography

   Pesito's brand register demands intentional entrances: the marketing
   site IS the product, and first impressions carry trust. These
   utilities implement a staggered fade-up entrance matching Apple's
   sheet-enter curve (iOS 26 modal presentation rhythm).

   Implemented as pure CSS keyframe animations that autoplay on load —
   NO JavaScript gate. The old pattern (start at opacity 0, wait for JS
   to add `.is-visible`) left the hero invisible whenever JS failed.

   Variants:
   - `.enter-delay-1` through `.enter-delay-5` add stagger (120ms steps)
   - `.enter-hero` skips the blur, hits faster (480ms vs 620ms)
   - `.enter-from-left` / `.enter-from-right` for horizontal slides

   Accessibility: prefers-reduced-motion jumps to the final state.
   ═══════════════════════════════════════════════════════════════════ */

@keyframes enter-rise {
  from { opacity: 0; transform: translateY(16px) scale(0.98); filter: blur(4px); }
  to   { opacity: 1; transform: translateY(0) scale(1); filter: blur(0); }
}
@keyframes enter-rise-hero {
  from { opacity: 0; transform: translateY(24px) scale(0.96); }
  to   { opacity: 1; transform: translateY(0) scale(1); }
}
@keyframes enter-from-left {
  from { opacity: 0; transform: translateX(-32px); }
  to   { opacity: 1; transform: translateX(0); }
}
@keyframes enter-from-right {
  from { opacity: 0; transform: translateX(32px); }
  to   { opacity: 1; transform: translateX(0); }
}

.enter {
  animation: enter-rise var(--dur-entrance) var(--ease-entrance) both;
}
.enter-hero {
  animation: enter-rise-hero 480ms var(--ease-entrance) both;
}
.enter-from-left {
  animation: enter-from-left var(--dur-entrance) var(--ease-entrance) both;
}
.enter-from-right {
  animation: enter-from-right var(--dur-entrance) var(--ease-entrance) both;
}

/* Stagger delays — applied ON TOP of .enter for cascading reveals.
   Each step is 120ms, matching the gap between items in a well-paced
   list entrance. Max 5 steps; beyond that the last items feel laggy. */
.enter-delay-1 { animation-delay: 120ms; }
.enter-delay-2 { animation-delay: 240ms; }
.enter-delay-3 { animation-delay: 360ms; }
.enter-delay-4 { animation-delay: 480ms; }
.enter-delay-5 { animation-delay: 600ms; }

/* Reduced motion: skip all entrance work. Elements start at final state. */
@media (prefers-reduced-motion: reduce) {
  .enter,
  .enter-hero,
  .enter-from-left,
  .enter-from-right {
    animation: none;
  }
}

/* ═══════════════════════════════════════════════════════════════════════════
   EDITORIAL LAYER — 2026-07 redesign.

   Design language: composed rounded panels on the hueso canvas — outlined
   copy panels, saturated color environments, media panels — with a chunky
   poster grotesk (Archivo Black, self-hosted, OFL) for display type, a
   numbered rail nav on wide viewports, full-width color divider bars, stat
   chips, and a grid-paper footer. Benchmarked against an editorial
   student-housing reference (see docs/compliance/website-copy-spec.md
   sibling docs for the claims side; this layer is purely visual).

   This layer deliberately OVERRIDES the shared classes used by every page
   (headings, .eyebrow, .btn, .faq-item, .footer) so subpages inherit the
   system without markup surgery, and defines new `ed-*` components used by
   the art-directed pages. It loads last, so equal-specificity rules here
   win over the legacy layer above.
   ═══════════════════════════════════════════════════════════════════════ */

@font-face {
  font-family: 'Archivo Black';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/assets/fonts/ArchivoBlack.woff2') format('woff2');
}

:root {
  /* Editorial environments (2026-07-16, Lotería): the color-blocked
     sections alternate saturated card hues — verde (flag green),
     bosque→azul cobalto (trust), menta→amarillo (highlight),
     verde-vivo (the money moment), tinta (near-black). Class names
     keep their mono-verde-era names so no markup changes; the fills
     are Lotería. Every panel wears the ink outline — flat bright
     color inside a black frame is the whole grammar. Rojo stays
     reserved for legal / error contexts. */

  --font-black: 'Archivo Black', 'Arial Black', 'Archivo', -apple-system,
                'Helvetica Neue', sans-serif;

  /* Panel geometry — one radius for the big blocks, one for chips/pills.
     The 2px ink border is the Lotería card outline; --card-lift is the
     only elevation in the system (a hard folk-poster offset, no blur). */
  --panel-radius: 28px;
  --panel-radius-sm: 18px;
  --panel-gap: clamp(10px, 1.2vw, 16px);
  /* A panel's own inset. Named because the article column has to match
     it: prose that sits outside a card still has to start on the same
     text edge as the copy inside one. */
  --panel-pad: clamp(24px, 3.4vw, 56px);
  --panel-border-width: 2px;
  /* The Loteria frame ink. In light it IS the ink; in dark it dims to
     printed-ink-on-dark-stock (OKLCH L64 C0.015 H82). Inverting the
     frame to full text-bone turned dark mode into a glowing wireframe:
     16:1 outlines competing with the type. At L64 every frame still
     reads (5.6:1 on canvas, 4.3:1 on cards) but ranks BELOW even
     footnote text (L70). Text keeps --tinta-900; fills that use the
     bone as a surface (ghost hover, lang indicator) keep it too -
     this token is only for strokes and the hard offset lift. */
  --frame-ink: light-dark(#0B0B0B, #918B82);
  --panel-border: var(--panel-border-width) solid var(--frame-ink);
  --card-lift: 3px 3px 0 var(--frame-ink);

  /* Semantic z-scale (never arbitrary). */
  --z-rail: 40;
  --z-sticky: 50;
  --z-sheet-backdrop: 60;
  --z-sheet: 70;
  --z-toast: 80;
}

/* ---------- Display typography: the poster grotesk ---------- */

/* The poster grotesk swaps family and weight only. Tracking and leading
   stay on the `.display-*` ladder so they remain size-specific (see
   "Display classes" above) instead of collapsing to a single value
   across a 2x size range. */
h1, h2,
.display-xxl, .display-xl, .display-l, .display-m {
  font-family: var(--font-black);
  font-weight: 400;              /* Archivo Black has a single weight */
  text-wrap: balance;
}
/* Unclassed headings fall back to UA sizes (h1 2em, h2 1.5em), so they
   sit at the bottom of the ladder and want its loosest pair. Every
   heading that carries a `.display-*` class outranks these. */
h1 { letter-spacing: -0.01em; line-height: 1.08; }
h2 { letter-spacing: -0.005em; line-height: 1.12; }
h3, .headline { letter-spacing: -0.005em; }
p { text-wrap: pretty; }

/* Light display type on dark panels breathes a little more. */
.ed-panel--verde h2, .ed-panel--bosque h2, .ed-panel--tinta h2 { line-height: 1.08; }

/* ---------- Kickers: outlined pills replace tracked eyebrows ---------- */

/* `inline-block`, not `inline-flex`: an eyebrow is a sentence, and as a
   flex container each run of text became its own flex item that wrapped
   on its own. "US → MEXICO · PRE-LAUNCH · WAITLIST OPEN" has a nested
   span around the corridor, so on a narrow hero the pill broke into two
   ragged columns with the `gap` sitting between them. As inline-block
   the whole label wraps as one run. None of these pills carry an icon,
   so nothing needed the flex alignment. */
.eyebrow {
  display: inline-block;
  font-family: var(--font-body);
  font-size: var(--fs-body-s);
  font-weight: 600;
  letter-spacing: 0;
  text-transform: none;
  line-height: 1.35;
  color: var(--tinta-900);
  background: transparent;
  border: 2px solid var(--frame-ink);
  border-radius: var(--radius-pill);
  padding: 6px 14px;
}
/* The corridor label is one unit, so it never splits across lines. */
.eyebrow [data-region-swap] { white-space: nowrap; }
/* Kickers sitting on dark/saturated panels invert. verde + bosque
   keep deep saturated fills in both themes, so their kicker is
   always white; tinta is the one panel that inverts wholesale, so
   it rides the neutral ramp. */
.ed-panel--verde .eyebrow, .ed-panel--bosque .eyebrow,
.eyebrow--invert {
  color: var(--paper-fixed);
  border-color: rgba(255, 255, 255, 0.75);
}
.ed-panel--tinta .eyebrow {
  color: var(--blanco);
  border-color: var(--blanco);
}
/* Bright FIXED fills (verde-vivo, menta) never theme, so their kicker
   pins to fixed ink: the themed ramp turned it bone-on-bright in dark
   mode. */
.ed-panel--verde-vivo .eyebrow, .ed-panel--menta .eyebrow {
  color: var(--ink-fixed);
  border-color: var(--ink-fixed);
}

/* ---------- Buttons: decisive pills ---------- */

.btn {
  border-radius: var(--radius-pill);
  font-weight: 700;
  padding: 14px 26px;
  transition: transform var(--dur-fast) var(--ease-standard),
              background-color var(--dur-fast) var(--ease-standard),
              color var(--dur-fast) var(--ease-standard);
}
.btn-primary {
  background: var(--verde-500);
  color: var(--ink-fixed);
  /* The frame is the same Lotería outline every panel/chip/bar wears:
     themed ink. Fixed ink here left the pill frameless on the dark
     canvas (black ring on near-black) while its hover lift flipped to
     bone: a mismatched double frame. The LABEL stays fixed ink: the
     verde fill never themes, so its text must not either. */
  border: 2px solid var(--frame-ink);
  box-shadow: none;
}
.btn-primary:hover { background: var(--verde-300); transform: translateY(-1px); }
.btn-ghost {
  background: transparent;
  color: var(--tinta-900);
  border: 2px solid var(--frame-ink);
}
.btn-ghost:hover { background: var(--tinta-900); color: var(--blanco); }
/* Ghosts on FIXED-fill panels leave the themed ramp: those fills never
   change with the theme, so their pills pin to the fixed ink/paper
   pair. Without this, dark mode painted a bone ghost on bright verde
   (2.8:1) inside verde-vivo panels. Deep fills (verde/bosque) mirror
   the eyebrow inversion. */
.ed-panel--verde-vivo .btn-ghost, .ed-panel--menta .btn-ghost {
  color: var(--ink-fixed);
  border-color: var(--ink-fixed);
}
.ed-panel--verde-vivo .btn-ghost:hover, .ed-panel--menta .btn-ghost:hover {
  background: var(--ink-fixed);
  color: var(--paper-fixed);
}
.ed-panel--verde .btn-ghost, .ed-panel--bosque .btn-ghost {
  color: var(--paper-fixed);
  border-color: var(--paper-fixed);
}
.ed-panel--verde .btn-ghost:hover, .ed-panel--bosque .btn-ghost:hover {
  background: var(--paper-fixed);
  color: var(--ink-fixed);
}
.btn:focus-visible,
.ed-rail-cta:focus-visible {
  outline: 3px solid var(--verde-700);
  outline-offset: 2px;
}
/* On deep saturated panels the green ring vanishes — flip to white. */
.ed-panel--verde .btn:focus-visible,
.ed-panel--bosque .btn:focus-visible {
  outline-color: var(--paper-fixed);
}
.ed-panel--tinta .btn:focus-visible {
  outline-color: var(--blanco);
}
/* verde-vivo is a BRIGHT fill: a white ring on verde-500 lands at
   2.5:1, below the 3:1 non-text minimum (WCAG 2.2 SC 1.4.11). Ink
   reads at 15.6:1 on the same fill. */
.ed-panel--verde-vivo .btn:focus-visible,
.ed-panel--menta .btn:focus-visible {
  outline-color: var(--ink-fixed);
}

/* ---------- Panels: the compositional unit ---------- */

.ed-panel {
  border-radius: var(--panel-radius);
  padding: var(--panel-pad);
  position: relative;
  /* Lotería: every panel is a card — flat fill inside an ink frame. */
  border: var(--panel-border);
}
.ed-panel--outline { background: var(--hueso); }
/* `.ed-grid--split` stretches both panels to the taller one. When that
   taller one is a media panel holding a portrait phone clip, the text
   panel beside it inherits ~600px of height with its copy pinned to the
   top, and the bottom half reads as a broken empty card. Centring the
   column spreads the slack above and below so the height reads as
   deliberate padding instead. */
.ed-panel--center-y {
  display: flex;
  flex-direction: column;
  justify-content: center;
}
.ed-panel--blanco { background: var(--blanco); }
/* verde: the brand environment — flag green, white type. */
.ed-panel--verde { background: var(--verde-700); color: var(--paper-fixed); }
.ed-panel--verde h2, .ed-panel--verde h3 { color: var(--paper-fixed); }
.ed-panel--verde p { color: rgba(250, 247, 242, 0.92); }
/* verde-vivo: the conversion environment — brand green, ink type.
   The loud money moment (pricing, final CTA). */
.ed-panel--verde-vivo { background: var(--fill-verde-vivo); color: var(--ink-fixed); }
.ed-panel--verde-vivo h2, .ed-panel--verde-vivo h3 { color: var(--ink-fixed); }
/* Body copy needs the same restatement the verde/bosque panels give
   their <p>: classes like .body-l and .wl-legal set an explicit THEMED
   gray that lands bone-on-bright-green in dark mode (the fill is
   fixed). Element+class outranks the single-class color rules. */
.ed-panel--verde-vivo p, .ed-panel--menta p { color: var(--ink-fixed); }
/* A nested themed card (the FX calculator) paints its own themed
   surface, so its copy goes back on the themed ramp. */
.ed-panel--verde-vivo .fx-card p, .ed-panel--menta .fx-card p { color: var(--tinta-500); }
/* bosque: the trust environment — Lotería cobalt, white type (security,
   institutional sections). Class keeps its historical name. */
.ed-panel--bosque { background: var(--fill-azul); color: var(--paper-fixed); }
.ed-panel--bosque h2, .ed-panel--bosque h3 { color: var(--paper-fixed); }
.ed-panel--bosque p { color: rgba(255, 255, 255, 0.94); }
/* menta: the festive highlight environment — golden amarillo, ink type.
   Class keeps its historical name. */
.ed-panel--menta { background: var(--fill-amarillo); color: var(--ink-fixed); }
.ed-panel--menta h2, .ed-panel--menta h3 { color: var(--ink-fixed); }
/* tinta: the only panel that inverts wholesale with the theme —
   near-black card in light mode, bone card in dark mode. It rides
   the neutral ramp on purpose. */
.ed-panel--tinta { background: var(--tinta-900); color: var(--blanco); }
/* Product-preview showcases: the stage stays INK in both themes. The
   clips are flattened onto #0B0B0B at encode time, so on a themed
   tinta panel the dark theme flipped the card to bone and left the
   dark phone floating in a blinding white frame. Pinned, the phone
   melts into the panel in both themes (same trick as
   .ed-media--contain below) and the card disappears around the
   animation instead of containing it. */
.ed-panel--tinta.ed-showcase,
.ed-panel--tinta.ed-stage { background: #0B0B0B; color: #F6F2E8; }
.ed-panel--tinta.ed-showcase h2,
.ed-panel--tinta.ed-showcase .title,
.ed-panel--tinta.ed-showcase .headline,
.ed-panel--tinta.ed-showcase .body-l,
.ed-panel--tinta.ed-showcase .caption,
.ed-panel--tinta.ed-stage h2,
.ed-panel--tinta.ed-stage .title,
.ed-panel--tinta.ed-stage .headline,
.ed-panel--tinta.ed-stage .body-l,
.ed-panel--tinta.ed-stage .caption { color: #F6F2E8; }
.ed-panel--tinta.ed-showcase .eyebrow,
.ed-panel--tinta.ed-stage .eyebrow {
  color: #F6F2E8;
  border-color: rgba(255, 255, 255, 0.75);
}
.ed-panel--media {
  padding: 0;
  overflow: hidden;
  background: var(--crema);
}
.ed-panel--media > img, .ed-panel--media > video, .ed-panel--media picture img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* A screenshot of the app is content, not a backdrop — it has to be
   shown whole. The `cover` above fills the panel by trimming, which on
   a 470×1024 phone capture lops the avatar off the top and the
   swipe-to-pay control off the bottom. This modifier opts a panel out:
   the capture keeps its own ratio and centres, and the panel takes the
   leftover space instead of the image being cut to fit it.

   Both compound selectors are needed. `.ed-media--contain img` alone
   loses to `.ed-panel--media picture img` on specificity, which is
   exactly the bug this replaces. */
.ed-panel--media.ed-media--contain {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: clamp(16px, 2vw, 32px);
}
.ed-panel--media.ed-media--contain picture {
  display: flex;
  align-items: center;
  justify-content: center;
  max-width: 100%;
}
.ed-panel--media.ed-media--contain picture img {
  width: auto;
  height: auto;
  max-width: 100%;
  max-height: 680px;
  object-fit: contain;
  border-radius: 22px;
}

/* The corridor clip is a phone rendered on a transparent canvas, and its
   alpha is flattened at encode time (see `.showcase-stage`). Baked pixels
   can only match ONE colour, so this panel is pinned to the black they
   were flattened onto rather than following `--crema`, which is cream on
   the light theme and near-black on the dark one. Leaving the panel to
   flip is what put a slab of backdrop around the hardware and made the
   phone read as double-framed: the clip starts with the phone smaller
   than its own frame and pushes in, so that backdrop is visible, not a
   hairline. Black also gives the green confirm sheet the most contrast
   to read against. Keep this hex, `.showcase-stage`'s background, and the
   encode's `color=c=` argument in step. The caption pill supplies its own
   `--blanco` background, so it stays legible on the black in both
   themes. */
.ed-panel--media.ed-media--contain { background: #0B0B0B; }
/* This clip is a supporting illustration beside the pricing fine print,
   not a hero, and at the showcase width its portrait aspect drove the
   row past 600px and left the text panel half empty. Trimming it here
   shortens the row; the showcases keep the larger default. */
.ed-panel--media.ed-media--contain .showcase-stage { max-width: 260px; }

/* Links inside saturated panels stay legible. */
.ed-panel--verde a:not(.btn), .ed-panel--bosque a:not(.btn) {
  color: var(--paper-fixed);
  text-decoration: underline;
}
.ed-panel--tinta a:not(.btn) {
  color: var(--blanco);
  text-decoration: underline;
}
/* .title, .headline, .body-l and .caption each set an explicit ink
   colour, which beats the `color: var(--blanco)` inherited from the
   panel and renders dark text on the dark fill. Panels used to dodge
   this by writing bare <p> and by repeating `color: var(--blanco)`
   inline on every .title, so the trap only surfaced the first time
   somebody reached for a size class in here. Restate the colour once. */
.ed-panel--tinta .title,
.ed-panel--tinta .headline,
.ed-panel--tinta .body-l,
.ed-panel--tinta .caption {
  color: var(--blanco);
}
.ed-panel--verde-vivo a:not(.btn), .ed-panel--menta a:not(.btn) {
  color: var(--ink-fixed);
  text-decoration: underline;
}

/* A brand-green button disappears on the brand-green panel: inside
   verde-vivo AND verde the primary CTA inverts to ink, the strongest
   thing on either surface. (--verde is verde-700: a verde-500 pill on
   it was green-on-green at ~1.9:1, the exact defect this inversion
   exists for. /lista-espera's form panel hit it.) */
.ed-panel--verde-vivo .btn-primary,
.ed-panel--verde .btn-primary {
  background: var(--ink-fixed);
  color: var(--paper-fixed);
  border-color: var(--ink-fixed);
}
.ed-panel--verde-vivo .btn-primary:hover,
.ed-panel--verde .btn-primary:hover { background: #2A2A2E; }
.ed-panel--verde-vivo .brand-dot { color: var(--ink-fixed); }

/* ---------- Divider bars: the section rhythm ---------- */

.ed-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  border: var(--panel-border);
  border-radius: var(--panel-radius);
  padding: clamp(14px, 2vw, 22px) clamp(20px, 3vw, 36px);
  margin: clamp(28px, 4vw, 56px) 0 var(--panel-gap);
}
.ed-bar h2, .ed-bar .ed-bar-title {
  font-family: var(--font-black);
  font-size: clamp(22px, 2.6vw, 38px);
  line-height: 1.05;
  margin: 0;
  text-align: center;
  flex: 1;
}
.ed-bar--verde { background: var(--fill-verde-vivo); color: var(--ink-fixed); }
.ed-bar--menta { background: var(--fill-amarillo); color: var(--ink-fixed); }
.ed-bar--blanco { background: var(--blanco); color: var(--tinta-900); }
/* Arrows: rosa mexicano on light bars, ink on saturated fills. */
.ed-bar-arrows {
  display: inline-flex;
  gap: 4px;
  color: var(--rosa-500);
  flex-shrink: 0;
}
.ed-bar--verde .ed-bar-arrows, .ed-bar--menta .ed-bar-arrows { color: var(--ink-fixed); }
.ed-bar-arrows svg { display: block; }
@media (max-width: 640px) {
  .ed-bar-arrows { display: none; }
}

/* ---------- Stat chips: big number, small label ---------- */

.ed-chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--panel-gap);
}
.ed-chip {
  display: flex;
  align-items: baseline;
  gap: 12px;
  background: var(--amarillo-500);
  color: var(--ink-fixed);
  border: var(--panel-border);
  border-radius: var(--panel-radius-sm);
  padding: 14px 20px;
  min-width: 0;
}
.ed-chip--menta { background: var(--amarillo-500); }
.ed-chip--verde { background: var(--verde-500); }
/* blanco + outline chips ride the neutral ramp, so their label has
   to come back to the themed ink rather than the fixed near-black. */
.ed-chip--blanco { background: var(--blanco); color: var(--tinta-900); }
.ed-chip--outline { background: transparent; color: var(--tinta-900); }
.ed-chip-num {
  font-family: var(--font-black);
  font-size: clamp(24px, 2.4vw, 34px);
  line-height: 1;
  white-space: nowrap;
}
.ed-chip-label {
  font-size: var(--fs-body-s);
  font-weight: 600;
  line-height: 1.3;
  max-width: 22ch;
}

/* ---------- FAQ: outlined rounded rows ---------- */

.faq-list, .ed-faq-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(420px, 100%), 1fr));
  gap: var(--panel-gap);
  align-items: start;
}
.faq-item {
  border: var(--panel-border);
  border-radius: var(--panel-radius-sm);
  background: var(--hueso);
  padding: 0;
  box-shadow: none;
  transition: background-color var(--dur-fast) var(--ease-standard);
}
.faq-item[open] { background: var(--blanco); }
.faq-item summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 18px 22px;
  font-weight: 700;
  cursor: pointer;
  list-style: none;
}
.faq-item summary::-webkit-details-marker { display: none; }
.faq-item summary::after {
  content: '+';
  font-family: var(--font-black);
  font-size: 22px;
  line-height: 1;
  flex-shrink: 0;
  transition: transform var(--dur-base) var(--ease-standard);
}
.faq-item[open] summary::after { transform: rotate(45deg); }
.faq-item .faq-chev { display: none; }
.faq-answer { padding: 0 22px 20px; max-width: 70ch; }

/* ---------- Rail navigation (≥1280px) ---------- */

.ed-rail {
  display: none;
}
@media (min-width: 1280px) {
  .ed-rail {
    display: flex;
    flex-direction: column;
    gap: var(--panel-gap);
    position: fixed;
    top: var(--panel-gap);
    left: var(--panel-gap);
    bottom: var(--panel-gap);
    width: 128px;
    z-index: var(--z-rail);
  }
  body.has-rail .nav { display: none; }
  body.has-rail main, body.has-rail .cta-footer-wrap {
    margin-left: calc(128px + var(--panel-gap) * 2);
  }
}
.ed-rail-logo {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 14px 8px;
  border: var(--panel-border);
  border-radius: var(--panel-radius-sm);
  background: var(--hueso);
  text-decoration: none;
}
.ed-rail-item {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: 18px;
  flex: 1;
  min-height: 84px;
  padding: 12px;
  border: var(--panel-border);
  border-radius: var(--panel-radius-sm);
  text-decoration: none;
  color: var(--tinta-900);
  font-weight: 700;
  font-size: var(--fs-body-s);
  line-height: 1.15;
  transition: transform var(--dur-fast) var(--ease-standard),
              filter var(--dur-fast) var(--ease-standard);
}
.ed-rail-item:hover { transform: translateX(3px); filter: brightness(1.06); }
.ed-rail-item:focus-visible { outline: 3px solid var(--verde-700); outline-offset: 2px; }
.ed-rail-num {
  font-family: var(--font-black);
  font-size: 15px;
  display: flex;
  justify-content: space-between;
  align-items: center;
}
/* Lotería card figures (2026-08-26). Each rail card gets its figure
   between the number and the caption, the way a lotería card stacks
   number / figure / name: El Nopal for how-it-works, La Moneda for
   pricing, El Candado for security, La Ruta for the roadmap, La
   Pregunta for the FAQ. Drawn as inline-SVG alpha masks painted with
   currentColor, so one drawing follows every colorway (ink on card
   stock and bright fills, paper on cobalt) and both themes, with no
   markup change on the 18 pages that carry the rail. Selected by
   href, not position, so reordering the deck can't miscast a card. */
.ed-rail-item::before { content: none; }
.ed-rail-item > span:last-child { order: 2; text-align: center; }
.ed-rail-item[href$="como-funciona"]::before,
.ed-rail-item[href$="how-it-works"]::before,
.ed-rail-item[href$="enviar-dinero-a-mexico"]::before,
.ed-rail-item[href$="send-money-to-mexico"]::before,
.ed-rail-item[href$="seguridad-y-cuenta"]::before,
.ed-rail-item[href$="safety-and-account"]::before,
.ed-rail-item[href$="/ruta"]::before,
.ed-rail-item[href$="roadmap"]::before,
.ed-rail-item[href$="/preguntas"]::before,
.ed-rail-item[href$="/en/faq"]::before {
  content: "";
  order: 1;
  width: 26px;
  height: 26px;
  flex: none; /* a flex child with no shrink guard: tight cards were crushing the figure (to 0 on laptops) */
  align-self: center;
  background: currentColor;
  -webkit-mask: var(--rail-icon) center / contain no-repeat;
  mask: var(--rail-icon) center / contain no-repeat;
}
/* The figure adds a third row; tighten the card's internal rhythm so
   five cards still deal onto a 900px-tall viewport. */
.ed-rail-item[href$="como-funciona"],
.ed-rail-item[href$="how-it-works"],
.ed-rail-item[href$="enviar-dinero-a-mexico"],
.ed-rail-item[href$="send-money-to-mexico"],
.ed-rail-item[href$="seguridad-y-cuenta"],
.ed-rail-item[href$="safety-and-account"],
.ed-rail-item[href$="/ruta"],
.ed-rail-item[href$="roadmap"],
.ed-rail-item[href$="/preguntas"],
.ed-rail-item[href$="/en/faq"] {
  gap: 10px;
}
/* El Nopal */
.ed-rail-item[href$="como-funciona"],
.ed-rail-item[href$="how-it-works"] {
  --rail-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32' fill='none' stroke='black' stroke-width='2.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M16 26V8'/%3E%3Cpath d='M16 18h-5.5v-6'/%3E%3Cpath d='M16 21h5.5v-4.5'/%3E%3Cpath d='M10 26h12'/%3E%3C/svg%3E");
  --rail-anim: rail-wiggle;
}
/* La Moneda — Iconoir "coins" (two coin stacks), owner-picked
   2026-08-26. Stroke bumped 1.5 -> 1.8 so its weight matches the
   other figures' 2.4-per-32 at the 26px render size. */
.ed-rail-item[href$="enviar-dinero-a-mexico"],
.ed-rail-item[href$="send-money-to-mexico"] {
  --rail-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M16 13c-2.76 0-5-1.12-5-2.5S13.24 8 16 8s5 1.12 5 2.5-2.24 2.5-5 2.5Z'/%3E%3Cpath d='M11 14.5c0 1.38 2.24 2.5 5 2.5s5-1.12 5-2.5'/%3E%3Cpath d='M3 9.5C3 10.88 5.24 12 8 12c1.13 0 2.16-.19 3-.5'/%3E%3Cpath d='M3 13c0 1.38 2.24 2.5 5 2.5 1.13 0 2.16-.19 3-.5'/%3E%3Cpath d='M3 5.5v11C3 17.88 5.24 19 8 19c1.13 0 2.16-.19 3-.5'/%3E%3Cpath d='M13 8.5v-3'/%3E%3Cpath d='M11 10.5v8c0 1.38 2.24 2.5 5 2.5s5-1.12 5-2.5v-8'/%3E%3Cpath d='M8 8C5.24 8 3 6.88 3 5.5S5.24 3 8 3s5 1.12 5 2.5S10.76 8 8 8Z'/%3E%3C/svg%3E");
  --rail-anim: rail-spin;
}
/* El Candado. Two drawings: shut (rest) and open (shackle raised,
   right leg free). The hover keyframes swap the mask between them -
   mask-image animates discretely, flipping midway between keyframes,
   which is exactly the snap a padlock should have. */
.ed-rail-item[href$="seguridad-y-cuenta"],
.ed-rail-item[href$="safety-and-account"] {
  --rail-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32' fill='none' stroke='black' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='9' y='14.5' width='14' height='10.5' rx='2.5'/%3E%3Cpath d='M12 14.5V11a4 4 0 0 1 8 0v3.5'/%3E%3Cpath d='M16 18.8v2.6'/%3E%3C/svg%3E");
  --rail-icon-open: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32' fill='none' stroke='black' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='9' y='14.5' width='14' height='10.5' rx='2.5'/%3E%3Cpath d='M12 14.5V9.5a4 4 0 0 1 7.6-1.8'/%3E%3Cpath d='M16 18.8v2.6'/%3E%3C/svg%3E");
  --rail-anim: rail-unlock;
  --rail-anim-dur: 960ms;
}
/* La Ruta */
.ed-rail-item[href$="/ruta"],
.ed-rail-item[href$="roadmap"] {
  --rail-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32' fill='none' stroke='black' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M10 27c8 0 12-4 8-8s-4-8 4-8' stroke-dasharray='3.6 3.2'/%3E%3Ccircle cx='23' cy='8' r='2.6'/%3E%3C/svg%3E");
  --rail-anim: rail-bob;
}
/* La Pregunta */
.ed-rail-item[href$="/preguntas"],
.ed-rail-item[href$="/en/faq"] {
  --rail-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32' fill='none' stroke='black' stroke-width='2.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 12.2a4.2 4.2 0 1 1 6.5 3.5c-1.6 1-2.5 1.8-2.5 3.3v.6'/%3E%3Ccircle cx='16' cy='24' r='1.7' fill='black' stroke='none'/%3E%3C/svg%3E");
  --rail-anim: rail-pop;
}
/* Each figure acknowledges the pointer with its own small gesture —
   the cactus sways, the coin spins, the lock springs open and clicks
   shut, the route bobs,
   the question pops. One beat per hover, motion-tolerant only. */
@media (prefers-reduced-motion: no-preference) {
  .ed-rail-item:hover::before,
  .ed-rail-item:focus-visible::before {
    animation: var(--rail-anim, none) var(--rail-anim-dur, 640ms) var(--ease-emphasized);
  }
  /* Locked -> unlocked -> locked. The mask flips discretely between
     the 18/28 and 62/72 keyframes; the lift and tilt around each flip
     sell the shackle popping free and clicking home. */
  @keyframes rail-unlock {
    0%, 18% {
      -webkit-mask-image: var(--rail-icon);
      mask-image: var(--rail-icon);
      transform: none;
    }
    28%, 62% {
      -webkit-mask-image: var(--rail-icon-open);
      mask-image: var(--rail-icon-open);
      transform: translateY(-2px) rotate(-4deg);
    }
    72%, 90% {
      -webkit-mask-image: var(--rail-icon);
      mask-image: var(--rail-icon);
      transform: translateY(0.5px);
    }
    100% {
      -webkit-mask-image: var(--rail-icon);
      mask-image: var(--rail-icon);
      transform: none;
    }
  }
  @keyframes rail-wiggle {
    0% { transform: rotate(0); }
    30% { transform: rotate(-7deg); }
    65% { transform: rotate(5deg); }
    100% { transform: rotate(0); }
  }
  @keyframes rail-spin {
    0% { transform: scaleX(1); }
    50% { transform: scaleX(-1); }
    100% { transform: scaleX(1); }
  }
  @keyframes rail-jiggle {
    0%, 100% { transform: translateX(0); }
    25% { transform: translateX(-2.5px) rotate(-3deg); }
    55% { transform: translateX(2.5px) rotate(3deg); }
    80% { transform: translateX(-1px); }
  }
  @keyframes rail-bob {
    0%, 100% { transform: translateY(0); }
    40% { transform: translateY(-3px) rotate(4deg); }
    75% { transform: translateY(1px); }
  }
  @keyframes rail-pop {
    0%, 100% { transform: scale(1); }
    45% { transform: scale(1.22) rotate(6deg); }
  }
}
/* On short viewports the deck tightens instead of losing its figures —
   most laptops live under 860px of viewport, and El Candado's hover
   flourish is a signature, not a garnish. Figures drop to 20px, the
   cards close ranks, and the rail may scroll internally as the last
   resort (this replaces both the old figure-hiding rule here and the
   old 720px scroll rule further down). */
@media (min-width: 1280px) and (max-height: 859.98px) {
  /* overflow-y:auto makes the rail a scroll container, which clips both
     axes at its padding box. Two consequences handled here: (1) the 3px
     hover slide was getting its right edge shaved off, so the rail grows
     a 3px right-hand lane (width + padding) that keeps every child at
     128px while giving the slide room inside the clip; (2) overflow-x is
     pinned to hidden so no horizontal scrollbar can ever appear under
     the deck. The focus ring draws inset so the left clip edge cannot
     shave it. */
  .ed-rail { overflow-y: auto; overflow-x: hidden; width: 131px; padding-right: 3px; gap: 10px; }
  /* No scrollbar, ever (owner decision). Below ~728px the deck
     physically cannot fit, and the rail hands off to the nav header
     instead of scrolling — see the next media block. This pair is the
     belt-and-braces for the sliver where content and viewport race. */
  .ed-rail { scrollbar-width: none; }
  .ed-rail::-webkit-scrollbar { display: none; }
  /* The card keeps the tall layout's exact anatomy — número+arrow row,
     figure alone and centered on its own band, name beneath — because
     that IS the Lotería deal. What changes is only how the figure loses
     height when the deck runs out of room: instead of squashing flat
     (production renders it 20x12 on laptops), it shrinks PROPORTIONALLY
     — flex-basis 24px down to a 16px floor, aspect-ratio keeping it
     square, the mask scaling crisply. Full 24px figures survive to
     ~768px viewports; below ~720px the rail scrolls as the last
     resort. */
  .ed-rail-item[href] {
    gap: 4px;
    padding: 8px 10px;
    /* Never below the card's own content: label lengths vary by
       language (Nahuatl runs long), and equal flex sharing was
       clipping words mid-card. When the deck outgrows the viewport
       the rail scrolls — its designed last resort — but text never
       gets cut. */
    min-height: max-content;
  }
  .ed-rail-item[href] .ed-rail-num { font-size: 13px; }
  .ed-rail-item[href] > span:last-child { overflow-wrap: break-word; min-width: 0; }
  .ed-rail-item[href]::before {
    width: auto;
    height: auto;
    aspect-ratio: 1;
    flex: 0 1 24px;
    min-height: 16px;
  }
  .ed-rail-item:focus-visible { outline-offset: -3px; }
}
/* Below ~728px of viewport the full deck (logo + five uncut cards +
   CTA + foot) physically cannot fit, and the owner's constraints are
   firm: no clipped words, no scrolling, no scrollbar. So the rail
   bows out and the regular nav header takes over — brand, menu, and
   the language dropdown all live there and work at any height. */
@media (min-width: 1280px) and (max-height: 727.98px) {
  .ed-rail { display: none; }
  body.has-rail .nav { display: block; }
  body.has-rail main, body.has-rail .cta-footer-wrap { margin-left: 0; }
  /* clearance for the fixed nav capsule, same as the FAQ pages */
  body.has-rail main { padding-top: 92px; }
}

/* Rail items deal the Lotería deck — now the FULL hand: coral, verde,
   cobalt, amarillo, rosa. Each card wears a lithograph-sky gradient
   (stops computed in OKLCH: top +6L, base, bottom -5L on one hue, so
   no muddy mid) the way printed Lotería decks put a graded sky behind
   the figure. Fixed stops in both themes: rail cards are the small
   festive pops that keep full brightness in dark mode. Text contrast
   verified per stop where the type actually sits (numeral on the top
   stop, caption on the bottom): worst case 4.56:1, the white numeral
   on the rosa top. Owner-requested gradient experiment 2026-08-26;
   themed panels stay flat per MASTER. */
.ed-rail-item--outline { background: var(--hueso); }
.ed-rail-item--menta {
  background: linear-gradient(180deg, #FFDD91 0%, #FFC400 55%, #EBB500 100%);
}
.ed-rail-item--verde-vivo, .ed-rail-item--verde {
  background: linear-gradient(180deg, #2EB963 0%, #00A651 55%, #009448 100%);
}
.ed-rail-item--bosque {
  background: linear-gradient(180deg, #1F57BA 0%, #0A45A6 55%, #003791 100%);
  color: var(--paper-fixed);
}
.ed-rail-item--coral {
  background: linear-gradient(180deg, #FF8367 0%, #FF5A36 55%, #ED4823 100%);
}
.ed-rail-item--rosa {
  background: linear-gradient(180deg, #DA2A7C 0%, #C4006B 55%, #AC005D 100%);
  color: var(--paper-fixed);
}
.ed-rail-item--menta, .ed-rail-item--verde-vivo, .ed-rail-item--verde,
.ed-rail-item--coral { color: var(--ink-fixed); }
.ed-rail-cta {
  display: block;
  text-align: center;
  background: var(--verde-500);
  color: var(--ink-fixed);
  /* Themed frame, fixed label: same reasoning as .btn-primary. */
  border: 2px solid var(--frame-ink);
  border-radius: var(--radius-pill);
  padding: 12px 8px;
  font-weight: 700;
  font-size: var(--fs-body-s);
  text-decoration: none;
  transition: background-color var(--dur-fast) var(--ease-standard);
}
.ed-rail-cta:hover { background: var(--verde-300); }
/* Language + appearance share the last row so the five numbered
   cards keep their height on a 900px-tall viewport. */
.ed-rail-foot {
  display: flex;
  gap: var(--panel-gap);
}
.ed-rail-foot > * { flex: 1 1 0; min-width: 0; }
.ed-rail-lang {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  background: var(--rosa-500);
  color: var(--paper-fixed);
  border: 2px solid var(--ink-fixed);
  border-radius: var(--radius-pill);
  padding: 10px 8px;
  font-weight: 700;
  font-size: var(--fs-caption);
  text-decoration: none;
}
/* Five-language menu for the rail foot (landing pages). A native
   <details> so it works with no JS; opens upward inside the rail. */
.ed-rail-langmenu { position: relative; flex: 1; min-width: 0; }
.ed-rail-langmenu summary {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  background: var(--rosa-500);
  color: var(--paper-fixed);
  border: 2px solid var(--ink-fixed);
  border-radius: var(--radius-pill);
  padding: 10px 8px;
  font-weight: 700;
  font-size: var(--fs-caption);
  cursor: pointer;
  list-style: none;
}
.ed-rail-langmenu summary::-webkit-details-marker { display: none; }
.ed-rail-langmenu ul {
  /* FIXED, not absolute: the rail becomes a scroll container on short
     viewports, and an absolute panel inside it gets squeezed by the
     scrollbar, clipped by overflow-x, and painted over the deck. A
     fixed panel escapes the scroll container entirely (the rail has no
     transform/filter, so the viewport is its containing block) and can
     be wider than the rail. The foot only ever opens while visible at
     the rail's bottom edge, so viewport-anchored coordinates hold. */
  position: fixed;
  left: 16px;
  bottom: 68px;
  width: 184px;
  margin: 0;
  padding: 6px;
  list-style: none;
  background: var(--hueso);
  border: 2px solid var(--frame-ink);
  border-radius: 14px;
  box-shadow: 0 10px 24px rgba(0, 0, 0, 0.18);
  z-index: 5;
}
.ed-rail-langmenu ul a {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px;
  border-radius: 9px;
  text-decoration: none;
  color: var(--tinta-900);
  font-weight: 600;
  font-size: var(--fs-caption);
}
.ed-rail-langmenu ul a:hover { background: light-dark(rgba(26, 18, 6, 0.06), rgba(255, 255, 255, 0.08)); }
.ed-rail-langmenu ul a[aria-current="true"] { font-weight: 800; }

/* ---------- Editorial page grid ---------- */

.ed-wrap {
  padding: var(--panel-gap) var(--panel-gap) 0;
  display: flex;
  flex-direction: column;
  gap: var(--panel-gap);
  max-width: 1720px;
  margin: 0 auto;
}
.ed-grid {
  display: grid;
  gap: var(--panel-gap);
}
/* The hero headline is sized against the panel, not the viewport. The
   copy panel is five of twelve columns, so a `4.9vw` rung outgrew its
   column: at 1440 the headline had ~480px to work with at 70px type,
   which is eleven characters a line, and a 52-character headline
   shattered into seven ragged decks that read as broken alignment.
   `cqw` resolves against the panel's own content box, so the line
   length holds at roughly fourteen characters at every width. */
.ed-grid--hero .ed-hero-copy { container-type: inline-size; }
@supports (font-size: 1cqw) {
  .ed-grid--hero .ed-hero-copy > h1 { font-size: clamp(38px, 11.5cqw, 72px); }
}
@media (min-width: 960px) {
  .ed-grid--hero { grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); align-items: stretch; }
  .ed-grid--split { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); align-items: stretch; }
  .ed-grid--split-wide { grid-template-columns: minmax(0, 7fr) minmax(0, 5fr); align-items: stretch; }
  .ed-grid--thirds { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

/* ---------- Grid-paper footer with giant wordmark ---------- */

.ed-footer-paper {
  position: relative;
  margin-top: clamp(32px, 4vw, 64px);
  border-top: var(--panel-border);
  background:
    linear-gradient(var(--tinta-300) 1px, transparent 1px),
    linear-gradient(90deg, var(--tinta-300) 1px, transparent 1px);
  background-size: 56px 56px;
  background-color: var(--hueso);
  overflow: hidden;
}
.ed-footer-wordmark {
  font-family: var(--font-black);
  font-size: clamp(72px, 17vw, 260px);
  line-height: 0.9;
  letter-spacing: -0.01em;
  color: var(--tinta-900);
  padding: clamp(32px, 6vw, 96px) 24px 0;
  margin: 0;
  user-select: none;
}
.ed-footer-cell {
  position: absolute;
  width: 28px;
  height: 28px;
  border: 2px solid var(--frame-ink);
}
/* Lotería deal: the scattered cells cycle the full card palette.
   Overrides the per-page inline verde values (decorative, aria-hidden)
   so 40+ pages retheme without markup edits. */
.ed-footer-paper > div.ed-footer-cell:nth-of-type(1) { background: var(--amarillo-500) !important; }
.ed-footer-paper > div.ed-footer-cell:nth-of-type(2) { background: var(--coral-500) !important; }
.ed-footer-paper > div.ed-footer-cell:nth-of-type(3) { background: var(--azul-700) !important; }
.ed-footer-paper > div.ed-footer-cell:nth-of-type(4) { background: var(--verde-500) !important; }
.ed-footer-paper > div.ed-footer-cell:nth-of-type(5) { background: var(--rosa-500) !important; }
@media (max-width: 640px) {
  .ed-footer-paper { background-size: 40px 40px; }
  .ed-footer-cell { width: 20px; height: 20px; }
}

/* The legacy dark footer becomes a light editorial footer sitting on the
   grid paper. Scoped to .ed-footer so legacy pages keep the dark footer
   until they migrate. */
.ed-footer .footer {
  background: transparent;
  color: var(--tinta-700);
  border-top: none;
}
.ed-footer .footer a { color: var(--tinta-900); }
.ed-footer .footer h4 { color: var(--tinta-900); }
.ed-footer .footer-mouseprint { color: var(--tinta-500); }
.ed-footer .wordmark--on-dark { color: var(--tinta-900); }

/* ---------- Motion: respect reduced motion for the new pieces ---------- */

@media (prefers-reduced-motion: reduce) {
  .ed-rail-item, .ed-rail-item:hover { transition: none; transform: none; filter: none; }
  .faq-item summary::after { transition: none; }
  .btn, .btn:hover { transition: none; transform: none; }
}

/* ---------- Editorial layer: legacy-bleed fixes (post-QA) ---------- */

/* The legacy eyebrow carried a ::before status dot and glass shadows;
   both bleed into the new outlined pill. Kill them here (this layer
   loads last). */
.eyebrow::before { display: none; }
.eyebrow, .section-head .eyebrow, .how-gallery .eyebrow {
  box-shadow: none;
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
  background-image: none;
}

/* Legacy glass button skin: flatten to the editorial pill. */
.btn-primary, .btn-primary:hover {
  background-image: none;
  box-shadow: none;
}
.btn-primary::before, .btn-primary::after { display: none; }
/* The legacy ghost hover carried a glass sheen + soft verde halo;
   the Lotería spec allows no halos. Kill the bleed. */
.btn-ghost:hover { box-shadow: none; }
/* `.btn-outline` was the Liquid Glass secondary (blur, rgba surface,
   1px glass stroke) and never got an editorial skin, so the mobile
   nav sheet and the story pages still rendered a glass pill next to
   Lotería ones. Flatten it to the ghost grammar. */
.btn-outline, .btn-outline:hover {
  background: transparent;
  color: var(--tinta-900);
  border: 2px solid var(--frame-ink);
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
  box-shadow: none;
}
.btn-outline:hover { background: var(--tinta-900); color: var(--blanco); }

/* Legacy [open] halo on FAQ rows. */
.faq-item[open] { box-shadow: none; }

/* Hero video: keep the loop composed, never a zoomed crop. The media
   panel matches the copy panel height on desktop but the video itself
   caps at a cinematic ratio. */
.ed-panel--media > video.hero-video {
  aspect-ratio: 16 / 9;
  height: auto;
  min-height: 100%;
}

/* ═══════════════════════════════════════════════════════════════════
   MOTION LAYER — 2026-07-11.

   Principle: reveal must enhance, never gate. Content is visible by
   default everywhere (no-JS, print, headless renderers, Firefox).
   Scroll choreography is implemented with CSS scroll-driven animations
   behind @supports — engines without `animation-timeline: view()`
   simply show the finished page. No JavaScript is involved.

   Vocabulary (kept deliberately small):
     • Panels / cells `[data-reveal]` — rise + settle as they enter.
     • Stat chips — same rise, sibling-staggered via animation-range.
     • Divider bars `.ed-bar` — a center-out clip wipe: the bar
       "opens" like a counter shutter. The one signature move.
   ═══════════════════════════════════════════════════════════════════ */

[data-reveal] {
  opacity: 1;
  transform: none;
  transition: none;
}

/* The from-state NEVER hides content: opacity floors at 0.72 and the
   travel is pure transform. A static capture (print, full-page
   screenshot, engine without scroll-driven support mid-scroll) shows
   readable content at worst slightly dimmed and shifted — never a
   blank section. That constraint is why there is no clip/fade-from-
   zero anywhere in this layer. */
@supports (animation-timeline: view()) {
  @media screen and (prefers-reduced-motion: no-preference) {
    @keyframes ed-reveal-rise {
      from { opacity: 0.72; transform: translateY(20px); }
      to   { opacity: 1; transform: translateY(0); }
    }
    [data-reveal] {
      animation: ed-reveal-rise var(--dur-entrance) var(--ease-entrance) both;
      animation-timeline: view();
      animation-range: entry 5% entry 40%;
    }
    /* Stat chips: the row container carries [data-reveal]; hand the
       motion to the chips themselves so the row can stagger. One list,
       four items — legitimate sibling stagger. */
    .ed-chips[data-reveal] { animation: none; }
    .ed-chips > .ed-chip {
      animation: ed-reveal-rise var(--dur-entrance) var(--ease-entrance) both;
      animation-timeline: view();
      animation-range: entry 5% entry 45%;
    }
    .ed-chips > .ed-chip:nth-child(2) { animation-range: entry 12% entry 52%; }
    .ed-chips > .ed-chip:nth-child(3) { animation-range: entry 19% entry 59%; }
    .ed-chips > .ed-chip:nth-child(4) { animation-range: entry 26% entry 66%; }

    /* Cards sharing one grid row stagger left-to-right the same way. */
    .ed-grid > [data-reveal]:nth-child(2) { animation-range: entry 11% entry 46%; }
    .ed-grid > [data-reveal]:nth-child(3) { animation-range: entry 17% entry 52%; }
    .ed-grid > [data-reveal]:nth-child(4) { animation-range: entry 23% entry 58%; }

    /* Divider bars: the bar rises with the page; its arrow pairs slide
       outward from the title — a small "opening" gesture on elements
       that are pure decoration (safe at any static state). */
    .ed-bar {
      animation: ed-reveal-rise var(--dur-deliberate) var(--ease-entrance) both;
      animation-timeline: view();
      animation-range: entry 10% entry 55%;
    }
    @keyframes ed-bar-arrows-l { from { transform: translateX(14px); } to { transform: translateX(0); } }
    @keyframes ed-bar-arrows-r { from { transform: translateX(-14px); } to { transform: translateX(0); } }
    .ed-bar .ed-bar-arrows:first-of-type {
      animation: ed-bar-arrows-l var(--dur-deliberate) var(--ease-entrance) both;
      animation-timeline: view();
      animation-range: entry 10% entry 60%;
    }
    .ed-bar .ed-bar-arrows:last-of-type {
      animation: ed-bar-arrows-r var(--dur-deliberate) var(--ease-entrance) both;
      animation-timeline: view();
      animation-range: entry 10% entry 60%;
    }

    /* App-showcase device art: a quiet settle behind the panel rise,
       so the phone reads as "landing" on the black canvas. */
    @keyframes ed-device-settle {
      from { opacity: 0.75; transform: translateY(14px) scale(0.98); }
      to   { opacity: 1; transform: translateY(0) scale(1); }
    }
    .ed-showcase-art .device-frame,
    .ed-showcase-art .showcase-stage {
      animation: ed-device-settle var(--dur-entrance) var(--ease-entrance) both;
      animation-timeline: view();
      animation-range: entry 15% entry 60%;
    }
  }
}

/* ---------- Signature: the brand dot stamps the headline ----------
   The hero h1 ends in Pesito's green dot. It lands ~200ms after the
   headline settles — a single decisive stamp (one overshoot frame on
   the emphasized curve, not a bounce). Scoped to h1 so section
   headings and the nav wordmark stay still. */
@keyframes brand-dot-stamp {
  from { opacity: 0; transform: translateY(-0.05em) scale(0); }
  70%  { opacity: 1; transform: translateY(-0.05em) scale(1.16); }
  to   { opacity: 1; transform: translateY(-0.05em) scale(1); }
}
h1 .brand-dot {
  animation: brand-dot-stamp 380ms var(--ease-entrance) 860ms both;
}
@media (prefers-reduced-motion: reduce) {
  h1 .brand-dot { animation: none; }
}

/* ---------- Micro-interactions: feedback layer ---------- */

/* Press: every pill compresses under the finger. 100ms class —
   instant feedback, no transition needed on the way down. */
.btn:active,
.ed-rail-cta:active,
.store-badge:active {
  transform: translateY(0) scale(0.97);
  transition-duration: var(--dur-instant);
}

/* Lotería lift: the conversion button rises off the table on a hard
   ink offset — folk-poster elevation, no blur, no glow. */
.btn-primary:hover {
  box-shadow: var(--card-lift);
}
/* Inverted (ink) primary inside verde-vivo/verde panels: same lift,
   but pinned to fixed ink. The themed --card-lift turned the offset
   bone in dark mode while the panel fill stayed fixed green, which
   framed the ink pill in white. */
.ed-panel--verde-vivo .btn-primary:hover,
.ed-panel--verde .btn-primary:hover {
  box-shadow: 3px 3px 0 var(--ink-fixed);
}

/* FAQ rows acknowledge the pointer before the click. */
.faq-item summary {
  transition: background-color var(--dur-fast) var(--ease-standard);
  border-radius: var(--panel-radius-sm);
}
.faq-item:not([open]) summary:hover {
  background-color: rgba(0, 166, 81, 0.07);
}

/* Store badges lift like buttons (they are buttons). */
.store-badge {
  transition: transform var(--dur-fast) var(--ease-standard),
              box-shadow var(--dur-fast) var(--ease-standard);
}
.store-badge:hover {
  transform: translateY(-1px);
}

/* Rail language pill + logo get the same acknowledgement. */
.ed-rail-lang:hover { background: var(--rosa-600); }
.ed-rail-logo { transition: transform var(--dur-fast) var(--ease-standard); }
.ed-rail-logo:hover { transform: translateY(-1px); }

@media (prefers-reduced-motion: reduce) {
  .btn:active,
  .ed-rail-cta:active,
  .store-badge:active,
  .store-badge:hover,
  .ed-rail-logo:hover {
    transform: none;
  }
  .faq-item summary,
  .store-badge,
  .ed-rail-logo {
    transition: none;
  }
}

/* ═══════════════════════════════════════════════════════════════════
   WAITLIST CTA — Lotería flow (2026-07-16, deepened 2026-07-18).

   Hover treatment for primary waitlist CTAs only, applied as
   `.btn-flow` by scripts/main.js (progressive enhancement: without
   JS, or on touch / reduced-motion, the button is exactly the
   standard editorial pill — every rule below is gated behind
   hover-capable + motion-tolerant media queries, so hover can never
   go "sticky" on touch and reduced-motion keeps the plain fast
   color fade).

   The interaction: FOUR oversized sheets pour upward through the
   pill in a stagger, shifting through the Lotería palette —
   amarillo leads, coral chases, rosa flashes, mint settles as the
   hover fill. Every sheet's top edge is a true sine wave (an SVG
   mask tile repeated across the crest; four different wavelengths
   and amplitudes so the layers read as breaking surf, not one
   shape re-timed), and every sheet drifts laterally while it
   rises, alternating directions, so the crests travel like water.
   Each hover seeds a fresh random wave phase per sheet
   (--flow-p1..4, JS on pointerenter) — no two pours break alike.
   A soft highlight bloom enters at the pointer position and
   follows it (--px/--py, rAF-throttled).

   Hover-out does NOT rewind: JS swaps `:hover` for `.is-exiting`
   and the sheets accelerate on through the TOP edge — mint first,
   amarillo last — then reset to the bottom with transitions
   disabled (`.is-resetting`). Liquid pours up and through, both
   directions, never back down. Entrance tail ≈ 1410 ms; exit ≈ 980.

   Ink label: ≥6:1 at rest (verde-500 6.2) and settle (mint 10.4);
   amarillo 12.3 and coral ~6.9 in transit; the rosa pass dips for
   ~95 ms by design — the transient the reference motion accepts.
   Inverted variant inside verde-vivo panels keeps its white label
   readable on EVERY frame (azul cobalto 8.7 → rosa-600 5.6 →
   verde-700 7.0 → tinta settle). Rojo stays semantic-only.
   Validated in docs/design-system/MASTER.md.
   ═══════════════════════════════════════════════════════════════════ */

.btn-flow {
  position: relative;
  overflow: hidden;
  /* The pour sheets are far larger than the pill (±16% wide, 210% tall,
     translated below it). overflow:hidden clips their PAINT, but their
     composited transforms can still leak into an ancestor scroll
     container's overflow accounting mid-pour — inside the rail that
     summoned a horizontal scrollbar on hover. Paint containment makes
     the clip airtight for overflow accounting too. */
  contain: paint;
  isolation: isolate;
  -webkit-tap-highlight-color: transparent;
}
/* The legacy glass-sheen pseudo skin still cascades onto
   .btn-primary pseudos (opacity/mask/blend dance at :hover). The
   sweep now lives in real child spans, so the pseudos are retired
   outright wherever the flow applies. */
.btn-flow::before,
.btn-flow::after {
  content: none;
}
/* JS wraps the label so it stacks above the sweep layers. */
.btn-flow .btn-flow-label {
  position: relative;
  z-index: 2;
  display: inline-flex;
  align-items: center;
  gap: inherit;
}
/* Sheet container + sheets exist only when JS ran (which already
   bails on touch and reduced motion), but every visual rule is
   still media-gated below, belt and braces. */
.btn-flow .btn-flow-layers {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
}

@media (hover: hover) and (pointer: fine) and (prefers-reduced-motion: no-preference) {
  /* The sheet is ONLY a pour carrier now: its transform/transition
     runs the rest → hover → exit → reset state machine, nothing is
     painted on it. All paint (background + wave masks) lives on the
     inner .btn-flow-water so the lateral drift can run as a pure
     compositor transform — animating mask-position here repainted
     the sheet every frame (measured 79% dropped frames on CTA
     hover). */
  .btn-flow .btn-flow-layers > span {
    position: absolute;
    left: -16%;
    right: -16%;
    top: 0;
    height: 210%;
    background: none;
    transform: translate3d(var(--flow-dx, 0%), 103%, 0);
    will-change: transform;
    transition: transform 820ms cubic-bezier(0.45, 0.05, 0.2, 1);
  }
  /* The water: a double-width paint layer inside each sheet
     (injected by scripts/main.js next to the sheets). The crest: a
     true sine wave, cut by an SVG mask tile repeated across the top
     22px (layer 1); a plain gradient keeps the body opaque from
     21px down (layer 2, 1px overlap so no seam). --flow-p* shifts
     the tile's phase — set once per hover by JS, never animated.
     The drift keyframes below translate the water ±half a tile, so
     the crests travel like water with zero repaint; sheets already
     overflow the button ±16%, so the 200%-wide water keeps the pill
     fully covered through the whole excursion. */
  .btn-flow .btn-flow-water {
    position: absolute;
    top: 0;
    bottom: 0;
    width: 200%;
    left: -50%;
    -webkit-mask-repeat: repeat-x, no-repeat, repeat-x;
    mask-repeat: repeat-x, no-repeat, repeat-x;
  }
  /* Four sine crests at four wavelengths/amplitudes — layered surf,
     not one shape re-timed. Sheet order = paint order: amarillo
     under coral under rosa under mint. Lateral drift alternates. */
  .btn-flow .btn-flow-layers > span:nth-child(1) {
    --flow-dx: 4%;
    transition-delay: 0ms;
  }
  .btn-flow .btn-flow-layers > span:nth-child(1) .btn-flow-water {
    background: var(--amarillo-500);
    -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 110 22'%3E%3Cpath d='M0 11 C18 2 37 2 55 11 C73 20 92 20 110 11 V22 H0 Z'/%3E%3C/svg%3E"), linear-gradient(#000, #000), url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 110 22'%3E%3Cpath d='M0 11 C18 2 37 2 55 11 C73 20 92 20 110 11 V0 H0 Z'/%3E%3C/svg%3E");
    mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 110 22'%3E%3Cpath d='M0 11 C18 2 37 2 55 11 C73 20 92 20 110 11 V22 H0 Z'/%3E%3C/svg%3E"), linear-gradient(#000, #000), url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 110 22'%3E%3Cpath d='M0 11 C18 2 37 2 55 11 C73 20 92 20 110 11 V0 H0 Z'/%3E%3C/svg%3E");
    -webkit-mask-size: 110px 22px, 100% calc(100% - 42px), 110px 22px;
    mask-size: 110px 22px, 100% calc(100% - 42px), 110px 22px;
    -webkit-mask-position: var(--flow-p1, 0px) 0, 0 21px, var(--flow-p1, 0px) 100%;
    mask-position: var(--flow-p1, 0px) 0, 0 21px, var(--flow-p1, 0px) 100%;
  }
  .btn-flow .btn-flow-layers > span:nth-child(2) {
    --flow-dx: -4.5%;
    transition-delay: 170ms;
  }
  .btn-flow .btn-flow-layers > span:nth-child(2) .btn-flow-water {
    background: var(--coral-500);
    -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 150 22'%3E%3Cpath d='M0 11 C25 4 50 4 75 11 C100 18 125 18 150 11 V22 H0 Z'/%3E%3C/svg%3E"), linear-gradient(#000, #000), url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 150 22'%3E%3Cpath d='M0 11 C25 4 50 4 75 11 C100 18 125 18 150 11 V0 H0 Z'/%3E%3C/svg%3E");
    mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 150 22'%3E%3Cpath d='M0 11 C25 4 50 4 75 11 C100 18 125 18 150 11 V22 H0 Z'/%3E%3C/svg%3E"), linear-gradient(#000, #000), url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 150 22'%3E%3Cpath d='M0 11 C25 4 50 4 75 11 C100 18 125 18 150 11 V0 H0 Z'/%3E%3C/svg%3E");
    -webkit-mask-size: 150px 22px, 100% calc(100% - 42px), 150px 22px;
    mask-size: 150px 22px, 100% calc(100% - 42px), 150px 22px;
    -webkit-mask-position: var(--flow-p2, 0px) 0, 0 21px, var(--flow-p2, 0px) 100%;
    mask-position: var(--flow-p2, 0px) 0, 0 21px, var(--flow-p2, 0px) 100%;
  }
  .btn-flow .btn-flow-layers > span:nth-child(3) {
    --flow-dx: 3.5%;
    transition-delay: 340ms;
  }
  .btn-flow .btn-flow-layers > span:nth-child(3) .btn-flow-water {
    background: var(--rosa-500);
    -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 90 22'%3E%3Cpath d='M0 11 C15 1 30 1 45 11 C60 21 75 21 90 11 V22 H0 Z'/%3E%3C/svg%3E"), linear-gradient(#000, #000), url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 90 22'%3E%3Cpath d='M0 11 C15 1 30 1 45 11 C60 21 75 21 90 11 V0 H0 Z'/%3E%3C/svg%3E");
    mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 90 22'%3E%3Cpath d='M0 11 C15 1 30 1 45 11 C60 21 75 21 90 11 V22 H0 Z'/%3E%3C/svg%3E"), linear-gradient(#000, #000), url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 90 22'%3E%3Cpath d='M0 11 C15 1 30 1 45 11 C60 21 75 21 90 11 V0 H0 Z'/%3E%3C/svg%3E");
    -webkit-mask-size: 90px 22px, 100% calc(100% - 42px), 90px 22px;
    mask-size: 90px 22px, 100% calc(100% - 42px), 90px 22px;
    -webkit-mask-position: var(--flow-p3, 0px) 0, 0 21px, var(--flow-p3, 0px) 100%;
    mask-position: var(--flow-p3, 0px) 0, 0 21px, var(--flow-p3, 0px) 100%;
  }
  /* Mint settles as the hover state; the white bloom rides on its
     water, centered on the pointer (falls back to a top-center
     bloom for keyboard focus). --px/--py are button-relative, so
     the 25% water offset (= half the sheet width) re-anchors the
     gradient where the sheet's own origin used to be. */
  .btn-flow .btn-flow-layers > span:nth-child(4) {
    --flow-dx: -2.5%;
    transition-duration: 900ms;
    transition-delay: 510ms;
  }
  .btn-flow .btn-flow-layers > span:nth-child(4) .btn-flow-water {
    background:
      radial-gradient(140px circle at calc(25% + var(--px, 25%)) var(--py, 30%),
        rgba(255, 255, 255, 0.38), rgba(255, 255, 255, 0) 72%),
      var(--verde-300);
    -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 180 22'%3E%3Cpath d='M0 11 C30 3 60 3 90 11 C120 19 150 19 180 11 V22 H0 Z'/%3E%3C/svg%3E"), linear-gradient(#000, #000), url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 180 22'%3E%3Cpath d='M0 11 C30 3 60 3 90 11 C120 19 150 19 180 11 V0 H0 Z'/%3E%3C/svg%3E");
    mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 180 22'%3E%3Cpath d='M0 11 C30 3 60 3 90 11 C120 19 150 19 180 11 V22 H0 Z'/%3E%3C/svg%3E"), linear-gradient(#000, #000), url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 180 22'%3E%3Cpath d='M0 11 C30 3 60 3 90 11 C120 19 150 19 180 11 V0 H0 Z'/%3E%3C/svg%3E");
    -webkit-mask-size: 180px 22px, 100% calc(100% - 42px), 180px 22px;
    mask-size: 180px 22px, 100% calc(100% - 42px), 180px 22px;
    -webkit-mask-position: var(--flow-p4, 0px) 0, 0 21px, var(--flow-p4, 0px) 100%;
    mask-position: var(--flow-p4, 0px) 0, 0 21px, var(--flow-p4, 0px) 100%;
  }

  /* Entrance: hover or keyboard focus pours the sheets up to a
     -30% overshoot, each drifting across to the opposite side of
     its resting offset — the wave travels sideways while it rises.
     The overshoot is deep because the 22px wave strip is a FIXED
     height while the sheet is proportional: even the small nav
     pill (~40px) must carry every layer's crest fully past its
     top edge at settle, or slivers of the sheet below peek
     through the valleys. */
  .btn-flow:hover .btn-flow-layers > span,
  .btn-flow:focus-visible .btn-flow-layers > span {
    transform: translate3d(calc(-1 * var(--flow-dx, 0%)), -33%, 0);
  }

  /* Exit: pour on through the top, reverse stagger (mint leads the
     way out, amarillo trails), slightly quicker and with an
     accelerating curve — liquid leaving, not rewinding. The drift
     keeps carrying in the same direction. */
  .btn-flow.is-exiting .btn-flow-layers > span {
    transform: translate3d(calc(-1.5 * var(--flow-dx, 0%)), -126%, 0);
    transition: transform 650ms cubic-bezier(0.5, 0.05, 0.45, 1);
  }
  .btn-flow.is-exiting .btn-flow-layers > span:nth-child(4) { transition-delay: 0ms; }
  .btn-flow.is-exiting .btn-flow-layers > span:nth-child(3) { transition-delay: 110ms; }
  .btn-flow.is-exiting .btn-flow-layers > span:nth-child(2) { transition-delay: 220ms; }
  .btn-flow.is-exiting .btn-flow-layers > span:nth-child(1) { transition-delay: 330ms; }

  /* Reset: sheets teleport back below the pill with transitions off
     (JS forces a reflow between adding and removing this class). */
  .btn-flow.is-resetting .btn-flow-layers > span {
    transition: none;
    transform: translate3d(var(--flow-dx, 0%), 103%, 0);
  }

  /* The pill itself: keep the editorial lift, add a whisper of
     scale on hover and a press on :active (covers .ed-rail-cta,
     which the legacy .btn:active rule never reached). */
  .btn-flow {
    transition: transform 240ms cubic-bezier(0.22, 1, 0.36, 1);
  }
  .btn-flow:hover,
  .btn-flow:focus-visible {
    transform: translateY(-1px) scale(1.015);
  }
  .btn-flow:active {
    transform: translateY(0) scale(0.985);
    transition-duration: 120ms;
  }
  /* Keep the base fill steady beneath the sweep — the legacy hover
     recolor would double up with the layers. */
  .btn-flow.btn-primary:hover, .btn-flow.ed-rail-cta:hover {
    background: var(--verde-500);
  }

  /* Inverted context: the ink CTA on the loud verde-vivo money
     panel. The Lotería shift picks the deep half of the palette so
     the white label stays readable on every single frame: cobalt →
     rosa-600 → verde-700 → tinta settle with a deep-verde bloom. */
  .ed-panel--verde-vivo .btn-flow .btn-flow-layers > span:nth-child(1) .btn-flow-water,
  .ed-panel--verde .btn-flow .btn-flow-layers > span:nth-child(1) .btn-flow-water {
    background: var(--azul-700);
  }
  .ed-panel--verde-vivo .btn-flow .btn-flow-layers > span:nth-child(2) .btn-flow-water,
  .ed-panel--verde .btn-flow .btn-flow-layers > span:nth-child(2) .btn-flow-water {
    background: var(--rosa-600);
  }
  .ed-panel--verde-vivo .btn-flow .btn-flow-layers > span:nth-child(3) .btn-flow-water,
  .ed-panel--verde .btn-flow .btn-flow-layers > span:nth-child(3) .btn-flow-water {
    background: var(--verde-700);
  }
  /* Settle sheet: FIXED deep tinta (#2A2A2E = light-mode tinta-700),
     never the themed token. Themed, the settle went bone in dark mode
     under the fixed white label: white on bone at hover settle. */
  .ed-panel--verde-vivo .btn-flow .btn-flow-layers > span:nth-child(4) .btn-flow-water,
  .ed-panel--verde .btn-flow .btn-flow-layers > span:nth-child(4) .btn-flow-water {
    background:
      radial-gradient(140px circle at calc(25% + var(--px, 25%)) var(--py, 30%),
        rgba(0, 166, 81, 0.35), rgba(0, 166, 81, 0) 72%),
      #2A2A2E;
  }
  .ed-panel--verde-vivo .btn-flow.btn-primary:hover,
  .ed-panel--verde .btn-flow.btn-primary:hover {
    background: var(--ink-fixed);
  }

  /* The waterline: while the button is poured (hover/focus) or
     pouring out, every wave drifts slowly sideways — alternating
     directions and speeds per sheet. Rebuilt as transform keyframes
     on the water (mask-position keyframes repainted every frame:
     79% dropped frames on CTA hover). Each loop travels exactly one
     mask tile width, centered ±half a tile around rest so the
     double-width water never exposes an edge — the mask repeats per
     tile, so from == to visually and the loop is seamless. Liquid,
     not wallpaper. Lives inside the hover+motion media gate, so
     touch and reduced-motion never run it, and at rest the sheets
     are off-screen with no animation at all. */
  @keyframes btn-flow-drift-1 {
    from { transform: translateX(-55px); }
    to { transform: translateX(55px); }
  }
  .btn-flow:hover .btn-flow-layers > span:nth-child(1) .btn-flow-water,
  .btn-flow:focus-visible .btn-flow-layers > span:nth-child(1) .btn-flow-water,
  .btn-flow.is-exiting .btn-flow-layers > span:nth-child(1) .btn-flow-water {
    animation: btn-flow-drift-1 7s linear infinite;
  }
  @keyframes btn-flow-drift-2 {
    from { transform: translateX(75px); }
    to { transform: translateX(-75px); }
  }
  .btn-flow:hover .btn-flow-layers > span:nth-child(2) .btn-flow-water,
  .btn-flow:focus-visible .btn-flow-layers > span:nth-child(2) .btn-flow-water,
  .btn-flow.is-exiting .btn-flow-layers > span:nth-child(2) .btn-flow-water {
    animation: btn-flow-drift-2 9.5s linear infinite;
  }
  @keyframes btn-flow-drift-3 {
    from { transform: translateX(-45px); }
    to { transform: translateX(45px); }
  }
  .btn-flow:hover .btn-flow-layers > span:nth-child(3) .btn-flow-water,
  .btn-flow:focus-visible .btn-flow-layers > span:nth-child(3) .btn-flow-water,
  .btn-flow.is-exiting .btn-flow-layers > span:nth-child(3) .btn-flow-water {
    animation: btn-flow-drift-3 8s linear infinite;
  }
  @keyframes btn-flow-drift-4 {
    from { transform: translateX(90px); }
    to { transform: translateX(-90px); }
  }
  .btn-flow:hover .btn-flow-layers > span:nth-child(4) .btn-flow-water,
  .btn-flow:focus-visible .btn-flow-layers > span:nth-child(4) .btn-flow-water,
  .btn-flow.is-exiting .btn-flow-layers > span:nth-child(4) .btn-flow-water {
    animation: btn-flow-drift-4 11s linear infinite;
  }
}


/* ═══════════════════════════════════════════════════════════════════
   BELOW-THE-FOLD RENDER DEFERRAL — 2026-07-19 perf audit.

   The editorial pages style ~90 below-fold panels in one startup
   layout pass (measured 1.6s desktop / FCP 21-34s on 3G).
   `content-visibility: auto` lets the engine skip layout+paint for
   off-screen section shells until they approach the viewport; the
   remembered-size placeholder (auto 900px) keeps the scrollbar sane.

   Explicit shell list only — the top-level blocks used below the
   hero on the two homepages (ed-* grids, section-level panels,
   showcases, FAQ, footer paper + footer). Never a universal
   selector, and never the hero (.ed-grid--hero), nav, or rail —
   those must render in the first frame.

   Scroll-driven reveals (`animation-timeline: view()` above) stay
   compatible: the animation starts when the shell first renders,
   which is before it enters the viewport. Nested shells (ES wraps
   grids in section.ed-panel--tinta) are fine — the outer skip
   covers the inner. */
.ed-grid--thirds,
.ed-grid--split,
.ed-grid--split-wide,
section.ed-panel--verde,
section.ed-panel--tinta,
section.ed-panel--bosque,
.faq-list,
.ed-footer,
.footer {
  content-visibility: auto;
  contain-intrinsic-size: auto 900px;
}


/* ═══════════════════════════════════════════════════════════════════
   THEME LAYER — 2026-08.

   Three appearance axes, in priority order:

     1. `data-theme` on <html>        — the visitor's explicit pick,
                                        written by the nav toggle and
                                        replayed pre-paint by the
                                        inline script in every <head>.
     2. `prefers-color-scheme`        — the OS default, honored with
                                        no JavaScript at all because
                                        html carries `color-scheme:
                                        light dark`.
     3. `prefers-contrast` /
        `forced-colors`               — accessibility overrides that
                                        win over both.

   Nearly all of the retheming already happened up in `:root` via
   `light-dark()`. What's left here is the handful of things that
   aren't colors (blend modes) plus a few places where the dark
   inversion changes which element needs the outline.
   ═══════════════════════════════════════════════════════════════ */

html[data-theme="light"] { color-scheme: light; }
html[data-theme="dark"]  { color-scheme: dark; }

/* Explicit toggle only: soften the dark<->light brightness jump with a
   short cross-ease. scripts/main.js holds `.theme-easing` on <html>
   for ~360ms around the swap; page loads and OS-preference changes
   never get it, so nothing transitions on first paint. */
@media (prefers-reduced-motion: no-preference) {
  html.theme-easing,
  html.theme-easing body,
  html.theme-easing *,
  html.theme-easing *::before,
  html.theme-easing *::after {
    transition:
      background-color 300ms var(--ease-standard),
      color 300ms var(--ease-standard),
      border-color 300ms var(--ease-standard),
      box-shadow 300ms var(--ease-standard) !important;
  }
}

/* Paper grain. On cream stock an OVERLAY pass at 0.55 reads as
   fibre; on ink stock the same pass lifts the blacks into a flat
   grey haze, so dark drops to a gentler SOFT-LIGHT at half strength
   — still tactile, no lift. Not expressible in `light-dark()`. */
html[data-theme="dark"] {
  --grain-opacity: 0.3;
  --grain-blend: soft-light;
  /* Dark glass: saturation to a whisper, no brightness lift (see the
     nav capsule tokens up top). */
  --nav-glass-saturate: 1.15;
  --nav-glass-saturate-scrolled: 1.3;
  --nav-glass-brightness: 1;
  /* Plain-hex dark fills for engines without light-dark() — mirrors
     the light-dark() second values declared in :root. */
  --fill-verde-vivo: #0C9247;
  --fill-amarillo: #E7B41E;
  --fill-azul: #05378C;
}
@media (prefers-color-scheme: dark) {
  html:not([data-theme="light"]) {
    --grain-opacity: 0.3;
    --grain-blend: soft-light;
    --nav-glass-saturate: 1.15;
    --nav-glass-saturate-scrolled: 1.3;
    --nav-glass-brightness: 1;
    --fill-verde-vivo: #0C9247;
    --fill-amarillo: #E7B41E;
    --fill-azul: #05378C;
  }
}

/* The app-showcase device art used to need a theme-dependent outline
   here, because a bare SVG screenshot with a near-white background
   dissolved into the tinta panel once dark mode inverted it to bone.
   `.device-frame` now wraps every mockup in real hardware — a bezel
   dark in both themes — so the screen is separated from the panel by
   the phone itself and no theme-specific rule is needed. */

/* ---------- High contrast (prefers-contrast: more) ----------
   Users asking for more contrast get the ink ramp pushed to its
   ends, translucency removed, and every hairline promoted to a
   real 2px rule so structure is never carried by a 9%-alpha line. */
@media (prefers-contrast: more) {
  :root {
    --tinta-700: light-dark(#000000, #FFFFFF);
    --tinta-500: light-dark(#1A1A1F, #EDEAE1);
    --tinta-400: light-dark(#1A1A1F, #EDEAE1);
    --hairline: light-dark(rgba(0, 0, 0, 0.5), rgba(255, 255, 255, 0.55));
    --hairline-strong: light-dark(#000000, #FFFFFF);
    --canvas-top: light-dark(#FFFFFF, #000000);
    --canvas-bottom: light-dark(#FFFFFF, #000000);
    --hueso: light-dark(#FFFFFF, #000000);
    --blanco: light-dark(#FFFFFF, #0D0D0D);
    --grain-opacity: 0;
  }
  .ed-panel, .ed-bar, .ed-chip, .faq-item, .ed-rail-item, .btn {
    border-width: 2px;
  }
  .ed-panel--verde p, .ed-panel--bosque p { color: var(--paper-fixed); }
  :focus-visible { outline-width: 3px; }
  /* Translucent chrome becomes opaque so text never rides a blur. */
  .nav, .nav-sheet {
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
    background: var(--hueso);
  }
}

/* ---------- Windows High Contrast / forced-colors ----------
   In forced-colors the UA replaces every color we set, so the job
   here is purely structural: make sure nothing depends on a color
   we no longer control. Decorative fills are dropped, outlines are
   restated in system keywords, and the focus ring is guaranteed. */
@media (forced-colors: active) {
  .ed-panel, .ed-bar, .ed-chip, .faq-item, .ed-rail-item,
  .ed-rail-cta, .ed-rail-lang, .btn, .fx-card, .fee-ladder {
    border: 2px solid CanvasText;
    forced-color-adjust: none;
    background: Canvas;
    color: CanvasText;
  }
  .btn-primary, .ed-rail-cta {
    background: Highlight;
    color: HighlightText;
    border-color: Highlight;
  }
  :focus-visible { outline: 3px solid Highlight; outline-offset: 2px; }
  /* Pure decoration that carries no meaning. */
  body::before,
  .ed-footer-cell,
  .ed-bar-arrows,
  .btn-flow .btn-flow-layers { display: none; }
  .ed-footer-paper { background-image: none; }
}


/* ═══════════════════════════════════════════════════════════════════
   SKIP LINK — WCAG 2.2 SC 2.4.1 (Bypass Blocks).

   Visually hidden until focused, then lands as a Lotería pill in the
   top-left gutter, above the rail (z-toast). It is the first
   focusable node in the document on every page.
   ═══════════════════════════════════════════════════════════════ */

.skip-link {
  position: fixed;
  top: 0;
  left: 0;
  z-index: var(--z-toast, 80);
  /* Off-canvas rather than `display:none` so it stays focusable. */
  transform: translateY(-200%);
  margin: var(--panel-gap, 12px);
  padding: 12px 22px;
  background: var(--amarillo-500);
  color: var(--ink-fixed);
  border: 2px solid var(--ink-fixed);
  border-radius: var(--radius-pill);
  font-weight: 700;
  font-size: var(--fs-body-s);
  text-decoration: none;
  box-shadow: var(--card-lift);
  transition: transform var(--dur-fast) var(--ease-entrance);
}
/* `:focus`, not `:focus-visible`. A skip link is unreachable by
   pointer, so every focus it ever receives is a keyboard focus —
   and gating on `:focus-visible` means it stays hidden when focus
   arrives programmatically (screen-reader virtual cursor, some
   switch-access drivers, automated a11y checks). */
.skip-link:focus {
  transform: translateY(0);
  outline: 3px solid var(--verde-700);
  outline-offset: 3px;
}
@media (prefers-reduced-motion: reduce) {
  .skip-link { transition: none; }
}
/* The landing target must not eat the ring under the fixed nav. */
#contenido, #content { scroll-margin-top: 96px; }


/* ═══════════════════════════════════════════════════════════════════
   APPEARANCE TOGGLE — the sun/moon control in the nav chrome.

   One button, two icons, CSS-only state: the button carries
   `aria-pressed` (true = dark is active) and the icons swap off that
   attribute, so the accessible state and the visual state cannot
   drift apart. Label text is supplied per-locale in the markup.
   ═══════════════════════════════════════════════════════════════ */

.theme-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  width: 40px;
  height: 40px;
  flex-shrink: 0;
  padding: 0;
  background: var(--hueso);
  color: var(--tinta-900);
  border: 2px solid var(--frame-ink);
  border-radius: var(--radius-pill);
  cursor: pointer;
  transition: background-color var(--dur-fast) var(--ease-standard),
              transform var(--dur-fast) var(--ease-standard);
}
.theme-toggle:hover { background: var(--amarillo-500); color: var(--ink-fixed); }
.theme-toggle:active { transform: scale(0.94); }
.theme-toggle:focus-visible { outline: 3px solid var(--verde-700); outline-offset: 2px; }
.theme-toggle svg { width: 18px; height: 18px; }
/* Sun shows while light is active, moon while dark is active. */
.theme-toggle .theme-icon-dark { display: none; }
.theme-toggle[aria-pressed="true"] .theme-icon-dark { display: block; }
.theme-toggle[aria-pressed="true"] .theme-icon-light { display: none; }
/* No JS → no working toggle → don't advertise one. */
html:not(.js) .theme-toggle { display: none; }

/* Rail variant: full-width card in the deck, matching .ed-rail-lang. */
.ed-rail-theme {
  width: auto;
  height: auto;
  flex: 1 1 0;
  padding: 10px 8px;
  background: var(--lila-300);
  color: var(--ink-fixed);
  border-color: var(--ink-fixed);
  border-radius: var(--radius-pill);
}
.ed-rail-theme:hover { background: var(--amarillo-500); }

/* Sheet variant: reads as a labelled row, not a bare icon. */
.nav-sheet .theme-toggle {
  width: 100%;
  height: auto;
  padding: 12px 18px;
  font-weight: 700;
  font-size: var(--fs-body-s);
}
.nav-sheet .theme-toggle .theme-toggle-text { display: inline; }
.theme-toggle-text { display: none; }

@media (prefers-reduced-motion: reduce) {
  .theme-toggle, .theme-toggle:active { transition: none; transform: none; }
}


/* ═══════════════════════════════════════════════════════════════════
   FEE CALCULATOR — the 1%-capped-at-$1 proof, on the homepage.

   The pricing claim is only convincing if you can push it around,
   so the calculator is the pricing section rather than an ornament
   beside it. Three parts:

     1. `.fee-calc-stage`  — the amount control (range + number,
                             two views of one value).
     2. `.fee-calc-out`    — fee / rate / recipient readout, wrapped
                             in a polite live region.
     3. `.fee-ladder`      — the cap made visible: five brackets
                             where the fee climbs, then flatlines.

   No-JS: the stage collapses to the number input (which still
   submits nothing and does nothing) and `.fee-ladder` is server-
   rendered static markup, so the cap story survives with scripting
   off, on a crawler, and in print.
   ═══════════════════════════════════════════════════════════════ */

/* Deliberately single-column: the panel it lives in is already a
   column of a page-level grid, so a media query here would size
   against the viewport rather than the panel and fold at the wrong
   moment. Stage over readout also matches how the number is read —
   set the amount, then look down for the consequence. */
.fee-calc {
  display: grid;
  gap: clamp(18px, 2vw, 26px);
  /* Clear air above the control: the compliance mouseprint sits
     directly overhead and without this the "how much?" prompt reads
     as the last line of the disclaimer. */
  margin-top: clamp(22px, 2.6vw, 34px);
}

.fee-calc-stage { min-width: 0; }
.fee-calc-legend {
  display: block;
  font-size: var(--fs-body);
  font-weight: 700;
  margin-bottom: 6px;
}

/* The amount, set in the poster grotesk so it reads as the headline
   of the panel rather than a form value. */
.fee-calc-amount {
  display: flex;
  align-items: baseline;
  gap: 4px;
  font-family: var(--font-black);
  font-size: clamp(40px, 5.6vw, 76px);
  line-height: 1;
  letter-spacing: -0.02em;
}
.fee-calc-amount input {
  font: inherit;
  color: inherit;
  width: 5ch;
  min-width: 0;
  padding: 0 2px;
  background: transparent;
  border: none;
  border-bottom: 3px solid currentColor;
  border-radius: 0;
  -moz-appearance: textfield;
  appearance: textfield;
}
.fee-calc-amount input::-webkit-outer-spin-button,
.fee-calc-amount input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.fee-calc-amount input:focus-visible { outline: 3px solid var(--ink-fixed); outline-offset: 4px; }
.fee-calc-currency { font-size: 0.42em; letter-spacing: 0.04em; }

/* Range track. Styled per-engine because there is still no single
   cross-browser slider anatomy; both ends land on the same 8px ink
   track with an amarillo fill and a 28px ink thumb (well past the
   24px AAA target-size floor, and the whole row is 44px tall). */
.fee-calc-range {
  -webkit-appearance: none;
  appearance: none;
  width: 100%;
  height: 44px;
  margin: 14px 0 6px;
  background: transparent;
  cursor: pointer;
}
.fee-calc-range:focus-visible { outline: 3px solid var(--ink-fixed); outline-offset: 4px; border-radius: var(--radius-pill); }
.fee-calc-range::-webkit-slider-runnable-track {
  height: 8px;
  border-radius: var(--radius-pill);
  border: 2px solid var(--ink-fixed);
  background:
    linear-gradient(90deg, var(--amarillo-500) var(--fee-fill, 0%), transparent var(--fee-fill, 0%));
}
.fee-calc-range::-moz-range-track {
  height: 8px;
  border-radius: var(--radius-pill);
  border: 2px solid var(--ink-fixed);
  background: transparent;
}
.fee-calc-range::-moz-range-progress {
  height: 8px;
  border-radius: var(--radius-pill);
  background: var(--amarillo-500);
}
.fee-calc-range::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 28px;
  height: 28px;
  margin-top: -12px;
  border-radius: var(--radius-pill);
  background: var(--ink-fixed);
  border: 3px solid var(--paper-fixed);
  box-shadow: 0 0 0 2px var(--ink-fixed);
}
.fee-calc-range::-moz-range-thumb {
  width: 28px;
  height: 28px;
  border-radius: var(--radius-pill);
  background: var(--ink-fixed);
  border: 3px solid var(--paper-fixed);
  box-shadow: 0 0 0 2px var(--ink-fixed);
}
.fee-calc-scale {
  display: flex;
  justify-content: space-between;
  font-size: var(--fs-caption);
  font-weight: 600;
  opacity: 0.75;
}

/* Readout rows. `tnum` is already on for the body font, so the
   numbers don't jitter as the slider moves. */
.fee-calc-out {
  display: grid;
  gap: 2px;
  border: 2px solid var(--ink-fixed);
  border-radius: var(--panel-radius-sm);
  overflow: hidden;
  background: var(--ink-fixed);
}
.fee-calc-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px;
  padding: 14px 18px;
  background: var(--verde-500);
  font-size: var(--fs-body-s);
  font-weight: 600;
}
.fee-calc-row dt { min-width: 0; }
.fee-calc-row dd {
  margin: 0;
  font-variant-numeric: tabular-nums;
  font-weight: 700;
  white-space: nowrap;
}
.fee-calc-row--fee { background: var(--amarillo-500); }
.fee-calc-row--total { background: var(--paper-fixed); padding: 18px; }
.fee-calc-row--total dt { font-size: var(--fs-body); font-weight: 700; }
.fee-calc-row--total dd {
  font-family: var(--font-black);
  font-size: clamp(24px, 2.6vw, 36px);
  line-height: 1;
}
.fee-calc-cap {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-left: 8px;
  padding: 3px 9px;
  border: 2px solid var(--ink-fixed);
  border-radius: var(--radius-pill);
  background: var(--paper-fixed);
  font-family: var(--font-body);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  white-space: nowrap;
}
/* The cap badge is meaning, not decoration — it must not be the
   only channel, so the fee row also restates it in the label copy
   (WCAG 1.4.1). Hidden until the cap actually binds. */
.fee-calc-cap[hidden] { display: none; }
.fee-calc-note { font-size: var(--fs-caption); line-height: 1.55; margin-top: 12px; }

/* ---------- Region switcher ----------
   Sending region decides the currency and the fee cap. English is
   locked to USD in scripts/region.js, so this control only ever
   appears on the Spanish pages, where it exists for the Los Angeles
   Spanish speaker who sends dollars rather than pesos. The guess is
   scripts/region.js's; this lets the visitor correct it, and the
   correction wins over the sensor for good. */
/* `display: inline-flex` below would otherwise defeat the `hidden`
   attribute the script sets on a locked (English) page. */
.region-switch[hidden] { display: none; }
.region-switch {
  display: inline-flex;
  align-items: stretch;
  gap: 0;
  margin-bottom: 18px;
  border: 2px solid var(--ink-fixed);
  border-radius: var(--radius-pill);
  overflow: hidden;
  background: var(--paper-fixed);
}
.region-switch-label {
  display: flex;
  align-items: center;
  padding: 0 14px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--ink-fixed);
  border-right: 2px solid var(--ink-fixed);
  white-space: nowrap;
}
.region-switch button {
  padding: 9px 16px;
  font-size: var(--fs-body-s);
  font-weight: 700;
  color: var(--ink-fixed);
  background: transparent;
  border: none;
  cursor: pointer;
  transition: background-color var(--dur-fast) var(--ease-standard);
}
.region-switch button:hover { background: rgba(11, 11, 11, 0.08); }
.region-switch button[aria-pressed="true"] {
  background: var(--ink-fixed);
  color: var(--paper-fixed);
}
.region-switch button:focus-visible {
  outline: 3px solid var(--ink-fixed);
  outline-offset: -3px;
}
@media (prefers-reduced-motion: reduce) {
  .region-switch button { transition: none; }
}

/* ---------- Region-only copy ----------
   Sentences that change by more than a number — the fine print, the
   FAQ answer, the corridor eyebrow — are authored once per region and
   selected here rather than assembled in JavaScript. The page stamps
   its default region on <html> at author time, so the correct variant
   is the one that renders with JavaScript off, in print, and for a
   crawler; scripts/region.js only ever changes the attribute when the
   visitor works the switcher.

   Hiding the losing variant (rather than showing the winner) keeps
   each element's natural display type, which matters because these
   are <tr>, <span> and <p> alike. */
html[data-region="us"] [data-region-only="mx"],
html[data-region="mx"] [data-region-only="us"] {
  display: none;
}

/* ---------- Fee ladder: the cap, drawn ---------- */

.fee-ladder {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0;
  border: 2px solid var(--ink-fixed);
  border-radius: var(--panel-radius-sm);
  overflow: hidden;
  background: var(--paper-fixed);
  color: var(--ink-fixed);
  font-variant-numeric: tabular-nums;
}
.fee-ladder caption {
  caption-side: top;
  text-align: left;
  font-size: var(--fs-body-s);
  font-weight: 700;
  padding-bottom: 10px;
}
.fee-ladder th, .fee-ladder td {
  padding: 11px 16px;
  text-align: left;
  font-size: var(--fs-body-s);
  border-bottom: 2px solid var(--ink-fixed);
}
.fee-ladder thead th {
  background: var(--ink-fixed);
  color: var(--paper-fixed);
  font-size: var(--fs-caption);
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
.fee-ladder tbody tr:last-child th,
.fee-ladder tbody tr:last-child td { border-bottom: none; }
.fee-ladder tbody th { font-weight: 700; }
.fee-ladder td:last-child { text-align: right; font-weight: 700; }
/* Capped rows get the amarillo wash + an explicit "capped" word in
   the cell, never color alone. */
.fee-ladder tr[data-capped] td { background: var(--amarillo-500); }
.fee-ladder .fee-ladder-tag {
  display: block;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  opacity: 0.78;
}
/* The row the slider is currently sitting in. */
.fee-ladder tr[aria-current="true"] th,
.fee-ladder tr[aria-current="true"] td {
  box-shadow: inset 4px 0 0 var(--rosa-500);
}


/* ═══════════════════════════════════════════════════════════════════
   ROADMAP TIMELINE — three phases as a hand of Lotería cards.

   Desktop lays the phases out along a single ink rule with a marker
   per phase; mobile stacks them and the rule becomes vertical. The
   status of each phase is carried by a worded pill, never by the
   card's fill alone.
   ═══════════════════════════════════════════════════════════════ */

.roadmap {
  display: grid;
  gap: var(--panel-gap);
  position: relative;
}
@media (min-width: 900px) {
  .roadmap { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

.roadmap-phase {
  display: flex;
  flex-direction: column;
  gap: 14px;
  /* `--panel-pad`, not a rung of its own: these are full content cards
     sitting in the same stack as the `.ed-panel` cards above and below,
     and a 2.6vw rung put their copy 15px left of everything else. */
  padding: var(--panel-pad);
  border: var(--panel-border);
  border-radius: var(--panel-radius);
  background: var(--hueso);
  min-width: 0;
}
.roadmap-phase--now { background: var(--verde-500); color: var(--ink-fixed); }
.roadmap-phase--next { background: var(--blanco); }
.roadmap-phase--later { background: var(--hueso); }
.roadmap-phase--now a:not(.btn) { color: var(--ink-fixed); }

.roadmap-marker {
  display: flex;
  align-items: center;
  gap: 10px;
}
.roadmap-num {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 42px;
  height: 42px;
  flex-shrink: 0;
  border: 2px solid currentColor;
  border-radius: var(--radius-pill);
  font-family: var(--font-black);
  font-size: 16px;
}
.roadmap-status {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 12px;
  border: 2px solid currentColor;
  border-radius: var(--radius-pill);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
.roadmap-phase h3 {
  font-family: var(--font-black);
  font-size: clamp(22px, 2.2vw, 30px);
  line-height: 1.08;
}
.roadmap-goal { font-size: var(--fs-body-s); font-weight: 600; }
.roadmap-list {
  display: grid;
  gap: 9px;
  font-size: var(--fs-body-s);
  line-height: 1.45;
}
/* The bullet is absolutely positioned rather than a flex sibling. As a
   flex row every inline child of the item became its own flex item and
   took the 10px gap, so "<strong>Pesito Lux</strong>, rewards" rendered
   as "Pesito Lux , rewards" with a hole before the comma. This is the
   same trap `.faq-item summary` documents above. Normal inline flow
   keeps the markup's own spacing. */
.roadmap-list li {
  position: relative;
  padding-left: 19px;
}
/* Bullet is a drawn circle, not a text glyph, so it never gets read out
   and never inherits a font that lacks it. */
.roadmap-list li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 7px;
  width: 9px;
  height: 9px;
  border: 2px solid currentColor;
  border-radius: 50%;
}
.roadmap-metric {
  margin-top: auto;
  padding-top: 14px;
  border-top: 2px solid currentColor;
  font-size: var(--fs-caption);
  line-height: 1.5;
}
.roadmap-metric strong { display: block; margin-bottom: 2px; }


/* ═══════════════════════════════════════════════════════════════════
   TRUST SIGNAL GRID — encryption / receipts / disclosure / support.
   A quieter sibling of `.ed-sec-badges` for use on light panels.
   ═══════════════════════════════════════════════════════════════ */

.trust-grid {
  display: grid;
  gap: var(--panel-gap);
  grid-template-columns: repeat(auto-fit, minmax(min(230px, 100%), 1fr));
}
.trust-signal {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 18px 20px;
  border: 2px solid var(--frame-ink);
  border-radius: var(--panel-radius-sm);
  background: var(--blanco);
  min-width: 0;
}
.trust-signal svg { color: var(--accent-verde); }
.trust-signal h3 { font-size: var(--fs-body); font-weight: 700; }
.trust-signal p { font-size: var(--fs-body-s); line-height: 1.5; color: var(--tinta-700); }


/* ═══════════════════════════════════════════════════════════════════
   ASSET + CHROME POLISH — 2026-08 review pass.

   Four things in this file predate the Lotería editorial redesign and
   still wore the old Liquid Glass skin: the store badges, the security
   badges, the stat chips and the app-showcase device art. On cream
   they merely looked like a different era; on ink stock they broke
   outright, because every one of them hard-coded a white translucent
   surface and then set ink-colored type on top of it.

   The fix in all four cases is the same: stop approximating a surface
   with `rgba(255,255,255,…)` + `backdrop-filter`, and use the flat
   fill + ink outline the rest of the system uses.
   ═══════════════════════════════════════════════════════════════ */

/* ---------- Store badges ----------
   Was: 55%-white glass with `--tinta-900` type. In dark mode the
   translucent white sat over near-black as a muddy grey while the
   type flipped to bone — grey-on-grey, ~1.6:1, the worst contrast on
   the page. Now a flat editorial pill that carries the real Apple and
   Google Play glyphs instead of relying on the store's name in text. */
.store-badge {
  flex-direction: row;
  align-items: center;
  gap: 10px;
  padding: 9px 18px;
  border-radius: var(--radius-pill);
  border: 2px solid var(--frame-ink);
  background: var(--blanco);
  color: var(--tinta-900);
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
  box-shadow: none;
}
/* The legacy iridescent sheen rode on these pseudos. It was tuned for
   a glass surface and smears on a flat one. */
.store-badge::before,
.store-badge::after { content: none; }
.store-badge:hover {
  background: var(--blanco);
  box-shadow: var(--card-lift);
}
.store-badge-glyph {
  width: 22px;
  height: 22px;
  flex-shrink: 0;
  color: var(--tinta-900);
}
.store-badge-copy {
  display: flex;
  flex-direction: column;
  line-height: 1.15;
  min-width: 0;
}
.store-badge span {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.01em;
  color: var(--tinta-500);
}
.store-badge strong {
  font-size: 15px;
  font-weight: 700;
  color: var(--tinta-900);
  letter-spacing: -0.005em;
  white-space: nowrap;
}
/* Inside the loud verde-vivo panel the pill keeps a bright fill in
   both themes, so its contents pin to fixed ink. */
.ed-panel--verde-vivo .store-badge {
  background: var(--paper-fixed);
  border-color: var(--ink-fixed);
}
.ed-panel--verde-vivo .store-badge strong,
.ed-panel--verde-vivo .store-badge .store-badge-glyph { color: var(--ink-fixed); }
.ed-panel--verde-vivo .store-badge span { color: #55555C; }

/* ---------- Security badges ----------
   Was: luminous glass card + a glossy green gradient chip behind each
   icon, with a green drop-glow. That chip is the single most
   "generated" -looking element on the site and it reads as a different
   product from the trust grid three sections below, which uses a plain
   stroked glyph. Same card, same glyph treatment, both places now. */
.security-badge {
  background: var(--blanco);
  border: 2px solid var(--frame-ink);
  border-radius: var(--panel-radius-sm);
  padding: 18px 20px;
  gap: 12px;
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
  box-shadow: none;
}
.security-badge::before { display: none; }
.security-badge:hover {
  background: var(--blanco);
  transform: none;
  box-shadow: var(--card-lift);
}
.security-badge-icon {
  width: 24px;
  height: 24px;
  padding: 0;
  border-radius: 0;
  background: none;
  box-shadow: none;
  color: var(--accent-verde);
}
.security-badge-text { font-size: var(--fs-body-s); }

/* ---------- Stat chips ----------
   Was: value and label side by side on a shared baseline. That reads
   fine when the value is "1%" and falls apart when it is "No markup"
   or "Fee + rate" — the phrase eats half the chip and squeezes the
   label into a four-line ribbon. Stacking makes every chip the same
   shape regardless of how long its value is, and the grid gives the
   row equal columns instead of flex-basis guesswork. */
.ed-chips {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(220px, 100%), 1fr));
  gap: var(--panel-gap);
}
.ed-chip {
  flex-direction: column;
  align-items: flex-start;
  gap: 8px;
  padding: 18px 20px;
}
.ed-chip-num {
  font-size: clamp(28px, 2.8vw, 40px);
  /* A numeric value must never break; a worded one must be allowed to. */
  white-space: normal;
  text-wrap: balance;
}
.ed-chip-label {
  max-width: none;
  font-size: var(--fs-body-s);
  line-height: 1.35;
}

/* ═══════════════════════════════════════════════════════════════════
   INVESTOR BLOCK — lives inside the roadmap page (2026-08).

   The investor page was merged into the roadmap because they were
   answering the same question from two directions: "where is this
   going" for a customer and "where is this going" for a cheque. One
   page, one narrative, one URL to send.

   Every number in this block is sourced inline. A stat without a
   citation is a liability in an investor conversation, so the source
   line is part of the component rather than a footnote.
   ═══════════════════════════════════════════════════════════════ */

.inv-metrics {
  display: grid;
  gap: var(--panel-gap);
  grid-template-columns: repeat(auto-fit, minmax(min(240px, 100%), 1fr));
}
.inv-metric {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 20px 22px;
  border: 2px solid var(--frame-ink);
  border-radius: var(--panel-radius-sm);
  background: var(--blanco);
  min-width: 0;
}
.inv-metric-value {
  font-family: var(--font-black);
  font-size: clamp(28px, 3vw, 42px);
  line-height: 1;
  letter-spacing: -0.02em;
}
.inv-metric-label { font-size: var(--fs-body-s); font-weight: 600; line-height: 1.4; }
.inv-metric-source {
  margin-top: auto;
  padding-top: 8px;
  font-size: 11px;
  line-height: 1.45;
  color: var(--tinta-500);
}

/* ---------- The market-size ladder ----------
   TAM → SAM → per-transaction reality, as one continuous argument
   rather than three disconnected numbers. The last rung is
   deliberately the least flattering one; see the copy. */
.inv-ladder {
  display: grid;
  gap: 0;
  border: 2px solid var(--ink-fixed);
  border-radius: var(--panel-radius-sm);
  overflow: hidden;
  counter-reset: rung;
}
.inv-rung {
  display: grid;
  gap: 4px;
  padding: 20px 22px;
  border-bottom: 2px solid var(--ink-fixed);
  background: var(--paper-fixed);
  color: var(--ink-fixed);
}
.inv-rung:last-child { border-bottom: none; }
.inv-rung--tam { background: var(--verde-500); }
.inv-rung--sam { background: var(--amarillo-500); }
/* Fourth rung, added for the expansion ladder on the investor section.
   Sits between the two saturated rungs and the plain closing rung so a
   four-step ladder still reads as a descent rather than two pairs. */
.inv-rung--wallet { background: var(--verde-300); }
.inv-rung--unit { background: var(--paper-fixed); }
.inv-rung-key {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}
.inv-rung-value {
  font-family: var(--font-black);
  font-size: clamp(26px, 2.8vw, 38px);
  line-height: 1.05;
}
.inv-rung-note { font-size: var(--fs-body-s); line-height: 1.5; max-width: 62ch; }
.inv-rung-math {
  font-family: var(--font-mono);
  font-size: 12px;
  line-height: 1.6;
  opacity: 0.85;
}

/* ---------- Numbered "why now" list ---------- */
.inv-now { display: grid; gap: var(--panel-gap); counter-reset: inv-now; }
.inv-now > li {
  counter-increment: inv-now;
  display: grid;
  grid-template-columns: 44px minmax(0, 1fr);
  gap: 16px;
  align-items: start;
  padding: 20px 22px;
  border: 2px solid var(--frame-ink);
  border-radius: var(--panel-radius-sm);
  background: var(--hueso);
}
.inv-now > li::before {
  content: counter(inv-now, decimal-leading-zero);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  border: 2px solid currentColor;
  border-radius: var(--radius-pill);
  font-family: var(--font-black);
  font-size: 15px;
}
.inv-now h4 { font-size: var(--fs-body-l); font-weight: 700; margin-bottom: 6px; }
.inv-now p { font-size: var(--fs-body-s); line-height: 1.55; }
.inv-now cite {
  display: block;
  margin-top: 8px;
  font-size: 11px;
  font-style: normal;
  color: var(--tinta-500);
}

/* ---------- Book-a-demo ---------- */
.inv-demo-mail {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  margin-top: 18px;
  padding: 14px 22px;
  border: 2px solid var(--ink-fixed);
  border-radius: var(--radius-pill);
  background: var(--paper-fixed);
  color: var(--ink-fixed);
  font-family: var(--font-mono);
  font-size: var(--fs-body);
  font-weight: 700;
  text-decoration: none;
  word-break: break-all;
}
.inv-demo-mail:hover { box-shadow: var(--card-lift); }

/* ---------- App-showcase device art ----------
   Was: a bare flat SVG floating on the panel, which read as a
   screenshot rather than a phone. The SVGs are the screen content
   only, so the hardware goes around them in CSS: a machined bezel, a
   Dynamic Island and a side button. Nothing here is theme-dependent —
   a phone is the same object with the lights off. */
.device-frame {
  position: relative;
  display: block;
  width: 100%;
  max-width: 280px;
  margin: 0 auto;
  padding: 11px;
  border-radius: 48px;
  background:
    linear-gradient(150deg, #4a4a52 0%, #17171b 38%, #0e0e11 62%, #3a3a42 100%);
  box-shadow:
    inset 0 0 0 1.5px rgba(255, 255, 255, 0.22),
    inset 0 0 0 6px #0d0d10,
    0 26px 52px -20px rgba(0, 0, 0, 0.65);
}
/* The screen. `.device-shot` is a <picture> wrapping the real app
   screenshot; the frame supplies the hardware around it. */
.device-frame .device-shot { display: block; }
.device-frame .device-shot img {
  display: block;
  width: 100%;
  max-width: none;
  height: auto;
  border: none;
  border-radius: 38px;
  /* The screenshots are captured at the device's own aspect ratio, so
     `contain` is the honest fit — `cover` would crop the status bar or
     the tab bar depending on the container. */
  object-fit: contain;
  background: #000;
  box-shadow: none;
}

/* Region-aware home screen. There are two real captures of the same
   screen — one holding MXN, one holding USD — and which one is true
   for a visitor depends on where they are sending from. Each page sets
   its own default in a page-scoped <style> so this still resolves with
   JavaScript disabled; `data-region` from scripts/region.js overrides
   it once resolved, and it outranks the page default on specificity
   rather than source order. */
html[data-region="us"] .device-shot--mxn { display: none; }
html[data-region="us"] .device-shot--usd { display: block; }
html[data-region="mx"] .device-shot--mxn { display: block; }
html[data-region="mx"] .device-shot--usd { display: none; }
/* ---------- Product-preview clips (homepage showcases) ----------
   These clips arrive as ProRes 4444 with a real alpha channel, but
   Safari ignores alpha in WebM and only macOS can encode HEVC-with
   -alpha, so shipping transparency would hand every Safari visitor a
   black box. Instead the alpha is flattened onto `--tinta-900`'s
   light value at encode time and the corners are clipped here: on the
   light theme the stage is the same colour as the panel and vanishes,
   and on the dark theme it reads as the phone's own body, the way
   `.device-frame` does for the static mockups above.

   The phone is rendered INTO the clip, so no CSS bezel wraps it. The
   radius is expressed per-axis in percentages so one value tracks the
   phone's own corner (~16% of its width) at any rendered size. */
.showcase-stage {
  display: block;
  width: 100%;
  max-width: 300px;
  margin: 0 auto;
  border-radius: 16% / 7.7%;
  overflow: hidden;
  background: #0B0B0B;
}
.showcase-video {
  display: block;
  width: 100%;
  height: auto;
}

/* Dynamic Island. Sits 10px into the screen, clear of the topmost
   label in every mockup (the SVGs start their content at y=50). */
.device-frame::after {
  content: "";
  position: absolute;
  top: 21px;
  left: 50%;
  transform: translateX(-50%);
  width: 82px;
  height: 23px;
  border-radius: var(--radius-pill);
  background: #08080a;
  z-index: 2;
  pointer-events: none;
}
/* Side button — the detail that stops the bezel reading as a border. */
.device-frame::before {
  content: "";
  position: absolute;
  right: -2px;
  top: 132px;
  width: 3px;
  height: 58px;
  border-radius: 0 2px 2px 0;
  background: linear-gradient(180deg, #55555e 0%, #23232a 100%);
}
