/* ============================================================
   DESIGN TOKENS  —  single source of truth for the VAL palette,
   spacing, radius, shadow and type scales.

   Loaded globally (base.html) BEFORE base.css and components.css,
   so every stylesheet — including the bespoke scoped pages
   (.cd-page / .sd-page / .if-page) — resolves the same var(--…).

   Do NOT hardcode palette hexes elsewhere; reference these tokens.

   ------------------------------------------------------------
   TODO — PHASE 2 (deferred, revisit later):
   This was step 1 (centralize shared styling). Step 2 is still
   pending — come back to make the styling *consistent* across the
   site: tune the canonical values below, finish deleting the
   per-page component copies left as intentional variants (e.g.
   the pill-styled create_lpo.css / edit_lpo.css), and extract the
   remaining inline styles from templates. See the css-design-system
   project memory for the full remaining scope.
   ------------------------------------------------------------
   ============================================================ */
:root {
    /* ---------- Brand / accent ---------- */
    --green: #8CC63F;        /* VAL primary green — buttons, active pill */
    --green-soft: #ecfce7;   /* light green tint / success background */
    --green-ink: #4d7a1e;    /* dark green text on soft-green */
    --green-deep: #7ab036;   /* canonical hover for green surfaces */

    --blue: #004d99;         /* value / link blue */
    --blue-soft: #e8f0fb;    /* light blue tint */

    /* ---------- Neutrals ---------- */
    --ink: #333333;          /* primary body text */
    --ink-strong: #222222;   /* strongest text */
    --slate: #6b7280;        /* top-bar, table headers, labels */
    --muted: #9ca3af;        /* secondary / placeholder text */

    /* The office top bar. Its own token rather than --slate directly, because
       --slate also paints table headers and labels, which must not follow the
       environment colour. */
    --topbar-bg: var(--slate);

    --sidebar: #282828;      /* dark chrome — floor rail, mobile toggle bars */
    --sidebar-deep: #1a1a1a; /* deeper dark chrome */
    --body-bg: #E6E5FA;      /* app body background — the gap around the shell tiles */
    /* --body-bg: #B5CA8D; */
    --surface: #ffffff;      /* cards / panels / table background */
    --soft: #f9fafb;         /* subtle fills / striped rows */
    --rule: #f3f4f6;         /* row hover / faint divider fill */
    --line: #e5e7eb;         /* default border / divider */
    --line-strong: #d1d5db;  /* stronger border */

    /* ---------- Ordinal ramp (charts) ----------
       One hue, light -> dark, for ORDERED stages: funnels, tiers, age bands.
       Ordered data must not use categorical hues — the ramp is what makes
       "more of the bar is dark" read as "more of it is done".

       Validated with the dataviz skill's validate_palette.js against our WHITE
       card surface (--surface), not the validator's default:
         validate_palette.js "#7ab036,#5f9a24,#4a7a1c,#2f5211" --ordinal
                             --mode light --surface "#ffffff"   -> ALL PASS
       (monotone lightness, adjacent dL >= 0.06, light end 2.60:1, hue spread 3deg)

       NOTE --green (#8CC63F) is only 1.99:1 on white and FAILS the 2:1 light-end
       floor, which is why the ramp starts at --green-deep rather than the brand
       green. Re-run the validator if you change any step. */
    --ramp-green-1: #7ab036;  /* = --green-deep */
    --ramp-green-2: #5f9a24;
    --ramp-green-3: #4a7a1c;
    --ramp-green-4: #2f5211;
    /* ---------- Null zone (charts) ----------
       Absence buckets beside the ramp — "nothing has happened here yet". These are
       deliberately NOT ramp steps: grey vs green is what makes the green mean
       "committed", so a bucket with no commitment must not wear one.

       Two steps, light -> dark, for stages that differ only in WHO is holding an
       untouched line. Validated as its own ordinal pair:
         validate_palette.js "#9ca3af,#6b7280" --ordinal
                             --mode light --surface "#ffffff"   -> ALL PASS
       (monotone lightness, dL gap 0.16, light end 2.54:1)

       The zone boundary breaks lightness monotonicity (--ramp-null L 0.551 is
       DARKER than --ramp-green-1 L 0.695), so all six steps together FAIL the
       one-ramp check — expected, and true before --ramp-null-soft existed. Validate
       the two zones separately; the break is the encoding, not a defect.

       --ramp-null-soft is 2.54:1, under the 3:1 preference: legal only because the
       funnel ships a swatch+label table row per bucket, so nothing rests on colour
       alone. Do not reuse it where that relief is absent. */
    --ramp-null-soft: var(--muted);
    --ramp-null: var(--slate);

    /* ---------- App shell ----------
       The office chrome is one L-shaped glass tile floating on --body-bg: a
       full-width top bar with the nav rail hanging below its left end, inset
       --shell-gap from every viewport edge. The three outer corners of the L
       take --radius-shell, and so does the inner corner where the bar's
       underside meets the rail's right edge -- that one is a fillet, cut out of
       the well's top-left corner. The region the L encloses has no surface of
       its own; the page's cards are what sit on it. --sidebar stays dark: the
       floor rail still wears it.

       --chrome-bg is translucent over the body, --nav-blur behind it, and
       --chrome-edge lights the two sides that face the body, as an inset shadow
       rather than a border, since a border on the bar would run across the
       rail's head too and the tile carries no line inside it. The pair is what
       reads as a pane of glass rather than a flat panel. --chrome-flat is the
       same colour opaque, for the few places that cannot composite -- the
       flyouts, the drawer and the env dot's ring.

       --nav-active-ink on --nav-active-bg is 5.29:1; the dim ink used for section
       headers and the collapsed rail is 4.83:1 on the composited chrome. */
    --chrome-flat: #e9ece5;
    --chrome-bg: rgba(233, 236, 229, 0.72);
    --chrome-rule: rgba(31, 41, 34, 0.10);
    --chrome-edge: rgba(255, 255, 255, 0.75);
    /* The tile must never cast a shadow on ITSELF -- the bar's underside sits
       on the rail's head, and an ordinary shadow there paints a dark band along
       the joint, which is the one thing this shape cannot have. So each piece
       casts only away from the other: the negative spread cancels the blur's
       reach back towards it (reach = offset + spread + blur/2, kept at or below
       zero on the shared edge). The bar's underside past the rail still needs an
       edge, and it cannot draw that itself, so the well draws it inward. */
    --shadow-chrome-up: 0 -2px 5px -1px rgba(31, 41, 34, 0.10);
    --shadow-chrome-down: 0 2px 5px -1px rgba(31, 41, 34, 0.10);
    --shadow-chrome-well: inset 0 4px 4px -4px rgba(31, 41, 34, 0.10);
    --nav-blur: blur(16px) saturate(125%);
    --nav-ink: var(--ink-strong);
    --nav-ink-dim: var(--slate);
    --nav-hover-bg: rgba(255, 255, 255, 0.72);
    --nav-active-bg: var(--green-soft);
    --nav-active-ink: var(--green-ink);
    /* Flyouts and the drawer wear the chrome's colour, but they float over page
       content rather than the body, so they state it at near-full opacity.
       Matching --chrome-bg's own alpha here would let a table read through the
       menu. */
    --nav-flyout-bg: rgba(233, 236, 229, 0.97);

    /* ---------- Scrollbars ----------
       The office scrollbars are drawn, not inherited: the platform default is a
       wide bar on an opaque track, and an opaque track is a white stripe down
       the edge of a glass rail or a bare well.

       --scrollbar-size is the full gutter; the thumb is that minus twice
       --scrollbar-pad, which is applied as a transparent border so the track
       shows through beside it. --scrollbar-inset holds the well's bar clear of
       the bar's and the rail's rounded corners. */
    --scrollbar-size: 14px;
    --scrollbar-pad: 4px;
    --scrollbar-inset: 14px;
    --scrollbar-thumb: rgba(31, 41, 34, 0.20);
    --scrollbar-thumb-hover: rgba(31, 41, 34, 0.34);

    --nav-width: 236px;
    --nav-width-collapsed: 68px;
    --topbar-h: 56px;
    --shell-gap: 10px;     /* the tile's inset from every viewport edge */
    --well-pad: 20px;      /* the gap between the frame and what sits in it */
    --radius-shell: 14px;

    /* ---------- Status ---------- */
    --danger: #e53e3e;
    --danger-deep: #c53030;
    --danger-soft: #fff5f5;
    --warning: #f59e0b;
    --success: #10b981;
    --info: #3b82f6;

    /* ---------- Radius ---------- */
    --radius-sm: 4px;
    --radius-md: 8px;
    --radius-lg: 12px;
    --radius-pill: 20px;
    /* Every button in the app. Pointing them all here is what makes the button
       shape a single decision — badges, chips and progress tracks keep
       --radius-pill, because a rounded badge is what tells them apart. */
    --radius-btn: var(--radius-sm);

    /* ---------- Shadow ---------- */
    --shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.06);
    --shadow-card: 0 2px 4px rgba(0, 0, 0, 0.05);
    --shadow-lifted: 0 4px 12px rgba(0, 0, 0, 0.1);
    --shadow-modal: 0 8px 32px rgba(0, 0, 0, 0.18);

    /* ---------- Type ---------- */
    --font-sans: Arial, sans-serif;
    --font-mono: ui-monospace, "Cascadia Code", Consolas, monospace;

    /* ---------- Environment ----------
       Purple is the only hue the palette has not already spent: green is brand,
       blue is values and links, red danger, amber warning, grey chrome. So a
       purple bar cannot be misread as a status.

       Two steps, because the two bars sit in different light. The office bar
       carries white text (7.10:1, better than the --slate it replaces). The floor
       bar is deliberately dark so it does not wash out under packhouse lighting,
       so it takes the deeper step and keeps that character: 12.73:1 on
       --floor-on-chrome, and 4.93:1 on --floor-on-chrome-dim, which is the binding
       constraint. A lighter violet fails that dim text -- #4c1d95 gives it only
       3.89:1 -- so do not brighten this step without rechecking. */
    --env-test: #6d28d9;
    --env-test-deep: #3b1173;

    /* ---------- Spacing (4px step) ---------- */
    --space-1: 4px;
    --space-2: 8px;
    --space-3: 12px;
    --space-4: 16px;
    --space-5: 20px;
    --space-6: 24px;
    --space-8: 32px;
}

/* ============================================================
   ENVIRONMENT TINT

   base.html and base_floor.html stamp settings.ENVIRONMENT onto <body> as
   `env-<name>`. Custom properties resolve from the nearest ancestor that sets
   them, and <body> is nearer than :root, so rebinding them here beats both this
   file's :root and floor.css's -- no specificity juggling, and no rule anywhere
   needs to know an environment exists.

   Live and laptops leave ENVIRONMENT unset and get no class at all.
   ============================================================ */
body.env-test {
    --topbar-bg: var(--env-test);
    --floor-chrome: var(--env-test-deep);
}
