/*
 * THE ARGUS HOUSE THEME. One file, and the only place a colour is chosen.
 *
 * Read docs/decisions/0020-one-house-theme-in-one-file.md for why this exists
 * and apps/shared/README.md for how to build a new screen on it.
 *
 * -------------------------------------------------------------------------
 * WHAT THIS REPLACED, so nobody rebuilds it
 * -------------------------------------------------------------------------
 * The palette used to be COPY-PASTED into five stylesheets, with two separate
 * tests asserting the copies still agreed. That works right up until somebody
 * adds a sixth screen, and it makes a theme change five identical edits.
 * Tokens are now defined HERE and nowhere else -- and that is not a convention,
 * it is enforced: tests/test_theme.py fails on a `--token:` definition found in
 * any app stylesheet. Do not reintroduce a `:root` block in an app sheet.
 *
 * -------------------------------------------------------------------------
 * THIS FILE IS BOTH LINKED AND INLINED, and it has to survive both
 * -------------------------------------------------------------------------
 * Every screen LINKS it as an absolute `/shared/theme.css`. Absolute, not
 * relative, for the reason scripts/build_site.sh already gives about
 * /shared/session.js: the Ardagh build lifts apps/live to the site root, so a
 * `../shared/` would resolve at one depth and 404 at the other. Every `make`
 * target serves apps/ rather than apps/<screen>/ so the same path works
 * locally. Never link this file relatively.
 *
 * The two dashboard builders INLINE it instead, because an emailed report is
 * one self-contained file (ADR-0007) that has no origin to fetch from. So:
 *
 *   - nothing in here may depend on a second file to be correct, and
 *   - the @font-face URL below WILL 404 in an emailed file.
 *
 * That second point is deliberate and is why every font stack here names real
 * fallbacks and every @font-face carries `font-display: swap`. An emailed
 * report renders in the fallback face and is still a finished document. A
 * theme that fell back to Times because the stack ended at `sans-serif` would
 * not be.
 *
 * -------------------------------------------------------------------------
 * WHY THE TYPEFACE IS VENDORED RATHER THAN FETCHED FROM GOOGLE
 * -------------------------------------------------------------------------
 * The house spec asks for Outfit via `@import` from fonts.googleapis.com.
 * That cannot work here, and it would fail SILENTLY, which is the failure mode
 * this repo has already been bitten by twice in production:
 *
 *   1. The deployed CSP is `style-src 'self' 'unsafe-inline'; font-src 'self'`.
 *      A Google Fonts stylesheet and the font file it points at are both
 *      blocked. The page still renders -- in system-ui -- and looks like a
 *      screen somebody forgot to style.
 *   2. tests/test_capture_app.py requires `integrity` on every remote
 *      stylesheet. Google serves DIFFERENT CSS per User-Agent (woff2 to a
 *      current browser, woff to an old one), so there is no stable hash to
 *      pin. A hash that is right today is a blank stylesheet tomorrow.
 *   3. A warehouse tablet on Bellville's wifi should not block first paint on
 *      a third-party fetch, and `make test` must work offline.
 *
 * So the file is served from our own origin, `font-src 'self'` already allows
 * it, no CSP change was needed and no integrity attribute is required because
 * it is same-origin. Outfit is under the SIL Open Font License 1.1, which
 * permits redistribution; the licence ships beside it in fonts/OFL.txt and
 * must stay there.
 *
 * It is ONE variable file per subset covering weights 300-800 -- 32KB latin,
 * 15KB latin-ext -- rather than six static weights, which is both smaller and
 * the reason `font-weight: 300 800` appears below.
 */

@font-face {
  font-family: "Outfit";
  font-style: normal;
  font-weight: 300 800;
  font-display: swap;
  src: url("/shared/fonts/outfit-latin.woff2") format("woff2");
  /* Latin. Everything this product writes, plus the currency and punctuation
     the reports use. */
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
                 U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122,
                 U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Outfit";
  font-style: normal;
  font-weight: 300 800;
  font-display: swap;
  src: url("/shared/fonts/outfit-latin-ext.woff2") format("woff2");
  /* Latin Extended. Carried for Afrikaans and for customer names that reach us
     with accents on them -- a name is stored EXACTLY AS TYPED (see the customer
     alias rules in CLAUDE.md), so the face has to be able to draw it. */
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
                 U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,
                 U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* ==========================================================================
 * TOKENS -- light
 *
 * Use the ROLE, never the hex. `var(--accent-figure)`, not `#4f5bd5`. A hex
 * written into a component is a colour that will not follow the theme into
 * dark mode and will not follow it into the next revision either.
 * ========================================================================== */
:root {
  color-scheme: light;

  /* ---- grounds. TWO per screen, and no third ----
     --page behind everything, --surface for cards. --surface-alt is an inset
     block sitting ON a card, and it carries no border and no shadow. */
  --page:        #f6f7fb;
  --surface:     #ffffff;
  --surface-alt: #f6f7fb;

  /* ---- ink ----
     Every value here is MEASURED against its ground, not eyeballed:
     tests/test_theme.py recomputes the WCAG contrast of each one and fails
     below the floor. Two of them differ from the house spec BECAUSE of that
     measurement, and the deviations are recorded in ADR-0020 rather than
     quietly applied:

       --footnote  spec #8d93a1 -> 2.88:1 on --page. Failed. This is the token
                   that renders "every grade split shown is invented" (ADR-0016)
                   and "demonstration data" under a client-facing figure. A
                   provenance line nobody can read is worse than no line, so it
                   was darkened until it passed.
       --faint     spec #9aa0ad -> 2.45:1. Held to 3:1 rather than 4.5:1, and
                   ONLY because it is fenced to absent-state glyphs that are
                   never the sole carrier of meaning -- the same rule the
                   capture screens already follow. Never set reading copy in it. */
  --ink:      #1c1f27;
  --body:     #4b5563;
  --muted:    #6b7280;
  --faint:    #737a89;
  --footnote: #6b7282;

  /* ---- lines. 1px, always; nothing heavier ---- */
  --hairline:    #eaedf2;
  --rule:        #dfe3ec;
  --card-border: #edeff5;

  /* ---- brand accent, periwinkle ----
     --accent is a FILL and carries no text: bars, the marker, the active
     control. --accent-figure is the one that text sits on or is set in, so it
     is the darker of the two and it is what the solid callout uses as its
     ground (white on it measures 5.54:1; --accent would have been 3.77:1 and
     the spec's own callout recipe would have failed its own floor). */
  --accent:        #6b7ae8;
  --accent-figure: #4f5bd5;
  --accent-solid:  #4f5bd5;
  --accent-tint:   #eef0fd;
  --accent-ink:    #5562d7;

  /* ---- status: a pale ground and a saturated ink, ALWAYS as a pair ----
     Never a saturated fill with white text. --ok-ink and --alert-ink are
     darker than the house spec for the reason given above: as published they
     measured 3.06:1 and 3.62:1 on their own grounds. */
  --ok-bg:    #e4f6f5;  --ok-ink:    #0b7c78;
  --warn-bg:  #fdf2e0;  --warn-ink:  #8a5c05;
  --alert-bg: #fdeee9;  --alert-ink: #c14423;

  /* ---- data viz ----
     --bar/--bar-light are the one- and two-series case. The s0-s9 ramp is the
     categorical one: ten hues that must stay TELLABLE APART, which is why it is
     not ten tints of the brand. Charts label their series as well as colouring
     them -- colour is never the only carrier. */
  --bar:       #6b7ae8;
  --bar-light: #c9cff5;

  /* Stacked-proportion fills, which are NOT status chips and cannot follow the
     status rule. The grade-split bar puts white text INSIDE each segment, so
     each fill has to stay dark enough to carry it -- and it has to do that in
     both themes, which the status inks cannot: --alert-ink lightens to a coral
     in dark mode and white on it measures 2.1:1. These are therefore the same
     value in both themes, deliberately, and are the ONLY tokens here that do
     not flip. Every one carries white at 5:1 or better. */
  --fill-good:   #0b7c78;
  --fill-repair: #8a5c05;
  --fill-scrap:  #c14423;

  --s0:#6b7ae8; --s1:#e05c34; --s2:#0e9b96; --s3:#c98a1e; --s4:#8b5cd6;
  --s5:#2f8fb0; --s6:#c2478f; --s7:#7d8a2e; --s8:#9a6740; --s9:#5a6478;

  /* Chart furniture. Deliberately weaker than --hairline: a gridline that
     competes with the data is noise. */
  --grid: #eaedf2;
  --axis: #cdd3de;

  /* ---- geometry ---- */
  --radius-card:  16px;
  --radius-inset: 12px;
  --radius-chip:  8px;
  --radius-pill:  20px;

  /* Two layers, both nearly transparent. A lift, not a drop shadow. Do not
     increase it -- the whole look rests on cards that sit barely off the page. */
  --card-shadow: 0 1px 2px rgba(28,31,39,.04), 0 10px 28px rgba(28,31,39,.035);

  /* Minimum tap target. 52 rather than the spec's 44 because these screens are
     used at arm's length with gloves on -- see apps/receiving/receiving.css.

     --tap-desk is the SAME ROLE for a screen operated at a desk on a laptop:
     apps/audit-intake is read sitting down, and 52px controls on it are the
     warehouse floor's requirement rather than that page's. It is a named token
     here rather than a `body { --tap: 40px }` in that stylesheet, because the
     rule is that a screen needing its own value needs a NAME for it -- a local
     redefinition is a colour (or a size) that will not follow the next change,
     in a file nobody will think to open. ADR-0020. */
  --tap: 52px;
  --tap-desk: 40px;

  /* ---- type ----
     The fallback stack is load-bearing, not decoration: it is what an emailed
     report and an offline tablet actually render in. */
  --font-sans: "Outfit", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;

  /* ==== COMPATIBILITY ALIASES ====
     The names the five app stylesheets already use, pointed at the new roles.
     They are declared ONCE and never repeated in the dark blocks below, because
     a custom property holding `var(--x)` resolves at USE time -- so these follow
     --accent and friends into dark mode automatically and cannot drift.
     New code should use the roles above; these exist so that a theme change did
     not have to become a rename across ~2 000 lines of untested CSS. */
  --surface-2:    var(--surface-alt);
  --sunken:       var(--surface-alt);
  --ink-2:        var(--body);
  --border:       var(--card-border);
  --hair:         var(--hairline);
  /* --series is BOTH a chart stroke and the ground of every filled control on
     the capture screens -- the active segment, the primary button, the pressed
     filter. Those carry white text, and white on --accent measures 3.77:1,
     which is under the floor. It points at the DARKER accent for that reason;
     fourteen rules across five stylesheets depend on this one line. Bar fills
     that carry no text can use --bar (= --accent) instead. */
  --series:       var(--accent-solid);
  --series-ink:   #ffffff;
  --raw:          var(--bar-light);
  --band:         var(--accent-tint);
  --chip:         var(--surface-alt);
  --critical:      var(--alert-ink);
  --critical-wash: var(--alert-bg);
  --good:          var(--ok-ink);
  --good-wash:     var(--ok-bg);
  --repair:        var(--warn-ink);
  --scrap:         var(--alert-ink);
  --warn-line:     var(--warn-ink);
  --radius:        var(--radius-card);
}

/* ==========================================================================
 * TOKENS -- dark
 *
 * The house spec has no dark mode and says so; what it does say is that if the
 * application needs one, the grounds go to a COOL near-black (the #1c1f27
 * family) rather than a warm one. These screens already had dark mode before
 * the theme landed and removing it would be a regression, so it is built to
 * that instruction.
 *
 * ONE DELIBERATE DEPARTURE: --accent-figure is LIFTED here rather than kept.
 * The spec says keep it for figures, and the ROLE is kept -- but #4f5bd5 on a
 * near-black ground measures 2.6:1, so keeping the VALUE would have made every
 * KPI on every dark screen unreadable. The instruction is honoured by keeping
 * the figures accented, not by keeping the hex.
 *
 * Both blocks below are the same declarations, and they have to be. The bare
 * media query is what the DEFAULT "system" setting hits, since it stamps no
 * attribute; the [data-theme] block is what an explicit choice hits. Redefine
 * TOKENS here and nothing else -- a component colour declared only inside one
 * of these blocks does not exist in the other state.
 * ========================================================================== */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;
    --page:        #14161c;
    --surface:     #1c1f27;
    --surface-alt: #232733;

    --ink:      #f2f4f8;
    --body:     #b9c0cd;
    --muted:    #949cab;
    --faint:    #7d8494;
    --footnote: #949cab;

    --hairline:    #272b36;
    --rule:        #333846;
    --card-border: #2a2f3b;

    --accent:        #7d8bf0;
    --accent-figure: #9aa5f5;
    --accent-solid:  #4a56cc;
    --accent-tint:   #262c48;
    --accent-ink:    #a7b1f7;

    --ok-bg:    #10312f;  --ok-ink:    #4fd5cf;
    --warn-bg:  #332711;  --warn-ink:  #e5b45c;
    --alert-bg: #3a2019;  --alert-ink: #ff8f6a;

    --bar:       #7d8bf0;
    --bar-light: #3b4270;

    --s0:#8b97f3; --s1:#f0805a; --s2:#3fc0ba; --s3:#e0aa47; --s4:#ab86e6;
    --s5:#54b0cf; --s6:#e074ad; --s7:#a3b04a; --s8:#c08a5e; --s9:#8f99ad;

    --grid: #272b36;
    --axis: #3c4353;

    --card-shadow: 0 1px 2px rgba(0,0,0,.30), 0 10px 28px rgba(0,0,0,.22);
  }
}
:root[data-theme="dark"] {
  color-scheme: dark;
  --page:        #14161c;
  --surface:     #1c1f27;
  --surface-alt: #232733;

  --ink:      #f2f4f8;
  --body:     #b9c0cd;
  --muted:    #949cab;
  --faint:    #7d8494;
  --footnote: #949cab;

  --hairline:    #272b36;
  --rule:        #333846;
  --card-border: #2a2f3b;

  --accent:        #7d8bf0;
  --accent-figure: #9aa5f5;
  --accent-solid:  #4a56cc;
  --accent-tint:   #262c48;
  --accent-ink:    #a7b1f7;

  --ok-bg:    #10312f;  --ok-ink:    #4fd5cf;
  --warn-bg:  #332711;  --warn-ink:  #e5b45c;
  --alert-bg: #3a2019;  --alert-ink: #ff8f6a;

  --bar:       #7d8bf0;
  --bar-light: #3b4270;

  --s0:#8b97f3; --s1:#f0805a; --s2:#3fc0ba; --s3:#e0aa47; --s4:#ab86e6;
  --s5:#54b0cf; --s6:#e074ad; --s7:#a3b04a; --s8:#c08a5e; --s9:#8f99ad;

  --grid: #272b36;
  --axis: #3c4353;

  --card-shadow: 0 1px 2px rgba(0,0,0,.30), 0 10px 28px rgba(0,0,0,.22);
}

/* ==========================================================================
 * BASE
 * ========================================================================== */
*, *::before, *::after { box-sizing: border-box; }

/* The `hidden` ATTRIBUTE has to beat any display rule set by a class.
 * `.banner { display:flex }` outranks the browser's `[hidden] { display:none }`
 * on specificity, so without this every banner shows on load and `el.hidden =
 * true` does nothing. This used to be repeated in all five stylesheets with a
 * test asserting each still had it; it is one rule now, in the file all five
 * of them load. The consequence of losing it is worst on the reports, whose
 * demonstration banner is DRIVEN BY THE DATA and is meant to disappear the day
 * real grading arrives (ADR-0016) -- a bug that surfaces on the day the
 * client's data becomes real. */
[hidden] { display: none !important; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--page);
  color: var(--body);
  font-family: var(--font-sans);
  font-size: 15px;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

/* Tight tracking scales with size; body never gets it. */
h1, h2, h3, h4 { color: var(--ink); font-weight: 600; text-wrap: balance; }
h1 { font-size: 30px; letter-spacing: -.022em; font-weight: 700; }
h2 { font-size: 21px; letter-spacing: -.01em; }
h3 { font-size: 16.5px; }

a { color: var(--accent-ink); }
a:hover { color: var(--accent-figure); }

/* The house style allows no second typeface EXCEPT for genuine code and IDs,
   which is what --font-mono is for. It is wired up here rather than left as a
   token nobody applies: Outfit ligates a double hyphen, so a token name written
   as `--body` in prose renders as a dash. */
code, kbd, samp, pre {
  font-family: var(--font-mono);
  font-size: 0.92em;
  font-variant-ligatures: none;
}
code {
  background: var(--surface-alt);
  padding: 1px 5px;
  border-radius: 5px;
}
pre code { background: none; padding: 0; }

/* One focus ring for the whole product. A warehouse tablet is also driven from
   a keyboard at the receiving desk. */
:focus-visible {
  outline: 2px solid var(--accent-figure);
  outline-offset: 2px;
  border-radius: 3px;
}

/* Figures line up in columns everywhere in this product. */
.n, .num, td.n, th.n, .pct, .fig .v, .kpi__n, .stat .n {
  font-variant-numeric: tabular-nums;
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
}

/* ==========================================================================
 * PRIMITIVES
 *
 * The small set every screen shares. An app stylesheet loads AFTER this file,
 * so it overrides any of these at equal specificity -- that is the intended
 * cascade, and it is why nothing here is written with an id or an !important
 * (bar the [hidden] rule above, which has to win).
 * ========================================================================== */

/* ---- kicker: the small uppercase category that sits ABOVE a title ----
   Module name above a screen name, group name above a panel. */
.kicker {
  display: block;
  font-size: 11.5px;
  font-weight: 600;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--muted);
  margin: 0 0 6px;
}

/* ---- card: THE container primitive. Everything else is a variant ---- */
.card {
  background: var(--surface);
  border: 1px solid var(--card-border);
  border-radius: var(--radius-card);
  padding: 22px;
  box-shadow: var(--card-shadow);
}

/* Cards are sized to their CONTENT and are never stretched to a tall sibling.
   This is load-bearing to the look, not a detail. */
.cardrow {
  display: grid;
  gap: 12px;
  align-items: start;
}

/* An inset block sitting on a card: no border, no shadow, quieter ground. */
.inset {
  background: var(--surface-alt);
  border-radius: var(--radius-inset);
  padding: 14px 16px;
}

/* ---- badge / status chip: pale ground, saturated ink, never a solid fill ---- */
.badge {
  display: inline-block;
  font-size: 11.5px;
  font-weight: 600;
  padding: 4px 10px;
  border-radius: var(--radius-chip);
  background: var(--accent-tint);
  color: var(--accent-ink);
  white-space: nowrap;
}
.badge--ok    { background: var(--ok-bg);    color: var(--ok-ink); }
.badge--warn  { background: var(--warn-bg);  color: var(--warn-ink); }
.badge--alert { background: var(--alert-bg); color: var(--alert-ink); }
.badge--quiet { background: var(--surface-alt); color: var(--muted); }
/* A status WORD, as opposed to a number, gets the tracking. */
.badge--state { text-transform: uppercase; letter-spacing: .1em; font-size: 10.5px; }

.pill {
  display: inline-block;
  font-size: 12px;
  font-weight: 600;
  padding: 4px 12px;
  border-radius: var(--radius-pill);
  background: var(--surface-alt);
  color: var(--body);
  white-space: nowrap;
}

/* ---- KPI row: plain grid, NO cards. Cards around KPIs make a screen busy ----
   Numeral in the accent, label under it in body ink. Never colour the label. */
.kpi {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: 20px 26px;
}
.kpi__n {
  display: block;
  font-size: 38px;
  font-weight: 700;
  letter-spacing: -.03em;
  line-height: 1.05;
  color: var(--accent-figure);
}
.kpi__l {
  display: block;
  font-size: 14px;
  color: var(--body);
  margin-top: 4px;
}

/* ---- callouts ----
   CONCLUSION: solid, ONE PER SCREEN at most. Ground is --accent-figure rather
   than --accent so that white body copy clears 4.5:1 -- see the token comment.
   CAVEAT: the honesty note. "Demonstration data", "prototype", "invented
   grading". Amber ground, amber ink, no border. */
.callout {
  border-radius: var(--radius-card);
  padding: 16px 18px;
}
.callout--conclusion {
  background: var(--accent-solid);
  color: #ffffff;
}
.callout--conclusion b,
.callout--conclusion strong,
.callout--conclusion h2,
.callout--conclusion h3 { color: #ffffff; font-weight: 600; }
/* A LITERAL WHITE, NOT A TOKEN, AND THAT IS THE WHOLE POINT.
   This read `color: var(--accent-tint)` until 2026-09-08. That token is a pale
   wash in light mode -- #eef0fd on #4f5bd5, 4.89:1, fine -- and a near-black
   navy in dark, #262c48 on #4a56cc, 2.27:1. So the body copy of the ONE solid
   callout in the system was unreadable on every dark screen.

   The ground here is the saturated accent in BOTH themes; that is what
   .callout--conclusion IS. Text on it must therefore NOT come from a token
   that flips, because a token that flips is answering a question this element
   never asks. 4.56:1 light, 4.91:1 dark.

   Found by rendering the mock-up in analytics/prototype/theme/, not by reading
   this file -- and NOT by the contrast test that was meant to cover it, which
   checked only white on fills. test_theme.py now checks the general case. */
.callout--conclusion p { color: rgba(255,255,255,.87); margin: 6px 0 0; }
.callout--caveat {
  background: var(--warn-bg);
  color: var(--warn-ink);
  font-weight: 500;
}
.callout--caveat b, .callout--caveat strong { color: var(--warn-ink); }
.callout--caveat p { color: var(--warn-ink); margin: 4px 0 0; }

/* ---- the accent marker: the one decorative element in the system ---- */
.marker {
  display: block;
  width: 52px;
  height: 5px;
  border-radius: 3px;
  background: var(--accent);
  margin: 0 0 14px;
}

/* ---- tables: hairline dividers only. No striping, no vertical borders,
        no outer frame. ---- */
.t {
  width: 100%;
  border-collapse: collapse;
  font-size: 14px;
}
.t th {
  text-align: left;
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .14em;
  color: var(--muted);
  padding: 0 12px 12px 0;
  border-bottom: 1px solid var(--rule);
  white-space: nowrap;
}
.t td {
  padding: 14px 12px 14px 0;
  border-bottom: 1px solid var(--hairline);
  color: var(--body);
}
.t tbody tr:last-child td { border-bottom: 0; }
.t td.n, .t th.n { text-align: right; white-space: nowrap; }
.t td.n { font-weight: 600; color: var(--ink); }
.t tr.total td { border-bottom: 0; font-weight: 600; color: var(--ink); }
.t tr.total td.n { font-weight: 700; }

/* ---- footer rule: pins a closing statement to the foot of a frame ---- */
.footrule {
  margin-top: 26px;
  padding-top: 14px;
  border-top: 1px solid var(--hairline);
  font-size: 12px;
  color: var(--footnote);
}

/* ---- buttons ----
   ONE saturated button per screen, and it is .btn--primary. Everything else is
   quiet. A warehouse screen gets --tap; a report toolbar does not need it. */
.btn {
  font: inherit;
  font-weight: 600;
  font-size: 14px;
  padding: 9px 16px;
  border-radius: var(--radius-chip);
  border: 1px solid var(--card-border);
  background: var(--surface);
  color: var(--ink);
  cursor: pointer;
}
.btn:hover { background: var(--surface-alt); }
/* --accent-solid, NOT --accent-figure. Both are periwinkle in light mode and
   the difference does not show; in dark mode --accent-figure LIGHTENS so a KPI
   can be read on a near-black ground, and white on it drops to 2.1:1. A
   primary button is a fill that carries text, so it takes the fill token.
   Caught by looking at the dark render, not by the test that was supposed to
   cover it -- which is why that test now derives its list from this file. */
.btn--primary {
  background: var(--accent-solid);
  border-color: var(--accent-solid);
  color: #ffffff;
}
.btn--primary:hover { filter: brightness(1.1); }
.btn[disabled] { opacity: .45; cursor: not-allowed; }

/* ---- the theme control ----
   Injected by /shared/theme-toggle.js into whatever element on a page carries
   `data-theme-toggle`. Styled here rather than in eight app stylesheets for
   the same reason every other primitive is. Deliberately quiet: it is a
   preference, not an action, and no screen's primary control. */
.themebtn {
  font: inherit;
  font-size: 12.5px;
  font-weight: 600;
  min-width: 4.6em;
  padding: 6px 12px;
  border-radius: var(--radius-chip);
  border: 1px solid var(--card-border);
  background: var(--surface);
  color: var(--body);
  cursor: pointer;
}
.themebtn:hover { background: var(--surface-alt); color: var(--ink); }

/* ==========================================================================
 * PRINT
 *
 * Every report on this product gets printed or saved to PDF by somebody. The
 * card shadow prints as a grey smear on most drivers, so it goes.
 * ========================================================================== */
@media print {
  /* Print always renders LIGHT, whatever the screen theme is. Without this a
     reader who prefers dark mode saves a PDF with a near-black ground, which
     wastes toner and is what a client forwards to somebody. This lived in
     apps/reports/audit-panels.css and therefore fixed exactly one of the
     product's report screens; it is here so it fixes all of them.

     Only TOKENS are redefined -- every component follows automatically. */
  :root, :root:not([data-theme="light"]), :root[data-theme="dark"] {
    color-scheme: light;
    --page:#ffffff; --surface:#ffffff; --surface-alt:#f6f7fb;
    --ink:#000000; --body:#333333; --muted:#5c5c5c;
    --faint:#6f6f6f; --footnote:#5c5c5c;
    --hairline:#e2e2e2; --rule:#cfcfcf; --card-border:#d5d9e2;
    --accent:#4f5bd5; --accent-figure:#3f49b5; --accent-solid:#3f49b5;
    --accent-tint:#eef0fd; --accent-ink:#3f49b5;
    --grid:#d5d5d0; --axis:#999999;
    --card-shadow: none;
  }
  body { background: #fff; }
  .card { box-shadow: none; border-color: #d5d9e2; break-inside: avoid; }
  .callout--conclusion { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
}
