/* ==========================================================================
   U Jabka — Soda design system: TOKENS
   Dark system derived from the approved reference (ujabka-3D-design-soda
   @ 864f914); visual values now come from the Vesper transplant blueprint.

   MIGRATION SEAM: scoped under `html.theme-soda`; pages opt in via $htmlClass.
   Nothing is on :root — that would silently re-colour un-migrated pages.

   TWO INDEPENDENT STATE SYSTEMS, and keeping them apart is the core contract:

   1. SURFACE THEME [data-theme="dark"] on <html> is fixed at first server
      render. Static contrast owns surfaces, text, borders, fields and focus;
      old light/auto preferences and system colour changes cannot restore them.

   2. PRODUCT MOOD   [data-mood="iphone"|"macbook"|"watch"] on <html> — accent
      identity ONLY: --accent / --accent-ink / --halo / --bg-* and their -rgb
      triplets, rewritten at runtime by the hero controller. Mood may NEVER
      carry text contrast: it appears as accents, glows, borders and fills
      behind non-text. It never decides dark or light.

   The fixed dark surface must read well with every product mood.
   Because contrast lives in the static layer, any mood passes WCAG.

   COMPONENTS CONSUME SEMANTIC NAMES, never raw palette values and never a
   category colour. The legacy aliases near the end keep existing CSS working.
   ========================================================================== */

/* Self-hosted webfonts, vendored by tools/vendor-fonts.mjs. No runtime Google
   Fonts request: the CSP is `font-src 'self'`, and a CDN font is also a GDPR and
   latency problem. General Sans is NOT subsetted — one ~23 KB file per weight
   already carries the full Czech diacritic set, so there is no latin/latin-ext
   split and nothing to preload wrongly. Licence: Fontshare Terms, not OFL; see
   NOTICE-GeneralSans.txt beside the fonts. */
@font-face {
    font-family: 'General Sans';
    font-style: normal;
    font-weight: 300;
    font-display: swap;
    src: url('../../fonts/soda/general-sans-300.woff2') format('woff2');
}

@font-face {
    font-family: 'General Sans';
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url('../../fonts/soda/general-sans-400.woff2') format('woff2');
}

@font-face {
    font-family: 'General Sans';
    font-style: normal;
    font-weight: 500;
    font-display: swap;
    src: url('../../fonts/soda/general-sans-500.woff2') format('woff2');
}

html.theme-soda {
    /* ------------------------------------------------- structural (themeless) */

    /* BLUR — 8px. Vesper blurs two surfaces only: header bar, footer bar. */
    --blur-glass: 8px;
    --blur-lg: var(--blur-glass);
    --blur-md: var(--blur-glass);
    --blur-sm: var(--blur-glass);

    /* RADIUS — a restrained scale, named by ROLE rather than by size, so a
       component declares what it is and inherits the corner that role gets.
       There is no literal radius value anywhere else in the system.

       Vesper's own landing page is completely square. U Jabka is not Vesper: it
       sells physical objects with rounded corners, and absolute zero read as
       harsh next to them. So the geometry softens by a few pixels and stops
       there — hairlines, spacing, typography and the dark/light plane
       composition are untouched, and nothing here approaches the 20-30px card
       the old system used.

       The tiers, and what each one is for:

         --r-plane    12px  major white planes and large editorial containers
                            (.soda-plane / .soda-card-w / a page section's
                            container, .soda-panel, .soda-empty)
         --r-card     10px  product cards and product MEDIA — the objects a
                            person clicks (.soda-card, .soda-cat-card,
                            .soda-prod__item, .soda-catalog-card, the detail
                            gallery stage, .soda-acc, .soda-pg-card/-step/-toc)
         --r-control   7px  things you operate or that sit inside a card:
                            buttons, inputs, selects, tiles, thumbnails,
                            filter options, nav items, icon buttons
         --r-mark      4px  marks smaller than a control — swatches, dots,
                            status chips, glyph boxes, grade letters
         --r-pill    999px  ONLY where the capsule shape carries the meaning:
                            the battery gauge and its fill, and the cart count.
                            Buttons and status chips deliberately do NOT use it
         --r-none        0  deliberately square, and the value every component
                            takes when it is flattened into a plane and becomes
                            a ruled region rather than a box

       The one rule that keeps this from drifting: a nested shape is never
       rounder than the shape containing it. */
    --r-plane: 12px;
    --r-card: 10px;
    --r-control: 7px;
    --r-mark: 4px;
    --r-pill: 999px;
    --r-none: 0;

    /* VESPER GROUND — owner decision 2026-08: the reference's deep-violet void
       is REJECTED; the ground is premium deep black (#0b0b0c, the near-black
       BRAND-SPEC.md §A recommends under the mark). Still themeless on purpose:
       the hero and the plane the white cards float over are this ground, not a
       surface theme, and the product mood must never repaint them. These are
       the ONLY three colours the composition uses besides hairline alphas. */
    --vesper-void: #0b0b0c;
    --vesper-chalk: #fdfdfd;
    --vesper-signal: #6cf3a3;

    /* SHADOW — none. Depth is a hairline and an opaque plane, never a cast. */
    --shadow-nav: none;
    --shadow-card: none;

    /* THE FRAME — held by the viewport edge. 1440 base; vw = Figma px / 14.4. */
    --gutter: 1.667vw;
    --container: 96.667vw;
    --section-gap: clamp(4.5rem, 8vw, 8rem);
    --gap: clamp(1rem, 1.4vw, 1.4rem);

    /* HEADER — fixed; content clears it via --header-band. Static, not
       JS-measured: layout keyed to a measurement is wrong for one frame.
       Geometry follows the ported Dalim navbar: .5rem top offset (mt-2),
       .5rem row padding each side of the 2.1rem lockup. */
    --header-top: .5rem;
    --header-h: 3.1rem;
    --page-intro-space: clamp(2.2rem, 4vw, 3.4rem);
    --header-band: calc(var(--header-h) + var(--header-top) * 2);

    /* TYPE SCALE — four sizes, and that is the whole scale. */
    --text-display: 5.556vw;   /* 80px — every h1/h2 */
    --text-stat: 2.222vw;      /* 32px — stat values, prices */
    /*
     * READABILITY FLOOR — a deliberate U Jabka deviation from Vesper.
     *
     * Vesper authors body copy at a bare 1.111vw, which is 11.4px at 1024 and
     * 12.8px at 1152. It gets away with that because it is a one-screen
     * showcase; a storefront asks people to read specs, prices and availability
     * at those widths. The vw term still does the artboard scaling, the px term
     * stops it going below readable. At 1440 both agree on exactly 16px, so the
     * design base is unchanged.
     */
    --text-body: max(15px, 1.111vw);   /* 16px @1440, never under 15 */
    --text-small: max(14px, 0.972vw);  /* 14px @1440, and never under it */

    --leading-title: .90;
    --leading-body: 1.2;
    --tracking-title: -.022em;

    /* ONE face. Vesper sets General Sans on every title, body, header and CTA;
       both names are kept because the component layer reads both. */
    --font-display: 'General Sans', system-ui, -apple-system, 'Segoe UI', sans-serif;
    --font-main: 'General Sans', system-ui, -apple-system, 'Segoe UI', sans-serif;

    /* MOTION — CSS standing in for Vesper's springs (no spring lib may be
       imported). easeOutQuint; press/hover/reveal. Nothing overshoots. */
    --ease: cubic-bezier(.23, 1, .32, 1);
    --ease-spring: var(--ease);
    --dur-fast: .09s;
    --dur: .18s;
    --dur-slow: 1s;

    --focus-ring: 2px;
    --focus-offset: 3px;

    /* Hero wash. Mood-driven and deliberately dark in BOTH themes: the hero is
       the one dramatic, full-bleed moment of the site. Everything below it
       calms down to the surface theme. */
    --bg-inner: #0c7a63;
    --bg-mid: #04473a;
    --bg-outer: #010f0c;
    --bg-outer-rgb: 1, 15, 12;
}

/* BELOW 1024px the vw ladder stops (Vesper ADR-0029) and every vw token above
   is restated in real rem — see the ladder in base.css. One DOM tree, two
   ranges; `rem` in a media query is always the initial 16px root. */

@media (max-width: 1023.98px) {
    html.theme-soda {
        --gutter: 1.5rem;
        --container: calc(100% - var(--gutter) * 2);

        --header-top: .5rem;
        --header-h: 3.75rem;

        /* 2.75rem is Vesper's section value; the hero may raise it locally. */
        --text-display: 2.75rem;
        --text-stat: 1.5rem;
        --text-body: .9375rem;
        --text-small: .875rem;
    }
}

@media (max-width: 640px) {
    html.theme-soda {
        --gutter: 1.25rem;

        --header-top: .5rem;

        --text-display: 2.125rem;
        --text-stat: 1.25rem;
        /* Vesper drops both to .8125rem (13px) here; we hold 14px for the same
           readability reason as the vw floor above. The product facts (spec,
           condition, battery, availability) use --text-small and are real
           content, not fine print, so nothing on a phone falls under 14px. */
        --text-body: .875rem;
        --text-small: .875rem;
    }
}

/* ============================================================== DARK SURFACE */

html.theme-soda[data-theme="dark"] {
    color-scheme: dark;

    --page-bg: #05070a;
    --surface-1: rgba(255, 255, 255, .035);
    --surface-2: rgba(255, 255, 255, .07);
    --surface-glass: rgba(255, 255, 255, .05);
    --surface-nav: rgba(255, 255, 255, .075);

    --ink: #ffffff;
    --ink-muted: rgba(255, 255, 255, .66);   /* 8.74:1 on --page-bg */
    /*
     * --ink-faint is .55, not the reference's .40 — a deliberate, measured
     * deviation. .40 composites to #696a6c over --page-bg, which is 3.73:1 and
     * below the 4.5:1 AA floor. The token is not decoration-only: shared
     * components use it for card spec lines, notes, breadcrumbs, empty states,
     * hints and placeholders. .55 measures 6.26:1 and stays visibly quieter
     * than --ink-muted, so the three-step ink hierarchy survives.
     */
    --ink-faint: rgba(255, 255, 255, .55);
    --ink-soft: rgba(255, 255, 255, .78);

    --line: rgba(255, 255, 255, .09);
    --line-hi: rgba(255, 255, 255, .16);
    --line-glass: rgba(255, 255, 255, .12);
    --line-nav: rgba(255, 255, 255, .18);

    --field-bg: rgba(0, 0, 0, .28);
    /* Native <option> popups do not reliably composite translucent fields. */
    --select-option-bg: #12161c;
    --select-option-ink: #ffffff;

    /* Interactive chrome. These were hardcoded in the component layer, which is
       why the light theme inherited a dark primary button, a dark sticky header
       and an invisible nav hover. */
    --btn-bg: rgba(0, 0, 0, .42);
    --btn-bg-hover: rgba(0, 0, 0, .68);
    --btn-border: rgba(255, 255, 255, .14);
    --hover-wash: rgba(255, 255, 255, .07);
    /* --header-scrolled-bg is gone from all four theme branches: the header bar
       is themeless Vesper dark (shell.css) and was its only consumer. */
    /*
     * Top-of-page wash: the colour the body fades FROM.
     *
     * On the dark ground this continues the hero's darkest mood colour, so the
     * seam under the hero is invisible. It must never be a mood colour on a
     * light ground — see the light branch.
     */
    --page-top-wash: var(--bg-outer);

    --success: #6ee7b7;
    --success-ink: #04231a;
    --warning: #fcd34d;
    --warning-ink: #2a1f02;
    --error: #fca5a5;
    --error-ink: #2c0b0b;

    --focus: #ffffff;

}

/* ============================================================= LIGHT SURFACE */

html.theme-soda[data-theme="light"] {
    color-scheme: light;

    /* Warm ivory, not harsh white — the light theme is meant to read as a
       deliberate second skin of the same design, not as an inversion. */
    --page-bg: #f4f1ea;
    --surface-1: rgba(255, 255, 255, .72);
    --surface-2: #ffffff;
    --surface-glass: rgba(255, 255, 255, .62);
    --surface-nav: rgba(255, 255, 255, .78);

    --ink: #14161a;                          /* 16.06:1 on --page-bg */
    --ink-muted: rgba(20, 22, 26, .72);      /* 6.79:1 */
    --ink-faint: rgba(20, 22, 26, .62);      /* 4.85:1 */
    --ink-soft: rgba(20, 22, 26, .55);

    --line: rgba(20, 22, 26, .12);
    --line-hi: rgba(20, 22, 26, .22);
    --line-glass: rgba(20, 22, 26, .10);
    --line-nav: rgba(20, 22, 26, .12);

    --field-bg: rgba(255, 255, 255, .9);
    --select-option-bg: #fffdf8;
    --select-option-ink: #14161a;

    /* The light primary button is a bright pill, matching the ghost button the
       design already uses (e.g. "Časté dotazy") rather than a dark slab. */
    --btn-bg: rgba(255, 255, 255, .88);
    --btn-bg-hover: #ffffff;
    --btn-border: rgba(20, 22, 26, .14);
    --hover-wash: rgba(20, 22, 26, .07);
    /*
     * NO top wash on a light ground.
     *
     * --bg-outer is a MOOD token — a near-black. Fading the body from it meant
     * every light page opened with a dark smear across its top quarter, sitting
     * directly under the header and the breadcrumbs and making that text hard to
     * read. A light page starts as the light page.
     */
    --page-top-wash: var(--page-bg);

    /* Re-picked for a light ground: the dark theme's pastels are invisible on
       ivory. Every one clears 4.5:1 against --page-bg. */
    /*
     * Deliberately darker than the minimum that clears 4.5:1 on --page-bg,
     * because these colours are also used as TEXT ON THEIR OWN 16% TINT in the
     * availability badges. Measured against that tint, not against the page:
     * the first pass picked #0f7a4f, which is 4.75:1 on ivory but only 3.84:1
     * on its own badge fill.
     */
    --success: #0b5c3b;                      /* 5.55:1 on its own tint */
    --success-ink: #ffffff;
    --warning: #6f4800;                      /* 5.59:1 on its own tint */
    --warning-ink: #ffffff;
    --error: #96201a;                        /* on its own tint, see above */
    --error-ink: #ffffff;

    --focus: #14161a;

}

/* `auto` resolves in CSS, so the server can render it with no JS and no flash. */
@media (prefers-color-scheme: dark) {
    html.theme-soda[data-theme="auto"] {
        color-scheme: dark;
        --page-bg: #05070a;
        --surface-1: rgba(255, 255, 255, .035);
        --surface-2: rgba(255, 255, 255, .07);
        --surface-glass: rgba(255, 255, 255, .05);
        --surface-nav: rgba(255, 255, 255, .075);
        --ink: #ffffff;
        --ink-muted: rgba(255, 255, 255, .66);
        --ink-faint: rgba(255, 255, 255, .55);
        --ink-soft: rgba(255, 255, 255, .78);
        --line: rgba(255, 255, 255, .09);
        --line-hi: rgba(255, 255, 255, .16);
        --line-glass: rgba(255, 255, 255, .12);
        --line-nav: rgba(255, 255, 255, .18);
        --field-bg: rgba(0, 0, 0, .28);
        --select-option-bg: #12161c;
        --select-option-ink: #ffffff;

        --btn-bg: rgba(0, 0, 0, .42);
        --btn-bg-hover: rgba(0, 0, 0, .68);
        --btn-border: rgba(255, 255, 255, .14);
        --hover-wash: rgba(255, 255, 255, .07);
        --page-top-wash: var(--bg-outer);
        --success: #6ee7b7;
        --success-ink: #04231a;
        --warning: #fcd34d;
        --warning-ink: #2a1f02;
        --error: #fca5a5;
        --error-ink: #2c0b0b;
        --focus: #ffffff;
    }
}

@media (prefers-color-scheme: light) {
    html.theme-soda[data-theme="auto"] {
        color-scheme: light;
        --page-bg: #f4f1ea;
        --surface-1: rgba(255, 255, 255, .72);
        --surface-2: #ffffff;
        --surface-glass: rgba(255, 255, 255, .62);
        --surface-nav: rgba(255, 255, 255, .78);
        --ink: #14161a;
        --ink-muted: rgba(20, 22, 26, .72);
        --ink-faint: rgba(20, 22, 26, .62);
        --ink-soft: rgba(20, 22, 26, .55);
        --line: rgba(20, 22, 26, .12);
        --line-hi: rgba(20, 22, 26, .22);
        --line-glass: rgba(20, 22, 26, .10);
        --line-nav: rgba(20, 22, 26, .12);
        --field-bg: rgba(255, 255, 255, .9);
        --select-option-bg: #fffdf8;
        --select-option-ink: #14161a;

        --btn-bg: rgba(255, 255, 255, .88);
        --btn-bg-hover: #ffffff;
        --btn-border: rgba(20, 22, 26, .14);
        --hover-wash: rgba(20, 22, 26, .07);
        --page-top-wash: var(--page-bg);
        --success: #0b5c3b;
        --success-ink: #ffffff;
        --warning: #6f4800;
        --warning-ink: #ffffff;
        --error: #96201a;
        --error-ink: #ffffff;
        --focus: #14161a;
    }
}

/* ================================================================ PRODUCT MOOD
   Accent identity only. Each mood ships a dark-ground and a light-ground accent
   because the dark theme's pastels vanish on ivory and the light theme's deep
   tones vanish on near-black. The HUE is the constant; the theme picks the
   lightness. Hero wash values are shared by both themes.
   ============================================================================ */

html.theme-soda[data-mood="watch"] {
    --accent: #a9f2d1;
    --accent-rgb: 169, 242, 209;
    --accent-ink: #02201a;
    --halo: #17c79a;
    --halo-rgb: 23, 199, 154;
    --bg-inner: #0c7a63;
    --bg-mid: #04473a;
    --bg-outer: #010f0c;
    --bg-outer-rgb: 1, 15, 12;
}

html.theme-soda[data-mood="macbook"] {
    --accent: #c3d6ff;
    --accent-rgb: 195, 214, 255;
    --accent-ink: #08122e;
    --halo: #4b7bff;
    --halo-rgb: 75, 123, 255;
    --bg-inner: #2b4dae;
    --bg-mid: #111f4d;
    --bg-outer: #04060f;
    --bg-outer-rgb: 4, 6, 15;
}

html.theme-soda[data-mood="iphone"] {
    --accent: #ffd3ab;
    --accent-rgb: 255, 211, 171;
    --accent-ink: #2a1105;
    --halo: #ff8f42;
    --halo-rgb: 255, 143, 66;
    --bg-inner: #c4661f;
    --bg-mid: #57250c;
    --bg-outer: #150804;
    --bg-outer-rgb: 21, 8, 4;
}

/* Light-ground accents. Same hue, re-picked for contrast on ivory; each clears
   4.5:1 against --page-bg and carries white as its ink. */
html.theme-soda[data-theme="light"][data-mood="watch"] {
    --accent: #0b6b52;                       /* 5.75:1 */
    --accent-rgb: 11, 107, 82;
    --accent-ink: #ffffff;
    --halo: #0f7a5e;
    --halo-rgb: 15, 122, 94;
}

html.theme-soda[data-theme="light"][data-mood="macbook"] {
    --accent: #2b4dae;                       /* 6.71:1 */
    --accent-rgb: 43, 77, 174;
    --accent-ink: #ffffff;
    --halo: #4b7bff;
    --halo-rgb: 75, 123, 255;
}

html.theme-soda[data-theme="light"][data-mood="iphone"] {
    --accent: #9c460b;                       /* 5.65:1 */
    --accent-rgb: 156, 70, 11;
    --accent-ink: #ffffff;
    --halo: #b4530f;
    --halo-rgb: 180, 83, 15;
}

@media (prefers-color-scheme: light) {
    html.theme-soda[data-theme="auto"][data-mood="watch"] {
        --accent: #0b6b52;
        --accent-rgb: 11, 107, 82;
        --accent-ink: #ffffff;
        --halo: #0f7a5e;
        --halo-rgb: 15, 122, 94;
    }

    html.theme-soda[data-theme="auto"][data-mood="macbook"] {
        --accent: #2b4dae;
        --accent-rgb: 43, 77, 174;
        --accent-ink: #ffffff;
        --halo: #4b7bff;
        --halo-rgb: 75, 123, 255;
    }

    html.theme-soda[data-theme="auto"][data-mood="iphone"] {
        --accent: #9c460b;
        --accent-rgb: 156, 70, 11;
        --accent-ink: #ffffff;
        --halo: #b4530f;
        --halo-rgb: 180, 83, 15;
    }
}

/* ============================================================= LEGACY ALIASES
   The component layer was authored against these names. Mapping them onto the
   semantic tokens is what let the whole light theme land without touching a
   single component rule. New CSS should use the semantic names above. */

html.theme-soda {
    --panel: var(--surface-1);
    --panel-hi: var(--surface-2);
    --glass-bg: var(--surface-glass);
    --glass-border: var(--line-glass);
    --nav-glass-bg: var(--surface-nav);
    --nav-glass-border: var(--line-nav);
    --ink-2: var(--ink-muted);
    --ink-3: var(--ink-faint);

    /* Stable brand accent for surfaces that must not follow the mood. */
    --brand-accent: var(--accent);
    --brand-accent-ink: var(--accent-ink);
}

/* ================================================================= HERO SCOPE
   The hero keeps its dramatic dark mood wash in BOTH themes, so its text must
   stay light there regardless of the surface theme. Scoping the ink tokens to
   the hero is what lets a light page carry a dark hero without a single
   component override. */

html.theme-soda .soda-hero {
    --ink: #ffffff;
    --ink-muted: rgba(255, 255, 255, .66);
    --ink-faint: rgba(255, 255, 255, .55);
    --ink-soft: rgba(255, 255, 255, .78);
    --ink-2: rgba(255, 255, 255, .66);
    --ink-3: rgba(255, 255, 255, .55);
    --line: rgba(255, 255, 255, .09);
    --line-hi: rgba(255, 255, 255, .16);
    --line-glass: rgba(255, 255, 255, .12);
    --glass-border: rgba(255, 255, 255, .12);
    --panel: rgba(255, 255, 255, .035);
    --panel-hi: rgba(255, 255, 255, .07);
    --surface-1: rgba(255, 255, 255, .035);
    --surface-2: rgba(255, 255, 255, .07);
    --focus: #ffffff;

    /*
     * The hero's CTA chrome must follow the hero's ground, not the page's.
     *
     * The ink tokens above are already forced to the dark-ground set because the
     * hero keeps its mood wash in both themes. The button tokens were not, so in
     * light mode the hero CTA rendered a near-white pill carrying the hero's
     * white label — 1.0:1, invisible at rest and on hover alike.
     */
    --btn-bg: rgba(0, 0, 0, .42);
    --btn-bg-hover: rgba(0, 0, 0, .68);
    --btn-border: rgba(255, 255, 255, .14);
    --hover-wash: rgba(255, 255, 255, .07);
}

/* The hero always uses the dark-ground accent, because its ground is dark. */
html.theme-soda[data-theme="light"] .soda-hero,
html.theme-soda[data-theme="auto"] .soda-hero {
    /* Default mood is iPhone, which now wears the warm palette. */
    --accent: #ffd3ab;
    --accent-rgb: 255, 211, 171;
    --accent-ink: #2a1105;
}

html.theme-soda[data-theme="light"][data-mood="macbook"] .soda-hero,
html.theme-soda[data-theme="auto"][data-mood="macbook"] .soda-hero {
    --accent: #c3d6ff;
    --accent-rgb: 195, 214, 255;
    --accent-ink: #08122e;
}

html.theme-soda[data-theme="light"][data-mood="watch"] .soda-hero,
html.theme-soda[data-theme="auto"][data-mood="watch"] .soda-hero {
    --accent: #a9f2d1;
    --accent-rgb: 169, 242, 209;
    --accent-ink: #02201a;
}

/* ===================================== 21ST EXACT-TRANSPLANT SCALE (UX-2)

   The seven authorised 21st.dev sources are Tailwind + shadcn. Their geometry
   is expressed in Tailwind's default scale, and "port, don't redesign" means
   those numbers must survive the translation to hand-written CSS EXACTLY —
   not be rounded to the nearest Soda spacing step.

   This block is the ONE place that scale is written down. Every transplanted
   surface (checkout, payment, order-submitted, loader, tracking, catalogue
   card, product detail) consumes these names, so all seven land on the same
   numbers and a later reviewer can diff a class list against the source
   without re-deriving rem values.

   Owned by the integrating agent. Per-surface CSS may NOT redefine these —
   a surface that needs a different number is a documented deviation in
   docs/design/21ST_EXACT_TRANSPLANT.md, not a local override.

   Values are Tailwind v3 defaults; --t21-fs-* pairs carry the source's
   font-size AND its coupled line-height, which is the half of the type scale
   a naive port usually drops. */
html.theme-soda {
    /* Spacing — Tailwind numeric steps (0.25rem base). */
    --t21-1: .25rem;
    --t21-1_5: .375rem;
    --t21-2: .5rem;
    --t21-3: .75rem;
    --t21-4: 1rem;
    --t21-5: 1.25rem;
    --t21-6: 1.5rem;
    --t21-8: 2rem;
    --t21-12: 3rem;

    /* Sizing — icon/control boxes used verbatim by the sources. */
    --t21-size-2: .5rem;      /* h-2 w-2  — product-detail gallery dot */
    --t21-size-4: 1rem;       /* h-4 w-4  — small lucide icon */
    --t21-size-5: 1.25rem;    /* h-5 w-5  — medium lucide icon */
    --t21-size-6: 1.5rem;     /* h-6 w-6  — tracking marker, colour swatch */
    --t21-size-8: 2rem;       /* h-8 w-8  — stepper marker, carousel arrow */
    --t21-size-12: 3rem;      /* h-12 w-12 — success icon, avatar, CTA height */

    /* Container widths. */
    --t21-max-sm: 24rem;      /* max-w-sm — order-submitted card, product card */
    --t21-max-md: 28rem;      /* max-w-md — payment stepper, tracking column */
    --t21-max-7xl: 80rem;     /* max-w-7xl — product-detail page */

    /* Radii. */
    --t21-radius-md: .375rem; /* rounded-md */
    --t21-radius-lg: .5rem;   /* rounded-ele in the checkout source */
    --t21-radius-xl: .75rem;  /* rounded-xl */
    --t21-radius-full: 9999px;

    /* Type scale — size + the line-height Tailwind couples to it. */
    --t21-fs-xs: .75rem;      --t21-lh-xs: 1rem;
    --t21-fs-sm: .875rem;     --t21-lh-sm: 1.25rem;
    --t21-fs-base: 1rem;      --t21-lh-base: 1.5rem;
    --t21-fs-lg: 1.125rem;    --t21-lh-lg: 1.75rem;
    --t21-fs-xl: 1.25rem;     --t21-lh-xl: 1.75rem;
    --t21-fs-2xl: 1.5rem;     --t21-lh-2xl: 2rem;
    --t21-fs-3xl: 1.875rem;   --t21-lh-3xl: 2.25rem;
    --t21-fs-4xl: 2.25rem;    --t21-lh-4xl: 2.5rem;

    /* Borders. */
    --t21-border: 1px;        /* border   */
    --t21-border-2: 2px;      /* border-2 — payment-method tiles, step markers */
    --t21-hairline: 1.5px;    /* w-[1.5px] — order-tracking connector */

    /* Shadows — Tailwind's own definitions, not Soda's elevation ramp. */
    --t21-shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, .05);
    --t21-shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, .1), 0 4px 6px -4px rgba(0, 0, 0, .1);
    --t21-shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, .1), 0 8px 10px -6px rgba(0, 0, 0, .1);

    /* Motion — extracted from the sources' Framer Motion / CSS transitions.
       These are the literal source values; nothing here is a Soda default.
       `ease-in-out` and `ease-out` are the CSS keywords Framer's easeInOut /
       easeOut map to, and the spring is the critically-shaped CSS
       approximation of `{type:"spring", stiffness:100..400}` documented per
       surface in the transplant ledger. */
    --t21-dur-fast: .2s;      /* payment-popup active-circle scale */
    --t21-dur-300: .3s;       /* product-list image fade, product-detail swap */
    --t21-dur-clip: .4s;      /* loading.md clip-path, order-submitted card */
    --t21-dur-collapse: .4s;  /* payment-popup content height/opacity */
    --t21-stagger: .1s;       /* order-submitted staggerChildren */

    /* TWO CURVE FAMILIES, AND CONFLATING THEM IS A FIDELITY BUG.

       (a) The sources' EXTRACTED animation specs. loading.md writes raw CSS
           transition strings -- "clip-path 0.4s ease-in-out" and
           "opacity 0.6s ease-out" -- so the CSS keywords apply, and Framer
           Motion's named easeInOut/easeOut resolve to those same classic
           curves. These are SYMMETRIC (.42/.58); they are not Tailwind's.
       (b) Tailwind's UTILITY curve, which is what bare `transition-colors`
           and `transition-transform` compile to in checkout.md and
           product-list.md: 150ms of cubic-bezier(.4, 0, .2, 1).

       An earlier revision of this block defined (a) using (b)'s curve. Every
       ported clip, collapse, reveal and card entry therefore ran on an
       asymmetric, slow-settling curve where the source is symmetric. Keep the
       two families separate and consume the one the source actually implies. */
    --t21-ease-in-out: cubic-bezier(.42, 0, .58, 1);  /* CSS/Framer easeInOut */
    --t21-ease-out: cubic-bezier(0, 0, .58, 1);       /* CSS/Framer easeOut   */
    --t21-ease-spring: cubic-bezier(.34, 1.4, .64, 1);
    --t21-dur-tw: .15s;                               /* Tailwind transition-* */
    --t21-ease-tw: cubic-bezier(.4, 0, .2, 1);        /* Tailwind ease-in-out  */
}
