/* ============================================================
   Brivtex brand tokens — the single source of truth.

   Before this file the same brand blue was declared five times
   across four stylesheets (--color-primary, --v2-glow-1,
   --signal-indigo, --v3-accent, --v3-flat) and four different
   radius scales fought over the same components. Everything a
   public page needs to know about colour, type, space, and shape
   is declared here once and consumed by brivtex-marketing.css.

   Loaded only by public marketing surfaces. The signed-in app
   keeps its own stylesheet chain untouched.

   THEMING: :root below is the complete light palette. The dark
   palette at the bottom of this file redefines colour tokens
   only — type, space, shape and motion are theme-independent
   and are declared exactly once. Every colour a public page
   renders resolves through a token here, which is what makes a
   second theme a palette swap instead of a stylesheet rewrite.
   ============================================================ */

:root {
  /* Drives native UI — scrollbars, form controls, the canvas the
     browser paints before CSS arrives. Without it a dark page
     still gets light scrollbars and light-styled inputs. */
  color-scheme: light;

  /* ── Brand ────────────────────────────────────────────────
     These three values are the brand and are preserved exactly
     as they were. Do not "improve" them. They are theme-fixed:
     a filled brand control is the same blue in both themes, and
     white on it measures 5.19:1 regardless of the page behind.  */
  --brand-blue: #1868DB;
  --brand-teal: #1EC7AE;
  --brand-gradient: linear-gradient(135deg, #1868DB, #1EC7AE);

  /* Brand blue in a FOREGROUND role — link text, focus rings —
     where the page surface sits behind it rather than the blue
     sitting behind white. #1868DB measures 5.19:1 on white but
     only 1.9:1 on the dark canvas, so this role needs its own
     token and the dark theme lightens it. Fills keep
     --brand-blue; only text and outlines read this. */
  --brand-ink: var(--brand-blue);

  /* The gradient is decorative ONLY. White text over its teal end measures
     2.1:1, so it cannot sit behind a label: every filled control that carries
     white text uses flat --brand-blue (5.19:1) instead. The gradient still
     appears on the hero glow, the step rules, and the list markers, which is
     where the teal earns its place without a contrast cost. */

  /* Derived states. Free to tune; the two brand values are not. */
  --brand-blue-hover: #1455B4;
  --brand-blue-press: #11499A;
  --brand-tint-06: rgba(24, 104, 219, 0.06);
  --brand-tint-10: rgba(24, 104, 219, 0.10);
  --brand-tint-16: rgba(24, 104, 219, 0.16);
  --brand-tint-28: rgba(24, 104, 219, 0.28);

  /* ── Ink ──────────────────────────────────────────────────
     One ramp. The old build had two competing inks (#1c1c28 in
     brivtex.css/v3, #111827 in signal-light), so signed-out and
     signed-in rendered different body text. Slightly blue-cast
     neutrals so they sit with the brand blue rather than fight
     it.                                                      */
  --ink-900: #0d1219;   /* headlines */
  --ink-700: #1f2937;   /* strong body */
  --ink-600: #3f4a5a;   /* body */
  --ink-500: #5b6675;   /* secondary */
  /* #78828f measured 3.95:1 on white and was carrying the nav links, so it
     failed AA for body-sized text. This value measures 5.08:1 on white and
     4.82:1 on --surface-sunken. */
  --ink-400: #64707d;   /* muted */
  --ink-300: #9aa3ae;   /* faint, decorative only, never body text */

  /* ── Surfaces ─────────────────────────────────────────────── */
  --surface: #ffffff;
  --surface-sunken: #f7f9fc;
  --surface-tint: #eef3fb;
  --hairline: #e4e9f0;
  --hairline-strong: #cfd7e2;

  /* The sticky nav's translucent bar. Was a literal white rgba in
     brivtex-marketing.css, which is exactly the kind of value that
     cannot follow a theme. */
  --nav-bg: rgba(255, 255, 255, 0.92);

  /* The dark hero surface. Theme-fixed: the hero is a dark band in
     BOTH themes, so these do not flip. In dark mode the hero's
     gradient resolves into a --surface that is itself dark, which
     is why the seam simply stops being visible rather than
     needing a second rule. */
  --surface-dark: #0b1220;
  --surface-dark-2: #101a2d;

  /* On-dark text. Alpha floors are set by contrast, not taste:
     the old --on-dark at 0.42 alpha over #0b1220 measured about
     3.7:1 and failed WCAG AA for body text. 0.60 is the lowest
     value here and measures about 6.8:1. */
  --on-dark: #ffffff;
  --on-dark-muted: rgba(255, 255, 255, 0.74);
  --on-dark-faint: rgba(255, 255, 255, 0.60);
  --on-dark-hairline: rgba(255, 255, 255, 0.14);
  --on-dark-fill: rgba(255, 255, 255, 0.06);

  /* ── Status ───────────────────────────────────────────────── */
  --status-ok: #12894f;
  --status-warn: #b26a00;
  --status-bad: #c8283f;

  /* Alert skins. brivtex-marketing.css previously wrote these nine
     values as literals, so the alerts were the one component that
     could not follow a theme. Each is the status hue at a border,
     fill and text weight. */
  --alert-ok-border: rgba(18, 137, 79, 0.28);
  --alert-ok-bg: rgba(18, 137, 79, 0.07);
  --alert-ok-ink: #0b5c36;

  --alert-warn-border: rgba(178, 106, 0, 0.28);
  --alert-warn-bg: rgba(178, 106, 0, 0.07);
  --alert-warn-ink: #7a4900;

  --alert-bad-border: rgba(200, 40, 63, 0.28);
  --alert-bad-bg: rgba(200, 40, 63, 0.07);
  --alert-bad-ink: #8e1b2c;

  /* ── Type ─────────────────────────────────────────────────
     Brand pairing, preserved: Bricolage Grotesque over DM Sans. */
  --font-display: 'Bricolage Grotesque', system-ui, -apple-system, sans-serif;
  --font-body: 'DM Sans', system-ui, -apple-system, sans-serif;

  /* Fluid ramp. The old build hard-coded 52px desktop / 34px
     mobile with nothing in between, which is what produced the
     reflow cliff between roughly 760px and 900px. */
  --t-display: clamp(2.125rem, 1.5rem + 2.6vw, 2.875rem);
  --t-h2: clamp(1.75rem, 1.31rem + 1.75vw, 2.375rem);
  --t-h3: clamp(1.0625rem, 0.98rem + 0.35vw, 1.3125rem);
  --t-lede: clamp(1.0625rem, 1.01rem + 0.28vw, 1.1875rem);
  --t-body: 1rem;
  --t-sm: 0.9375rem;
  --t-xs: 0.8125rem;

  --lh-tight: 1.08;
  --lh-snug: 1.22;
  --lh-body: 1.62;

  --tracking-display: -0.028em;
  --tracking-h2: -0.02em;
  --tracking-h3: -0.012em;

  --measure-lede: 48ch;
  --measure-body: 68ch;

  /* ── Space ────────────────────────────────────────────────── */
  --s-1: 4px;
  --s-2: 8px;
  --s-3: 12px;
  --s-4: 16px;
  --s-5: 24px;
  --s-6: 32px;
  --s-7: 48px;
  --s-8: 64px;
  --s-9: 96px;

  /* Section rhythm. Fluid so mobile no longer collapses to the
     22px top padding the old v4 override applied at 640px. */
  --section-y: clamp(4.5rem, 2.4rem + 6vw, 7.5rem);
  --container: 1120px;
  --container-narrow: 760px;
  --gutter: clamp(1.25rem, 0.9rem + 1.4vw, 2rem);

  /* ── Shape ────────────────────────────────────────────────
     ONE radius scale. Buttons stay pill because that is the
     existing brand feel; every surface uses the ramp below. */
  --r-xs: 6px;
  --r-sm: 10px;
  --r-md: 14px;
  --r-lg: 20px;
  --r-xl: 28px;
  --r-pill: 9999px;

  /* ── Elevation ────────────────────────────────────────────
     One neutral-tinted scale, replacing three that used three
     different tints. */
  --shadow-sm: 0 1px 2px rgba(13, 18, 25, 0.06), 0 1px 3px rgba(13, 18, 25, 0.04);
  --shadow-md: 0 4px 12px rgba(13, 18, 25, 0.07), 0 2px 4px rgba(13, 18, 25, 0.04);
  --shadow-lg: 0 12px 32px rgba(13, 18, 25, 0.10), 0 4px 10px rgba(13, 18, 25, 0.05);
  --shadow-xl: 0 24px 60px rgba(13, 18, 25, 0.14), 0 8px 20px rgba(13, 18, 25, 0.06);

  /* ── Motion ───────────────────────────────────────────────
     MOTION_INTENSITY 3: hover, focus, and active feedback only.
     No entrance animation, no scroll-driven anything. */
  --ease: cubic-bezier(0.2, 0, 0.2, 1);
  --t-fast: 120ms var(--ease);
  --t-base: 180ms var(--ease);

  /* ── Compatibility aliases ────────────────────────────────
     The Catch-up demo reuses baskets/_catch_up_results.html, so
     its stylesheet block reads the older token names. Aliasing
     here rather than rewriting that block keeps one definition
     of each value and leaves the app markup untouched. */
  --color-primary: var(--brand-blue);
  --color-ink: var(--ink-900);
  --color-ink-secondary: var(--ink-600);
  --color-ink-muted: var(--ink-400);
  --color-canvas: var(--surface);
  --color-hairline: var(--hairline);
  --color-divider-soft: var(--hairline);
  --radius-sm: var(--r-sm);
  --radius-md: var(--r-md);
  --radius-lg: var(--r-lg);
  --radius-pill: var(--r-pill);
}

/* ============================================================
   DARK PALETTE

   Applied in two situations, which is why the declarations
   appear twice:

     1. The visitor has not chosen a theme and their OS asks for
        dark. :root carries no data-theme, so the media query
        governs. The :not([data-theme="light"]) guard lets an
        explicit light choice win over the OS.
     2. The visitor chose dark explicitly, and the inline script
        in public_base.html stamped data-theme="dark". This wins
        in both directions and works with the OS set to either.

   CSS cannot share one declaration block between a media query
   and a plain selector, so the two copies MUST be kept in sync.
   Change one, change the other. Only colour tokens appear here;
   anything absent is deliberately theme-independent.

   Contrast is measured against the dark canvas #0e1420, the
   same way the light values were measured against white. Body
   ink lands at 10.5:1 and the lowest non-decorative value
   (--ink-400) at 6.4:1, both comfortably past AA.
   ============================================================ */

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;

    /* Brand blue is unchanged as a FILL. As foreground it would
       measure 1.9:1 on this canvas, so the ink role lightens to
       #6ea8f8 (7.6:1) while --brand-blue itself is untouched. */
    --brand-ink: #6ea8f8;
    --brand-blue-hover: #3d84e6;
    --brand-blue-press: #679fef;
    --brand-tint-06: rgba(110, 168, 248, 0.08);
    --brand-tint-10: rgba(110, 168, 248, 0.14);
    --brand-tint-16: rgba(110, 168, 248, 0.20);
    --brand-tint-28: rgba(110, 168, 248, 0.34);

    /* Ink ramp inverts. Same six roles, same ordering. */
    --ink-900: #f1f5fb;   /* headlines      ~17:1 */
    --ink-700: #dbe3ee;   /* strong body    ~13:1 */
    --ink-600: #b8c4d4;   /* body           10.5:1 */
    --ink-500: #9dabbe;   /* secondary       8.0:1 */
    --ink-400: #8b98aa;   /* muted           6.4:1 */
    --ink-300: #66738a;   /* faint, decorative only, never body text */

    /* Sunken goes lighter than the canvas here, not darker: on a
       dark ground recession reads as a lift, and a section darker
       than #0e1420 would crush against the hero. */
    --surface: #0e1420;
    --surface-sunken: #141d2d;
    --surface-tint: #16233c;
    --hairline: #202b3d;
    --hairline-strong: #33415a;

    --nav-bg: rgba(14, 20, 32, 0.88);

    --status-ok: #46d69a;
    --status-warn: #e8ab4d;
    --status-bad: #ff7b8c;

    --alert-ok-border: rgba(70, 214, 154, 0.30);
    --alert-ok-bg: rgba(70, 214, 154, 0.10);
    --alert-ok-ink: #8ee9c2;

    --alert-warn-border: rgba(232, 171, 77, 0.32);
    --alert-warn-bg: rgba(232, 171, 77, 0.10);
    --alert-warn-ink: #f0c98a;

    --alert-bad-border: rgba(255, 123, 140, 0.32);
    --alert-bad-bg: rgba(255, 123, 140, 0.10);
    --alert-bad-ink: #ffb3bd;

    /* The light scale is a neutral tint at low alpha, which is
       invisible against a dark ground. Depth on dark comes from
       near-black at much higher alpha. */
    --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.44), 0 1px 3px rgba(0, 0, 0, 0.32);
    --shadow-md: 0 4px 12px rgba(0, 0, 0, 0.48), 0 2px 4px rgba(0, 0, 0, 0.34);
    --shadow-lg: 0 12px 32px rgba(0, 0, 0, 0.54), 0 4px 10px rgba(0, 0, 0, 0.36);
    --shadow-xl: 0 24px 60px rgba(0, 0, 0, 0.62), 0 8px 20px rgba(0, 0, 0, 0.42);
  }
}

/* Copy 2 of 2 — keep identical to the block above. */
:root[data-theme="dark"] {
  color-scheme: dark;

  --brand-ink: #6ea8f8;
  --brand-blue-hover: #3d84e6;
  --brand-blue-press: #679fef;
  --brand-tint-06: rgba(110, 168, 248, 0.08);
  --brand-tint-10: rgba(110, 168, 248, 0.14);
  --brand-tint-16: rgba(110, 168, 248, 0.20);
  --brand-tint-28: rgba(110, 168, 248, 0.34);

  --ink-900: #f1f5fb;
  --ink-700: #dbe3ee;
  --ink-600: #b8c4d4;
  --ink-500: #9dabbe;
  --ink-400: #8b98aa;
  --ink-300: #66738a;

  --surface: #0e1420;
  --surface-sunken: #141d2d;
  --surface-tint: #16233c;
  --hairline: #202b3d;
  --hairline-strong: #33415a;

  --nav-bg: rgba(14, 20, 32, 0.88);

  --status-ok: #46d69a;
  --status-warn: #e8ab4d;
  --status-bad: #ff7b8c;

  --alert-ok-border: rgba(70, 214, 154, 0.30);
  --alert-ok-bg: rgba(70, 214, 154, 0.10);
  --alert-ok-ink: #8ee9c2;

  --alert-warn-border: rgba(232, 171, 77, 0.32);
  --alert-warn-bg: rgba(232, 171, 77, 0.10);
  --alert-warn-ink: #f0c98a;

  --alert-bad-border: rgba(255, 123, 140, 0.32);
  --alert-bad-bg: rgba(255, 123, 140, 0.10);
  --alert-bad-ink: #ffb3bd;

  --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.44), 0 1px 3px rgba(0, 0, 0, 0.32);
  --shadow-md: 0 4px 12px rgba(0, 0, 0, 0.48), 0 2px 4px rgba(0, 0, 0, 0.34);
  --shadow-lg: 0 12px 32px rgba(0, 0, 0, 0.54), 0 4px 10px rgba(0, 0, 0, 0.36);
  --shadow-xl: 0 24px 60px rgba(0, 0, 0, 0.62), 0 8px 20px rgba(0, 0, 0, 0.42);
}
