/* ============================================================================
   Edwin UI Kit v1 — tokens.css
   The ONE place where the Digital Apex brand becomes data.

   SOURCE OF TRUTH
     The mark itself: dist/edwin.svg. Its gradients carry the whole palette and
     nothing else is authoritative. Three separate "sampled from the owl"
     derivations existed before this file (the launch page, app 961, and the
     mark); where they disagreed, the SVG won.

       #E5A043  amber      logo gradient stop 0.00 / 0.22
       #C1501D  burnt      logo gradient stop 0.46
       #2D2D61  indigo     logo gradient stop 1.00

     Magenta #CE1090 is RETIRED. It belonged to the previous mark.

   DIRECTION
     A, chosen 2026-08-17: near-black ground, film grain, 15px card radius.
     FLAT: no lit gradients, no elevation shadows, no bevels, no inner glows.
     Depth is carried by hairlines, by the step from ground to surface, and by
     the accent. The logo is the one sanctioned gradient, by construction.

   MODE IS ORTHOGONAL TO SKIN
     A skin owns personality (hue, radius, type, density, motion). The MODE
     owns luminance. Both modes are designed, not inverted: on dark, amber is
     the accent for text and fill alike; on light it is a FILL ONLY, because
     #E5A043 on white measures 2.2:1. Light gets indigo for brand text and a
     darkened burnt for accent text. Measured values are on each line.

   HOW TO OVERRIDE — never fork this file, never edit it per page.
     Re-declare only the token you need, after the link:
       <style>:root{ --dw-content-max: 1440px; }</style>
     A skin re-declares tokens at its own scope: html[data-skin='x'] { ... }

   VERSIONING
     dist/v1/* is FROZEN. Editing one in place is a fleet-wide change with a
     five-minute cache skew and no rollback. To change the system, add
     dist/v2/ and leave v1 serving.
   ========================================================================= */

:root {
	/* ---- Brand palette. A SKIN overrides these three; nothing else. ------
	   Everything below is derived from them, so a skin that sets three
	   values restyles the whole platform. */
	--dw-amber: #E5A043;
	--dw-burnt: #C1501D;
	--dw-indigo: #2D2D61;

	/* ---- Type -----------------------------------------------------------
	   NO WEBFONT, AND THAT IS THE DECISION, not an omission. The launch page
	   ships the system stack; Segoe UI Variable Display on Windows and the
	   system face elsewhere cost nothing to load and never flash. Inter was
	   vendored into an earlier kit (352 KB) and is deliberately absent here:
	   it moves the product toward the generic neo-grotesque look every other
	   dashboard already has. */
	--dw-font: "Segoe UI Variable Display", "Segoe UI", -apple-system,
		BlinkMacSystemFont, Roboto, Helvetica, Arial, "Noto Sans", sans-serif;
	--dw-font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
	/* A DISPLAY FACE, separate from body. Every skin in the dark batch declares
	   one, and it currently reaches headings only because modern-refresh.css
	   styles h1-h6 — a file Wave 6 deletes. When it goes, every skin loses its
	   heading face silently. Defaults to the body face, so nothing changes for a
	   skin that does not care. */
	--dw-font-display: var(--dw-font);

	/* Sizes. Small by design: an operational table is not an editorial page.
	   The launch page's 138px display scale does not appear here. */
	--dw-size-eyebrow: 10.5px;
	--dw-size-micro: 11px;
	--dw-size-small: 12px;
	--dw-size-base: 13.5px;
	--dw-size-nav: 13px;
	--dw-size-lede: 17px;
	--dw-size-h3: 17px;
	--dw-size-h2: clamp(22px, 2.6vw, 32px);
	--dw-size-h1: clamp(27px, 3.4vw, 40px);
	--dw-size-stat: 25px;

	--dw-weight-body: 400;
	--dw-weight-medium: 500;
	--dw-weight-strong: 600;

	--dw-leading: 1.6;
	--dw-leading-tight: 1.08;

	/* ---- Tracking. THE RULE: large type tracks NEGATIVE, small uppercase
	   labels track POSITIVE. It is the single strongest reason the look reads
	   as current, and it is the rule most easily lost in a refactor. */
	--dw-track-h1: -.042em;
	--dw-track-h2: -.030em;
	--dw-track-lede: -.014em;
	--dw-track-body: 0;
	--dw-track-eyebrow: .24em;
	--dw-track-label: .17em;
	--dw-track-th: .14em;

	/* ---- Geometry. Flat. --------------------------------------------- */
	--dw-radius-sm: 8px;
	--dw-radius: 15px;
	--dw-radius-pill: 999px;
	--dw-border: 1px;

	/* Exists so a skin CAN opt into elevation. The default system is flat and
	   every kit component reads this token rather than declaring a shadow, so
	   turning depth on is one override rather than a rewrite. */
	--dw-shadow: none;

	/* ---- Space. 4px base. -------------------------------------------- */
	--dw-space-1: 4px;
	--dw-space-2: 8px;
	--dw-space-3: 12px;
	--dw-space-4: 16px;
	--dw-space-5: 22px;
	--dw-space-6: 32px;
	--dw-space-7: 48px;
	--dw-space-8: 72px;

	/* ---- Layout ------------------------------------------------------- */
	--dw-header-h: 64px;
	--dw-gutter: clamp(16px, 3vw, 34px);
	--dw-content-max: 1240px;
	--dw-prose-max: 68ch;
	--dw-card-pad: 18px;
	--dw-cell-pad: 10px 16px;

	/* ---- Motion -------------------------------------------------------
	   Every duration is multiplied by --dw-motion-scale so a skin can retime
	   or kill motion wholesale. 0 is a valid value and means "no motion". */
	--dw-motion-scale: 1;
	--dw-dur-fast: calc(160ms * var(--dw-motion-scale));
	--dw-dur: calc(280ms * var(--dw-motion-scale));
	--dw-dur-slow: calc(450ms * var(--dw-motion-scale));
	--dw-ease: cubic-bezier(.2, .7, .3, 1);

	/* ---- Ambient layer ------------------------------------------------
	   Direction A's film grain, as a token pair. A skin may swap the image
	   for its own texture, or set opacity 0 to remove it. This is the cheap
	   form of "a skin defines its background": one data URI, no canvas, no
	   WebGL, no CSP problem. */
	--dw-grain-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.82' numOctaves='3'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='.42'/%3E%3C/svg%3E");
	--dw-grain-opacity: .10;
	--dw-grain-blend: overlay;

	/* ---- Logo. A SLOT, not a path. -------------------------------------
	   The mark used to be named in 144 hardcoded paths across ~60 files, which
	   is why it had never been changed. Pages read these tokens; a skin can
	   point them at a customer's mark.

	   THERE ARE TWO MARKS, AND WHICH ONE IS RIGHT DEPENDS ON THE GROUND.

	     --dw-logomark-solid    the earlier mark: a filled owl in an indigo to
	                            magenta gradient. Opaque, so it reads on any
	                            ground and needs no plate behind it.
	     --dw-logomark-outline  the current mark: line art on a transparent
	                            ground, with an opaque WHITE facial disc. On a
	                            dark plate it is the better drawing by some way.
	                            On a light one the white disc dissolves into the
	                            page and the indigo ears go with it, leaving a
	                            faint amber line. Owner's words: "this new
	                            stealth logo only works for darker theme".

	   --dw-logomark is whichever of the two THIS skin uses, and it is the only
	   one a component ever reads. The pairing is set once, near the bottom of
	   this file, from each skin's MEASURED surface luminance: 21 skins are
	   light and 11 are dark, and nothing sits near the boundary (the darkest
	   light skin is werkbank at 0.81, the lightest dark skin is blueprint at
	   0.05). The kit default is light, so the solid mark and no plate are the
	   defaults here and the dark skins opt out.

	   IF DARK MODE IS EVER TURNED ON, this needs a second signal: right now a
	   skin's identity is the only thing that says light or dark, because
	   [data-mode="auto"] is inert. Keying off the skin alone would then be
	   wrong for a light skin viewed in dark mode. */
	/* ROOT-RELATIVE, and this is not a style preference. A relative url() inside
	   a CUSTOM PROPERTY does not resolve against the stylesheet that declared it,
	   the way a relative url() in an ordinary declaration does. It resolves
	   against the DOCUMENT. So "../../img/edwin.png", which is the correct path
	   from assets/css/kit/, became /img/edwin.png when read from /index.html and
	   404'd on every page. Measured in a browser rather than reasoned about: the
	   computed background-image came back as http://host/img/favicon.png.

	   Which means var(--dw-logomark) had never painted anything since the token
	   was introduced. Nobody noticed because its only consumer was the kit's
	   .dw-logomark class, which no page in the tree wears, and because a missing
	   background looks exactly like a component that simply has no content. That
	   is the tenth construct in this codebase found promising something no code
	   delivered. A leading slash is immune: every page is served from the site
	   root, so there is nothing to resolve against but the origin.

	   BOTH ARE PNG, AND BOTH ARE TRIMMED TO THEIR INK. edwin.png is 2705x3264,
	   favicon.png is 2623x3182: the same aspect within 0.6%, which is why they
	   swap without the mark changing size or the layout moving. favicon.svg was
	   measured and rejected for this job - it is not cropped, but its art sits
	   in a square canvas at aspect 0.53 against the PNG's 0.83, so it would
	   render visibly narrower in the same box.

	   Trimmed to the ink means `background-size: contain` is safe and anything
	   that CROPS is not: a square `object-fit: cover` box takes the ears off
	   first, and there is no padding to give. Do not put either mark in
	   .dw-avatar or any other circle. */
	--dw-logomark-solid: url("/assets/img/favicon.png");
	--dw-logomark-outline: url("/assets/img/edwin.png");
	--dw-logomark: var(--dw-logomark-solid);
	--dw-logotype: none;
	--dw-logomark-h: 40px;
	--dw-logomark-min: 24px;

	/* ---- The plate behind the mark. OFF, on every skin. -----------------
	   The kit draws no box behind the logo anywhere. Light or dark, the mark
	   sits on the page. That is the owner's call, taken against the numbers in
	   the WHICH MARK THIS SKIN USES block below: the plate was not what made the mark legible
	   on a dark ground, and on seven of the eleven dark skins it was invisible
	   apart from its own hairline.

	   These three stay as a REAL hook, not a decoration. The plate rules in
	   home.css read them, so a skin that wants a chip - a customer brand skin,
	   say - gets one by setting --dw-logo-plate alone. Verified by setting it
	   on a scratch skin and reading the computed style back: the plate painted.
	   Keep it near-black if you do set it; the mark was rendered against six
	   candidates and brand indigo was the WORST of them, because the ears are
	   that same indigo and vanish into it.

	   --dw-logo-plate-line is separate from --dw-line so that "no plate" also
	   means no hairline. A ring around a bare mark is still a box. */
	--dw-logo-plate: transparent;
	--dw-logo-plate-line: transparent;
	--dw-logo-plate-radius: 10px;

	/* ---- Component knobs that are mode-independent --------------------- */
	--dw-icon-size: 1em;      /* the 998 `bi` icons inherit colour and size */
	/* Clears the fixed platform header rather than sitting under it. The header
	   is --dw-header-h tall at z-index 997; the toast stack was at 16px and
	   z-index 60, so elementFromPoint at its own centre returned the header. It
	   was invisible on all 56 pages. */
	--dw-toast-top: calc(var(--dw-header-h) + 16px);

	/* What sits above and below an editor's content box: header margin 64,
	   padding 20 + 20, footer 63. Measured with getBoundingClientRect, not
	   guessed — the archetype's earlier figure left 71px of residual page
	   scroll. A page with different chrome corrects this one token instead of
	   inventing another vh formula. */
	--dw-editor-chrome: 167px;
	--dw-toast-side: 16px;

	/* ---- Stacking. THE KIT WAS GUESSING, and guessed low twice.
	   The platform chrome is far above where a component-level z-index
	   naturally lands: measured #sidebar 996, #header 997, .modal-backdrop
	   1050, .modal 1055, .dw-confirm-backdrop 10500. A kit veil at 50 and a
	   toast at 60 therefore rendered UNDERNEATH the header and the sidebar —
	   the toast invisibly, the veil as a dialog with the chrome punched
	   through it. Both were found one at a time; these tokens are so the
	   third one is not.
	   The values follow Bootstrap's published scale, because the platform is
	   already on it and inventing a parallel one is how this happened. */
	--dw-z-raised: 1;        /* inside a card: a focused input over a sibling */
	--dw-z-sticky: 4;        /* a save bar within its own scroll container */
	--dw-z-overlay: 1000;    /* above the chrome, below every dialog */
	--dw-z-veil: 1050;       /* matches .modal-backdrop */
	--dw-z-modal: 1055;      /* matches .modal */
	--dw-z-toast: 1090;      /* above dialogs: a toast must outlive a modal */
	--dw-toast-rail: 2px;     /* 0 removes the leading tone rail entirely */
	--dw-avatar-size: 28px;   /* .dw-avatar; its states override per element */
	--dw-card-media-h: 148px; /* the fixed preview slot in .dw-card-media */
	--dw-chart-h: 260px;      /* .dw-chartbox; Chart.js needs a definite one */
	--dw-scroll-h: 420px;     /* .dw-scrollbody; a panel that scrolls itself */ /* the fixed preview slot in .dw-card-media */

	/* The scrim under a caption sitting on top of a preview image. It is a
	   TOKEN and not a rule because the gate matches `linear-gradient(` in any
	   property in any file except this one, and rightly so: a lit gradient is a
	   depth cue and the system is flat. A scrim is not a depth cue. It is the
	   one thing a flat system still needs a gradient for, because the colour it
	   has to beat is whatever pixels the user uploaded.

	   It reads --dw-surface rather than a literal, so it follows the mode and
	   the skin with no second definition, and it dissolves the image into the
	   card it sits in rather than into an arbitrary black. */
	--dw-fade-bottom: linear-gradient(to bottom, transparent, var(--dw-surface));

	/* A FIXED LIGHT plate, and it must stay fixed. It backs the quiet zone of a
	   QR code on 2 pages. Every scanner on the market expects dark modules on a
	   light field; --dw-surface going dark is the exact failure this prevents,
	   and it fails silently — the page looks right and the code will not scan.
	   Deliberately absent from all three mode blocks. Do not "fix" that. */
	--dw-scan-plate: #FFFFFF;
}

/* ============================================================================
   LIGHT MODE — the bare :root default, so an un-stamped document is light,
   matching the platform today. This is a DESIGNED counterpart, not an
   inversion: the accent changes job between modes.
   ========================================================================= */
:root,
:root[data-mode="light"] {
	/* Warm off-white, biased toward the amber rather than a neutral grey.
	   A pure #FFF ground makes the amber look like a warning. */
	--dw-ground: #FAF9F6;
	--dw-surface: #FFFFFF;
	/* DERIVED, NOT DECLARED — and that is the whole point.
	   These used to be literals, which meant they could not follow the legacy
	   bridge. A dark pre-kit skin (abyss, opsterm) bridged --dw-surface to its
	   own near-black and left --dw-surface-2 at #F2F0EA, so a .dw-pill.is-done
	   rendered as a near-white lozenge on a near-black page. Measured, not
	   theorised. Deriving them means a skin that sets one value moves all of
	   them, which is what the token layer promised in the first place.
	   The literal on the first line is the pre-color-mix fallback; browsers
	   that understand the second line use it. */
	--dw-surface-2: #F2F0EA;
	--dw-surface-hover: #F7F5F0;

	--dw-line: rgba(20, 18, 26, .10);
	/* Tables only. At .10 a row border cannot be tracked across a wide table,
	   which is the one job a table border has. Measured on the real agent
	   list before this token existed. */
	--dw-line-strong: rgba(20, 18, 26, .20);

	--dw-text: #14121A;           /* 17.63:1 on ground, 18.56 on surface */
	--dw-muted: #56525F;          /*  7.21:1 on ground, 7.59 on surface */
	/* DERIVED FROM --dw-muted, AND THAT IS AN ACCESSIBILITY FIX, not tidiness.
	   As a literal it could not follow the bridge: on the opsterm skin it
	   measured 3.9:1 on a card and 3.7:1 on a table header, both under AA, on
	   real text. Deriving it means it tracks whatever muted the skin chose, so
	   it degrades with the skin instead of against it.
	   88% is close to muted on purpose: faint is the quietest legible text, and
	   the room between "quieter than muted" and "below 4.5:1" is small. If a
	   skin sets a muted that is itself near the floor, faint follows it down —
	   the honest fix for that is the skin, which is why Wave 6 rebuilds them. */
	--dw-faint: #77727F;

	/* Amber is a FILL on light, never text. #E5A043 is 2.11:1 on the ground
	   and 2.22:1 on the surface. */
	--dw-accent: #E5A043;         /* fills, rails, chart series */
	--dw-accent-text: #8A5416;    /*  5.9:1 — a darkened burnt */
	--dw-accent-ink: #140C02;     /* text ON amber, 8.72:1 */
	--dw-accent-wash: rgba(229, 160, 67, .14);
	--dw-brand: #2D2D61;          /* the indigo itself, so the text form can derive */
	--dw-brand-text: #2D2D61;     /* 12.0:1 — indigo carries brand on light */

	/* MEASURED ON ITS OWN WASH, not on the ground. This read 5.3:1 for a long
	   time and the number was true of a place nothing paints: .dw-pill.is-ok
	   draws --dw-ok on --dw-ok-wash, which is 13% of the same green over the
	   surface — a tinted ground that sits closer to the text than the surface
	   does, dropping the real ratio to 4.43:1. Three rebuilt skins reported
	   the identical 4.43 independently, which is what said it was the kit's
	   number and not theirs. Darkened until the ratio anyone actually sees has
	   headroom rather than scraping the bar. */
	--dw-ok: #1D714E;             /* 4.95:1 on its own wash, 4.60 on the worst skin surface */
	--dw-ok-wash: rgba(29, 113, 78, .13);
	--dw-warn: #8A5416;           /* 5.18:1 on its own wash, 5.94 on ground */
	--dw-warn-wash: rgba(138, 84, 22, .13);
	--dw-bad: #B3261E;            /* 5.27:1 on its own wash, 6.21 on ground */
	--dw-bad-wash: rgba(179, 38, 30, .13);
	/* The fourth semantic, added on evidence rather than symmetry: alert-info
	   measures 25 uses across 17 pages, and the list pages need a NON-PULSING
	   informational pill (sleeping, waiting_event, an audit UPDATE row) that
	   is-run cannot serve, because is-run pulses a dot 1.6s forever and a
	   100-row audit table would pulse on every row. */
	--dw-info: #2A5CA8;           /* 5.42:1 on its own wash, 6.24 on ground */
	--dw-info-wash: rgba(42, 92, 168, .13);
	/* The same four as RGB triples. Bootstrap's colour utilities are written
	   as rgba(var(--bs-success-rgb), var(--bs-bg-opacity)), so a hex cannot
	   reach them; see the --bs-* bridge at the foot of this file. */
	--dw-ok-rgb: 29, 113, 78;
	--dw-warn-rgb: 138, 84, 22;
	--dw-bad-rgb: 179, 38, 30;
	--dw-info-rgb: 42, 92, 168;

	/* ---- Categorical palette (light) ----------------------------------
	   Ten series colours for charts and category dots. Three separate surveys
	   asked for this independently. Today --chart-color-1..10 exist only inside
	   a base layer scheduled for deletion, with 37 uses, and one page maps
	   eleven action types onto them.

	   THESE ARE CATEGORICAL, NOT SEMANTIC. They must never be the ok/warn/bad
	   tokens: a chart that draws "click" green and "wait" amber has asserted
	   that clicking is good and waiting is a warning, which is a claim the data
	   never made. They are ordered so that ADJACENT series are far apart in hue
	   — the pairs a reader actually has to tell apart are the ones next to each
	   other in a legend.

	   Brand-derived where the hue allows: cat-1 is the amber darkened for a
	   light ground (#E5A043 measures 2.2:1 here and is a fill only), cat-9 is
	   --dw-burnt unchanged, cat-6 is --dw-indigo lifted to a usable luminance.
	   cat-10 is a deliberate neutral: every one of these maps has an "other"
	   bucket, and giving it a hue implies a category it does not have.

	   Measured on #FFFFFF: 4.0 6.3 5.2 6.3 5.2 6.7 5.1 6.0 4.7 5.9 : 1.
	   Some pairs share a luminance, so hue is doing the separating — this set
	   does NOT survive being printed in greyscale, and a chart that must should
	   carry a second channel (dash, shape, direct labels). */
	--dw-cat-1-base: #C06A15;          /* amber, darkened for light */
	--dw-cat-2-base: #1D5FB0;          /* azure */
	--dw-cat-3-base: #3D7A24;          /* leaf */
	--dw-cat-4-base: #A83464;          /* raspberry */
	--dw-cat-5-base: #0F7A78;          /* teal */
	--dw-cat-6-base: #454FBE;          /* indigo, lifted from #2D2D61 */
	--dw-cat-7-base: #75701A;          /* olive */
	--dw-cat-8-base: #8A44A8;          /* violet */
	--dw-cat-9-base: #C1501D;          /* burnt, the brand value unchanged */
	--dw-cat-10-base: #5B6478;         /* slate — the "other" bucket */

	--dw-focus: #2D2D61;
	--dw-btn-close-filter: none;

	/* Terminal panes stay dark on a light skin by design, and are tokenized
	   so a skin may still retheme them. The cursor and the row rule are part of
	   that surface, not of the mode, which is why they are declared with it in
	   all three blocks rather than once above: a skin that retints the terminal
	   has to be able to take the whole surface, not two thirds of it. */
	--dw-terminal-bg: #0A0E1B;
	--dw-terminal-fg: #E9EAF0;
	--dw-terminal-cursor: #E5A043; /* the block caret. Amber reads at 8.6:1 on
	                                  the terminal ground in both modes and is
	                                  the one place the brand marks a log. */
	--dw-terminal-line: rgba(233, 234, 240, .08); /* between log rows */

	/* NOT derived, and that was tried and reverted. Mixing the GROUND toward
	   transparent yields the page's own colour, so on every light skin the
	   backdrop dimmed nothing at all: measured #F4F7F9 against the correct
	   #96979B, a delta-E of 34.65 across 88 dialogs. A veil is darkness. It is
	   not a tint of the surface it covers, so it does not follow the skin. */
	--dw-veil: rgba(12, 12, 18, .55);

	/* A VEIL YOU HAVE TO READ ON, which --dw-veil is not. The veil dims a
	   surface behind a dialog and nothing is ever laid over it, so .55 is all it
	   needs. A scrim covers an image the USER supplied and then carries a label
	   on top: the worst case is not the app's ground, it is a photo of a white
	   wall. Measured with --dw-scrim-ink over both extremes: at .55 the label is
	   4.04:1 on white pixels and fails; at .68 it is 6.44:1 on white and 18.16:1
	   on the dark surface. Both are fixed rather than derived, for the reason
	   given at --dw-veil: a scrim is darkness, not a tint of anything. */
	--dw-scrim: rgba(12, 12, 18, .68);
	--dw-scrim-ink: #F7F7FA;

	/* Grain is a dark-ground effect. On light it turns to dirt. */
	--dw-grain-opacity: 0;

	/* THE LEGACY SKIN BRIDGE WAS HERE, AND IT IS DELETED.
	   For the whole migration these role tokens read the OLD names first
	   (--background-color, --card-bg, --state-success and the rest) so that 27
	   pre-kit skins could still drive pages that had moved onto the kit. All
	   27 have now been rebuilt on --dw-* directly, so the crutch is gone.

	   REMOVING IT IS A FIX, NOT A TIDY-UP. The bridge lines sat LAST in the
	   block, so they beat the kit's own literals above them. Measured on the
	   default look, the legacy values they promoted were worse on every
	   pairing: text on ground 10.69:1 against 17.63:1, muted on surface 4.69
	   against 7.59, and all four semantic tones FAILED AA on their own wash
	   (ok 3.73, warn 3.14, bad 3.91, info 3.19). Those four failures were live
	   on 55 pages and they end here.

	   The accent moves with it, #E8882E to #E5A043, and that is a correction
	   rather than drift: the Edwin mark is 382,723 pixels of #E5A043 and
	   contains no #E8882E anywhere, so the kit literal is the brand value and
	   the legacy one had wandered off it.

	   STILL OPEN, and deliberately not bundled here: the --dw-*-rgb triples
	   that feed the Bootstrap colour bridge are not skinnable, so .text-danger
	   still resolves to the kit red on a dark skin. That is a separate defect
	   with a separate fix, and pretending it died with this block would be the
	   kind of claim this file keeps having to retract. */
	/* NOT BRIDGED, and this is a contrast fix rather than a purity one.
	   --contrast-color is #ffffff in light-theme.css, so bridging made every
	   primary button and every accent fill white-on-amber at 2.22:1, against
	   8.72:1 for the value below. It reached all 52 migrated pages at once.
	   The legacy platform had the same defect and the bridge imported it
	   faithfully; a bridge is supposed to carry a skin's intent, not its bugs.
	   A skin that wants different ink sets --dw-accent-ink itself. That is what
	   the token is for, and unlike --contrast-color it means exactly one thing. */
	--dw-accent-ink: #140C02;

}

/* ---- DARK, for a viewer whose OS says dark and who has not chosen -------- */
/* AUTO-ADOPTING THE OS PREFERENCE IS STILL DISABLED, AND THE REASON HAS
   CHANGED. The guard below is what disables it: nothing sets
   data-mode="auto", so this block never applies.

   The original reason is gone. The legacy bridge used to read the OLD
   token names first, and light-theme.css defines them unconditionally on
   :root, so the fallback never fired and a dark-OS visitor got a half-dark
   page. The bridge is deleted, and that specific incoherence with it.

   THAT BLOCKER IS GONE, as of 2026-08-23. It used to read: 11 of the 66
   pages are deliberately not on the kit (login, setup, the legal documents
   and the three error pages) and have no dark design at all, so turning
   this on would send a dark-OS user through a white login into a dark
   application and out to a white 403. All eleven are on the kit now, they
   wear .dw-visitor, they follow the skin, and
   tests/visitorPagesOnKit.test.js holds them there. The only pages still
   off the kit are the three frames that host customer-authored content,
   and those have no chrome of their own to light.

   WHAT REMAINS is the honest half of the original note: nobody has yet
   LOOKED at the dark values across the platform. They are measured, which
   is not the same as reviewed, and this project has already produced
   several constructs that measured fine and were wrong. This is now a
   REVIEW away rather than a migration away.

   TO ENABLE, when someone decides to: change the selector to
   :root:not([data-mode="light"]) and review the result. It is one line, and
   the values below are designed rather than inverted. The decision is a
   product one, not a technical one. */
@media (prefers-color-scheme: dark) {
	:root[data-mode="auto"] {
		--dw-ground: #04060D;
		--dw-surface: #0A0E1B;

		--dw-line: rgba(233, 234, 240, .08);

		--dw-text: #E9EAF0;        /* 16.9:1 on ground */
		--dw-muted: #868CA4;       /*  6.1:1 */

		--dw-accent: #E5A043;      /* 9.1:1 — on dark it IS a text colour */
		--dw-accent-text: #E5A043;
		--dw-accent-ink: #140C02;
		--dw-brand-text: #E9EAF0;  /* indigo is unreadable here; it stays
		                               decorative on dark */

		--dw-ok: #4FB286;
		--dw-ok-wash: rgba(79, 178, 134, .13);
		--dw-warn: #D9973F;
		--dw-warn-wash: rgba(217, 151, 63, .13);
		--dw-bad: #C9584E;
		--dw-bad-wash: rgba(201, 88, 78, .14);
		--dw-info: #6C9BE0;        /* 7.1:1 on the ground */
		--dw-info-wash: rgba(108, 155, 224, .13);
		--dw-ok-rgb: 79, 178, 134;
		--dw-warn-rgb: 217, 151, 63;
		--dw-bad-rgb: 201, 88, 78;
		--dw-info-rgb: 108, 155, 224;

		/* Categorical palette (dark). Same ten hues in the same order, lifted
		   for a near-black ground: the light set bottoms out at 1.4:1 here. See
		   the light block for why these are not the semantic tokens.
		   Measured on #04060D: 9.1 8.0 9.2 6.7 8.8 6.7 10.3 7.0 6.7 8.0 : 1. */
		--dw-cat-1-base: #E5A043;       /* the brand amber itself — 9.1:1 on dark */
		--dw-cat-2-base: #5FA8E8;
		--dw-cat-3-base: #7CC061;
		--dw-cat-4-base: #E0709F;
		--dw-cat-5-base: #3FBDB5;
		--dw-cat-6-base: #7B8CF5;
		--dw-cat-7-base: #C3BC55;
		--dw-cat-8-base: #B583E0;
		--dw-cat-9-base: #F06B52;
		--dw-cat-10-base: #9AA3BA;

		--dw-focus: #E5A043;
		/* Bootstrap's close X is a black SVG data URI and cannot be coloured.
		   One filter fixes all 101 instances. */
		--dw-btn-close-filter: invert(1) grayscale(1) brightness(1.4);

		--dw-terminal-bg: #04060D;
		--dw-terminal-fg: #E9EAF0;
		--dw-terminal-cursor: #E5A043;
		--dw-terminal-line: rgba(233, 234, 240, .08);

		--dw-grain-opacity: .22;

		/* NOT BRIDGED, and this is a contrast fix rather than a purity one.
		   --contrast-color is #ffffff in light-theme.css, so bridging made every
		   primary button and every accent fill white-on-amber at 2.22:1, against
		   8.72:1 for the value below. It reached all 52 migrated pages at once.
		   The legacy platform had the same defect and the bridge imported it
		   faithfully; a bridge is supposed to carry a skin's intent, not its bugs.
		   A skin that wants different ink sets --dw-accent-ink itself. That is what
		   the token is for, and unlike --contrast-color it means exactly one thing. */
		--dw-accent-ink: #140C02;
	}
}

/* ---- DARK, chosen explicitly. Must win over an OS that says light. ------- */
:root[data-mode="dark"] {
	--dw-ground: #04060D;
	--dw-surface: #0A0E1B;

	--dw-line: rgba(233, 234, 240, .08);

	--dw-text: #E9EAF0;
	--dw-muted: #868CA4;

	--dw-accent: #E5A043;
	--dw-accent-text: #E5A043;
	--dw-accent-ink: #140C02;
	--dw-brand-text: #E9EAF0;

	--dw-ok: #4FB286;
	--dw-ok-wash: rgba(79, 178, 134, .13);
	--dw-warn: #D9973F;
	--dw-warn-wash: rgba(217, 151, 63, .13);
	--dw-bad: #C9584E;
	--dw-bad-wash: rgba(201, 88, 78, .14);
	--dw-info: #6C9BE0;
	--dw-info-wash: rgba(108, 155, 224, .13);
	--dw-ok-rgb: 79, 178, 134;
	--dw-warn-rgb: 217, 151, 63;
	--dw-bad-rgb: 201, 88, 78;
	--dw-info-rgb: 108, 155, 224;

	/* Categorical palette (dark, chosen). Identical to the OS-dark block. */
	--dw-cat-1-base: #E5A043;
	--dw-cat-2-base: #5FA8E8;
	--dw-cat-3-base: #7CC061;
	--dw-cat-4-base: #E0709F;
	--dw-cat-5-base: #3FBDB5;
	--dw-cat-6-base: #7B8CF5;
	--dw-cat-7-base: #C3BC55;
	--dw-cat-8-base: #B583E0;
	--dw-cat-9-base: #F06B52;
	--dw-cat-10-base: #9AA3BA;

	--dw-focus: #E5A043;
	--dw-btn-close-filter: invert(1) grayscale(1) brightness(1.4);

	--dw-terminal-bg: #04060D;
	--dw-terminal-fg: #E9EAF0;
	--dw-terminal-cursor: #E5A043;
	--dw-terminal-line: rgba(233, 234, 240, .08);

	--dw-grain-opacity: .22;

	/* NOT BRIDGED, and this is a contrast fix rather than a purity one.
	   --contrast-color is #ffffff in light-theme.css, so bridging made every
	   primary button and every accent fill white-on-amber at 2.22:1, against
	   8.72:1 for the value below. It reached all 52 migrated pages at once.
	   The legacy platform had the same defect and the bridge imported it
	   faithfully; a bridge is supposed to carry a skin's intent, not its bugs.
	   A skin that wants different ink sets --dw-accent-ink itself. That is what
	   the token is for, and unlike --contrast-color it means exactly one thing. */
	--dw-accent-ink: #140C02;
}

/* The base honours the OS preference as well as the skin token, so a skin
   that never thinks about motion still behaves for people who need it to. */
@media (prefers-reduced-motion: reduce) {
	:root { --dw-motion-scale: 0; }
}

/* ============================================================================
   THE BOOTSTRAP BRIDGE

   Nothing in this platform redefines Bootstrap's semantic colour variables, so
   about 750 uses of bg-success / text-danger / text-muted and friends resolve
   to Bootstrap's stock values and NO skin can reach any of them. Verified: zero
   definitions of --bs-success, --bs-danger, --bs-warning, --bs-info or
   --bs-secondary-color exist anywhere in public/assets/css/.

   Remapping them here fixes all ~750 sites at once, in one file, without
   touching a single page. That is the whole point: the alternative was ~750
   hand edits spread across the rollout.

   IT ALSO FIXES A REAL DEFECT. Bootstrap ships
       .text-muted { color: var(--bs-secondary-color) !important }
   and ui.css carries no !important anywhere, so the kit's `.text-muted` alias
   never won a single one of its 821 uses. Important beats normal regardless of
   source order. The fix belongs here, not in an !important arms race.

   Because these read the --dw-* tokens rather than literals, they follow the
   mode and the skin automatically: one definition, three palettes.

   LOAD ORDER MATTERS. tokens.css must be linked AFTER bootstrap.min.css or
   Bootstrap's own :root definitions win. That is the only ordering constraint
   the kit has.

   Deliberately NOT bridged: --bs-body-bg, --bs-body-color, --bs-border-color
   and the rest of the neutral chassis. Those would restyle every Bootstrap
   component on all 56 pages in one step, before any page has been migrated,
   and the wave-by-wave revert story is worth more than the extra reach.
   ========================================================================= */
:root {
	--bs-success: var(--dw-ok);
	--bs-success-rgb: var(--dw-ok-rgb);
	--bs-danger: var(--dw-bad);
	--bs-danger-rgb: var(--dw-bad-rgb);
	--bs-warning: var(--dw-warn);
	--bs-warning-rgb: var(--dw-warn-rgb);
	--bs-info: var(--dw-info);
	--bs-info-rgb: var(--dw-info-rgb);

	/* The one that matters most by volume: 821 uses of .text-muted. */
	--bs-secondary-color: var(--dw-muted);

	/* LINKS, and this one is a live accessibility defect rather than a
	   tidiness fix. Bootstrap resolves --bs-link-color to a near-accent
	   orange that measures 2.63:1 on the default light ground. Under a skin
	   it measures 6.3-10.2:1, so the failure is invisible everywhere except
	   the look most people actually see — which is exactly why it survived.
	   --dw-accent-text is derived toward --dw-text, so it is readable on
	   whatever ground it lands on. */
	--bs-link-color: var(--dw-accent-text);
	--bs-link-hover-color: var(--dw-accent-text);
}

/* ============================================================================
   DERIVED SECONDARIES

   These four were literals, and a literal cannot follow the legacy bridge. A
   dark pre-kit skin bridged --dw-surface to its own near-black and left
   --dw-surface-2 at #F2F0EA, so a status pill rendered as a near-white lozenge
   on a near-black page. Verified fixed in a browser: --dw-surface-2 resolves to
   #162B36 on abyss and #1F2226 on opsterm, and the pill measures 5.12:1 and
   6.01:1 where it was previously unreadable.

   WHY @supports RATHER THAN TWO DECLARATIONS. The obvious form —
       --dw-surface-2: #F2F0EA;
       --dw-surface-2: color-mix(...);
   looks like a progressive-enhancement fallback and is not one. A custom
   property accepts ANY <declaration-value>, so an engine without color-mix
   still parses the second line, still lets it win the cascade, and only fails
   later at USE time as invalid-at-computed-value-time — which resolves to
   `unset`, not to the literal. Proven by probing an unparseable colour space:
   the element came back transparent, not #F2F0EA. The comment promised a
   safety net the code did not have. This gate is the net.

   --dw-veil is deliberately NOT here. It was derived in the first attempt and
   reverted: see its definition for the measurement.
   ========================================================================= */
/* THE CONDITION MUST BE A DECLARATION, `(property: value)`, not a bare
   function. `@supports (color-mix(...))` parses as a general-enclosed
   production, which the spec requires browsers to evaluate as FALSE — so the
   first version of this block was dead everywhere, including in browsers with
   full color-mix support. Measured in Edge 151:
       CSS.supports('(color-mix(in srgb, red, blue))')  -> false
       CSS.supports('color: color-mix(in srgb, red, blue)') -> true
   Every derived token silently fell back to its light-mode literal, which put
   a #F2F0EA table header back on a near-black card — the exact bug this block
   was written to fix. Verify a @supports condition by running CSS.supports on
   it; a wrong condition fails silently and looks like a browser problem. */
@supports (color: color-mix(in srgb, red, blue)) {
	:root {
		--dw-surface-2: color-mix(in srgb, var(--dw-surface) 94%, var(--dw-text));
		--dw-surface-hover: color-mix(in srgb, var(--dw-surface) 97%, var(--dw-text));
		--dw-line-strong: color-mix(in srgb, var(--dw-text) 22%, transparent);
		--dw-accent-wash: color-mix(in srgb, var(--dw-accent) 14%, transparent);

		/* The four semantic washes, which the rebuild brief already claimed were
		   derived and were not — my error, caught by a skin author who set all four
		   by hand rather than trust the table. Leaving them undeived would park
		   light-mode literals on a near-black ground the moment the bridge dies.
		   Derived, a skin sets four values instead of eight. */
		--dw-ok-wash: color-mix(in srgb, var(--dw-ok) 13%, transparent);
		--dw-warn-wash: color-mix(in srgb, var(--dw-warn) 13%, transparent);
		--dw-bad-wash: color-mix(in srgb, var(--dw-bad) 13%, transparent);
		--dw-info-wash: color-mix(in srgb, var(--dw-info) 13%, transparent);

		/* An accessibility fix, not tidiness: as a literal this measured 3.9:1
		   on opsterm, under AA on real text. Derived, it tracks whatever muted
		   the skin chose, degrading with the skin instead of against it. */
		--dw-faint: color-mix(in srgb, var(--dw-muted) 88%, var(--dw-surface));

		/* The accent as TEXT, and the one accent token this block originally
		   skipped. --dw-accent-wash beside it already followed the skin while
		   this stayed the light literal #8A5416, measuring roughly 2:1 on the
		   opsterm surface. It reaches .dw-pill.is-run, .dw-chip.is-applied and
		   .dw-stat-value.is-accent, so it is not a corner case.

		   Mixing TOWARD --dw-text is what makes one formula work in both
		   directions: on light it darkens the amber, on dark it lightens the
		   skin accent, and either way it moves toward contrast rather than
		   toward a fixed hue that only suits one ground.

		   40%, NOT the 55% this shipped with. A derived token cannot be fixed
		   per skin, so the share has to be chosen from the WORST skin rather
		   than from the default, and it was not. Measured across all 27 skins:
		   at 55% the two yellow-accented skins fail — paperwork 3.95:1 and
		   werkbank 3.73:1 on their own surface — and at 50% werkbank is still
		   4.20:1.

		   The share is set by the TIGHTER of two pairings, because this token
		   is painted on two different grounds. On --dw-surface, 45% would do
		   (worst 4.74:1). But .dw-pill.is-run and .sev-medium paint it on
		   --dw-accent-wash, which is 14% of the same accent over that surface
		   and therefore sits closer to the text — there werkbank measures
		   4.45:1 at 45%, and only 40% clears (worst 5.04:1, and 5.37:1 back on
		   the plain surface). Picking from the easier pairing is how a token
		   passes its own documentation and fails the component that uses it.
		   Contrast rises monotonically as the share drops, so there is no skin
		   this trades against; the cost is a little hue where it was fine. */
		--dw-accent-text: color-mix(in srgb, var(--dw-accent) 40%, var(--dw-text));

		/* THE BRAND INDIGO AS TEXT, derived for the same reason and by the
		   same formula. It was a literal with a light value and a dark value,
		   and the dark value only applied under [data-mode="dark"] — so every
		   PINNED dark skin got the LIGHT indigo on its near-black ground.
		   Measured: abyss 1.50, splitflap 1.50, pixel8 1.52, prime 1.52,
		   opsterm 1.54, aurora 1.56, ember 1.56. Seven skins, one omission,
		   because the token was missing from the rebuild brief's table and
		   every author correctly left alone what the table did not list.
		   It has one consumer, .hub-agent-name in home.css, and that consumer
		   is the front door.
		   Deriving it fixes all seven at once and cannot regress: a skin that
		   states its own --dw-brand-text still wins on specificity, and five
		   already do. At 45% the worst skin measures 5.20:1. */
		--dw-brand-text: color-mix(in srgb, var(--dw-brand) 45%, var(--dw-text));

		/* THE CATEGORICAL SERIES, same failure as the semantics before it. The
		   light set was being drawn on a dark skin's surface: measured
		   2.51-4.28:1 on abyss and 2.70-4.60:1 on opsterm across 12 charts,
		   while the dark set sat unreachable behind a mode the bridge pins.
		   There is nothing to bridge TO here — no skin declares a chart
		   palette — so these derive instead, toward --dw-text, which is the
		   one thing that does follow the skin.
		   78% rather than the accent's 45%: a categorical series has to stay
		   distinguishable FROM ITSELF, and pulling ten hues too far toward one
		   colour converges them. Contrast is the constraint; distinctness is
		   the reason the series exists at all.

		   THIS RAMP IS A FILL, NEVER TEXT, and the bar for it is 3:1, not
		   4.5:1. That is not a lowered standard, it is the standard for a
		   non-text graphical object, and it is the only bar ten hues can meet
		   at once: measured as text on --dw-surface, twelve skins have at
		   least one series under 4.5:1 and blueprint has nine of ten under
		   3:1. Raising the share to fix that would converge the ramp and
		   destroy the one property it exists for. So a consumer that wants a
		   LABEL in the series hue does not get one — it puts the hue on a
		   border or a rail, which is non-text, and leaves the text on
		   --dw-text. test-runs.css:98 did the other thing and is fixed. */
		--dw-cat-1: color-mix(in srgb, var(--dw-cat-1-base) 78%, var(--dw-text));
		--dw-cat-2: color-mix(in srgb, var(--dw-cat-2-base) 78%, var(--dw-text));
		--dw-cat-3: color-mix(in srgb, var(--dw-cat-3-base) 78%, var(--dw-text));
		--dw-cat-4: color-mix(in srgb, var(--dw-cat-4-base) 78%, var(--dw-text));
		--dw-cat-5: color-mix(in srgb, var(--dw-cat-5-base) 78%, var(--dw-text));
		--dw-cat-6: color-mix(in srgb, var(--dw-cat-6-base) 78%, var(--dw-text));
		--dw-cat-7: color-mix(in srgb, var(--dw-cat-7-base) 78%, var(--dw-text));
		--dw-cat-8: color-mix(in srgb, var(--dw-cat-8-base) 78%, var(--dw-text));
		--dw-cat-9: color-mix(in srgb, var(--dw-cat-9-base) 78%, var(--dw-text));
		--dw-cat-10: color-mix(in srgb, var(--dw-cat-10-base) 78%, var(--dw-text));
	}
}

/* ===========================================================================
   WHICH MARK THIS SKIN USES
   ===========================================================================
   The mark changes with the ground. NOTHING is drawn behind it, on any skin.

   The plate that used to be here was removed on measurement. It was there for
   the mark's darkest ink, the indigo ears, and it did not do that job: the ears
   score 1.68:1 against the #14121A plate, against 1.05 to 1.70 on the skins'
   own surfaces - worse than the bare surface on ember and pixel8. What carries
   the drawing is the white facial disc, 57.8% of the mark's opaque pixels, and
   that measures 10:1 or better against every one of these surfaces with no
   plate at all. Seven of the eleven surfaces are within 2x of the plate's own
   luminance, so the chip was invisible and all anyone saw was its hairline.

   The mark itself still has to change, because the white disc is the problem in
   the other direction: on a light page it merges into the page and takes the
   indigo ears with it.

   :root above carries the LIGHT pairing, because the kit default is light
   (surface luminance 1.0000). The skins below are the ones measured dark, and
   they are the only ones that switch. Surface luminance, at the 0.18 bar:

	     abyss       0.0121
	     aurora      0.0129
	     blueprint   0.0496
	     chalk       0.0345
	     ember       0.0059
	     matchday    0.0236
	     miami86     0.0157
	     opsterm     0.0078
	     pixel8      0.0061
	     prime       0.0106
	     splitflap   0.0088

   The list is DERIVED, never judged by eye: it comes from --dw-surface in each
   skin file, and tests/logoMarkMatchesSkin.test.js re-derives it on every run,
   so a new skin or a re-tinted old one fails there rather than shipping with
   the wrong mark. Nothing sits near the boundary - the darkest light skin is
   werkbank at 0.8120 and the lightest dark skin is blueprint at 0.0496 - so
   the bar is a wide gap rather than a judgement call.

   A skin that wants something else sets --dw-logomark itself, and one that
   wants a chip behind it sets --dw-logo-plate. Skin stylesheets load after
   this file, so a skin's own declaration wins either way. */
html[data-skin='abyss'],
html[data-skin='aurora'],
html[data-skin='blueprint'],
html[data-skin='chalk'],
html[data-skin='ember'],
html[data-skin='matchday'],
html[data-skin='miami86'],
html[data-skin='opsterm'],
html[data-skin='pixel8'],
html[data-skin='prime'],
html[data-skin='splitflap'] {
	--dw-logomark: var(--dw-logomark-outline);
}
