/* Design tokens — one semantic layer, swapped by two INDEPENDENT attributes on <html>.
 *
 *   [data-theme]  light | dark      the room: page, chrome, type, all the semantic tokens.
 *                                   ("system" is a stored PREFERENCE that resolves to one of these;
 *                                   it is never written to data-theme.)
 *   [data-cards]  colour | mono     the wallet deck's palette, and ONLY that.
 *
 * They are separate because they answer different questions. Light/dark is about the room you are
 * reading in; colour/mono is about whether the deck is a set of named objects or a single tonal
 * ramp. Folding the second into the first — which is what the old four-button row did — meant you
 * could not have a monochrome deck at night, or a coloured one by day, without also changing the
 * other. Four combinations, two controls.
 *
 * light/dark used to replicate blipton's warm palette (cream ink on black, warm paper in light).
 * That warmth was dropped 2026-08-03 (Danny) for apple.com's neutral ramp — pure white paper on
 * #f5f5f7, neutral greys, no beige anywhere — because the off-white read as a tint rather than as
 * paper. The BEHAVIOUR blipton set is kept: buttons INVERT rather than colour, so --acc is ink
 * rather than a hue and links carry an underline, which is what makes the two products look like
 * one family. Only the hue is gone.
 *
 * The neutral ramp, one set of values shared by both rooms and by site/src/app.css:
 *   #ffffff  #f5f5f7  #e8e8ed  #d2d2d7  #a1a1a6  #86868b  #787880  #6e6e73  #424245  #38383a
 *   #2c2c2e  #1d1d1f  #000000
 * The semantic pills (--fill / --warn / --amber) keep their hue: they carry meaning, not chrome.
 *
 * The NSW Design System theme was removed 2026-07-29 (Danny): the brand blue is not wanted, and the
 * button it occupied is now the colour/mono control. Its two documented traps are recorded in
 * DESIGN.md in case it ever comes back — Link Focus #0085B3 is 4.20:1 (a focus ring only, never
 * body text) and Grey 03 #CDD3D6 is 1.51:1 (decorative hairlines only).
 *
 * Every colour pair is checked with the WCAG relative-luminance formula, not taken on trust — run
 * `node web/contrast.mjs`. Targets: body text >= 4.5:1 (1.4.3 AA), large text >= 3:1, non-text UI
 * >= 3:1 (1.4.11); wallet ink >= 5:1, wallet label >= 4.5:1.
 */

/* Self-hosted: no font CDN, so no third-party request for a government SOE to block, and no
   dependence on Public Sans being installed — measured on this machine, it is not. */
@font-face {
  font-family: 'Public Sans';
  src: url('./fonts/public-sans-var.woff2') format('woff2');
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

/* The mono is self-hosted for the SAME reason, and the reason is stronger here than for the sans.
   This stack used to be `ui-monospace, 'SF Mono', Menlo, monospace`, and every name in it is
   Apple-only: `ui-monospace` is not mapped by Chromium on Windows, and SF Mono / Menlo do not exist
   there. So a Windows SOE — the machine this product is being sold onto — fell through to the
   generic `monospace`, which Chrome resolves to **Courier New**. Mono is not decorative here: it
   carries the .spec labels, the write-log cell refs, and the replayable query URL, which is the one
   string in the product a reader is expected to re-type and verify character by character. That was
   being served in Courier New.

   SF Mono cannot replace it — Apple licenses it for use on Apple platforms only, so embedding it as
   a webfont is not available to us at any price.

   IBM Plex Mono (SIL OFL 1.1), latin subset, three weights, 30 KB total. Its zero is slashed and
   its 1/l/I are unambiguous, which is what a hand-verified URL actually needs. 400 for code and
   query strings, 500 for .spec labels, 600 for the status pills — and 600 is a real file rather than
   a synthesised bold because those pills are where `Complies` / `Does not comply` / `assumed` live.
   Colour is never the sole carrier of meaning here, so the WORD is load-bearing, at 10px, and a
   faux-bold 10px word is the wrong place to save 10 KB.

   The fallback chain still names Consolas: if the woff2 ever fails to load, Windows lands on a real
   screen mono instead of back on Courier New. */
@font-face {
  font-family: 'IBM Plex Mono';
  src: url('./fonts/plex-mono-400-latin.woff2') format('woff2');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'IBM Plex Mono';
  src: url('./fonts/plex-mono-500-latin.woff2') format('woff2');
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'IBM Plex Mono';
  src: url('./fonts/plex-mono-600-latin.woff2') format('woff2');
  font-weight: 600;
  font-style: normal;
  font-display: swap;
}

/* ---- type scale ------------------------------------------------------------------------------
 * Back to blipton's compact scale for now. The NSW scale (14px floor / 16px body) is one line away
 * — swap the two blocks below — and is what the accessibility argument for the planner wants:
 * designsystem.nsw.gov.au/core/typography puts its *smallest* permitted size at 14px, so anything
 * under that is below what NSW allows. Kept in rem so it still scales with the browser's own text
 * setting (WCAG 1.4.4 wants 200% without loss).
 *
 *   NSW scale:  --fs-spec .75rem | --fs-sm .875rem | --fs-base 1rem | --fs-intro 1.125rem
 */
:root {
  --fs-spec: 0.6875rem; /* 11px — blipton .spec */
  --fs-sm: 0.75rem;     /* 12px */
  --fs-base: 0.8125rem; /* 13px */
  --fs-intro: 0.9375rem;/* 15px */
  --fs-h4: 0.875rem;    /* 14px / 700 */
  --fs-h3: 1rem;        /* 16px / 700 */
  --fs-h2: 1.1875rem;   /* 19px / 700 */
  --fs-h1: 1.5rem;      /* 24px / 700 */

  --lh-tight: 1.25;
  --lh-small: 1.4;
  --lh-base: 1.5;       /* WCAG 1.4.12 floor */
  --lh-spec: 1.6;       /* blipton .spec */

  --tap-min: 44px;
}

/* ---- light: pure white paper on Apple's neutral grey ------------------------------------------ */
:root,
:root[data-theme='light'] {
  color-scheme: light;
  --font-ui: 'Public Sans', Arial, -apple-system, sans-serif;
  --font-mono: 'IBM Plex Mono', ui-monospace, SFMono-Regular, Consolas, monospace;

  --bg: #f5f5f7;           /* apple.com page grey */
  --surface: #ffffff;      /* pure white — the raised paper */
  --surface-sunk: #e8e8ed; /* apple.com's next step down */

  --ink: #1d1d1f;          /* apple.com body ink */
  --mut: #424245;          /* apple.com secondary ink — 7.72 on the sunk panel, the tightest ground */
  --line: #d2d2d7;         /* apple.com hairline */
  --line-strong: #6e6e73;  /* meaningful borders */

  --acc: #1d1d1f;          /* monochrome: buttons invert rather than colour */
  --acc-ink: #ffffff;

  --fill: #eaf2ea;
  --fill-ink: #146c34;
  --warn: #fbeceb;
  --warn-ink: #b32218;
  --amber: #f8f0e2;
  --amber-ink: #804b07;
  /* The same two hues at the chroma of the SURFACE rather than of the pill — see the note beside
     the dark values, where the difference is the visible one. */
  --fill-soft: #eef4ef;
  --amber-soft: #f7f3ec;

  --focus: #1d1d1f;
  --focus-ring: 0 0 0 3px rgba(29, 29, 31, 0.28);

  /* The Sources wallet card is the one card with no hue — it is the evidence under her form, not a
     section of it — so it is the one card that DOES follow the theme. Written as literal values
     rather than `var(--surface)`: an expanded card re-points `--surface` at its own `--wcard`, and
     `--wcard: var(--surface)` would be a custom-property cycle, which resolves to nothing at all. */
  --w-sources-card: #ffffff;
  --w-sources-ink: #1d1d1f;
}

/* ---- dark: the same neutral ramp, run the other way ------------------------------------------- */
:root[data-theme='dark'] {
  color-scheme: dark;
  --bg: #000000;
  --surface: #1d1d1f;      /* apple.com's raised near-black panel */
  --surface-sunk: #2c2c2e;

  --ink: #f5f5f7;
  --mut: #a1a1a6;          /* 5.42 on the sunk panel — apple.com's own #86868b does not clear AA there */
  --line: #38383a;         /* hairline */
  --line-strong: #787880;

  --acc: #f5f5f7;          /* monochrome inversion */
  --acc-ink: #000000;

  --fill: #10241a;
  --fill-ink: #7fe0aa;
  --warn: #2e1613;
  --warn-ink: #ff9d9d;
  --amber: #2a2110;
  --amber-ink: #f0c46a;
  /* A PILL and a PANEL are not the same object, and giving them the same background was the
     mistake. --fill is a small saturated lozenge: correct when the colour is most of what you
     see, wrong for the wide box under the fill button, where a forest-green slab DARKER than the
     panel it sits in reads as a hole punched in the card — and reads foreign next to the wallet
     deck below it, whose whole palette is chalky and low-chroma (#859e76 sage, #aac1b7).
     The -soft pair is the same hue at the surface's chroma: near enough to --surface (#1d1d1f)
     to be the same material, green enough to still say "written". Measured, like everything
     here — contrast.mjs checks both against their ink. */
  --fill-soft: #1e2723;
  --amber-soft: #282520;

  --focus: #f5f5f7;
  --focus-ring: 0 0 0 3px rgba(245, 245, 247, 0.35);

  --w-sources-card: #1d1d1f;
  --w-sources-ink: #f5f5f7;
}

/* ---- wallet palette ----------------------------------------------------------------------------
 * One colour per section of the planner's form, in the form's own order. The reference is an Apple Wallet
 * deck: a card's colour is its IDENTITY, not its status — Amazon is orange whatever you spent, and
 * "Major Site Constraints" is red on a clean site as much as a compromised one. The face therefore
 * always states the finding in words (n flagged / none flagged), because the colour is a name.
 *
 * THE COLOURS ARE THE SAME IN LIGHT AND DARK, and that is a measured result rather than a
 * preference. Every darkened variant of these eight hues was swept (web/contrast.mjs's method, on
 * a global multiplier and per-hue): between k≈0.55 and k≈0.95 each card passes through a dead zone
 * where NEITHER near-black nor white ink clears the floor, and the only multiplier where all eight
 * pass is k≈1. So a "dark palette" here would have meant shipping label text below AA on half the
 * deck. What changes with the theme is the room, not the cards: the page goes black, the neutral
 * Sources card goes near-black, and an EXPANDED card keeps its colour only in the header band while
 * its content sits on --surface — which is also why a full-screen pastel never glares at night.
 *
 * Floors, enforced by web/contrast.mjs: primary ink 5:1 (a margin over AA, because a section colour
 * is chosen for recognition first and the ink has to absorb whatever hue that leaves), muted label
 * 4.5:1 (AA body text — it is real content at 12px, not decoration).
 *
 * Deck-adjacent cards are as low as 1.32:1 (khaki beside brown). That is deliberate and is why the
 * card edge is carried by FORM — a 26px radius plus a drop shadow — never by a colour step. Three
 * of the eight hues sit at practically the same luminance, so the order below separates them.
 */
:root, :root[data-cards='colour'] {
  /* wallet:colour */
  --w-key-card: #edec8e;            --w-key-ink: #0b0b0c;
  --w-transport-card: #aac1b7;      --w-transport-ink: #0b0b0c;
  --w-site-details-card: #ad9774;   --w-site-details-ink: #0b0b0c;
  --w-zoning-card: #c1b280;         --w-zoning-ink: #0b0b0c;
  --w-constraints-card: #9e271d;    --w-constraints-ink: #ffffff;
  --w-accessibility-card: #f1702e;  --w-accessibility-ink: #0b0b0c;
  --w-services-card: #b2b2b2;       --w-services-ink: #0b0b0c;
  --w-permissibility-card: #859e76; --w-permissibility-ink: #0b0b0c;
  /* Market Analysis — the dev-gated milestone rig (panel.js, `dev` mode). Pale denim, because blue
     is the one family the eight above do not use: the card has to read as a NAME the deck does not
     already own, and every warm hue left would have read as a variant of zoning or site-details.
     Measured on contrast.mjs's method: ink 9.47:1 (floor 5), muted label 5.76:1 (floor 4.5), and
     1.41:1 against its deck neighbour permissibility — wider than the deck's current worst
     adjacency (1.32, khaki beside brown), so it needs no more separation than form already gives. */
  --w-market-analysis-card: #a3b6cb; --w-market-analysis-ink: #0b0b0c;
  /* Rulebook — the other dev-gated rig, and the one card that had to sit between two others that
     were themselves new. Parchment, for the obvious reason (it is the instruments, read), and
     because it is the only tone that clears BOTH neighbours: 1.90:1 against permissibility above
     it and 1.34:1 against market-analysis below, where every mauve and lilac tested fell to 1.07
     -1.18 against the denim. Ink 12.74:1, muted label 6.90:1. */
  --w-rulebook-card: #d7cfc2;       --w-rulebook-ink: #0b0b0c;
  /* /wallet:colour */
}

/* ---- wallet palette: MONOCHROME -----------------------------------------------------------------
 * The same nine cards as one tonal ramp, in her form order — Summary at one end, Sources at the
 * other. Direction follows the room: in LIGHT the deck runs dark -> light, in DARK it runs
 * light -> dark, so in both cases Sources (the evidence layer, not part of her form) sits nearest
 * the page and Summary stands furthest off it.
 *
 * WHY EACH RAMP STAYS INSIDE ONE BAND, rather than running the full black -> white it looks like it
 * should. Measured: a neutral grey can carry legible text only at sRGB 0-89 (with white ink) or
 * 149-255 (with near-black ink). Everything between — sRGB 90 to 148, the entire middle of the
 * ramp — fails BOTH inks at these floors, because a mid grey is equidistant from both. A true
 * black-to-white ramp across nine cards would therefore have put three or four cards in a band
 * where no ink is legible, or forced an ink flip mid-deck.
 *
 * So each mode ramps within its own usable band: light mode 150 -> 250 on near-black ink, dark mode
 * 88 -> 12 on white. One ink per mode, no flip, no dead cards, and the deck sits in the same
 * tonal world as the page around it.
 */
:root[data-cards='mono'] {
  /* wallet:mono-light */
  --w-key-card: #969696;            --w-key-ink: #0b0b0c;
  --w-transport-card: #a2a2a2;      --w-transport-ink: #0b0b0c;
  --w-site-details-card: #afafaf;   --w-site-details-ink: #0b0b0c;
  --w-zoning-card: #bcbcbc;         --w-zoning-ink: #0b0b0c;
  --w-constraints-card: #c8c8c8;    --w-constraints-ink: #0b0b0c;
  --w-accessibility-card: #d4d4d4;  --w-accessibility-ink: #0b0b0c;
  --w-services-card: #e1e1e1;       --w-services-ink: #0b0b0c;
  --w-permissibility-card: #eeeeee; --w-permissibility-ink: #0b0b0c;
  /* The rig card takes the MIDPOINT of the last two steps rather than the ramp being re-spaced
     across ten. Re-spacing would have moved all nine shipped greys to make room for a card that is
     invisible unless dev mode is on — a hidden rig must not change what ships. The cost is that the
     final two steps are half-width (238 -> 244 -> 250), which is below the threshold anyone can see
     on a deck whose edges are carried by form. Same treatment in mono-dark. */
  --w-rulebook-card: #f1f1f1;       --w-rulebook-ink: #0b0b0c;
  --w-market-analysis-card: #f4f4f4; --w-market-analysis-ink: #0b0b0c;
  --w-sources-card: #fafafa;        --w-sources-ink: #0b0b0c;
  /* /wallet:mono-light */
}

:root[data-theme='dark'][data-cards='mono'] {
  /* wallet:mono-dark */
  --w-key-card: #585858;            --w-key-ink: #ffffff;
  --w-transport-card: #4e4e4e;      --w-transport-ink: #ffffff;
  --w-site-details-card: #454545;   --w-site-details-ink: #ffffff;
  --w-zoning-card: #3c3c3c;         --w-zoning-ink: #ffffff;
  --w-constraints-card: #323232;    --w-constraints-ink: #ffffff;
  --w-accessibility-card: #282828;  --w-accessibility-ink: #ffffff;
  --w-services-card: #1f1f1f;       --w-services-ink: #ffffff;
  --w-permissibility-card: #161616; --w-permissibility-ink: #ffffff;
  --w-rulebook-card: #131313;       --w-rulebook-ink: #ffffff;
  --w-market-analysis-card: #111111; --w-market-analysis-ink: #ffffff;
  --w-sources-card: #0c0c0c;        --w-sources-ink: #ffffff;
  /* /wallet:mono-dark */
}

/* ---- shared ------------------------------------------------------------------------------------ */

/* Visible focus everywhere — never removed, only restyled (WCAG 2.4.7).
 *
 * Three things this rule used to do that it had no business doing, and they compounded into the
 * "strong border outline" that read as a rendering glitch rather than as focus:
 *
 *   border-radius: 6px    the ELEMENT's geometry, not the indicator's. On the card's round × and
 *                         on the pill-shaped fill button it squared off a control the rest of the
 *                         stylesheet had gone to trouble to round.
 *   box-shadow            a 3px --focus-ring halo UNDER a 2px outline held 2px off the edge, so
 *                         every focused control wore a ~5px slab rather than a ring.
 *   var(--focus)          the THEME's ink. Every control inside a wallet card is drawn from the
 *                         CARD's palette (--wink), so this was the one thing on an open card that
 *                         belonged to nothing around it — a white ring on the red Constraints card,
 *                         and a white ring on pale yellow Summary, where it all but vanished.
 *
 * Now: one 2px ring in the LOCAL ink, following whatever radius the control already has. --wink is
 * set on .wcard and inherits, so a control inside a card gets the card's ink and everything else
 * falls through to the theme's. --focus-ring survives as a token because .tswitch and .envslider
 * draw their own indicator with it — they hide the outline and ring the part that actually moves,
 * which is right for a control whose hit area is not its visible shape. */
:where(a, button, input, select, textarea, [tabindex]):focus-visible {
  outline: 2px solid var(--wink, var(--focus));
  outline-offset: 2px;
}

/* Animation is decoration, never the only signal (WCAG 2.3.3). */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* Hand colour back to the OS rather than fighting it. */
@media (forced-colors: active) {
  * { forced-color-adjust: auto; }
}
