/*
 * cortex-theme.css - the single source of truth for Cortex's palette, in both themes.
 *
 * WHY THIS FILE EXISTS. The palette was declared 24 times, once per page, in a local `:root` block,
 * and it had drifted into TWO FAMILIES rather than 24 variations:
 *
 *   Family A, the TBR standard, on 12 pages (tbr-run, tbr-review, tbr, scope, jobs, customers,
 *     readiness, secure-score, shadow-ai, software-scan, device-spec, sda-licence-tiers):
 *     --ink #231f20 (the brand charcoal), --line #e8e9ef, --bg #f5f5f6, --ok #5a8a2a, and the
 *     --epx-* brand tokens with the Aspire SC / Poppins faces.
 *   Family B, older, on 12 pages (access, admin, library, parts, partner-permissions, profile,
 *     scoping, security, server-stats, survey, surveys, usage):
 *     --ink #1a1a1a, --line #e4e4e4, --bg #fafafa, --ok #1a7f4b, no brand tokens and no brand fonts.
 *
 * Dave's instruction, 2026-07-28: the TBR review pages have had the most design work, so the wizard
 * and the agenda are the reference and everything else is styled around them, accepting that Family
 * B pages will shift slightly to meet the standard. So FAMILY A IS THE STANDARD, and Family B's
 * values appear nowhere below. The visible effect on those twelve pages is a slightly warmer page
 * background, the brand charcoal instead of near-black text, the brand green for success, and the
 * brand faces.
 *
 * The per-page CSS as it stood before the sweep is preserved in docs/ux/css-backups/, so any earlier
 * look can be recovered without going through git history.
 *
 * ---------------------------------------------------------------------------------------------
 * THE NAMING RULE, and it is the thing to hold on to. Two kinds of token, told apart by the prefix:
 *
 *   --epx-*   BRAND CONSTANTS. Fixed by the EPX brand system and identical in both themes. Navy
 *             002060, blue 2b3678, red e9212e, lime 9bc55e, charcoal 231f20. Never themed, never
 *             redefined, never approximated.
 *   --name    ROLES. What a colour is FOR, not what it is: --surface, --ink, --line, --accent. These
 *             are what flip between light and dark.
 *
 * The consequence is that any `var(--epx-navy)` still in a rule is a decision waiting to be made:
 * fine as a FILL (a navy band stays navy in the dark), wrong as TEXT (navy text on a dark surface
 * fails contrast). That is why --brand-ink exists separately from --epx-navy. Do not use a brand
 * constant for text; use --brand-ink and it will lighten itself in the dark.
 *
 * ---------------------------------------------------------------------------------------------
 * MIGRATION. The old unprefixed names (--bg, --muted, --phase, --hover) are kept below as ALIASES of
 * the new roles. That is deliberate and it is what makes the sweep safe: a page's local `:root` can
 * be deleted today and the page keeps rendering from these aliases, so removing 24 blocks does not
 * have to happen in the same change as rewriting every rule that reads them. Each alias is marked,
 * and they come out once nothing references them.
 *
 * ORDER MATTERS: link this BEFORE epx-ui.css and before a page's own <style>, so a page can still
 * override a role locally where it genuinely needs to.
 */

:root {
  /* ----- brand constants: fixed in both themes ----- */
  --epx-navy: #002060;
  --epx-blue: #2b3678;
  --epx-blue-hover: #202a63;
  --epx-lime: #9bc55e;
  --epx-red: #e9212e;
  --epx-charcoal: #231f20;
  --epx-white: #ffffff;           /* white is white in both themes; used with a fallback on the
                                     login pages, so it is defined here to stop the literal
                                     spreading further */
  --epx-yellow: #ffde21;          /* outside the five-colour core palette, but in use on the
                                     scoping wizard, so it is named rather than left as a literal */

  --font-heading: 'Aspire SC', 'Poppins', sans-serif;
  --font-body: 'Poppins', -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;

  --radius: 12px;
  --radius-sm: 8px;
  --shadow: 0 1px 2px rgba(16, 24, 64, 0.04), 0 2px 8px rgba(16, 24, 64, 0.05);

  /* ----- surfaces, lightest-sitting-on-darkest ----- */
  --surface: #f5f5f6;             /* the page itself */
  --surface-raised: #ffffff;      /* a card, panel or table sitting on the page */
  --surface-sunken: #eef2fb;      /* an inset band: a phase header, a nested block */
  --surface-hover: #f2f8ea;       /* a row or control under the pointer */

  /* ----- text ----- */
  --ink: #231f20;                 /* the brand charcoal */
  --ink-muted: #696969;           /* secondary text, column headings, hints. Was #6b6b6b, which
                                     measured 4.40:1 on a --line fill (a chip, a disabled button) */
  /* Text on a SATURATED DARK fill (a navy band, a red pill). Near-white in BOTH themes, because the
     fill it sits on is dark in both: the dark theme darkens a navy band, it does not lighten it.
     Text on a LIGHT fill (the lime accent) is a different question and uses --accent-ink. Keeping
     these two apart is the whole reason --ink-inverse is not just "the opposite of --ink": an
     inverted token would put dark text on a navy band the moment the theme flipped. */
  --ink-inverse: #ffffff;
  --brand-ink: #002060;           /* brand colour used AS TEXT: lightens in the dark */

  /* ----- lines ----- */
  --line: #e8e9ef;
  --line-strong: #d3d6e2;         /* a divider that has to carry more weight than a cell border */

  /* ----- brand chrome: the blue title band on a card ----- */
  --brand-band: #2b3678;
  --brand-band-ink: #ffffff;

  /* ----- the interactive accent is lime -----
     Lime fills carry NAVY ink, because white on lime fails contrast. Accent text or borders on a
     light surface use the deeper --accent-strong so they read at all. */
  --accent: #9bc55e;
  --accent-hover: #8ab84e;
  --accent-ink: #002060;
  --accent-strong: #4e7a27;       /* was #4f7d28, then #4e7b27: 4.49:1 on the page missed AA by
                                     0.01 and the ghost button's text on its own tint was 4.40:1 */
  --accent-tint: #eef6e2;

  /* The boundary of a text field, which is the ONLY thing that says a field is there, so WCAG
     1.4.11 applies to it at 3:1. The lime accent measures 1.99:1 on a card and cannot reach 3:1 at
     any opacity, so a field border is its own role rather than a reuse of --accent. Dark keeps the
     lime, which has plenty of contrast against a dark surface. */
  --field-border: #729a37;

  /* ----- status: a fill tint, a border and an ink per state -----
     Three tokens per state rather than one, because every banner in the product needs all three and
     the alternative is the literal hex trio repeated in 24 files. */
  /* The status inks are all TEXT on their own tint, so each is the darkest value that still reads
     as its own hue at AA against both white and that tint (measured by scripts/check_contrast.py).
     The amber was the worst in the product at 3.05:1. */
  --ok: #507a25;                  /* was #5a8a2a (3.86:1 on its tint), then #527d26, which still
                                     measured 4.34:1 on the inset-band background */
  --ok-tint: #f1faf4;
  --ok-line: #a8dcbf;
  /* Ink for text ON an --ok FILL, and the only status colour that needs one, because --ok is the only
     one that crosses from dark to light between the themes: a deep green here, a bright green in the
     dark theme. So the ink has to cross the other way, exactly as --accent-ink does for lime. White on
     the dark theme's green measured 2.00:1 (the jobs stepper's completed dot). Deriving this by
     inverting --ink would be wrong for the same reason --ink-inverse is not an inversion. */
  --ok-ink: #ffffff;

  --warn: #936b09;                /* was #b8860b: 3.05:1 on its tint, the worst pairing measured */
  --warn-tint: #fff7e6;
  --warn-line: #f0d99a;
  /* The amber FILL for a data segment in a stacked bar, which is a different job from `--warn` (ink)
     and `--warn-tint` (a wash for ink to sit on). The amber segment of `.sp-bar` used the tint and so
     was invisible in both themes: 1.05:1 against its own track on light, 1.19:1 on dark.
     This is the one bar colour that cannot be a single fixed value the way the lime, the salmon red
     and the slate black are. Those three sit mid-luminance and read on either track; an amber light
     enough for the dark track (9.77:1) is 1.71:1 on the light one, and an amber dark enough for the
     light track goes muddy on dark. So it moves with the theme, and each value is measured against
     the track it is actually drawn on. */
  --bar-amber: #b86e0a;           /* 3.55:1 on --surface-sunken */

  --danger: #b00020;
  --danger-tint: #fff5f6;
  --danger-line: #f0b0b8;

  --info: #2b3678;
  --info-tint: #eef2fb;
  --info-line: #cdd8f0;

  /* ----- data tiers (the AI data-classification standard) and risk bands -----
     These CARRY MEANING, so they are never derived by inverting or filtering the theme: each gets a
     dark variant chosen for contrast on a dark surface. Green/Amber/Red/Black are the tiers from the
     policy, so their hues are not free to change. */
  --tier-green: #1a7f4b;
  --tier-green-tint: #f1faf4;
  --tier-amber: #936b09;          /* darkened with --warn, which it shares a hue with: an Amber tier
                                     label was 3.05:1 on its own tint. The HUE is the information
                                     and it is unchanged; only the lightness moved. */
  --tier-amber-tint: #fff7e6;
  --tier-red: #b00020;
  --tier-red-tint: #fff5f6;
  --tier-black: #231f20;
  --tier-black-tint: #f1f1f4;

  /* ----- severity, for the software scan and the shadow-AI findings -----
     Four steps rather than the three status colours, because a finding list needs to separate
     Critical from High and the status palette has no room for it. Same rule as the tiers: these mean
     something, so the dark variants are chosen rather than derived. */
  --sev-critical: #b00020;
  --sev-high: #cb4e1c;            /* was #d9531e: 4.03:1 on a card */
  --sev-medium: #936b09;          /* the amber again, and for the same reason */
  --sev-low: #6b6b6b;

  /* ----- a faint hairline, and the "off" dot in a bucket list -----
     Not text anywhere in the tree. It does carry state as a dot, so it is held to the 3:1 graphics
     threshold rather than 4.5:1; it measured 2.32:1 on the page. */
  --faint: #838eaa;

  /* ----- focus -----
     A focus indicator has to reach 3:1 against what it sits on (1.4.11), and translucent lime never
     did: rgba(155,197,94,0.45) over the page composites to 1.30:1, and the 0.35 spelling that was
     copied into twenty pages is worse still. Solid, and dark enough to see. The only use is
     `outline: 2px solid`, so the transparency was buying nothing. */
  --focus-ring: #729a37;

  /* ================= ALIASES, kept only for the sweep =================
     The names the 24 per-page blocks used. Keeping them here means a page's local :root can be
     deleted without touching the rules that read these, so the consolidation is reviewable in
     pieces instead of one 2,000-line diff. Delete an alias once nothing references it. */
  --bg: var(--surface);
  --muted: var(--ink-muted);
  --hover: var(--surface-hover);
  --phase: var(--surface-sunken);
  --hover-lime: var(--accent-tint);
  --no: var(--danger);
  --navy: var(--epx-navy);
  --blue: var(--epx-blue);
  --bad: var(--danger);
  --red: var(--epx-red);
}

/*
 * ================================ DARK =================================
 * Applied by an attribute on <html>, set by a one-line INLINE script in every page's <head>, before
 * this stylesheet paints. Inline and duplicated per page on purpose: an external script cannot be
 * guaranteed to execute before the first paint, and a page that themes itself after layout flashes
 * white. An attribute rather than a class so both states can be written explicitly and neither has
 * to be "the absence of the other".
 *
 * localStorage("cortex-theme") holds light | dark | system. The DATABASE is the truth
 * (cortex_access.prefs, migration 0066) and /profile writes it into localStorage on every load, so a
 * new device converges on the stored choice on the first page after sign-in.
 *
 * STILL OWED: nothing listens for the OS theme changing while a page is open, so a person on
 * "system" who flips their laptop to dark sees it on the next navigation rather than immediately.
 *
 * The surfaces are neutral greys with a slight blue cast rather than brand navy: navy is a
 * saturated accent and a whole interface built on it is exhausting to read for long. The brand
 * still shows in the bands, the accent and the tints.
 *
 * NOT AN INVERSION. Two things had to be chosen rather than computed:
 *   --accent-strong and --brand-ink both go LIGHTER, because the deep lime #4f7d28 and the brand
 *     navy are text colours picked to read on white and both disappear on a dark surface;
 *   the tier and status tints become low-opacity washes of their own hue instead of the near-white
 *     pastels, which would be brighter than the page and turn every banner into a lamp.
 */
:root[data-theme="dark"] {
  --surface: #14161c;
  --surface-raised: #1c1f27;
  --surface-sunken: #101218;
  --surface-hover: #232733;

  --ink: #e8e9ee;
  --ink-muted: #a2a7b4;
  --ink-inverse: #f2f4fa;         /* still near-white: a navy band is dark in both themes */
  --brand-ink: #a8bbef;           /* the navy, lightened until it reads on --surface */

  --line: #2c3040;
  --line-strong: #3d4356;

  --brand-band: #232a55;          /* the blue, darkened so a band is chrome and not a light source */
  --brand-band-ink: #eef2fb;

  --accent: #9bc55e;              /* the lime holds up on dark, so it does not move */
  --accent-hover: #aad472;
  --accent-ink: #14161c;          /* on a lime fill, dark ink still reads: it is the fill that is light */
  --accent-strong: #b7dd8a;       /* the deep lime would vanish here */
  --accent-tint: #22301a;
  --field-border: #9bc55e;        /* the lime has ample contrast on a dark surface, so a field keeps
                                     the accent here and only the light theme needed a deeper value */

  --ok: #7cc054;
  --ok-tint: #16251a;
  --ok-line: #2f5a38;
  --ok-ink: #14161c;              /* the green is LIGHT here, so its ink is dark */

  --warn: #e0b64a;
  --warn-tint: #2a2213;
  --warn-line: #5c4a1c;
  --bar-amber: #e0b64a;           /* 9.77:1 on --surface-sunken */

  --danger: #ef7b8a;
  --danger-tint: #2b1418;
  --danger-line: #6b2734;

  --info: #93a6e8;
  --info-tint: #171d33;
  --info-line: #333c66;

  --tier-green: #7cc054;
  --tier-green-tint: #16251a;
  --tier-amber: #e0b64a;
  --tier-amber-tint: #2a2213;
  --tier-red: #ef7b8a;
  --tier-red-tint: #2b1418;
  --tier-black: #cfd2db;          /* the "black" tier has to be VISIBLE on a dark surface */
  --tier-black-tint: #24262e;

  /* Severity, lifted to read on a dark surface. Critical stays the most alarming of the four and
     Low stays the quietest, because the ORDER is the information. */
  --sev-critical: #ef7b8a;
  --sev-high: #f0955f;
  --sev-medium: #e0b64a;
  --sev-low: #8b91a0;

  --faint: #6e748a;

  /* Solid here too, though the translucent version did pass: a translucent ring's contrast depends
     on whatever it happens to sit on, so it has to be re-measured for every surface, and a solid one
     does not. */
  --focus-ring: #9bc55e;

  /* Tell the browser, so form controls, scrollbars and the caret follow. Without this a native
     <select> stays a white slab in the middle of a dark page. */
  color-scheme: dark;
}

:root[data-theme="light"] { color-scheme: light; }

/* A focus ring that survives both themes. It was previously spelled out per page, and on a dark
   surface several of those spellings were invisible. */
:where(a, button, input, select, textarea, [tabindex]):focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
}
