/* ================================================================
   CAPABILITIES SHOWCASE — DESIGN TOKENS
   ================================================================

   Three layers, per the Bluedog Digital Design System:
     1. PRIMITIVES — fixed base palette + this product's accent slots
     2. SEMANTIC   — named aliases
     3. ROLES      — what an element reaches for (--fg, --em, --icon…),
                     resolved by the surface it sits on

   THE RULE THAT MATTERS: set the surface, everything derives.
   A screen declares data-surface="light|dark" and the whole foreground
   palette resolves from it. Elements name ROLES, never fixed colors.
   No element carries a fixed color, so there is no code path that puts
   light-surface ink on a dark surface.

   Source: ~/Bluedog/02-Internal/digital-design-system/tokens.css
   Vendored, not imported — no build step, and the Showcase overrides
   its client accent slots below. When the design system changes,
   diff this file against it deliberately rather than auto-syncing.

   Spec: SPINE_MAP_HYG_V1.md
   ================================================================ */

:root {

  /* ── Base palette — fixed across every Bluedog project ────────
     These four already matched V1's palette exactly, which is why
     the V2 re-skin is cheaper than it looks. */
  /* --paper is the primitive; --bg is the role-ish name that points at it.
     Two names for one value on purpose: see the note on --brand-warm. A consumer
     that aliases --bg (as Presenter Controls does, per surface) would otherwise
     create a cycle with anything defined as var(--bg) — which is exactly what
     happened to --spine-light. A primitive must be reachable by a name that
     nothing points back to. */
  --paper:           #F5F4F2;
  --bg:              var(--paper);
  --surface:         #FFFFFF;
  --ink:             #06004A;
  --ink-mute:        rgba(6,0,74,0.55);
  --ink-faint:       rgba(6,0,74,0.30);
  --rule:            rgba(6,0,74,0.10);

  --ink-inverse:      #FFFFFF;
  --ink-inverse-mute: rgba(255,255,255,0.55);

  /* ── Accent slots — surface-reactive ──────────────────────────
     The core color rule: the highlight changes with the surface, and
     no element decides its own color.

       · LIGHT surfaces → --accent (the Showcase's warm orange)
       · DARK  surfaces → --cyan

     This IS Matt's 2026-06-13 instruction ("on any dark-blue section
     use cyan as the highlight instead of the warm orange"). The design
     system already specifies a surface-reactive highlight, so the
     instruction needed a value, not a mechanism.

     --accent departs from the design system's own build, which sets
     the light-surface highlight to blue (#2251CC). --accent is a
     client slot, and the Showcase's brand thread is the warm orange
     V1 has always used. Deliberate, and the one token where this
     product and design-system.html hold different values. */
  /* The brand warm orange has its OWN name as well as filling the --accent slot.
     Without this, anything wanting "the warm one" has to reference --accent — and
     since --em is defined AS var(--accent), a consumer aliasing --accent back to
     --em creates a circular reference and BOTH silently resolve to nothing.
     Presenter Controls hit exactly that. A primitive should always be reachable
     by a name no role points at. */
  /* SIENNA. Chosen by Matt 2026-07-28 from the seven-candidate day-mode study
     in docs/reference/color-study-day-mode.html.

     Why this one earns it: at 4.72:1 against the cream and 5.19:1 for white text
     on the fill, it is the only candidate that clears the 4.5:1 body-text bar in
     BOTH directions. So it needs no light/deep token split and no button-label
     enlargement to be accessible — one token, correct everywhere. The warmer,
     lighter candidates all needed a second token propping them up.

     It replaces #DB683E, which measured 3.15:1 on cream and 3.46:1 white-on-fill:
     both below threshold. Every warm emphasis in this product was failing
     contrast before this change, on the Showcase as well as in Presenter
     Controls.

     APPLIED GLOBALLY, deliberately. This is a shared token, so the Showcase's
     light-screen emphasis moves with it — 17 uses of --em in styles/spine.css.
     Scoping it to Presenter Controls would have left the two surfaces on
     different oranges, which is the inconsistency the 2026-07-27 brand-split
     reversal existed to remove. */
  --brand-warm:      #B84A28;
  /* The deeper partner. Not needed for contrast now that the base passes, but
     kept for hover and pressed states, and for anything that ever sits on a
     lighter tint of the accent. */
  --brand-warm-deep: #6E2811;
  --accent:          var(--brand-warm);   /* highlight on LIGHT */
  --accent-soft:     #F7B778;   /* the softer warm tone, V1's second orange */
  --cyan:            #2DE2F0;   /* highlight on DARK. CONFIRMED by Matt 2026-07-27. */
  --cyan-deep:       #15B9C7;
  /* Retained for hover fills and rules, NOT as a screen background. The closing
     screen used to ground on this; Matt's 2026-07-27 direction unified every dark
     screen onto --spine-dark instead, which resolved the question of what value
     belonged here. */
  --accent-deep:     #080B45;

  /* Amber is EXCEPTION-ONLY. Permitted: focus rings, and "recommended"
     or "primary" markers. Never a body, heading, or eyebrow color, and
     never surface-reactive — it does not participate in the role layer.
     If you want emphasis, use --em. */
  --accent-2:        #D97706;

  --highlight-on-light: var(--accent);
  --highlight-on-dark:  var(--cyan);

  /* ── TWO BACKGROUNDS, and only two ────────────────────────────
     Matt's direction, 2026-07-27, after Graham's feedback: use screen 1's navy
     everywhere a screen is dark, and one cream everywhere it's light. Nothing
     else. The previous build gave each dark screen its own navy
     (--surface-dark / -dark-2 / -darker / --accent-deep) and the result was
     colours popping against each other down the scroll.

     This is a simplification of the design system's own approach, which lets
     each beat keep its own background. Compatible: the surface ROLE layer still
     resolves ink from data-surface, so one background for all dark screens
     changes nothing about how any element gets its colour.

     --spine-dark carries the cascade stage's gradient verbatim, including the
     radial cyan bloom that gives it depth — the "light gradient" in it. */
  --spine-dark:
    radial-gradient(ellipse 70% 60% at 50% 45%,
                    color-mix(in srgb, var(--cyan) 9%, transparent) 0%,
                    transparent 68%),
    linear-gradient(160deg, #040028 0%, #06003A 55%, #040025 100%);

  /* The one light ground. References the PRIMITIVE, not --bg: a consumer that
     overrides --bg per surface would otherwise put this in a cycle. */
  --spine-light:      var(--paper);

  /* Flat fallbacks. A gradient cannot be used where a solid colour is required
     (border-colour, a mask, an SVG fill), so keep the darkest stop available. */
  --spine-dark-flat:  #040028;

  --surface-light:    #FFFFFF;
  --surface-paper:    #FAFAF8;

  /* ── Layer 3: surface roles — LIGHT defaults ──────────────────
     Mirrors the light theme so anything outside a themed screen still
     renders correctly. A [data-surface="dark"] screen overrides these
     wholesale; see the theme blocks below. */
  --fg:          var(--ink);
  --fg-soft:     var(--ink);
  --fg-mute:     var(--ink-mute);
  --fg-faint:    var(--ink-faint);
  --eyebrow:     var(--accent);
  --em:          var(--accent);
  --em-ink:      #FFFFFF;
  --icon:        var(--ink);
  --rule-c:      var(--rule);
  --bar-c:       rgba(6,0,74,0.45);
  --card-bg:     var(--surface);
  --card-border: var(--rule);
  --icon-filter: none;

  /* ── Typography ───────────────────────────────────────────────
     DM Sans, per the design system. This is a visible departure from
     V1's Helvetica Neue and belongs in the re-approval pass alongside
     the dark cascade stage. Helvetica Neue stays in the fallback
     stack, so a font-load failure degrades to V1's look rather than
     to something arbitrary. */
  --font-sans: 'DM Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;

  --weight-light:   300;
  --weight-regular: 400;
  --weight-medium:  500;
  --weight-bold:    700;

  /* Emphasis is carried by WEIGHT, never slant. A title is set light
     (300); an <em> inside it steps to bold and takes --em for color.
     Never add font-style: italic to emphasis.
     Note this is the opposite of the PROSE rule, where bold is
     structural and italic carries stress. Don't let one leak. */
  --weight-em:      var(--weight-bold);

  --text-base:        17px;
  --line-height-base: 1.55;

  /* ── Title scale — two tiers ──────────────────────────────────
     A screen that OPENS A SECTION uses the display tier; sub-beats use
     the beat tier. The design system defines this as "opens a numbered
     beat" — the Showcase renders no numbers (CEO directive), so the
     trigger is section-opener instead. Same behavior, different test.
     See SPINE_MAP_HYG_V1.md, departure 1. */
  --text-display:     clamp(34px, 5.4vw, 58px);
  --text-display-lg:  clamp(42px, 5.4vw, 72px);
  --text-eyebrow:     12px;
  --text-caption:     12px;
  --text-lede:        19px;

  /* Hook cascade sets larger than any title tier — it is the arrival,
     not a heading. */
  --text-cascade:     clamp(2.4rem, 6vw, 4.4rem);

  /* ── Tracking — in em, never px ───────────────────────────────
     A fixed px value across a fluid clamp() makes large type track
     LOOSER than small type, which is backwards. These are em, so the
     optical treatment is constant at every size.
     The bold run gets tracking BACK: heavier letterforms carry more
     mass and need more room, not less. Always applied as
     calc(base + adjust) — never as a second hand-written value. */
  --tracking-display:     -0.0207em;
  --tracking-display-lg:  -0.031em;
  --tracking-em-adjust:    0.0138em;
  /* OPTION A, chosen by Matt 2026-07-29 from the eyebrow study.
     Was 1.4px, which was two problems in one value: it broke the tracking-in-em
     rule (a px value does not scale, so the same 1.4px read tight at 12px and
     loose at 10px), and it left the spine and the branches on different numbers.
     0.18em at 12px is 2.16px — slightly airier than before on the spine, and much
     tighter than the 0.25em the branches had. */
  --tracking-eyebrow:      0.18em;

  --opsz-display: 40;

  /* ── Layout ───────────────────────────────────────────────────── */
  --nav-h:        52px;   /* fixed top bar. The keyboard-nav module reads
                             this via getComputedStyle rather than
                             hardcoding 52 the way V1 did. */
  --flyout-w:     360px;
  --measure:      1200px; /* content max-width */
  --measure-text: 680px;  /* comfortable prose measure */

  /* ── Spacing ──────────────────────────────────────────────────── */
  --space-xs:  8px;
  --space-sm:  14px;
  --space-md:  18px;
  --space-lg:  28px;
  --space-xl:  48px;
  --space-2xl: 56px;
  --space-3xl: 100px;

  /* ── Shape — the sharp-edge rule ──────────────────────────────── */
  --radius:       0;
  --radius-round: 50%;

  /* ── Fixed chrome ──────────────────────────────────────────────
     Elements that never participate in surface alternation: the nav is always
     light, the flyout panel always light, the footer always dark. They still
     get tokens rather than literals, because "no element carries a fixed
     colour" is the rule and a near-duplicate literal is how palettes drift.
     (--footer-bg used to be a hand-typed #040020 sitting four units away from
     --spine-dark-flat's #040028 — exactly that kind of drift.) */
  --nav-bg:          rgba(255,255,255,0.92);
  --nav-bg-scrolled: rgba(255,255,255,0.97);
  --nav-border:      rgba(6,0,74,0.06);
  --scrim:           rgba(0,0,0,0.15);
  --footer-bg:       var(--spine-dark-flat);
  --footer-rule:     rgba(255,255,255,0.06);
  /* The exact value V1's queued footer change specified, lightened from the
     near-invisible 0.2 it used to be. Kept as a token so the number that was
     deliberately chosen isn't retyped from memory later. */
  --footer-fg:       rgba(238,240,255,0.62);

  /* ── Motion ───────────────────────────────────────────────────── */
  --ease:         cubic-bezier(0.4, 0, 0.2, 1);
  --dur-fast:     0.2s;
  --dur-base:     0.3s;

  /* ── Breakpoints (reference — media queries can't read vars) ──── */
  --bp-mobile: 600px;
  --bp-tablet: 900px;
}

/* ================================================================
   SURFACE THEMES — the third layer

   A screen opts in with one attribute: <section data-surface="dark">
   Everything inside inherits the right foreground palette.

   These blocks set ROLES ONLY, never background. A screen keeps its
   own background, which is what lets the gradient cascade stage, the
   deep-blue close, and a client's full-bleed photograph all coexist
   under one dark role set.
   ================================================================ */

[data-surface="light"] {
  --fg:          var(--ink);
  --fg-soft:     var(--ink);
  --fg-mute:     var(--ink-mute);
  --fg-faint:    var(--ink-faint);
  --eyebrow:     var(--accent);
  --em:          var(--accent);
  --em-ink:      #FFFFFF;
  --icon:        var(--ink);
  --rule-c:      var(--rule);
  --bar-c:       rgba(6,0,74,0.45);
  --card-bg:     var(--surface);
  --card-border: var(--rule);
  --icon-filter: none;
  color: var(--fg);
}

[data-surface="dark"] {
  --fg:          #FFFFFF;
  --fg-soft:     rgba(255,255,255,0.82);
  --fg-mute:     rgba(255,255,255,0.55);
  --fg-faint:    rgba(255,255,255,0.45);
  --eyebrow:     var(--cyan);
  --em:          var(--cyan);
  --em-ink:      var(--ink);
  --icon:        #FFFFFF;
  --rule-c:      rgba(255,255,255,0.35);
  --bar-c:       rgba(255,255,255,0.55);
  --card-bg:     rgba(255,255,255,0.08);
  --card-border: rgba(255,255,255,0.18);
  --icon-filter: brightness(0) invert(1);
  color: var(--fg);
}

/* ── Emphasis registers — three volumes of one mechanism ─────────
   1. em         weight only. The default. Unlimited.
   2. .em-rule   + a hairline. Section openers that run sub-beats.
                 Max one per screen. This spine: screens 3 and 6.
   3. .em-block  knockout fill. Dark surfaces, occasional. Max one per
                 screen, never consecutive, target ≤1 dark screen in
                 four. This spine: screen 9 only.
                 Frequency is the floor, not the test. The knockout
                 ASSERTS, so it needs a reader who has already
                 committed — not an opening screen, where it reads as
                 shouting at someone who hasn't sat down yet.
   All three share the weight and the tracking compensation. */

em {
  color: var(--em);
  font-weight: var(--weight-em);
  font-style: normal;
  letter-spacing: calc(var(--tracking-display) + var(--tracking-em-adjust));
}

.em-rule {
  box-shadow: inset 0 -0.075em 0 color-mix(in srgb, var(--em) 26%, transparent);
}

.em-block {
  color: var(--em-ink);
  background: var(--em);
  padding: 0.015em 0.14em 0.075em;
  letter-spacing: calc(var(--tracking-display) + var(--tracking-em-adjust) + 0.004em);
  box-decoration-break: clone;
  -webkit-box-decoration-break: clone;
}

svg [data-ink] { stroke: var(--icon); fill: var(--icon); }
.icon img      { filter: var(--icon-filter); }
hr, .rule      { border-color: var(--rule-c); }
