/* IPP design kit - the ONE place type and text colour are defined (#1058).
 *
 * The standard this encodes was accepted by the owner on 2026-09-26 and lives
 * on feature #1057. Measured on the dev release that day: 25 font sizes, 5
 * weights and 82 text colours were declared across the front end, and what a
 * browser actually drew was 20 sizes, 4 weights and 51 colours. Section
 * headings rendered at 12 different sizes; buttons at 11 sizes and 18 colours.
 * No type token existed at all, and the only two shared text colours were
 * redefined by nine separate pages.
 *
 * #413 and #420 each repaired one page and the drift came back, which is why
 * this is a standard with a GUARD (#1064) rather than another tidy-up.
 *
 * A page uses a NAME. It does not invent a number - that is the whole rule,
 * and the drift guard enforces it.
 *
 * WHAT THIS FILE DOES NOT DO. Spacing, layout and component structure are out
 * of scope for #1057. Control defaults (#1060), the Montserrat webfont IPP
 * serves itself (#1059) and the per-page migrations (#1061-#1063) are their
 * own stories: this file only declares the vocabulary they all use.
 */

/* ==========================================================================
   Montserrat, served by IPP itself (#1059)
   ==========================================================================
   ONE FILE, 37,956 bytes: the Latin subset as a VARIABLE face covering the
   whole 100-900 weight axis. Google's own CSS serves exactly this file for all
   three weights, and a probe confirmed it - 400, 600 and 700 render at three
   distinct, increasing widths from this one download.

   THREE DECLARATIONS, NOT ONE 100-900 RANGE, and that is the interesting
   choice. A single `font-weight: 100 900` face would render `font-weight: 500`
   at 500, so an off-standard weight would look deliberate. Declaring exactly
   the three weights the standard allows makes the browser SNAP anything else to
   the nearest of them - the standard becomes true in the rendering engine, not
   only in the linter. #1064 still refuses the literal; this is what happens if
   one ever gets past it.

   `font-display: swap` so text is readable in the fallback while the face
   loads. Nothing here blocks first paint.

   LICENCE: SIL Open Font License 1.1, which permits self-hosting and requires
   the licence travel with the font. It is in static/fonts/OFL.txt.
   ========================================================================== */
@font-face {
    font-family: 'Montserrat';
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url('/static/fonts/montserrat-latin.woff2') format('woff2');
}
@font-face {
    font-family: 'Montserrat';
    font-style: normal;
    font-weight: 600;
    font-display: swap;
    src: url('/static/fonts/montserrat-latin.woff2') format('woff2');
}
@font-face {
    font-family: 'Montserrat';
    font-style: normal;
    font-weight: 700;
    font-display: swap;
    src: url('/static/fonts/montserrat-latin.woff2') format('woff2');
}

:root {
    /* ── FAMILIES ─────────────────────────────────────────────────────────
     * Montserrat everywhere in the app. The fallbacks matter until #1059
     * serves the webfont from IPP: without them a page renders in whatever
     * the browser picked, which is how 8 of 10 date pickers ended up in the
     * browser's monospace font.
     */
    --font-sans: 'Montserrat', 'Segoe UI', Arial, Helvetica, sans-serif;
    /* ONE monospace stack, for code and IDs only. */
    --font-mono: ui-monospace, 'Cascadia Mono', 'Consolas', 'Courier New',
                 monospace;

    /* ── SIZES ────────────────────────────────────────────────────────────
     * Six sizes, plus one for KPI figures. Named by their value on purpose:
     * a semantic name (--size-sm) invites a page to decide what "small"
     * means here, which is the drift this replaces.
     */
    --size-11: 11px;   /* badge, caption */
    --size-12: 12px;   /* field label, table header */
    --size-13: 13px;   /* button */
    --size-14: 14px;   /* body, sub-heading, text field */
    --size-16: 16px;   /* section heading */
    --size-24: 24px;   /* page title */
    --size-kpi: 28px;  /* KPI figures, and nothing else */

    /* ── WEIGHTS ──────────────────────────────────────────────────────────
     * Three. 500 is not one of them: it was the second most common weight in
     * the front end and it is not in the standard, so labels rendered at four
     * different weights.
     */
    --weight-regular: 400;
    --weight-semibold: 600;
    --weight-bold: 700;

    /* ── TEXT COLOURS ─────────────────────────────────────────────────────
     * Nine roles. Every one of them is a text colour.
     *
     * This comment used to end "brand fills and borders are not this file's
     * business", and #1077 made that false: the SURFACES block below holds
     * three. The sentence is corrected rather than left standing, because a
     * comment that contradicts the file it sits in is the defect this kit
     * exists to remove.
     */
    --text-primary: #1e293b;
    /* #1083: was #64748b, which had no margin anywhere. On the four
     * surfaces it is actually drawn on it measured 4.76 (white), 4.55
     * (#f8fafc, 12 sites), 4.36 (the page background) and 4.34 (#f1f5f9, 8
     * sites) - failing two and clearing a third by 0.05. #475569's worst is
     * 6.92.
     *
     * The front end had already voted for this value: 43 hand-written uses
     * across 15 files, more than any other literal text colour in the tree,
     * and two pages wrote it as the explicit FALLBACK to this token -
     * var(--text-secondary, #475569) - so the value that applied only when
     * the token was missing was better than the token.
     *
     * The cost is that secondary sits closer to primary than it did: 1.93x
     * rather than 3.07x. #1e293b against #475569 is slate-800 against
     * slate-600, a conventional pairing, and the alternative was text that
     * cannot be read on two of its four surfaces.
     */
    --text-secondary: #475569;
    --text-disabled: #94a3b8;   /* DISABLED TEXT ONLY - not a third grey */
    --text-brand: #1e3a8a;
    --text-link: #2563eb;
    --text-error: #b91c1c;
    --text-success: #166534;
    --text-warning: #92400e;
    --text-on-dark: #ffffff;
    /* THE TENTH. #1057 accepted nine, and this is the one addition, recorded
     * on that feature the way #1077's surfaces were. The app uses purple to
     * mean pending or proposed and the kit held no purple at all, so 21 sites
     * reached for --pending-color #8b5cf6 - a FILL weight - and rendered text
     * at 3.59 to 4.23 against their own backgrounds, every one under the 4.5
     * body text needs (#1081).
     *
     * NOT the darkest violet that passes. At 7.10 on white this matches
     * --text-success (7.13) and --text-warning (7.09), so a pending label
     * reads at the same visual weight as its siblings instead of being the
     * heaviest thing on the page. On the two tints these labels sit on it is
     * 6.02 (#f3e8ff) and 6.48 (#f5f3ff).
     */
    --text-pending: #6d28d9;

    /* ── SURFACES ─────────────────────────────────────────────────────────
     * THE STANDARD WAS DELIBERATELY WIDENED HERE (#1077). #1057 scoped the kit
     * to type and TEXT colour, and the decision to admit surfaces is recorded
     * on #1057 itself rather than implied by this file - widening a standard
     * quietly is how standards stop meaning anything.
     *
     * WHY THESE THREE AND NOTHING ELSE. A surface is what text is drawn ON.
     * The kit already owns both ends of a type decision but only one end of a
     * contrast decision, so a check could say what colour the text is and not
     * what it sits on. These three close that.
     *
     * They were declared privately in nine page fragments, same value every
     * time, so a tenth page using one resolved to nothing - which is exactly
     * what happened to the style guide (#1066) and what #1058's
     * orphan-reference guard caught.
     *
     * NOT a general palette, and NOT state colour: danger/success/warning/
     * pending are fills, neither type nor surface, and they are shell defaults
     * in spa_shell.css rather than standard vocabulary.
     *
     * The two awkward names are deliberate and temporary. `--background` and
     * `--card-bg` do not follow the kit's `--category-variant` convention, but
     * renaming them touches every call site, which is its own story.
     */
    --background: #f5f5f5;    /* the page behind everything */
    --card-bg: #ffffff;       /* a panel or card sitting on it */
    --border-color: #e2e8f0;  /* the line between two surfaces */

    /* ── ROLES ────────────────────────────────────────────────────────────
     * The standard's nine roles as pairs, so a page names the role rather
     * than reassembling it from a size and a weight - and so the drift guard
     * (#1064) has the mapping to check against in one machine-readable place.
     *
     * Deliberately NOT a `font:` shorthand token. The shorthand resets
     * font-style to normal, so a role applied to an empty-state hint would
     * silently un-italicise it, and the one place italic is allowed is
     * exactly those hints.
     */
    --type-page-title-size: var(--size-24);
    --type-page-title-weight: var(--weight-semibold);

    --type-section-heading-size: var(--size-16);
    --type-section-heading-weight: var(--weight-semibold);

    --type-sub-heading-size: var(--size-14);
    --type-sub-heading-weight: var(--weight-semibold);

    --type-body-size: var(--size-14);
    --type-body-weight: var(--weight-regular);

    --type-field-label-size: var(--size-12);
    --type-field-label-weight: var(--weight-semibold);

    --type-text-field-size: var(--size-14);
    --type-text-field-weight: var(--weight-regular);

    --type-button-size: var(--size-13);
    --type-button-weight: var(--weight-semibold);

    --type-table-header-size: var(--size-12);
    --type-table-header-weight: var(--weight-semibold);

    --type-badge-size: var(--size-11);
    --type-caption-size: var(--size-11);
}
