/* =============================================================================
   Design tokens - EDC v2
   -----------------------------------------------------------------------------
   The single source of truth for every visual decision in the application.
   Nothing else in the code base may contain a literal colour, font stack, pixel
   spacing, radius or shadow - components consume these variables only.

   WHERE THESE VALUES COME FROM
   The palette, the semantic mapping and both themes are EDC's v2 design system,
   ported from `design/colors/v2/`. The names below are this application's; the
   values are EDC's, so the two products look like one family.

   SIX VALUES ARE OURS, NOT EDC's
   Measuring EDC's set against WCAG 2.1 found pairs that do not clear AA. Each
   correction is marked DERIVED with its measured ratio, so nobody later mistakes
   it for an EDC value and "fixes" it back:

     --color-accent          light  #1971A4  EDC's link colour is #CEE7F7, which is
                                             1.28:1 on white - invisible as text
     --color-focus-ring      light  #2195D8  EDC's is 2.33:1; WCAG 1.4.11 needs 3.0
                                             for a focus indicator
     --color-warning-text    light  #90621C  EDC has successDark and dangerDark but
                                             no warningDark; #DEA854 is 2.14:1
     --color-danger-strong          #744040  EDC's danger as a button fill fails with
                                             both white (3.73) and dark (4.08) text
     --color-text-muted      dark   #989999  EDC's tertiary is 2.85:1 on a dark card
     status subtle backgrounds, dark        EDC defines these for light only

   -----------------------------------------------------------------------------
   BREAKPOINTS - the one thing here that is NOT a variable
   -----------------------------------------------------------------------------
   CSS custom properties cannot be used in a media query: `@media (max-width:
   var(--bp-lg))` is invalid and silently never matches. So the scale below is the
   source of truth by convention, and the literal values are repeated in every
   media query in this code base.

       --bp-sm    30rem   480px   large phone
       --bp-md    48rem   768px   tablet portrait; below this, tables become cards
       --bp-lg    64rem  1024px   tablet landscape; below this, the shell goes mobile
       --bp-xl    90rem  1440px   wide desktop

   Always author `max-width` queries against these so the mobile layout is the one
   that survives a value nobody anticipated.
   ============================================================================= */

:root {
    /* -------------------------------------------------------------------------
       Palette - EDC v2 raw values. Never referenced by a component; the semantic
       tokens below are the only public surface.
       ------------------------------------------------------------------------- */

    /* softMarine. Pale on purpose: it is a *fill* that carries dark text, not a
       colour for text or icons. See --color-on-primary. */
    --color-marine: #cee7f7;
    --color-marine-pressed: #bcd3e1;
    --color-marine-strong: #5ab3e6;

    /* rusticStone */
    --color-stone: #d6b890;
    --color-stone-pressed: #b99a71;

    --color-white: #ffffff;
    --color-black: #000000;

    /* EDC's grey ramp, warm-neutral rather than blue. Every body-text pairing in
       both themes measures above 4.9:1. */
    --color-gray-50: #fafafa;
    --color-gray-75: #f8f8f8;
    --color-gray-100: #f5f5f5;
    --color-gray-120: #ebeded;
    --color-gray-160: #e8e8e8;
    --color-gray-180: #d1d2d2;
    --color-gray-200: #dddede;
    --color-gray-240: #c6c7c7;
    --color-gray-300: #afb0b0;
    --color-gray-330: #989999;
    --color-gray-400: #6a6b6b;
    --color-gray-500: #535454;
    --color-gray-600: #464949;
    --color-gray-690: #3b3d3d;
    --color-gray-700: #303232;
    --color-gray-800: #2d2f2f;
    --color-gray-820: #282a2a;
    --color-gray-900: #242626;
    --color-gray-910: #1d2020;
    --color-gray-950: #1a1d1d;
    --color-gray-960: #151717;
    --color-gray-965: #181a1a;
    --color-gray-975: #111313;

    --color-green: #a5ba78;
    --color-green-dark: #4e6b26;
    --color-amber: #dea854;
    --color-red: #ce6464;
    --color-red-dark: #744040;

    /* -------------------------------------------------------------------------
       Semantic - light theme, and the base set
       -----------------------------------------------------------------------
       EVERY semantic token is defined here, in the bare :root block. The dark
       blocks below only ever REDEFINE them. A colour defined solely inside a media
       query or a [data-theme] block is missing in the states that block does not
       match - which is how a page ends up rendering one theme's text on the other
       theme's background.
       ------------------------------------------------------------------------- */

    /* Tells the browser which way native UI - scrollbars, form controls, the
       caret - should be drawn. Without it a dark page keeps light scrollbars. */
    color-scheme: light;

    /* Brand. --color-primary is a fill; --color-accent is the same identity at a
       weight that can carry text. Conflating the two is what makes EDC's link
       colour unreadable, so they are deliberately separate here. */
    --color-primary: var(--color-marine);
    --color-primary-hover: var(--color-marine-pressed);
    --color-primary-active: var(--color-marine-pressed);
    --color-primary-subtle: #f6fdfe;
    /* Dark text on the pale fill: 11.88:1. White would be 1.28:1. */
    --color-on-primary: var(--color-gray-900);

    /* DERIVED - 5.33:1 on white, 4.54:1 on the deepest light surface. */
    --color-accent: #1971a4;
    --color-accent-hover: #14597f;

    --color-secondary: var(--color-stone);
    --color-secondary-hover: var(--color-stone-pressed);
    --color-on-secondary: var(--color-gray-900);

    /* The stone's other two thirds, so it can carry meaning the way the status
       colours do. Travelling is painted in stone on the day picker, and a fill
       alone cannot draw an edge round itself or wash a day that has not happened
       yet. DERIVED - 5.60:1 on white, 4.91:1 on the subtle below; the stone fill
       itself is 1.79:1 and is a fill only. */
    --color-secondary-text: #8a5f2a;
    --color-secondary-subtle: #f7efe4;

    /* Structural surfaces - position in the hierarchy, not appearance. EDC's
       naming, which survives a re-theme in a way "raised" and "sunken" do not.
       Light depth order: root -> page -> container(white) -> inset */
    --color-surface-root: var(--color-gray-50);
    --color-surface-page: var(--color-gray-100);
    --color-surface-container: var(--color-white);
    --color-surface-inset: var(--color-gray-50);
    --color-surface-overlay: var(--color-white);
    --color-surface-contrast: var(--color-gray-200);
    --color-surface-table-header: var(--color-gray-120);
    --color-surface-input: var(--color-white);
    --color-surface-inverse: var(--color-gray-950);

    /* Interaction states, applied as a translucent layer *over* whatever surface
       is already there - so one rule works on every button variant and every
       card, instead of each needing its own hover colour. See base.css. */
    --color-state-hover: rgb(0 0 0 / 4%);
    --color-state-pressed: rgb(0 0 0 / 8%);
    --color-state-selected: rgb(206 231 247 / 30%);

    --color-overlay: rgb(0 0 0 / 45%);

    --color-text: var(--color-gray-900);
    --color-text-secondary: var(--color-gray-500);
    --color-text-muted: var(--color-gray-400);
    --color-text-inverse: var(--color-white);

    --color-border: var(--color-gray-200);
    --color-border-subtle: var(--color-gray-160);
    --color-border-muted: var(--color-gray-120);
    --color-border-strong: var(--color-gray-300);
    --color-border-outline: var(--color-gray-300);

    --color-link: var(--color-accent);
    --color-link-hover: var(--color-accent-hover);

    /* DERIVED - 3.3:1 on white. WCAG 1.4.11 requires 3.0 for a focus indicator,
       and EDC's #5AB3E6 gives 2.33:1. */
    --color-focus-ring: #2195d8;

    /* Status. Three tokens each, because one colour cannot do all three jobs:
         --color-x          the fill or graphic
         --color-x-text     the same meaning at a weight that passes AA as text
         --color-x-subtle   a background to sit that text on
       In light mode the fill tones are far too pale for text - success is 2.12:1 -
       which is exactly why EDC ships successDark and dangerDark. */
    --color-success: var(--color-green);
    --color-success-text: var(--color-green-dark);
    --color-success-subtle: #f8fbe9;

    --color-warning: var(--color-amber);
    /* DERIVED - 5.32:1 on white. EDC has no warningDark. */
    --color-warning-text: #90621c;
    --color-warning-subtle: #fdf6dd;

    --color-danger: var(--color-red);
    --color-danger-text: var(--color-red-dark);
    --color-danger-subtle: #fcebe2;
    /* For a destructive button fill: white on this is 8.24:1, where white on
       --color-danger is 3.73:1 and dark text on it is 4.08:1. Neither works. */
    --color-danger-strong: var(--color-red-dark);

    --color-info: var(--color-marine);
    --color-info-text: var(--color-accent);
    --color-info-subtle: #f6fdfe;

    /* Severity - a four-step *scale* for scored observations, distinct from the
       three status states. Identical in both themes: the meaning is the score, not
       the surface. FILLS ONLY - as text on white these run 1.7:1 to 3.7:1, so they
       belong in chips and cells that carry dark text, never in the text itself. */
    --color-severity-good: #b1c87f;
    --color-severity-minor: #efbf7f;
    --color-severity-moderate: #e48383;
    --color-severity-severe: #d95d5d;
    /* DERIVED - the darkest grey rather than --color-text. On the severe step,
       #242626 measures 4.11:1 and misses AA; this gives 5.11:1. Darkening the text
       is the right fix here because the four severity hues ARE the scale - shifting
       one to suit its label would change what the scale means. */
    --color-on-severity: var(--color-gray-975);

    /* -------------------------------------------------------------------------
       Chart categories and the heat scale
       -----------------------------------------------------------------------
       ADDED FOR THE REPORTS SCREEN, which is the first thing in this product that
       has to colour a pie slice or a heatmap cell. Everything before it encoded
       meaning - success, warning, severity - and could reuse the semantic tokens.
       A category has no meaning to encode: "Piping" is not better or worse than
       "Electrical", so a ramp would be a lie and the hues must only differ.

       EIGHT, AND NO MORE. Eight is about the limit of what a legend can carry and
       a reader can hold, so a breakdown with more values shows the largest seven
       and folds the rest into one "Everything else" row rather than inventing a
       ninth hue nobody can name. They are muted on purpose - this palette is
       warm-neutral greys and one marine accent, and eight saturated hues would
       look like a different application.

       Each is a graphical object rather than text, so the bar is 3:1 against the
       surface behind it, not 4.5:1.
       ------------------------------------------------------------------------- */
    --color-category-1: #1971a4;
    --color-category-2: #4e6b26;
    --color-category-3: #b8792e;
    --color-category-4: #8c4a4a;
    --color-category-5: #4a5f7a;
    --color-category-6: #6b5b8c;
    --color-category-7: #2c7a70;
    --color-category-8: #8a7040;

    /* A sequential ramp, for the one chart where more IS more. Five steps of the
       accent hue, and deliberately kept pale enough that ONE text colour works on
       every step - a heatmap that switches its text colour halfway up the scale
       has to decide where halfway is, and gets it wrong at the boundary. */
    --color-scale-1: #eef4f9;
    --color-scale-2: #d3e5f0;
    --color-scale-3: #b0d0e4;
    --color-scale-4: #86b6d5;
    --color-scale-5: #5b9cc4;
    --color-on-scale: var(--color-gray-975);

    /* -------------------------------------------------------------------------
       Firm colours - the site map, and nothing else
       -----------------------------------------------------------------------
       A SECOND CATEGORICAL SET, WHERE THE EIGHT ABOVE ARE DELIBERATELY NOT SHARED.
       Those exist so that Piping is the same colour in the bar list, the pie and the
       heatmap: one breakdown drawn three ways, and a reader moving between them
       matching by hue. A firm on the map is a row of none of those three, its legend
       is the row of swatches directly under the drawing, and nothing on this screen
       ever asks anybody to carry a colour from a dot to a pie slice. Sharing the
       eight bought an agreement nobody was reading and cost the map the one thing it
       actually needed, which is enough colours for a site run through twenty
       subcontractors.

       TEN, PLUS TWO NEUTRALS. Past the tenth the firms share --color-firm-other and
       the legend says how many are in it. See Features/Reports/Map/FirmPalette.cs,
       which decides that fold once over the whole period rather than per frame: the
       legend re-sorts itself by head count as the animation runs, so a per-frame fold
       would move a firm between a colour and the grey halfway through playback, and
       following one subcontractor across a period is the only reason the marks are
       coloured.

       THE LADDER RUNS ALONG THE AXIS COLOUR BLINDNESS DESTROYS, NOT THE ONE IT
       LEAVES. A deuteranope and a protanope keep lightness and a single blue-yellow
       axis; the red-green axis collapses. So the rule here is not "alternate light
       and deep as the hue advances" - that separates hue-neighbours, and confusion
       partners are not hue-neighbours, they sit opposite each other across the axis
       that has just been lost. Built that way a teal at L* 48 and a rose at L* 50
       measure dE00 1.03 for a protanope: one colour, under a legend naming two firms.
       Instead each colour's lightness is set by where it lands AFTER simulation, so
       anything that projects near something else is pushed apart vertically and
       anything already far apart on blue-yellow is free to share a lightness. Three
       of the ten sit together at L* 30 for exactly that reason. Measured over all
       twelve entries with Vienot 1999 and CIEDE2000, the worst pair is dE00 10.08 for
       a protanope and 9.97 for a deuteranope, against 4.88 and 4.33 for the eight
       chart categories above.

       MUTED BY NARROW CHROMA, NOT BY WASHING OUT. All ten sit between C* 21.9 and
       34.9 where the chart set runs 17.7 to 52.4, which is why its ochre shouts and
       its slate mumbles. Even chroma is what makes ten colours read as one family on
       a warm-neutral-grey page with a single marine accent. A dot is a much smaller
       patch than a bar and does need more separation, not less - but raising chroma
       is the weak lever, because small-field discrimination degrades in chroma faster
       than in lightness, and ten hues at one lightness collapse for a dichromat
       whatever their chroma. The separation is bought in lightness, and the dot can
       carry it because .sitemap__mark already rings every mark in the surface colour,
       which puts a constant reference beside each one.

       Each is a graphical object and never text, so the bar is 3:1 against the
       surface behind it rather than 4.5:1. The two ratios on each line are on
       --color-surface-container, the map's own panel, and on --color-surface-inset,
       the readout strip the legend swatches sit on. The inset binds in BOTH themes -
       here because it is fractionally darker than the white container, and in the
       dark blocks because it is lighter than the container there.
       ------------------------------------------------------------------------- */
    --color-firm-1: #3b6fa2;    /* marine     5.27:1 / 5.05:1 */
    --color-firm-2: #9d6d64;    /* clay       4.37:1 / 4.19:1 */
    --color-firm-3: #005139;    /* pine       9.39:1 / 9.00:1 */
    --color-firm-4: #a67e4b;    /* brass      3.68:1 / 3.53:1 */
    --color-firm-5: #735070;    /* plum       6.75:1 / 6.47:1 */
    --color-firm-6: #4b8c9c;    /* steel      3.80:1 / 3.64:1 */
    --color-firm-7: #3e4e16;    /* fern       9.11:1 / 8.72:1 */
    --color-firm-8: #873b50;    /* oxblood    7.58:1 / 7.26:1 */
    --color-firm-9: #736733;    /* olive      5.65:1 / 5.41:1 */
    --color-firm-10: #484072;   /* indigo     9.33:1 / 8.93:1 */

    /* THE TWO NEUTRALS MEAN DIFFERENT THINGS AND MUST NOT BE ONE COLOUR. "Other
       firms" is a bucket the legend names and counts; "no firm recorded" is somebody
       whose employer was never entered, who is in no bucket and whom no chip can
       honestly count as a firm. Grey for both, because grey is the one thing on this
       page that is not a firm - and they are told apart by weight rather than by hue:
       the unattributed dot is the quietest thing on the plan in BOTH themes, which
       here means the palest and in the dark theme means the dimmest. The bucket is
       this theme's own muted-text grey; the unknown grey is DERIVED, because nothing
       on the ramp sits at that lightness and clears the bar - gray-330 measures
       2.74:1 on the inset. */
    --color-firm-other: var(--color-gray-400);   /* grey       5.35:1 / 5.12:1 */
    --color-firm-unknown: #838585;               /* pale grey  3.71:1 / 3.56:1 */

    /* Application shell. EDC puts the sidebar on surface.root, which means it is
       light in the light theme - not the dark rail this application started with. */
    --color-sidebar-background: var(--color-surface-root);
    --color-sidebar-text: var(--color-gray-500);
    --color-sidebar-text-active: var(--color-gray-900);
    --color-sidebar-active: var(--color-gray-120);
    --color-sidebar-border: var(--color-border-muted);
    --color-topbar-background: var(--color-surface-container);

    /* -------------------------------------------------------------------------
       Typography
       EDC's token files carry no type scale, so this remains ours.

       The system font stack is a DECISION, not a placeholder. EDC's Söhne was
       offered and declined: it is a commercial typeface and nobody here had read
       its licence. The stack below downloads nothing, renders natively on every
       device, and never blocks first paint - which matters more than the typeface
       on an application loaded over a site connection. Changing it means reading
       a licence first; see docs/TODO.md for what else it would involve.
       ------------------------------------------------------------------------- */
    --font-family-base: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
    --font-family-heading: var(--font-family-base);
    --font-family-mono: ui-monospace, SFMono-Regular, "Cascadia Mono", Consolas, "Liberation Mono", monospace;

    --font-size-xs: 0.75rem;    /* 12px */
    --font-size-sm: 0.875rem;   /* 14px */
    --font-size-base: 1rem;     /* 16px - never smaller for body text: the app is read outdoors, in gloves, on a phone */
    --font-size-lg: 1.125rem;   /* 18px */
    --font-size-xl: 1.375rem;   /* 22px */
    --font-size-2xl: 1.75rem;   /* 28px */
    --font-size-3xl: 2.25rem;   /* 36px */

    --font-weight-regular: 400;
    --font-weight-medium: 500;
    --font-weight-semibold: 600;
    --font-weight-bold: 700;

    --line-height-tight: 1.2;
    --line-height-base: 1.5;
    --line-height-relaxed: 1.7;

    --letter-spacing-tight: -0.01em;
    --letter-spacing-base: 0;
    --letter-spacing-wide: 0.02em;

    /* -------------------------------------------------------------------------
       Spacing - a 4px scale. Ours; a finer superset of EDC's five steps.
       ------------------------------------------------------------------------- */
    --space-0: 0;
    --space-1: 0.25rem;   /* 4px  */
    --space-2: 0.5rem;    /* 8px  */
    --space-3: 0.75rem;   /* 12px */
    --space-4: 1rem;      /* 16px */
    --space-5: 1.5rem;    /* 24px */
    --space-6: 2rem;      /* 32px */
    --space-7: 3rem;      /* 48px */
    --space-8: 4rem;      /* 64px */

    /* -------------------------------------------------------------------------
       Border radius - EDC's values.
       ------------------------------------------------------------------------- */
    --radius-none: 0;
    --radius-sm: 0.25rem;
    --radius-md: 0.375rem;
    --radius-lg: 0.5rem;
    --radius-xl: 1rem;
    --radius-full: 9999px;

    /* THREE TOKENS THAT WERE BEING USED AND DID NOT EXIST, which CSS reports by saying nothing at
       all: an undefined custom property makes the declaration invalid at computed-value time, so the
       property silently falls back to its inherited or initial value. A pill border came out square,
       a focus outline came out at the browser's default width, and an SVG fill came out BLACK -
       because black is what `fill` initialises to.

       Named here rather than by correcting each call site, because several components had already
       standardised on these names: the report filter chips, the update banner, the site switcher and
       the dashboard all ask for --radius-pill. The names were right; the definitions were missing. */
    --radius-pill: var(--radius-full);

    /* Matches the ring --shadow-focus draws, so an outlined control and a shadowed one look alike. */
    --focus-ring-width: 3px;

    /* The panel a chart or a shape sits ON, as opposed to the page behind it. An alias rather than a
       new colour: --color-surface-container is what every panel in the application already uses, and
       a second value here would be a second thing to keep in step. */
    --color-surface: var(--color-surface-container);

    --border-width: 1px;

    /* -------------------------------------------------------------------------
       Elevation - EDC's shadow tones.
       ------------------------------------------------------------------------- */
    --shadow-none: none;
    --shadow-sm: 0 1px 2px rgb(0 0 0 / 5%);
    --shadow-md: 0 2px 6px rgb(36 38 38 / 11%);
    --shadow-lg: 0 8px 24px rgb(36 38 38 / 20%);
    --shadow-xl: 0 16px 40px rgb(36 38 38 / 30%);
    /* A top highlight plus a drop shadow, which is what gives a card an edge
       rather than just a blur beneath it. */
    --shadow-container: inset 0 1px 0 rgb(0 0 0 / 6%), 0 2px 8px rgb(0 0 0 / 7%);
    --shadow-focus: 0 0 0 3px var(--color-focus-ring);

    /* -------------------------------------------------------------------------
       Application shell layout
       ------------------------------------------------------------------------- */
    --layout-sidebar-width: 16rem;
    --layout-topbar-height: 3.5rem;
    --layout-content-max-width: 80rem;

    /* Touch targets. Site users wear gloves; nothing interactive goes below this. */
    --layout-touch-target-min: 2.75rem;

    /* Safe areas, so content clears a notch or a home indicator. index.html sets
       viewport-fit=cover, which is what makes these non-zero on such devices. */
    --layout-safe-top: env(safe-area-inset-top, 0px);
    --layout-safe-right: env(safe-area-inset-right, 0px);
    --layout-safe-bottom: env(safe-area-inset-bottom, 0px);
    --layout-safe-left: env(safe-area-inset-left, 0px);

    /* -------------------------------------------------------------------------
       Motion
       ------------------------------------------------------------------------- */
    --motion-duration-fast: 100ms;
    --motion-duration-base: 180ms;
    --motion-duration-slow: 260ms;
    --motion-easing-standard: cubic-bezier(0.2, 0, 0, 1);

    /* -------------------------------------------------------------------------
       Stacking order - declared centrally so z-index never becomes a guessing game
       ------------------------------------------------------------------------- */
    --z-sidebar: 100;
    --z-topbar: 200;
    --z-dropdown: 300;
    --z-modal: 400;
    --z-toast: 500;
}

/* =============================================================================
   Dark theme - EDC v2 darkTokens
   -----------------------------------------------------------------------------
   Only SEMANTIC tokens are redefined. The two blocks below cover the two ways a
   reader reaches dark mode, and there are three states to serve, not two:

     1. system preference, nothing stamped on <html>  -> the media query
     2. explicit "dark" chosen in the app             -> [data-theme="dark"]
     3. explicit "light" chosen on a dark OS          -> neither must apply

   The media query is guarded with :not([data-theme="light"]) so an explicit light
   choice beats a dark operating system, and the [data-theme="dark"] block repeats
   the values so an explicit dark choice beats a light one. Without both, the
   toggle only works in one direction.

   The two blocks MUST stay identical. Any change goes in both.
   ============================================================================= */

@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) {
        color-scheme: dark;

        /* The pale marine fill is unchanged in dark mode - it already carries dark
           text - and at 13.25:1 on the page surface it also works as accent text,
           so accent and primary converge here. */
        --color-accent: var(--color-marine);
        --color-accent-hover: var(--color-white);

        --color-surface-root: var(--color-gray-975);
        --color-surface-page: var(--color-gray-950);
        --color-surface-container: var(--color-gray-900);
        --color-surface-inset: var(--color-gray-820);
        --color-surface-overlay: var(--color-gray-800);
        --color-surface-contrast: var(--color-gray-600);
        --color-surface-table-header: var(--color-gray-700);
        --color-surface-input: var(--color-gray-965);
        --color-surface-inverse: var(--color-white);

        --color-state-hover: rgb(255 255 255 / 6%);
        --color-state-pressed: rgb(255 255 255 / 10%);
        --color-state-selected: rgb(206 231 247 / 15%);

        --color-overlay: rgb(0 0 0 / 60%);

        --color-text: var(--color-gray-50);
        --color-text-secondary: var(--color-gray-300);
        /* DERIVED - EDC's tertiary (#6A6B6B) is 2.85:1 on a dark card. */
        --color-text-muted: var(--color-gray-330);
        --color-text-inverse: var(--color-gray-900);

        --color-border: var(--color-gray-690);
        --color-border-subtle: var(--color-gray-700);
        --color-border-muted: var(--color-gray-800);
        --color-border-strong: var(--color-gray-600);
        --color-border-outline: var(--color-gray-600);

        /* Already visible on dark, so EDC's own primary serves. */
        --color-focus-ring: var(--color-marine);

        /* The fill tones all clear AA as text on dark, so no darkened variants.
           The stone's darkened line is 2.72:1 on a dark card, so it flips to the
           fill here exactly as the four status tones do. */
        --color-success-text: var(--color-green);
        --color-warning-text: var(--color-amber);
        --color-danger-text: var(--color-red);
        --color-info-text: var(--color-marine);
        --color-secondary-text: var(--color-stone);

        /* DERIVED - EDC defines subtle backgrounds for light only. Translucent
           tints of the status colour, so they sit correctly on any dark surface. */
        --color-success-subtle: rgb(165 186 120 / 16%);
        --color-warning-subtle: rgb(222 168 84 / 16%);
        --color-danger-subtle: rgb(206 100 100 / 18%);
        --color-info-subtle: rgb(206 231 247 / 14%);
        --color-primary-subtle: rgb(206 231 247 / 14%);
        --color-secondary-subtle: rgb(214 184 144 / 18%);

        --color-sidebar-background: var(--color-surface-root);
        --color-sidebar-text: var(--color-gray-300);
        --color-sidebar-text-active: var(--color-gray-50);
        --color-sidebar-active: var(--color-gray-820);
        --color-sidebar-border: var(--color-gray-800);
        --color-topbar-background: var(--color-surface-container);

        --shadow-sm: 0 1px 2px rgb(0 0 0 / 30%);
        --shadow-md: 0 2px 8px rgb(0 0 0 / 40%);
        --shadow-lg: 0 10px 28px rgb(0 0 0 / 50%);
        --shadow-xl: 0 18px 44px rgb(0 0 0 / 60%);
        --shadow-container: inset 0 1px 0 rgb(255 255 255 / 8%), 0 2px 8px rgb(0 0 0 / 40%);

        /* The eight hues again, lifted off a dark ground. Same identities, same order,
           so a legend read in one theme still means the same thing in the other -
           somebody comparing a screenshot to a screen must not find that Piping
           changed colour. */
        --color-category-1: #5aa8d6;
        --color-category-2: #9dbd6a;
        --color-category-3: #e0a558;
        --color-category-4: #d18585;
        --color-category-5: #8aa2c4;
        --color-category-6: #a794c9;
        --color-category-7: #5fb5a8;
        --color-category-8: #c7ab72;

        /* The ramp inverts: the dark end is now the low end, and the single text
           colour becomes light. Still five steps, still one text colour. */
        --color-scale-1: #1a2830;
        --color-scale-2: #22394a;
        --color-scale-3: #2b4d63;
        --color-scale-4: #35637f;
        --color-scale-5: #40799b;
        --color-on-scale: var(--color-gray-50);

        /* The same ten identities lifted off a dark ground: same slot, and the same
           hue to within six degrees, so a firm named in a screenshot is the same firm
           on screen. The LIGHTNESS is refitted rather than mirrored, and that is
           deliberate - the two themes have opposite lightness budgets, and forcing
           this set into the light theme's ordering cost four points of dichromat
           separation for a consistency nobody can see, since a reader never has both
           themes in front of them at once. Worst pair over all twelve: dE00 8.30
           protanope, 8.36 deuteranope. Ratios are on --color-surface-container and
           --color-surface-inset, and the inset binds here because it is the lighter
           of the two. */
        --color-firm-1: #739dce;    /* marine     5.39:1 / 5.12:1 */
        --color-firm-2: #b7827a;    /* clay       4.70:1 / 4.46:1 */
        --color-firm-3: #86bd9f;    /* pine       7.10:1 / 6.73:1 */
        --color-firm-4: #e4bc93;    /* brass      8.63:1 / 8.19:1 */
        --color-firm-5: #ba99b8;    /* plum       6.04:1 / 5.73:1 */
        --color-firm-6: #7ecee2;    /* steel      8.56:1 / 8.12:1 */
        --color-firm-7: #7e8455;    /* fern       3.85:1 / 3.66:1 */
        --color-firm-8: #ecb9c5;    /* oxblood    8.92:1 / 8.46:1 */
        --color-firm-9: #b3a271;    /* olive      6.03:1 / 5.72:1 */
        --color-firm-10: #7e7ba2;   /* indigo     3.80:1 / 3.60:1 */

        /* The unattributed dot stays the quieter of the two neutrals, which on a dark
           ground means the dimmer rather than the paler. DERIVED again, for the same
           reason and in the other direction: gray-400 is 2.70:1 on the inset here. */
        --color-firm-other: var(--color-gray-330);   /* grey      5.33:1 / 5.05:1 */
        --color-firm-unknown: #7f8181;               /* dim grey  3.88:1 / 3.68:1 */
    }
}

:root[data-theme="dark"] {
    color-scheme: dark;

    --color-accent: var(--color-marine);
    --color-accent-hover: var(--color-white);

    --color-surface-root: var(--color-gray-975);
    --color-surface-page: var(--color-gray-950);
    --color-surface-container: var(--color-gray-900);
    --color-surface-inset: var(--color-gray-820);
    --color-surface-overlay: var(--color-gray-800);
    --color-surface-contrast: var(--color-gray-600);
    --color-surface-table-header: var(--color-gray-700);
    --color-surface-input: var(--color-gray-965);
    --color-surface-inverse: var(--color-white);

    --color-state-hover: rgb(255 255 255 / 6%);
    --color-state-pressed: rgb(255 255 255 / 10%);
    --color-state-selected: rgb(206 231 247 / 15%);

    --color-overlay: rgb(0 0 0 / 60%);

    --color-text: var(--color-gray-50);
    --color-text-secondary: var(--color-gray-300);
    --color-text-muted: var(--color-gray-330);
    --color-text-inverse: var(--color-gray-900);

    --color-border: var(--color-gray-690);
    --color-border-subtle: var(--color-gray-700);
    --color-border-muted: var(--color-gray-800);
    --color-border-strong: var(--color-gray-600);
    --color-border-outline: var(--color-gray-600);

    --color-focus-ring: var(--color-marine);

    --color-success-text: var(--color-green);
    --color-warning-text: var(--color-amber);
    --color-danger-text: var(--color-red);
    --color-info-text: var(--color-marine);
    --color-secondary-text: var(--color-stone);

    --color-success-subtle: rgb(165 186 120 / 16%);
    --color-warning-subtle: rgb(222 168 84 / 16%);
    --color-danger-subtle: rgb(206 100 100 / 18%);
    --color-info-subtle: rgb(206 231 247 / 14%);
    --color-primary-subtle: rgb(206 231 247 / 14%);
    --color-secondary-subtle: rgb(214 184 144 / 18%);

    --color-sidebar-background: var(--color-surface-root);
    --color-sidebar-text: var(--color-gray-300);
    --color-sidebar-text-active: var(--color-gray-50);
    --color-sidebar-active: var(--color-gray-820);
    --color-sidebar-border: var(--color-gray-800);
    --color-topbar-background: var(--color-surface-container);

    --shadow-sm: 0 1px 2px rgb(0 0 0 / 30%);
    --shadow-md: 0 2px 8px rgb(0 0 0 / 40%);
    --shadow-lg: 0 10px 28px rgb(0 0 0 / 50%);
    --shadow-xl: 0 18px 44px rgb(0 0 0 / 60%);
    --shadow-container: inset 0 1px 0 rgb(255 255 255 / 8%), 0 2px 8px rgb(0 0 0 / 40%);

    /* The eight hues again, lifted off a dark ground. Same identities, same order,
       so a legend read in one theme still means the same thing in the other -
       somebody comparing a screenshot to a screen must not find that Piping
       changed colour. */
    --color-category-1: #5aa8d6;
    --color-category-2: #9dbd6a;
    --color-category-3: #e0a558;
    --color-category-4: #d18585;
    --color-category-5: #8aa2c4;
    --color-category-6: #a794c9;
    --color-category-7: #5fb5a8;
    --color-category-8: #c7ab72;

    /* The ramp inverts: the dark end is now the low end, and the single text
       colour becomes light. Still five steps, still one text colour. */
    --color-scale-1: #1a2830;
    --color-scale-2: #22394a;
    --color-scale-3: #2b4d63;
    --color-scale-4: #35637f;
    --color-scale-5: #40799b;
    --color-on-scale: var(--color-gray-50);

    /* The same ten identities lifted off a dark ground: same slot, and the same hue
       to within six degrees, so a firm named in a screenshot is the same firm on
       screen. The LIGHTNESS is refitted rather than mirrored, and that is deliberate
       - the two themes have opposite lightness budgets, and forcing this set into the
       light theme's ordering cost four points of dichromat separation for a
       consistency nobody can see, since a reader never has both themes in front of
       them at once. Worst pair over all twelve: dE00 8.30 protanope, 8.36
       deuteranope. Ratios are on --color-surface-container and --color-surface-inset,
       and the inset binds here because it is the lighter of the two. */
    --color-firm-1: #739dce;    /* marine     5.39:1 / 5.12:1 */
    --color-firm-2: #b7827a;    /* clay       4.70:1 / 4.46:1 */
    --color-firm-3: #86bd9f;    /* pine       7.10:1 / 6.73:1 */
    --color-firm-4: #e4bc93;    /* brass      8.63:1 / 8.19:1 */
    --color-firm-5: #ba99b8;    /* plum       6.04:1 / 5.73:1 */
    --color-firm-6: #7ecee2;    /* steel      8.56:1 / 8.12:1 */
    --color-firm-7: #7e8455;    /* fern       3.85:1 / 3.66:1 */
    --color-firm-8: #ecb9c5;    /* oxblood    8.92:1 / 8.46:1 */
    --color-firm-9: #b3a271;    /* olive      6.03:1 / 5.72:1 */
    --color-firm-10: #7e7ba2;   /* indigo     3.80:1 / 3.60:1 */

    /* The unattributed dot stays the quieter of the two neutrals, which on a dark
       ground means the dimmer rather than the paler. DERIVED again, for the same
       reason and in the other direction: gray-400 is 2.70:1 on the inset here. */
    --color-firm-other: var(--color-gray-330);   /* grey      5.33:1 / 5.05:1 */
    --color-firm-unknown: #7f8181;               /* dim grey  3.88:1 / 3.68:1 */
}
