/* ==========================================================================
   VP Design System — Design Tokens
   --------------------------------------------------------------------------
   Single source of truth. Every value in components.css / sections.css /
   motion.css resolves back to a variable declared here. No raw hex, no raw
   ms, no raw cubic-bezier anywhere downstream.

   Provenance:
     - Colour ramp + radius scale are adopted VERBATIM from the Lumora
       reference (`lumora-.../index.html` :root). Approved by Vitor;
       see docs/meta/decisions-log.md ADR-0001 and ADR-0002.
     - Motion curves are extracted from Lumora's transition declarations.
     - Keyframe-loop durations are extracted from textura.agency's real CSS.
   ========================================================================== */

:root {
  /* ======================================================================
     1. COLOUR — primitives
     Adopted verbatim from Lumora. These are the only colour literals in
     the system; everything else is a semantic alias or an alpha of these.
     ====================================================================== */
  --background:  #ffffff;
  --foreground:  #14181c;
  --ink:         #0a0a0a;
  --muted:       #536878;  /* alias of --accent; kept because Lumora ships both names */
  --subtle:      #7a8d9c;
  --line:        #e5e4e2;
  --surface:     #f4f4f2;
  --surface-2:   #e5e4e2;
  --accent:      #536878;
  --accent-from: #687e90;
  --accent-to:   #394853;
  --hero-from:   #ecebe9;
  --hero-to:     #c9c9c9;

  /* Hero background video is graded to grayscale, then re-tinted with
     --gradient-accent via mix-blend-mode: color (see sections.css). The
     source clip is dark; light mode inverts its luminance first so it reads
     as a light, airy texture instead of a dark video sitting on a white page. */
  --hero-video-invert: 1;

  /* Lumora uses this one inline (never tokenised there); it is the
     pressed/hover surface and the light pill fill. Tokenised here so it
     stops being a magic number. */
  --surface-warm: #f1f0ee;
  --line-warm:    #e6e5e2;

  /* ----------------------------------------------------------------------
     1a. COLOUR — alpha ladder
     Opacity variants of --foreground and white. Named by ROLE, not by
     number, so a component asks for what it means, not for a percentage.

     THE LADDER IS SPLIT BY WCAG, NOT BY TASTE. Contrast ratios below are
     measured against the surface each rung is designed to sit on
     (--background #ffffff for --fg-*, --ink #0a0a0a for --on-dark-*).
     Anything under 4.5:1 is NOT approved for text at any size this system
     ships, because the smallest text token (--text-helper, 11px) is nowhere
     near the 18.66px-bold / 24px threshold for "large text".
     Full table: docs/foundations/color.md
     ---------------------------------------------------------------------- */

  /* --- TEXT-APPROVED on --background (all >= 4.5:1, WCAG AA) ------------ */
  --fg-90: rgba(20, 24, 28, 0.90);  /* 14.0:1  emphatic body / .body-lg    */
  --fg-80: rgba(20, 24, 28, 0.80);  /* 10.1:1  default long-form body      */
  --fg-70: rgba(20, 24, 28, 0.70);  /*  6.6:1  secondary text, eyebrow     */
  --fg-60: rgba(20, 24, 28, 0.60);  /*  4.7:1  meta, labels, helper, caption
                                          — this is the FLOOR for text     */

  /* --- DECORATIVE ONLY — never on text --------------------------------- */
  --fg-50: rgba(20, 24, 28, 0.50);  /*  3.4:1  eyebrow dot, icon fill      */
  --fg-45: rgba(20, 24, 28, 0.45);  /*  2.9:1  disabled-state fill         */
  --fg-40: rgba(20, 24, 28, 0.40);  /*  2.6:1  ghost tile fill             */
  --fg-30: rgba(20, 24, 28, 0.30);  /*  bullet separators                  */
  --fg-20: rgba(20, 24, 28, 0.20);  /*  inactive dot, input hover border   */
  --fg-10: rgba(20, 24, 28, 0.10);  /*  hairline rule                      */

  /* --- TEXT-APPROVED on --ink (all >= 4.5:1, WCAG AA) ------------------ */
  --on-dark-90: rgba(255, 255, 255, 0.90); /* 16.0:1                       */
  --on-dark-70: rgba(255, 255, 255, 0.70); /*  9.8:1                       */
  --on-dark-60: rgba(255, 255, 255, 0.60); /*  7.3:1                       */
  --on-dark-55: rgba(255, 255, 255, 0.55); /*  6.3:1  — FLOOR for text     */

  /* --- DECORATIVE ONLY on --ink ---------------------------------------- */
  --on-dark-45: rgba(255, 255, 255, 0.45); /*  4.5:1  borderline; rules only */
  --on-dark-40: rgba(255, 255, 255, 0.40); /*  3.8:1  column rules         */
  --on-dark-25: rgba(255, 255, 255, 0.25); /*  2.1:1  chip borders, numerals */
  --on-dark-15: rgba(255, 255, 255, 0.15); /*  borders                     */
  --on-dark-10: rgba(255, 255, 255, 0.10); /*  borders, badge fill         */
  --on-dark-05: rgba(255, 255, 255, 0.05); /*  hairline on dark            */

  /* Text/icon colour ON an --accent fill. White works on the light-mode accent
     (5.8:1) and fails on the dark-mode accent (2.7:1), so it is a token. */
  --on-accent: #ffffff;

  /* Feedback — the only non-neutral, non-accent hue in the system. Error is
     the one state that cannot be carried by a monochrome palette. */
  --error:      #b3453a;
  --error-text: #8c322a;   /* 5.4:1 on --background */
  --error-ring: rgba(179, 69, 58, 0.15);

  /* Accent alphas — glow, focus ring, tinted wash */
  --accent-40: rgba(83, 104, 120, 0.40);
  --accent-25: rgba(83, 104, 120, 0.25);
  --accent-12: rgba(83, 104, 120, 0.12);
  --accent-05: rgba(83, 104, 120, 0.05);


  /* ----------------------------------------------------------------------
     1c. COLOUR — material surfaces
     Glass and field fills were literal white/near-white alphas inline in the
     components. On a dark page those go milky, so they are tokens now.
     ---------------------------------------------------------------------- */
  --glass:            rgba(255, 255, 255, 0.70);
  --glass-panel:      rgba(241, 240, 238, 0.70);
  --glass-chip:       rgba(255, 255, 255, 0.40);
  --glass-line:       rgba(230, 229, 226, 0.80);
  --field-bg:         rgba(241, 240, 238, 0.50);
  --field-bg-focus:   #ffffff;
  --surface-elevated: #ffffff;
  --surface-sticky:   rgba(255, 255, 255, 0.85);  /* sticky docs bar         */
  --surface-tint:     rgba(241, 240, 238, 0.50);  /* row hover / demo boxes  */
  /* Hero vignette. Must match the PAGE, not be white — in dark mode a white
     falloff over a dark field reads as fog. */
  --vignette: linear-gradient(to bottom,
              rgba(255,255,255,0.35), transparent 35%, transparent 65%, rgba(255,255,255,0.55));

  /* ----------------------------------------------------------------------
     1b. COLOUR — gradients
     ---------------------------------------------------------------------- */
  --gradient-accent: linear-gradient(to bottom right, var(--accent-from), var(--accent-to));
  --gradient-vision: linear-gradient(115deg, #0a0a0a 0%, #1f272d 38%, #394853 75%, #536878 90%, #e5e4e2 100%);
  --gradient-hero:   linear-gradient(to bottom, var(--hero-from), var(--hero-to));
  /* Loop gradient — sized 200% 200% so `gradientAnimation` has somewhere to
     travel. Mechanism + 241deg angle ported from textura.agency `.lbdlSd`.
     The stops are NOT textura's: theirs run pale peach -> pale cyan, which
     would put white CTA text at ~1.6:1. These run dark -> mid accent, so
     white text measures 9.4:1 at the dark end and 5.8:1 at the light end —
     AA at every frame of the loop, which is the whole point of animating it.

     --cta-* exists so the CTA can keep these exact stops in dark mode. The
     dark theme lightens --accent for text use, which would drop white-on-accent
     to 2.7:1 — so the button gets its own pair and simply is not overridden. */
  --cta-from: #394853;   /* literal on purpose: NOT var(--accent-to), because  */
  --cta-to:   #536878;   /* the dark theme lightens --accent-* for text use and */
                         /* would drag white CTA text down to 2.7:1.            */
  --gradient-loop:   linear-gradient(241deg, var(--cta-from) -30%, var(--cta-to) 130%);

  /* Edge — the border that gives a dark surface a readable outline.
     In light mode a dark card separates from a white page by contrast alone
     (19.8:1), so it needs none. In dark mode ink-on-page is only 1.16:1, so
     elevation has to be carried by an edge instead. One token, both themes. */
  --edge: 1px solid transparent;

  /* ======================================================================
     2. SHAPE — radius
     Adopted verbatim from Lumora. Supersedes the older "zero decorative
     curve" rule for UI surfaces; that rule still governs the brand symbol.
     See ADR-0002.
     ====================================================================== */
  --radius-pill:     9999px;
  --radius-card:     2rem;
  --radius-card-sm:  1.25rem;
  --radius-control:  0.875rem;

  /* ======================================================================
     3. LAYOUT
     ====================================================================== */
  --container-shell: 88rem;   /* 1408px at the 16px base */
  --gutter:          1.25rem;
  --gutter-lg:       2rem;

  /* Spacing scale — 0.25rem base, used for every gap/padding/margin */
  --space-1:  0.25rem;
  --space-2:  0.5rem;
  --space-3:  0.75rem;
  --space-4:  1rem;
  --space-5:  1.25rem;
  --space-6:  1.5rem;
  --space-8:  2rem;
  --space-10: 2.5rem;
  --space-12: 3rem;
  --space-14: 3.5rem;
  --space-16: 4rem;
  --space-20: 5rem;
  --space-28: 7rem;

  /* ======================================================================
     4. TYPOGRAPHY
     Sizes are in rem and ride the fluid `html { font-size: Xvw }` ladder in
     base.css — so one scale covers every breakpoint. See ADR-0004.
     ====================================================================== */
  --font-display:   'Neue Montreal', 'Onest', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --font-ui:        'Onest', 'Onest Static', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
  --font-mono:      ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, 'Liberation Mono', monospace;
  /* Third voice, pull-quote only — see docs/foundations/typography.md and
     open-questions.md Q7. Content-scope only as of 2026-07-27. */
  --font-editorial: 'Instrument Serif', Georgia, serif;

  /* Type scale */
  --text-display:  4.5rem;    /* 72px — hero h1                */
  --text-h1:       3.75rem;   /* 60px                          */
  --text-h2:       3rem;      /* 48px                          */
  --text-h3:       1.875rem;  /* 30px                          */
  --text-h4:       1.25rem;   /* 20px                          */
  --text-h5:       1.125rem;  /* 18px                          */
  --text-h6:       1rem;      /* 16px                          */
  --text-body-lg:  1.0625rem; /* 17px — long-form paragraph    */
  --text-body:     1rem;      /* 16px                          */
  --text-body-sm:  0.875rem;  /* 14px — UI default             */
  --text-caption:  0.75rem;   /* 12px                          */
  --text-label:    0.75rem;   /* 12px — uppercase              */
  --text-helper:   0.6875rem; /* 11px                          */
  --text-micro:    0.65rem;   /* 10.4px — carousel kicker      */

  /* Weights */
  --weight-light:    300;
  --weight-regular:  400;
  --weight-medium:   500;
  --weight-semibold: 600;
  --weight-bold:     700;

  /* Line heights */
  --lh-display: 0.98;
  --lh-tight:   1.05;
  --lh-snug:    1.2;
  --lh-heading: 1.35;
  --lh-body:    1.6;
  --lh-relaxed: 1.7;

  /* Letter spacing */
  --ls-display: -0.03em;
  --ls-tight:   -0.02em;
  --ls-snug:    -0.01em;
  --ls-normal:  0em;
  --ls-wide:    0.025em;
  --ls-wider:   0.05em;

  /* ======================================================================
     5. MOTION
     Curves lifted from Lumora's transition declarations, named
     by intent. Durations from the same source; loop durations from
     textura.agency. See docs/motion/motion-system.md.
     ====================================================================== */
  --ease-standard: cubic-bezier(0.2, 0.8, 0.2, 1);      /* hover / state   */
  --ease-entrance: cubic-bezier(0.215, 0.61, 0.355, 1); /* line reveal     */
  --ease-word:     cubic-bezier(0.165, 0.84, 0.44, 1);  /* word reveal     */
  --ease-soft:     cubic-bezier(0.16, 1, 0.3, 1);       /* fade reveal     */
  --ease-curtain:  cubic-bezier(0.22, 1, 0.36, 1);      /* loader / header */
  --ease-out:      ease-out;

  --duration-instant: 0.2s;
  --duration-fast:    0.3s;
  --duration-hover:   0.35s;
  --duration-medium:  0.4s;
  --duration-slow:    0.5s;
  --duration-reveal:  0.7s;
  --duration-fade:    0.8s;
  --duration-line:    0.9s;

  /* Loop durations — ported from textura.agency */
  --loop-gradient: 5s;    /* @keyframes vp-gradient-drift */
  --loop-star:     10s;   /* @keyframes vp-star-spin      */
  --loop-shimmer:  1s;    /* @keyframes vp-shimmer        */

  /* Stagger step — one number governs every sequenced reveal in the system */
  --stagger-word: 35ms;
  --stagger-line: 120ms;
  --stagger-item: 90ms;

  /* ======================================================================
     6. ELEVATION
     ====================================================================== */
  --shadow-card:  0 4px 20px rgba(0, 0, 0, 0.05);
  --shadow-panel: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
  --shadow-deep:  0 25px 50px -12px rgba(0, 0, 0, 0.35);
  --shadow-soft:  0 2px 8px rgba(0, 0, 0, 0.03);

  /* ======================================================================
     7. Z-INDEX — one ladder, no ad-hoc numbers
     ====================================================================== */
  --z-base:    0;
  --z-raised:  1;
  --z-content: 20;
  --z-header:  50;
  --z-dim:     100;
  --z-modal:   110;
  --z-menu:    115;
  --z-loader:  120;
}

/* ==========================================================================
   DARK THEME
   --------------------------------------------------------------------------
   Same token NAMES, different values. Nothing downstream changes: no component
   rule, no utility, no JS selector knows a theme exists. That is the entire
   point of having had one source of truth. See ADR-0014.

   MODEL — elevation, not inversion.
   `--ink` stays dark and becomes an *elevated* surface rather than flipping to
   white. That keeps two names honest: `--ink` still means "darker than the
   page" and `--on-dark-*` still means "white text on it". The cost is that
   ink-on-page separation falls to 1.16:1, so shape legibility moves to `--edge`.

   ALPHA LADDER — the numbers stay, the base flips to white.
   A given alpha yields MORE contrast on a dark ground than on a light one, so
   every rung improves. Measured on `--background #0d1013`:
     --fg-90 15.4:1 · --fg-80 12.3:1 · --fg-70 9.5:1 · --fg-60 7.2:1 (floor)
     --fg-50 5.3:1 · --fg-45 4.5:1 · --fg-40 3.8:1
   The approved/decorative split is deliberately NOT relaxed even though the
   lower rungs would now pass — one rule, both themes, so a component can never
   be theme-dependently accessible.

   ON --ink #1b2026:
     #fff 16.4:1 · --on-dark-90 13.5:1 · --on-dark-70 8.6:1 · --on-dark-60 6.7:1
     --on-dark-55 5.9:1 (floor) · --on-dark-45 4.4:1 (decorative, as in light)
   ========================================================================== */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) { color-scheme: dark; }
}
:root[data-theme="dark"] { color-scheme: dark; }

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --background:  #0d1013;   /* page                                      */
    --foreground:  #eceff2;   /* 16.5:1 on page — AAA                      */
    --ink:         #1b2026;   /* elevated surface: card, footer, dark pill */
    --muted:       #8aa2b5;
    --subtle:      #7e909f;   /* 5.8:1 — large display text only           */
    --line:        #272e35;
    --surface:     #14181c;
    --surface-2:   #232a31;
    --surface-warm:#262d34;
    --line-warm:   #2c343b;
    --accent:      #8aa2b5;   /* lightened: 7.2:1 on page — AAA            */
    --accent-from: #a2b7c7;   /* 9.2:1 on page                             */
    --accent-to:   #6c8398;
    --hero-from:   #171c21;
    --hero-to:     #0d1013;
    --hero-video-invert: 0;

    --fg-90: rgba(255, 255, 255, 0.90);
    --fg-80: rgba(255, 255, 255, 0.80);
    --fg-70: rgba(255, 255, 255, 0.70);
    --fg-60: rgba(255, 255, 255, 0.60);
    --fg-50: rgba(255, 255, 255, 0.50);
    --fg-45: rgba(255, 255, 255, 0.45);
    --fg-40: rgba(255, 255, 255, 0.40);
    --fg-30: rgba(255, 255, 255, 0.30);
    --fg-20: rgba(255, 255, 255, 0.20);
    --fg-10: rgba(255, 255, 255, 0.12);

    --accent-40: rgba(138, 162, 181, 0.40);
    --accent-25: rgba(138, 162, 181, 0.25);
    --accent-12: rgba(138, 162, 181, 0.18);
    --accent-05: rgba(138, 162, 181, 0.08);

    /* --cta-from / --cta-to are deliberately NOT overridden: the primary CTA
       keeps its light-mode dark-blue stops so white text stays 9.4→5.8:1. */

    --gradient-hero: linear-gradient(to bottom, var(--hero-from), var(--hero-to));
    --gradient-vision: linear-gradient(115deg, #05070a 0%, #131a20 38%, #2c3945 75%, #44586a 90%, #8aa2b5 100%);


    --on-accent: #0d1013;
  --error:      #e08479;
  --error-text: #f0a79d;   /* 8.1:1 on the dark page */
  --error-ring: rgba(224, 132, 121, 0.22);
    --error:      #e08479;
    --error-text: #f0a79d;   /* 8.1:1 on the dark page */
    --error-ring: rgba(224, 132, 121, 0.22);   /* 7.2:1 on the dark-mode accent */
    /* Keep the accent gradient dark so its #fff tile text stays legible. */
    --gradient-accent: linear-gradient(to bottom right, #536878, #394853);

    --glass:            rgba(38, 45, 52, 0.70);
    --glass-panel:      rgba(20, 24, 28, 0.70);
    --glass-chip:       rgba(38, 45, 52, 0.55);
    --glass-line:       rgba(44, 52, 59, 0.90);
    --field-bg:         rgba(20, 24, 28, 0.60);
    --field-bg-focus:   #14181c;
    --surface-elevated: #14181c;
    --surface-sticky:   rgba(13, 16, 19, 0.85);
    --surface-tint:     rgba(255, 255, 255, 0.04);
    --vignette: linear-gradient(to bottom,
                rgba(13,16,19,0.55), transparent 35%, transparent 65%, rgba(13,16,19,0.75));


    --edge: 1px solid var(--line-warm);

    --shadow-card:  0 4px 20px rgba(0, 0, 0, 0.45);
    --shadow-panel: 0 25px 50px -12px rgba(0, 0, 0, 0.7);
    --shadow-deep:  0 25px 50px -12px rgba(0, 0, 0, 0.8);
    --shadow-soft:  0 2px 8px rgba(0, 0, 0, 0.3);
  }
}

/* Manual override — identical block, selected by attribute instead of by
   media query. Duplicated rather than shared because a `:where()` union of a
   media query and an attribute selector cannot be expressed in one rule.
   If these two drift, dark mode is broken; keep them edited together. */
:root[data-theme="dark"] {
  --background:  #0d1013;
  --foreground:  #eceff2;
  --ink:         #1b2026;
  --muted:       #8aa2b5;
  --subtle:      #7e909f;
  --line:        #272e35;
  --surface:     #14181c;
  --surface-2:   #232a31;
  --surface-warm:#262d34;
  --line-warm:   #2c343b;
  --accent:      #8aa2b5;
  --accent-from: #a2b7c7;
  --accent-to:   #6c8398;
  --hero-from:   #171c21;
  --hero-to:     #0d1013;
  --hero-video-invert: 0;

  --fg-90: rgba(255, 255, 255, 0.90);
  --fg-80: rgba(255, 255, 255, 0.80);
  --fg-70: rgba(255, 255, 255, 0.70);
  --fg-60: rgba(255, 255, 255, 0.60);
  --fg-50: rgba(255, 255, 255, 0.50);
  --fg-45: rgba(255, 255, 255, 0.45);
  --fg-40: rgba(255, 255, 255, 0.40);
  --fg-30: rgba(255, 255, 255, 0.30);
  --fg-20: rgba(255, 255, 255, 0.20);
  --fg-10: rgba(255, 255, 255, 0.12);

  --accent-40: rgba(138, 162, 181, 0.40);
  --accent-25: rgba(138, 162, 181, 0.25);
  --accent-12: rgba(138, 162, 181, 0.18);
  --accent-05: rgba(138, 162, 181, 0.08);

  --gradient-hero: linear-gradient(to bottom, var(--hero-from), var(--hero-to));
  --gradient-vision: linear-gradient(115deg, #05070a 0%, #131a20 38%, #2c3945 75%, #44586a 90%, #8aa2b5 100%);


  --on-accent: #0d1013;
  --error:      #e08479;
  --error-text: #f0a79d;   /* 8.1:1 on the dark page */
  --error-ring: rgba(224, 132, 121, 0.22);
  --gradient-accent: linear-gradient(to bottom right, #536878, #394853);

  --glass:            rgba(38, 45, 52, 0.70);
  --glass-panel:      rgba(20, 24, 28, 0.70);
  --glass-chip:       rgba(38, 45, 52, 0.55);
  --glass-line:       rgba(44, 52, 59, 0.90);
  --field-bg:         rgba(20, 24, 28, 0.60);
  --field-bg-focus:   #14181c;
  --surface-elevated: #14181c;
    --surface-sticky:   rgba(13, 16, 19, 0.85);
  --surface-tint:     rgba(255, 255, 255, 0.04);
  --vignette: linear-gradient(to bottom,
              rgba(13,16,19,0.55), transparent 35%, transparent 65%, rgba(13,16,19,0.75));


  --edge: 1px solid var(--line-warm);

  --shadow-card:  0 4px 20px rgba(0, 0, 0, 0.45);
  --shadow-panel: 0 25px 50px -12px rgba(0, 0, 0, 0.7);
  --shadow-deep:  0 25px 50px -12px rgba(0, 0, 0, 0.8);
  --shadow-soft:  0 2px 8px rgba(0, 0, 0, 0.3);
}

/* ==========================================================================
   THE ONE BREAKPOINT OVERRIDE IN THE SYSTEM
   --------------------------------------------------------------------------
   ADR-0004 claims one type scale covers every breakpoint via the fluid `vw`
   root. That holds for every role except the display tier at the small end,
   and here is why: below 640px the root ladder scales *up* (4.444vw), so at a
   390px viewport 1rem = 17.3px and --text-display 4.5rem resolves to 78px.
   A 23-character headline line at 78px needs ~900px of width and has ~347px.

   So the display tier — and only the display tier — gets a small-screen
   override. Body and UI roles genuinely need none.
   ========================================================================== */
@media (max-width: 639.98px) {
  :root {
    --text-display: 1.6rem;   /* ~28px at 390px, ~45px at 640px */
    --text-h1:      1.5rem;
    --text-h2:      1.35rem;
    --text-h3:      1.15rem;
  }
}
