/* Platform design tokens — the ONE source of truth for colour, type and
 * elevation across every app.
 *
 * WHY THIS FILE EXISTS
 * --------------------
 * Before it, each app's styles.css opened with its own near-identical
 * `:root` block, and the four had already diverged: hub/admin carried a
 * 13-token set, trash-analysis had added --teal-* / --amber-* / --bg, and
 * rent-analysis had bolted a whole second palette (--rd-navy, --rd-slate-*)
 * on top of the shared one. On top of that, 186 raw hex literals bypassed
 * tokens entirely, most of them just re-typing a value a token already
 * held (#6b7280 twelve times, #d1d5db seven, and so on).
 *
 * That is the same class of drift libs/frontend_common/platform.css was
 * created to end for the widget layer. This file does it for the palette,
 * and platform.css's docstring ("Deliberately NOT here: :root tokens")
 * has been updated to point here rather than to four app stylesheets.
 *
 * LOAD ORDER
 * ----------
 * Every app's index.html loads, in this order:
 *
 *     tokens.css      <- this file: :root variables + @font-face only
 *     platform.css    <- shared widget layer, consumes these tokens
 *     styles.css      <- the app's own layout, consumes these tokens
 *
 * tokens.css must come FIRST because both files below it resolve var()
 * against it. tests/js/test_shared_css_loaded.js gates this ordering, and
 * scripts/new_app.py's scaffold ships it so a future app cannot miss it.
 *
 * NO DARK MODE, DELIBERATELY
 * --------------------------
 * These are print-oriented compliance tools — rent rolls and vendor
 * invoices that get printed and circulated to people who never open the
 * app (see the @media print rules in platform.css and each app's
 * styles.css). A dark theme would have to be designed against those print
 * rules and against ~200 remaining hardcoded colours, and nobody has asked
 * for one. If it is ever wanted, the work is: add a
 * @media (prefers-color-scheme: dark) block HERE that redefines the
 * semantic tokens only — never the legacy aliases, and never in an app's
 * own stylesheet.
 */

/* ── Typeface: IBM Plex, self-hosted ───────────────────────────────────
 * Self-hosted rather than linked from a font CDN: the apps sit behind one
 * CloudFront distribution with a WAF and a strict same-origin posture, and
 * a third-party font host would be both a new external dependency on every
 * page load and a privacy leak of user IPs to that host. These are the
 * latin subset only (U+0000-00FF + punctuation), 116KB across seven files,
 * served from each app's own bucket by cdn_stack.py's frontend_common
 * source — no build step, no npm.
 *
 * IBM Plex is licensed under the SIL Open Font License 1.1, which permits
 * redistribution like this. See fonts/OFL.txt.
 *
 * Three families, three jobs, one type system — which also retires the
 * THREE different monospace stacks that had accumulated across the apps
 * ("SF Mono"/"Fira Code" in trash-analysis, ui-monospace in admin, bare
 * `monospace` elsewhere), so aligned numeric data now looks the same
 * everywhere it appears.
 */
@font-face {
  font-family: 'IBM Plex Sans';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('fonts/ibm-plex-sans-400.woff2') format('woff2');
}
@font-face {
  font-family: 'IBM Plex Sans';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('fonts/ibm-plex-sans-600.woff2') format('woff2');
}
@font-face {
  font-family: 'IBM Plex Sans';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('fonts/ibm-plex-sans-700.woff2') format('woff2');
}
@font-face {
  font-family: 'IBM Plex Serif';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('fonts/ibm-plex-serif-600.woff2') format('woff2');
}
@font-face {
  font-family: 'IBM Plex Serif';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('fonts/ibm-plex-serif-700.woff2') format('woff2');
}
@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('fonts/ibm-plex-mono-400.woff2') format('woff2');
}
@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('fonts/ibm-plex-mono-500.woff2') format('woff2');
}

:root {
  /* ── Type ───────────────────────────────────────────────────────────
   * Display is used with RESTRAINT: the wordmark, page/section headings,
   * and KPI numerals. Everything operational — body copy, labels, buttons,
   * table cells — is sans. Mono is for data that must align in a column
   * (ids, amounts, timestamps); pair it with font-variant-numeric:
   * tabular-nums, which --font-mono does not imply on its own.
   */
  --font-display: 'IBM Plex Serif', Georgia, 'Times New Roman', serif;
  --font-sans: 'IBM Plex Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  --font-mono: 'IBM Plex Mono', ui-monospace, 'SF Mono', Menlo, monospace;

  /* ── Ground and ink ─────────────────────────────────────────────────
   * A warm parchment ground -- treatment A, which Ellen Rourke chose on
   * the call of 2026-09-08 and then confirmed unprompted: "I like A... It's
   * very similar to what we have already done. I like the cleanest of it"
   * (16:34) and, to Kevin half an hour later, "that's what I chose. Option
   * A" (28:02).
   *
   * READ THIS BEFORE CHANGING IT TO A COOL GREY. That change was already
   * made, on 2026-09-08, and REVERSED the same day. This comment described
   * the cool grey for a while after the values had gone back to A, which is
   * the more dangerous half of the mistake: the next reader finds a
   * confident rationale for a decision the client did not make, and makes
   * it again. Two quotes carried it, and both were misread:
   *
   *   - "I don't like the beigey background... I don't like the beigey
   *     kind of stuff" (15:05) is real, and it was about the ADMIN page,
   *     whose <main> and <table> were transparent so the ground filled the
   *     whole content area. Insurance put its content in white tiles, so
   *     the same token read as a thin frame. One token, two renderings;
   *     she was reacting to the wall. That is fixed where it happened --
   *     admin's `main > section` sits on --surface -- not in this file.
   *   - "instead of being light, could be blue... I think we can make it
   *     richer" (44:47) is about her MARKETING WEBSITE and the words
   *     "National Housing Advisors" on it. A different site entirely. It
   *     was half the evidence for repainting this app and it was not
   *     evidence at all.
   *
   * She named a colour once, about admin, and chose A twice, about the app.
   * infra/cdk/scripts/browser_client_requirements_check.py's
   * `palette-still-matches-option-a` pins --paper and --accent on the
   * DEPLOYED page so a drift away from her choice fails out loud rather
   * than being argued about.
   *
   * The ink ramp is four steps, and every one of them clears WCAG AA
   * (4.5:1) on BOTH --paper and --surface. That is a real fix, not a
   * restatement: the muted greys these replace did not. #9ca3af (used for
   * .search-empty, .overview-empty, .search-result-meta) measured 2.54:1
   * on white and #8595ac (the .nha-copyright footer, every app) measured
   * 3.05:1 — both failing, both shipping.
   */
  --paper: #F6F3EC;          /* page background */
  --surface: #FFFFFF;        /* cards, panels, table backgrounds */
  --surface-sunken: #EFEBE1; /* subtle fills, hover rows, inset wells */

  --ink: #211D17;            /* headings, strong emphasis      15.1:1 on paper */
  --ink-body: #3E382E;       /* body copy                      10.5:1 on paper */
  --ink-soft: #6B6355;       /* secondary text, labels          5.4:1 on paper */
  --ink-faint: #766D5E;      /* muted/meta text — the floor     4.6:1 on paper */

  --line: #E3DDCE;           /* hairlines, card borders, dividers */
  --line-strong: #CFC6B2;    /* emphasized dividers, table rules */

  /* ── Accent: Clay ───────────────────────────────────────────────────
   * The one colour allowed to be loud, and the only one used for "this is
   * actionable": links, primary buttons, focus rings, active tabs,
   * selected state. Spend boldness here and nowhere else.
   *
   * A terracotta, and treatment A's accent -- see the ground block above,
   * including why it is NOT the blue this comment used to describe.
   *
   * It does not compete with --navy below because they are different hues,
   * not different lightnesses: navy is furniture, this is the thing you can
   * click. Kevin Rourke's "make the colors a little sharper and more
   * contrasting" on the same call is answered by the ink ramp above -- four
   * steps, every one at or above 4.5:1 on BOTH --paper and --surface, where
   * the greys they replaced measured 2.54:1 and 3.05:1 -- and by the width
   * change below, not by repainting the accent she picked.
   */
  --accent: #B0451F;         /* 5.1:1 on paper, 5.7:1 on white */
  --accent-hover: #963A18;
  --accent-soft: #EFDFD3;    /* tinted background for accent-marked blocks */

  /* ── Structure: Navy ────────────────────────────────────────────────
   * Equal-weight structural partner to the accent, not a second accent. It
   * carries chrome — the app header, KPI accent bars, chart axes — so the
   * accent never has to compete with the furniture. This is also where the
   * platform's previous --blue-900 identity survives, deliberately: the
   * change is meant to read as the same product maturing, not a new one.
   * It is the ONLY blue in the palette; --accent is not one (see above).
   */
  --navy: #1B2B40;           /* header ground; 14.3:1 against white text */

  /* ── Layout ─────────────────────────────────────────────────────────
   * How wide the content column may get, for every app.
   *
   * Kevin Rourke, 2026-09-08: "make it bigger and make it fill up the page
   * more... I see a lot of white space to the side." Measured that day:
   * insurance capped at 980px in a 1378px viewport -- 358px of gutter, 26%
   * of the window -- while the shared footer capped at 1180px, so the FOOTER
   * rendered wider than the content above it. hub and admin were narrower
   * still at 900px, and rent-analysis and trash-analysis each carried their
   * own 1400px. Five answers to one question.
   *
   * 1600px, not 100%: on the ~1378-1440px laptops they actually use this is
   * full width, which is what was asked for, while a 2560px monitor still
   * gets a readable measure instead of a 300-character line. If they want it
   * edge-to-edge everywhere, this is the one line to change.
   *
   * OVERRIDDEN 2026-09-17, Dan, explicit go-ahead after being told this
   * reopens Kevin's request above: "lots of whitespace... Ellen's app was
   * small and compact... everything looks super wide" -- matched to her own
   * original app's `main { max-width:1180px }` exactly (nha-insurance-apps),
   * not picked freehand. This is a real, acknowledged reversal of the
   * 2026-09-08 decision, not an unawares one -- if width complaints come
   * back from Kevin's side, that history is still live above, not erased.
   */
  --content-max: 1180px;

  --navy-light: #2C425E;
  --navy-soft: #EBEEF1;

  /* ── Semantic state ─────────────────────────────────────────────────
   * Separate from the accent on purpose: --accent means "you can act on
   * this", these mean "this is the state of the data". A KPI that is
   * merely important is navy; a KPI that is BAD is danger. Every one of
   * these clears 4.5:1 as text on white and on paper, and carries white
   * text at 4.5:1 when used as a fill.
   */
  /* Darkened from #3F7A4D / #35673F on 2026-09-08, by measurement rather than
     taste: at the old value this colour rendered 4.14:1 on its OWN soft tint
     (--success-soft), which is the badge pattern it exists for. Not a change
     to the look Ellen Rourke approved -- the ground, accent and type are
     untouched -- it is a state colour meeting the floor its comment already
     claimed. */
  --success: #387046;
  --success-hover: #2F6039;
  --success-soft: #DCEBDF;
  /* Darkened from #9A6410, same reason as --success above: 4.26:1 on its own
     --warn-soft tint. (And NOT #B4791E, the value before that, which measured
     3.69:1 -- this token has now failed the floor twice by being tuned against
     one ground and rendered on another.) */
  --warn: #8F5C0E;
  --warn-soft: #F7ECD5;
  --danger: #B23B34;
  --danger-soft: #F7E3E1;
  /* The border for a soft-ground danger panel. --danger at full strength
     was too loud as a 1px rule around a tinted block; this is the tint's
     own darker edge. A token because two apps needed the same value and a
     literal in both is how the old palette drifted in the first place. */
  --danger-border: #E9BDB9;

  /* ── Categorical ────────────────────────────────────────────────────
   * For encoding WHICH, never HOW BAD. Housing program codes (PBV, PBRA,
   * LIHTC), unit statuses, vendor types, chart series — things that are
   * merely different from each other, with no ordering and no severity.
   *
   * These exist because collapsing them into the accent would have
   * destroyed real information: rent-analysis carried .badge-blue/-pink/
   * -peach/-orange and .program-PBV as raw hexes, and those distinctions
   * are what let someone scan a rent roll and see programme mix at a
   * glance. The old values were stock Tailwind ramps (#dbeafe/#1d4ed8,
   * #fce7f3/#be185d) chosen per badge with no relationship to each other;
   * these are one set, chosen together, on the warm ground.
   *
   * Every pairing clears WCAG AA both ways: --cat-N as text on
   * --cat-N-soft, --cat-N as text on --paper, and white on --cat-N. Use
   * -soft as the chip background with the strong value as its text.
   */
  --cat-teal: #2F6F7B;    --cat-teal-soft: #E6EDEF;
  --cat-indigo: #3B4E8F;  --cat-indigo-soft: #E7E9F1;
  --cat-plum: #7A3E76;    --cat-plum-soft: #EFE7EE;
  --cat-olive: #56651F;   --cat-olive-soft: #EAECE4;
  --cat-rust: #8F4718;    --cat-rust-soft: #F1E8E3;
  --cat-slate: #48586B;   --cat-slate-soft: #E9EAED;

  /* --alt predates the set above and is kept as the name trash-analysis's
   * --teal-* alias resolves through. It is simply the first categorical. */
  --alt: var(--cat-teal);
  --alt-soft: var(--cat-teal-soft);

  /* ── Elevation and shape ────────────────────────────────────────────
   * Shadows are warm-tinted to match the ground; a neutral-black shadow
   * on parchment reads as grey haze.
   */
  --shadow-sm: 0 1px 2px rgba(33,29,23,.06), 0 1px 3px rgba(33,29,23,.08);
  --shadow: 0 2px 6px rgba(33,29,23,.07), 0 4px 12px rgba(33,29,23,.07);
  --shadow-lg: 0 4px 12px rgba(33,29,23,.09), 0 12px 32px rgba(33,29,23,.10);
  --radius: 12px;
  --radius-sm: 8px;
  --radius-xs: 6px;

  /* Focus ring, defined once. Keyboard focus was previously either the
   * browser default or, worse, `outline: none` with only a border-colour
   * change to replace it (see the old .modal-input:focus). */
  --focus-ring: 0 0 0 3px rgba(176,69,31,.35);

  /* ── Legacy aliases ─────────────────────────────────────────────────
   * The previous token names, remapped onto the palette above. There are
   * ~228 var(--blue-*)/var(--gray-*) call sites across the four apps and
   * platform.css; aliasing them means the whole platform picks up the new
   * palette coherently in one step, instead of a half-migrated UI where
   * converted rules look new and unconverted ones look old.
   *
   * These are a MIGRATION SHIM, not part of the design system. Do not use
   * them in new code — use the semantic tokens above. They can be deleted
   * once no stylesheet references them; scripts/check_local.sh does not
   * enforce that yet because the remaining call sites are still being
   * converted app by app.
   *
   * The mapping that matters most: --blue-500 was the interactive accent
   * (links, primary buttons, focus, tile titles), so it becomes --accent.
   * --blue-900/-700 were chrome (the header gradient), so they become
   * navy. Getting those two backwards would put the loud colour on the
   * furniture and the quiet colour on the buttons.
   */
  --blue-900: var(--navy);
  --blue-700: var(--navy-light);
  --blue-500: var(--accent);
  --blue-300: #C9825F;
  --blue-100: var(--accent-soft);

  --gray-900: var(--ink);
  --gray-700: var(--ink-body);
  --gray-500: var(--ink-soft);
  --gray-400: var(--ink-faint);
  --gray-300: var(--line);
  --gray-100: var(--surface-sunken);
  --white: #FFFFFF;

  --red-500: var(--danger);
  --red-100: var(--danger-soft);
  --green-500: var(--success);
  --green-100: var(--success-soft);
  --amber-500: var(--warn);
  --amber-100: var(--warn-soft);
  --teal-500: var(--alt);
  --teal-100: var(--alt-soft);

  /* trash-analysis's page-background token. */
  --bg: var(--paper);
}
