/* =============================================================================
   RECIPE TOKENS
   =============================================================================

   ARCHITECTURE: the standard three-tier token model. Not invented here —
   Material 3 calls the tiers ref / sys / comp, Adobe Spectrum and Salesforce
   Lightning call them global / alias / component. Same model, same reasons.
   Adopting it rather than deriving our own.

     --al-ref-*        REFERENCE   a raw value. The only place a literal appears.
                                   Named for what it IS. Nobody consumes these
                                   except the sys tier.

     --al-sys-*        SYSTEM      a shared decision. "The control height is 36."
                                   Named for what it MEANS. Changing one moves
                                   every component that opted in.

     --al-<component>-*  COMPONENT what a component author touches, and the ONLY
                                   thing they touch. Points at a sys token to opt
                                   into a shared decision — or holds its own value
                                   to deliberately opt out.

   WHY THE COMPONENT TIER EXISTS — the question it answers
   Without it, someone building `.plant-card` reaches for --al-sys-control-height-md,
   later needs 40px, changes it, and silently resizes every button, input and select
   in the product. Nothing in the name warned them it was shared. With it, they write
   `--al-plantcard-height: var(--al-sys-control-height-md)` — they opt IN visibly and
   can diverge later without touching anything else. Coupling becomes deliberate
   instead of accidental.

   THE RULES
     1. A token exists only because a component needs it. No speculative entries.
        ONE EXCEPTION: a ramp, a scale or a status family is admitted as a WHOLE once
        it is decided — the neutral ramp, the type scale and the status colours are
        single decisions about a family, and
        cherry-picking the rungs in use today would just mean re-deriving the rest
        later and getting a slightly different answer. Rungs not yet consumed are
        decided, not speculative. Nothing else gets this latitude.
     2. ONE HOP BETWEEN ADJACENT TIERS. ref <- sys <- comp. Never skip a tier, and
        never alias within a tier — with exactly ONE sanctioned exception, below.

        THE COMPOSITION ALIAS. A component may declare one LADDER of tokens that its
        other component tokens read: here --al-button-intent and its two deeper rungs,
        --al-button-intent-strong and --al-button-intent-stronger. They point at sys,
        only their own siblings read them, and they never chain further. One ladder
        per component — three rungs of the same idea, not three separate aliases. That makes the colour
        chain three hops (ref <- sys <- intent <- fill) rather than two, and it is
        worth it — it is what lets emphasis and intent compose, and it is why
        .al-btn-danger is a single line that cannot miss a surface. Anything beyond
        this one alias per component is the thing the rule forbids.

        The cautionary tale this rule was written from: the old system's focus ring
        was FOUR same-tier aliases deep, which is how it hid being the same colour
        as the button it ringed. Note what actually prevents that here — NOT the hop
        count, since --al-button-focus-ring-color reads the same intent the fill
        does, so the ring IS the fill colour. What saves it is the hairline on each
        side of the ring. Depth was never the safeguard; the hairlines are. Anyone
        copying the composition alias into a new component must copy them with it.
     3. A component's CSS PROPERTIES read only its own --al-<component>-* tokens.
        `background: var(--al-sys-color-accent-primary)` in a button rule is the bug
        this architecture prevents. Declaring `--al-button-fill: var(--al-sys-...)`
        is the opposite — that IS the opt-in, and it is the only place a sys token
        may appear in a component rule. Lintable as: sys tokens on the right-hand
        side of a --al-<component>-* declaration, nowhere else.
     4. TIER THE NAME WHERE THE VALUES COEXIST. Three control heights exist at the
        same time, so none of them gets to be --al-sys-control-height. A button has
        exactly one height in force, so the component token is --al-button-height
        and a size variant re-points it. Suffixing the component tier as well was an
        earlier draft, and it produced .al-btn-sm { --al-button-height-md: 28px },
        which says the opposite of what it does.
     5. No abbreviations, no borrowed shorthand. --al-sys-color-text-on-accent, not
        --al-sys-color-on-accent, which only reads if you already know Material.
        This governs TOKENS, which are read far more often than they are typed. CLASS
        names go the other way — `al-btn` is typed on every button on every screen, and
        the shorter name is the kinder one. Hence --al-button-* and .al-btn.
     6. Every entry carries the reason it exists.
     7. NOTHING ENTERS THIS FILE BEFORE IT IS APPROVED. A token being tried out lives
        in the testbed's own <style> block until the control graduates. This file is
        the decided system — if candidates land here, two rounds of "actually, make it
        bluer" leave residue nobody can tell from a real decision.
     8. A VARIANT RE-POINTS TOKENS; IT NEVER OVERRIDES PROPERTIES. This is what keeps
        a variant from ever "forgetting" a state: it does not own the state rules, it
        owns the values those rules read. It also means every variant class ties on
        specificity with .al-btn, so ORDER IS LOAD-BEARING — the base block comes
        first and the variants follow.

   NAMESPACE — CHECKED, AND IT MATTERS FOR THE ROLLOUT
   The deprecated public/css/tokens.css holds 301 --al-* tokens, all flat: --al-neutral-*,
   --al-primary-*, --al-cat-*, --al-pad-*, and so on. None of them carries a tier segment,
   so nothing here collides with anything there — verified by grep, not assumed. Nor does
   `.al-btn` exist anywhere in the product. That is deliberate: implementation will be
   page by page, both stylesheets will be loaded at once for a while, and a name clash
   during that window would silently repaint screens nobody was working on.

   WHAT THIS FILE HOLDS
     Reference values, system roles, and the two grounds. That is all of it — the layer
     a theme re-points, and nothing a theme should have to read past. Forty reference
     values, a hundred and sixteen system roles.

     WHAT IT DOES NOT HOLD, DELIBERATELY
     Any control. A control's own tokens are declared ON the control, in the control's
     file, beside the properties that read them — which is also where a var() inside a
     custom property has to be declared for it to resolve against the right element.
     Four controls did live here, from an earlier pass where "lock it in" was read as
     "put it in the locked file". They now sit with the other forty-eight in
     recipe-controls.css, and nothing about them changed on the way.

   GROUNDS — the one rule a page author has to know
     A control cannot see what it is sitting on; CSS gives it no way to read its own
     background. So a ground is DECLARED: a panel that paints a background names itself
     .al-on-dark or .al-on-light. Both are the same shape — a list of sys roles and not
     one control named — which is what lets a control written next year be correct on
     both the day it is written, and what lets a theme reach every pixel by re-pointing
     the same list. If you paint a background, name the ground.

   WHAT AN AUDIT PROVED, so it is not re-litigated
     Four hostile themes (pitch black, saturated yellow, mid-grey, high-contrast), each
     re-pointing only sys roles and naming no control, were built and measured against
     this file. They found thirteen places a theme could not reach. All but two are
     closed here; the two left open are recorded as deliberate:
       - No "inverse" mirror of every colour role. It would double the colour surface to
         save a theme author writing .al-on-dark { }, and a ground is already a named,
         re-pointable scope. Deliberate.
       - No "inverse" role for every surface. Same reasoning as the colour mirror: a
         ground is already the scope that answers it.
     Both of those held. The spacing scale that audit said nothing needed yet is here
     now, because forty-eight more controls arrived and every one of them wanted gaps.
   ============================================================================= */

:root {
	/* ==========================================================================
	   1. REFERENCE — raw values, consumed only by the sys tier.
	   Ramps are named for their HUE and numbered light-to-dark, 50 → 900, the
	   convention most people already know. Singular values that are not part of a
	   ramp (white) are named plainly. "neutral-00" was neither: it needed you to
	   know both that neutral meant greyscale and that 00 meant the light end.
	   ========================================================================== */

	--al-ref-emerald-50: #ebf9f4; /* the tint a success message sits on */
	--al-ref-emerald-300: #72caa7; /* the brand, lightened enough to read on a dark
	     ground: 8.81:1 against slate-900, where emerald-500 manages only 3.24. */
	--al-ref-emerald-500: #157a52; /* the brand. 500 is the base rung — a darker one
	     for a hover or pressed state would be emerald-600, a tint emerald-100. */

	--al-ref-red-50: #fcefee; /* the tint an error message sits on */
	--al-ref-red-300: #db7b76; /* the same job for danger: 5.84:1 on slate-900. */
	--al-ref-red-500: #c0463f; /* destructive. Deliberately the same 500 rung as the
	     emerald so the two read at equal weight sitting beside each other — a
	     brighter red would make every delete button the loudest thing on the page. */

	--al-ref-emerald-600: #126846; /* one rung down. Exists so a label can deepen when
	     the ground behind it lightens, keeping the pair legible. */
	--al-ref-emerald-700: #0d4d34; /* two rungs down, for the heavier pressed ground. */

	--al-ref-red-600: #a33c36;
	--al-ref-red-700: #8a322d;

	/* Two new hues, and only two. Error and success reuse the red and emerald ramps
	   already here: deriving them fresh gave #c1453e and #157951 against the decided
	   #c0463f and #157a52 — near-duplicates that nobody could tell apart and that
	   would drift out of step. The 700 rungs were already present too, from the
	   button's pressed label, and they clear their tints at 7.27 and 9.11. Minting
	   new values at those same names would have silently repainted the button. */
	--al-ref-amber-50: #fdf3e3;
	--al-ref-amber-500: #c38313;
	--al-ref-amber-700: #855a0f;

	--al-ref-blue-50: #e9f1fb;
	--al-ref-blue-500: #3268ae;
	--al-ref-blue-700: #294c7a;

	--al-ref-white: #ffffff; /* white is white. Named plainly rather than forced onto
	     a ramp as "neutral-00", which told you nothing about what it was. */
	/* -- ICON SHAPES. A glyph is a raw value like a colour is, and this is the only
	      place one appears. Built as data URIs at a 16 box with a 1.7 stroke so the
	      family reads as one set, and masked rather than drawn so the COLOUR stays a
	      token. Minted by the message family, which was the first thing that needed a
	      mark that means something rather than decorates. */
	--al-ref-icon-circle-cross: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='8' cy='8' r='6.6'/%3E%3Cpath d='M5.8 5.8 10.2 10.2M10.2 5.8 5.8 10.2'/%3E%3C/svg%3E");
	--al-ref-icon-triangle-bang: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M8 2.4 14.8 13.6H1.2z'/%3E%3Cpath d='M8 6.4v3'/%3E%3Ccircle cx='8' cy='11.6' r='.95' fill='%23000' stroke='none'/%3E%3C/svg%3E");
	--al-ref-icon-circle-check: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='8' cy='8' r='6.6'/%3E%3Cpath d='M5.1 8.2 7.1 10.2 10.9 6'/%3E%3C/svg%3E");
	--al-ref-icon-circle-i: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='8' cy='8' r='6.6'/%3E%3Cpath d='M8 7.6v3.7'/%3E%3Ccircle cx='8' cy='4.9' r='.95' fill='%23000' stroke='none'/%3E%3C/svg%3E");
	--al-ref-icon-chevron-down: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m4 6 4 4 4-4'/%3E%3C/svg%3E");
	--al-ref-icon-cross: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round'%3E%3Cpath d='M4.3 4.3 11.7 11.7M11.7 4.3 4.3 11.7'/%3E%3C/svg%3E");

	--al-ref-slate-950: #0d1117; /* below the ink. The only thing under a card on a dark
	     ground, where the page has to go DARKER than the card rather than lighter. */
	--al-ref-black: #000000; /* only ever used as a shadow ink on a dark ground, where
	     tinted shadow reads as a smudge rather than as depth */
	/* THE ACCENT AT FOUR FIXED ALPHAS, for the row tints below. Same bargain as
	   --al-ref-slate-900-translucent above: the hex is repeated on purpose because
	   color-mix() is Chrome 111 and the POS terminals run 101, where a token holding
	   an unparseable color-mix is stored happily and then invalidates whatever uses
	   it -- so a selected row loses its tint entirely rather than falling back. These
	   are the same four mixes, precomputed. emerald-500 is #157a52, emerald-300 is
	   #72caa7. If either primitive moves, these move with it. */
	--al-ref-emerald-500-translucent-10: rgba(21, 122, 82, 0.1);
	--al-ref-emerald-500-translucent-20: rgba(21, 122, 82, 0.2);
	--al-ref-emerald-300-translucent-16: rgba(114, 202, 167, 0.16);
	--al-ref-emerald-300-translucent-40: rgba(114, 202, 167, 0.4);
	--al-ref-white-translucent: rgba(255, 255, 255, 0.09); /* white at 9%, for lifting a
	     dark ground slightly — a read-only field on a dark panel. Deliberately a lift
	     rather than a rung: the ramp has no step between slate-700 and slate-900 to sink
	     to, and more importantly a fixed rung would be wrong on any dark canvas that is
	     not this one, while a translucent lift is correct over all of them. Named the
	     same way as --al-ref-slate-900-translucent above, which does the mirror job. */

	/* The neutral ramp. Blue-grey rather than a true grey: it shares a hue with the
	   ink at the dark end, so the whole family reads as one thing, and a pure grey
	   reads clinical where this reads considered. Saturation tapers toward the light
	   end so the tints do not look stained. Most of any screen is neutral, so this
	   decides more of how the product looks than the accent does. */
	--al-ref-slate-50: #f7f8f9;
	--al-ref-slate-100: #eff1f3;
	--al-ref-slate-200: #e0e3e7;
	--al-ref-slate-300: #ccd1d9;
	--al-ref-slate-400: #94a0b3;
	--al-ref-slate-500: #5d708b;
	--al-ref-slate-600: #48576d;
	--al-ref-slate-700: #333f50;
	--al-ref-slate-900: #161b24; /* the darkest neutral, and the ink */
	--al-ref-slate-900-translucent: rgba(22, 27, 36, 0.55); /* the ink at 55%. The one
	     place a hex is deliberately repeated: written as color-mix() it would make the
	     whole focus box-shadow invalid wherever color-mix is unsupported, and since
	     box-shadow does not inherit it would compute to `none` — deleting the entire
	     focus ring rather than just this hairline. */


	/* -- rungs added when the catalogue needed them ---------------------------
	   Each of these was a measured gap, not a guess. slate-800 because a read-only
	   field on a dark panel had nothing to sink to between 700 and 900. emerald-200
	   and red-200 because the emphasis ladder only ran DARKER, so on a dark ground a
	   pressed outline button walked its own label into the background. amber-300 and
	   blue-300 for the same reason on the status hues. */
	--al-ref-slate-800: #242e3c;
	--al-ref-emerald-200: #a3ddc4;
	--al-ref-red-200: #eaa9a5;
	--al-ref-amber-300: #e0ad57;
	--al-ref-blue-300: #7fa6d8;

	/* ==========================================================================
	   2. SYSTEM — shared decisions. Changing one of these moves every component
	   that opted into it. That is the point, and it is why components do not
	   reference them directly.
	   ========================================================================== */

	/* -- colour ------------------------------------------------------------- */
	/* FILL versus LINE. The accent does two different optical jobs and they stop
	   agreeing the moment the ground moves. As a FILL it is a ground in its own right
	   and carries white text on top of it. As a LINE — an outline button's edge, its
	   label, a focus ring — it is ink laid ON the ground and has to separate from
	   whatever that ground is. One value cannot do both: on a dark canvas the fill
	   must stay the same emerald so a solid button looks like itself everywhere, while
	   the line has to lighten or it disappears.

	   On a light ground the two are the same value, so nothing changes today. The split
	   exists so a theme can move one without dragging the other, and it replaces the
	   old -light rung, whose only consumer sat on a ground a theme could not set. */
	--al-sys-color-accent-primary: var(--al-ref-emerald-500); /* the brand action colour, as a FILL */
	--al-sys-color-accent-primary-line: var(--al-ref-emerald-500); /* the same, as a LINE */
	--al-sys-color-accent-primary-strong: var(--al-ref-emerald-600);
	--al-sys-color-accent-primary-stronger: var(--al-ref-emerald-700);
	--al-sys-color-accent-danger: var(--al-ref-red-500); /* destructive actions, as a FILL */
	--al-sys-color-accent-danger-line: var(--al-ref-red-500); /* the same, as a LINE */
	--al-sys-color-accent-danger-strong: var(--al-ref-red-600);
	--al-sys-color-accent-danger-stronger: var(--al-ref-red-700);
	/* THE THIRD INTENT: SOLID, TAPPABLE, AND MEANING NOTHING.
	   Primary says money and danger says destruction. Most buttons in a product say
	   neither -- they are utilities -- and with only two intents they were all painted
	   the brand, which says "brand action" seventy times on one screen and so says it
	   nowhere. Neutral is the absence of a claim, and it is what lets emerald mean
	   money and only money (ALLM-7250). */
	--al-sys-color-accent-neutral: var(--al-ref-slate-100);
	--al-sys-color-accent-neutral-line: var(--al-ref-slate-600);
	--al-sys-color-accent-neutral-strong: var(--al-ref-slate-700);
	--al-sys-color-accent-neutral-stronger: var(--al-ref-slate-900);
	/* STATUS — describes a THING, not what a control does.
	   Deliberately separate from the accent intents even where the value is the same:
	   --al-sys-color-status-error and --al-sys-color-accent-danger both resolve to the
	   red ramp today, but one means "this value is wrong" and the other means "this
	   button destroys something". Keeping them apart here is what lets either move
	   later without dragging the other. Rendered side by side before this was settled:
	   one red reads as consistency, two reads as a mistake.

	   Each has three parts: the mark (an icon, a border), the tint it sits on, and the
	   text on that tint. Colour is never the only signal — every status carries an icon
	   and words too, so it survives colour blindness, a poor screen and a printout. */
	--al-sys-color-status-error: var(--al-ref-red-500);
	--al-sys-color-status-error-tint: var(--al-ref-red-50);
	--al-sys-color-status-error-text: var(--al-ref-red-700);

	--al-sys-color-status-warning: var(--al-ref-amber-500);
	--al-sys-color-status-warning-tint: var(--al-ref-amber-50);
	--al-sys-color-status-warning-text: var(--al-ref-amber-700);

	--al-sys-color-status-success: var(--al-ref-emerald-500);
	--al-sys-color-status-success-tint: var(--al-ref-emerald-50);
	--al-sys-color-status-success-text: var(--al-ref-emerald-700);

	--al-sys-color-status-info: var(--al-ref-blue-500);
	--al-sys-color-status-info-tint: var(--al-ref-blue-50);
	--al-sys-color-status-info-text: var(--al-ref-blue-700);

	/* THREE GROUNDS, not one. They are the same colour today and that is exactly why
	   they were one token — which meant "make every canvas yellow" also made the
	   inside of every text field yellow, because a control's interior was reading the
	   page's ground. A theme has to be able to move a page without moving the controls
	   standing on it. */
	--al-sys-color-surface: var(--al-ref-white); /* the ground a card or a page sits on */
	--al-sys-color-surface-control: var(--al-ref-white); /* the INTERIOR of a control */
	/* A SURFACE THAT FLOATS. Every control before the select sat IN the page; a menu is
	   the first that sits ON it. The pair is a ground like any other, so it is declared
	   here with the rest and re-pointed by each ground rather than baked into the
	   control — which is exactly the mistake that produced a white menu carrying white
	   text on a dark panel the first time it was written. */
	--al-sys-color-surface-raised: var(--al-ref-white);
	--al-sys-elevation-raised:
		0 1px 2px rgb(from var(--al-sys-color-shadow-ink) r g b / 0.1),
		0 10px 28px -6px rgb(from var(--al-sys-color-shadow-ink) r g b / 0.22);

	--al-sys-color-surface-page: var(--al-ref-slate-50); /* the ground BEHIND the cards.
	     It went away when nothing painted a page; a card and a panel paint one, so it
	     is back — earning its place by having a consumer, not by being expected. */
	/* WHICH GLYPH MEANS WHICH STATUS. Beside the status colours, not inside any one
	   control, for the same reason the colours are: an error must be the same mark in a
	   form, a toast and a banner. A glyph does not change with the ground, so no ground
	   re-points these — that is the point of them being shapes. */
	--al-sys-icon-status-error: var(--al-ref-icon-circle-cross);
	--al-sys-icon-status-warning: var(--al-ref-icon-triangle-bang);
	--al-sys-icon-status-success: var(--al-ref-icon-circle-check);
	--al-sys-icon-status-info: var(--al-ref-icon-circle-i);
	--al-sys-icon-dismiss: var(--al-ref-icon-cross);

	--al-sys-color-scrim: var(--al-ref-slate-900-translucent); /* what a modal dims the
	     page with. The same ink at 55% the focus hairline uses, not a second value. */
	--al-sys-color-surface-sunken: var(--al-ref-slate-50); /* a recessed ground: the tint
	     that marks a read-only field. Renamed from --al-sys-color-surface-page, which
	     claimed to be "the ground behind the cards" and never painted one — its only
	     consumer in the whole file was the read-only fill. A real page ground comes
	     back when a page or card control exists to need it. */
	--al-sys-color-border: var(--al-ref-slate-200); /* a hairline BETWEEN things — a
	     rule under a heading, the line around a card. It only has to be seen, not
	     found: 1.29 against white. */
	--al-sys-color-border-control: var(--al-ref-slate-500); /* the EDGE OF A CONTROL,
	     which is a different job — it is the only thing telling someone they can type
	     here, so it has to be findable, not merely present. WCAG 1.4.11 asks 3:1 for
	     the boundary of a form control; this is 5.05 against white and 4.75 against the
	     page ground. The hairline above fails that at 1.29, and so does every rung up
	     to slate-400 at 2.65 — measured, which is why they are not used here. */
	--al-sys-color-text-primary: var(--al-ref-slate-900); /* headings and emphasis */
	--al-sys-color-text-secondary: var(--al-ref-slate-700); /* body copy */
	/* ONE CHEVRON, SHARED. This glyph was declared inside the .al-grid block, which meant no
	   other consumer could read it: a var() inside a custom property resolves against the
	   element the property is DECLARED on. The Kendo override layer therefore could not draw
	   the recipe's sort mark and recoloured the vendor's font glyph instead, so the app showed
	   one arrow and the design system showed another. At :root both can share it. */
	--al-sys-icon-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M4 6l4 4 4-4' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
	/* The sort mark's own rung. 12px rather than the 14px sm tier, and that is deliberate:
	   .al-grid's comment says the mark sits inside a 13px label and the icon tier crowds it. */
	--al-sys-icon-chevron-size: 12px;

	--al-sys-color-text-muted: var(--al-ref-slate-500); /* timestamps, counts, meta, and
	     the quiet marks that sit ON a page: a field's hint line, a required asterisk */
	--al-sys-color-text-placeholder: var(--al-ref-slate-500); /* quiet ink INSIDE a
	     control. Same value as muted today and split off for one reason: it is the only
	     quiet ink standing on the control's own interior rather than on the page, so
	     the moment those two grounds differ it needs to move independently. */
	/* Text when the ground IS the accent. One per intent, not one shared, for the same
	   reason the fill and the line had to part company: the two intents are only
	   equally dark by coincidence here, and a theme is under no obligation to keep them
	   that way. A pale accent needs a black label; a deep one needs white. */
	--al-sys-color-text-on-accent: var(--al-ref-white);
	--al-sys-color-text-on-accent-danger: var(--al-ref-white);
	/* Neutral is the one accent whose fill is LIGHT, so its label is ink rather than
	   white. slate-700 on slate-100 is 9.43:1. */
	--al-sys-color-text-on-accent-neutral: var(--al-ref-slate-700);

	/* TWO STEPS OF ONE LADDER, for any list of rows. The chosen row is tinted and
	   carries a mark; the row the pointer or the keyboard is on is tinted harder. They
	   are steps of the same idea rather than two competing treatments, which is what
	   stops "where you are" and "what is chosen" fighting over the same background. */
	--al-sys-color-row-tint-selected: color-mix(in srgb, var(--al-sys-color-accent-primary) 10%, transparent);
	--al-sys-color-row-tint-active: color-mix(in srgb, var(--al-sys-color-accent-primary) 20%, transparent);

	/* The overlay that hover and press lay over a control. Named for the ground it sits
	   ON, because that is what decides which way it can go. Over an ACCENT fill it
	   always darkens: the fill is a known colour carrying a known label, and lightening
	   it would eat the label's contrast. That is the whole of this role's job, and it
	   used to also be the fallback for outline and quiet — where the ground is the
	   canvas, not the accent, so on a dark panel it was a dark veil over a dark ground
	   and the press state produced no visible change at all. Those two now take their
	   own intent instead (see the emphasis variants), so this role does one thing. */
	--al-sys-color-veil-on-accent: var(--al-ref-slate-900);

	/* Focus gets its own colour role rather than borrowing the brand action colour.
	   They point at the same value today. They are separate because a theme has a real
	   reason to want them apart — a focus indicator that reads on every ground is a
	   different requirement from a brand — and because with one role a theme could not
	   move either without moving both. */
	--al-sys-color-focus-ring: var(--al-sys-color-accent-primary-line);

/* -- TYPEFACE. The system names its own faces rather than inheriting whatever the
	      host page has. A control that changes shape depending on the page it is
	      dropped into is not a control, and this is the same failure as borrowing
	      box-sizing or line-height, one tier up. --------------------------------- */
	--al-sys-font-family: 'Public Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI',
		Roboto, 'Helvetica Neue', Arial, sans-serif;
	--al-sys-font-family-numeric: 'Public Sans', -apple-system, 'Segoe UI', Roboto, sans-serif;

	/* Weight is a role, not a number picked per component. Four is all this system
	   needs, and holding it to four is what stops headings drifting apart. */
	--al-sys-font-weight-body: 400;
	--al-sys-font-weight-medium: 500; /* a label that must separate from its value */
	--al-sys-font-weight-control: 600; /* anything you can click or type into */
	--al-sys-font-weight-heading: 700;

	/* Three line heights: one for a headline, one for prose, one for a control whose
	   box owns its height. The control value is 1.2 because inheriting the page's
	   1.65 put a 21px line box inside a 28px control. */
	--al-sys-line-height-heading: 1.25;
	--al-sys-line-height-body: 1.55;
	--al-sys-line-height-control: 1.2;
	--al-sys-measure: 68ch; /* prose stops here — beyond it the eye loses the line */

	/* -- SPACING. Steps of 4, numbered like the type scale so the two read as one
	      family. 100 is the base step. Deliberately sparse: a scale with eleven rungs
	      is a scale nobody can hold in their head, and the gaps get filled with
	      arbitrary values anyway. ------------------------------------------------ */
	--al-sys-space-25: 2px; /* hairline separation, a badge's inner gap */
	--al-sys-space-50: 4px;
	--al-sys-space-100: 8px; /* the base step */
	--al-sys-space-150: 12px;
	--al-sys-space-200: 16px; /* the default gap between things that belong together */
	--al-sys-space-300: 24px; /* between groups */
	--al-sys-space-400: 32px;
	--al-sys-space-600: 48px; /* between sections of a page */
	--al-sys-space-800: 64px;

	/* -- RADIUS. Three jobs, and they are not interchangeable: a control, a container
	      that holds controls, and something the shape of a lozenge. A container is
	      rounder than the controls inside it, which is what stops a card reading as a
	      very large button. ------------------------------------------------------ */
	--al-sys-radius-sm: 5px; /* a row inside a menu, a small chip */
	--al-sys-radius-container: 12px; /* cards, panels, modals, drawers */
	--al-sys-radius-pill: 999px; /* badges, tags, avatars */

	/* -- ELEVATION. A ladder of three, and the rung says how far off the page a thing
	      is, not how pretty it looks. Sticky is barely there because it only has to
	      say "content is passing under me". ------------------------------------- */
	/* The shadow INK is a role, so a theme can reach it. Written as one colour with the
	   opacity varied per rung rather than six separate literals, which is what it was
	   before and which no theme could touch. */
	--al-sys-color-shadow-ink: var(--al-ref-slate-900);
	--al-sys-elevation-sticky:
		0 1px 0 rgb(from var(--al-sys-color-shadow-ink) r g b / 0.06),
		0 2px 6px -2px rgb(from var(--al-sys-color-shadow-ink) r g b / 0.08);
	/* --al-sys-elevation-raised is declared with the grounds — it changes per ground */
	--al-sys-elevation-overlay:
		0 2px 4px rgb(from var(--al-sys-color-shadow-ink) r g b / 0.08),
		0 24px 60px -12px rgb(from var(--al-sys-color-shadow-ink) r g b / 0.32);

	/* -- LAYERING. One scale, wide gaps, so nothing ever needs a number invented for
	      it in a component file. The order is the order things must sit in: a tooltip
	      explains a toast, a toast survives a modal, a modal covers a drawer. ----- */
	--al-sys-layer-sticky: 100;
	--al-sys-layer-drawer: 300;
	--al-sys-layer-scrim: 400;
	--al-sys-layer-modal: 500;
	--al-sys-layer-popover: 600;
	--al-sys-layer-toast: 700;
	--al-sys-layer-tooltip: 800;

	/* -- ROW HEIGHTS, for tables and lists. Derived rather than invented: a row is a
	      control tier plus 4px of air top and bottom, so a row with a button in it is
	      the same height as a row without one. ---------------------------------- */
	/* THE DATA RUNG. Below the control tiers on purpose: a grid row is not something you
	   aim at, it is something you scan, and the app has been rendering 31px rows for long
	   enough that Jeff picked them over the recipe's 44px when the two were put side by
	   side. The recipe moves to the app here rather than the app growing 40% taller and
	   losing a third of the rows on screen. Only a data grid should reach for it. */
	--al-sys-row-height-data: 31px;
	--al-sys-row-height-sm: 36px;
	--al-sys-row-height-md: 44px;
	--al-sys-row-height-lg: 52px;

	/* -- ICONS. Three sizes, paired to the control tiers. ---------------------- */
	--al-sys-icon-size-sm: 14px;
	--al-sys-icon-size-md: 16px;
	--al-sys-icon-size-lg: 20px;

	/* -- MOTION, arriving and leaving. A thing that arrives decelerates into place;
	      a thing that leaves accelerates away. Exit is quicker than enter, because
	      waiting for something to go is worse than waiting for it to come. ------- */
	--al-sys-motion-duration-enter: 0.2s;
	--al-sys-motion-duration-exit: 0.14s;
	--al-sys-motion-easing-enter: cubic-bezier(0.16, 1, 0.3, 1);
	--al-sys-motion-easing-exit: cubic-bezier(0.4, 0, 1, 1);

	/* -- control size tiers. Height, horizontal padding and font size are PAIRED:
	      pick a tier, get all three, and they cannot drift apart. The heights step
	      evenly 28 / 36 / 44, which leaves 52 as the obvious slot if an extra-large
	      ever earns its place. Type is PROPORTIONAL AT THE TOP, FLOORED AT THE
	      BOTTOM: 44px takes 17px because a big box with small type looks broken,
	      while 28px stops at 13px rather than the strictly proportional 11px,
	      because legibility is a floor and geometry does not get to cross it. ---- */
	--al-sys-control-height-sm: 28px; /* compact rows: grid actions, table cells */
	--al-sys-control-padding-x-sm: 12px;

	--al-sys-control-height-md: 36px; /* the default. Buttons, inputs, selects — this
	     is why a button lines up with a field beside it */
	--al-sys-control-padding-x-md: 16px;

	--al-sys-control-height-lg: 44px; /* the touch rung */
	--al-sys-control-padding-x-lg: 20px;

	/* -- ONE TYPE SCALE. Control labels are rungs in the same ladder headings and
	      body copy use, so a button label and a heading can never disagree about
	      what 14px means. Numbered light-to-heavy like the colour ramps.

	      CONSEQUENCE, worth knowing: height and font size are no longer paired in
	      this tier. A size variant now picks a height rung and a type rung, and the
	      pairing is held by the variant block keeping its three re-points together.
	      That is the price of one shared scale, and it was the deliberate choice. */
	--al-sys-font-size-100: 0.75rem; /* 12px — captions, dense table meta */
	--al-sys-font-size-200: 0.8125rem; /* 13px — small control labels */
	--al-sys-font-size-300: 0.875rem; /* 14px — body copy, default control label */
	--al-sys-font-size-400: 1.0625rem; /* 17px — large control labels, sub-headings */
	--al-sys-font-size-500: 1.25rem; /* 20px — section headings */
	--al-sys-font-size-600: 1.5625rem; /* 25px — page headings */
	--al-sys-font-size-700: 1.9375rem; /* 31px — the one number a screen is about */

	/* -- untiered: genuinely one value at every size ------------------------- */
	--al-sys-control-radius: 8px;
	--al-sys-control-border-width: 1px;
	--al-sys-control-gap: 6px; /* glyph to label. NOT tiered: this is optical
	     separation between two marks, and 6px reads correctly at 28 and at 44. A
	     gap that scaled with the box would gape on the large rung. */
	--al-sys-font-weight-control: 600;

	/* -- motion. Two durations now exist, so by rule 4 neither is the unnamed
	      default. -state is how long a control takes to change appearance; -loop is
	      one revolution of something that repeats. --------------------------- */
	--al-sys-motion-duration-state: 0.15s;
	--al-sys-motion-duration-loop: 0.6s; /* fast enough to read as working, slow
	     enough not to strobe */
	--al-sys-motion-easing: ease;

	/* -- state ---------------------------------------------------------------
	      Hover lightens, press darkens. The veil CHANGES COLOUR between the two, and
	      that flip is most of the signal, so the two alphas sit close together on
	      purpose — they are not doing the work by themselves. */
	--al-sys-state-hover-opacity-on-fill: 0.12; /* a dark veil over an accent fill */
	--al-sys-state-hover-opacity-on-surface: 0.10; /* an intent veil over the surface.
	     Lower than it was, so press at 0.16 can sit ABOVE it — see the press pair. */
	/* Press splits the same way hover already did, and for the same reason. Over an
	   accent FILL the veil is a neutral darkening one and the white label above it only
	   gains contrast as it deepens. Over the SURFACE the veil is the intent itself and
	   the label is that same intent, so pushing it harder moves the ground TOWARD the
	   label: a pressed danger outline button on a dark panel measured 4.48 against a
	   4.5 floor. Hover had a separate on-surface value from the start and press did
	   not, and that asymmetry was the whole defect.

	   The values are not a free choice either. Press has to read as STRONGER than
	   hover, so the on-surface pair is 0.10 / 0.16 rather than dialling press down
	   below hover — which was the first attempt and made pressing feel like letting go.
	   0.16 is the ceiling: it is where a pressed danger outline button on a dark panel
	   measures 4.65, and the intent veil moves the ground TOWARD the label there, so
	   anything heavier walks it under the floor. */
	--al-sys-state-press-opacity-on-fill: 0.18;
	--al-sys-state-press-opacity-on-surface: 0.16;
	--al-sys-state-press-scale: 0.97; /* colour alone under-sells a press; the scale
	     is what makes it feel physical, and it reads on every fill */
	--al-sys-state-disabled-opacity: 0.5; /* blanket — a role keeps its own colours at
	     half strength, so a disabled outline button still reads as an outline button */

	/* -- busy indicator ------------------------------------------------------- */
	--al-sys-spinner-ring-width: 2px; /* the ring's stroke. Not the control border
	     width: 1px reads as a hairline artefact once it is spinning. */

	/* -- focus: a ring with a hairline on each side ----------------------------
	      There is no gap any more. The earlier design painted a 2px gap the colour of
	      the surface; it was never wrong — measured, it passed on every ground — but a
	      2px white band reads as a halo once a button sits on anything dark or
	      coloured.

	      A hairline each side does the same job in less space, and the pair is what
	      makes it unloseable: the light hairline separates the ring from a dark
	      ground, the dark hairline separates it from a light one. One of the two
	      always has contrast, whatever is behind, so the indicator cannot vanish —
	      including on a bar the same colour as the ring itself, which is exactly
	      where a transparent gap disappears. */
	--al-sys-focus-hairline-width: 1px;
	--al-sys-focus-ring-width: 2px;
	--al-sys-color-focus-hairline-light: var(--al-ref-white);
	--al-sys-color-focus-hairline-dark: var(--al-ref-slate-900-translucent);

	/* A GROUND MUST SET `color`, not only re-point the role behind it.
	   `color` is INHERITED, and inheritance carries the value computed at the ancestor:
	   the page resolves --al-sys-color-text-primary to the ink once, and every element
	   inside a dark panel that does not set its own colour inherits that same ink. The
	   re-point never reaches it. Measured before this line existed: 100 of 262 pieces of
	   text inside a dark panel were wearing the light ground's ink.
	   This is the same failure as a control not being able to see what it sits on, one
	   level subtler — the ground has to speak for everything that never asked. */
	color: var(--al-sys-color-text-primary);
}

/* =============================================================================
   ON A DARK GROUND

   A control cannot see what it is sitting on — CSS gives an element no way to read
   its own background — so the container has to say. The whole mechanism is the block
   below, and the important thing about it is what it does NOT contain: not one rule
   names a control.

   That is deliberate and it is the difference between a system that can be themed and
   one that cannot. Dark used to be written onto each control by hand
   (.al-on-dark .al-input, .al-on-dark .al-field, .al-on-dark .al-btn-outline), which
   cost a block per control, quietly outranked each control's own state rules — that
   is why a read-only field and an invalid field both vanished on a dark panel — and
   left the values reachable only by editing the file. A theme re-pointing every sys
   role could not move a single one of those pixels.

   Written this way instead: a control that reads the system's roles is dark-correct
   the day it is written, with no dark work of its own, and a full dark mode is this
   same block set at the root rather than on a panel. A custom theme is the same shape
   with different values.

   The FILLED button is deliberately unchanged — same emerald, same white label as
   anywhere else. It separates from a dark ground by 3.24:1 as a shape, which is
   quieter than an inverted light fill would be, but it means a filled button looks
   like itself everywhere. That was the choice, and the fill/line split above is what
   lets it hold while an outline button's edge lightens around it.
   ============================================================================= */
.al-on-dark {
	/* A GROUND MUST ALSO SAY SO TO THE BROWSER, because a role only reaches what the
	   PAGE paints. A native select's option list, scrollbars, date and time pickers,
	   checkbox and radio glyphs and autofill are drawn by the UA, which picks its
	   palette from color-scheme and cannot see a token. Undeclared computes to
	   `normal`, i.e. light, so those parts kept the light palette on a dark page —
	   and the `color` below is inherited, so an <option> took the dark ink onto the
	   UA's white menu. White text on a white list: worse than leaving them alone. */
	color-scheme: dark;

	/* A GROUND MUST SET `color`, not only re-point the role behind it.
	   `color` is INHERITED, and inheritance carries the value computed at the ANCESTOR:
	   the page resolves --al-sys-color-text-primary to the dark ink once, and every
	   element inside a dark panel that never sets its own colour inherits that same ink.
	   The re-point never reaches it. Measured: 100 of 262 pieces of text inside a dark
	   panel were wearing the light ground's ink.
	   Same failure as a control not being able to see what it sits on, one level
	   subtler — the ground has to speak for everything that never asked. */
	color: var(--al-sys-color-text-primary);
	--al-sys-color-surface: var(--al-ref-slate-900);
	/* a control's interior is the dark ground itself: an input on a dark panel is a
	   lightened edge around the panel, not a box of some other colour */
	--al-sys-color-surface-control: transparent;
	--al-sys-color-surface-sunken: var(--al-ref-white-translucent);
	/* the menu lifts AWAY from a dark panel rather than sinking into it, and its shadow
	   deepens because a dark shadow on a dark ground does almost nothing on its own */
	/* NOT slate-700, and the reason is a COLLISION rather than a preference. --al-sys-color-border
	   is slate-700 in dark too, so a hairline on a raised surface was painted in the colour of the
	   surface behind it: measured at ratio 1.00 on the Move dialog, on two of its seven bordered
	   elements. The failure is silent -- the element is there, the rule applies, the border paints.
	   slate-800 lands that hairline at 1.29, which is exactly what the LIGHT theme gives the same
	   hairline, so the recipe keeps one answer for what a hairline is worth instead of two.
	   ALLM-7304, ruled by Jeff. */
	--al-sys-color-surface-raised: var(--al-ref-slate-800);
	--al-sys-elevation-raised:
		0 1px 2px rgb(from var(--al-sys-color-shadow-ink) r g b / 0.5),
		0 12px 32px -6px rgb(from var(--al-sys-color-shadow-ink) r g b / 0.65);

	--al-sys-color-text-primary: var(--al-ref-white);
	--al-sys-color-text-secondary: var(--al-ref-slate-300);
	--al-sys-color-text-muted: var(--al-ref-slate-300);
	--al-sys-color-text-placeholder: var(--al-ref-slate-300);

	--al-sys-color-border: var(--al-ref-slate-700);
	--al-sys-color-border-control: var(--al-ref-slate-400);

	/* the accent as a LINE lightens; as a FILL it does not. See the split above. */
	--al-sys-color-accent-primary-line: var(--al-ref-emerald-300);
	--al-sys-color-accent-danger-line: var(--al-ref-red-300);

	/* NEUTRAL IS THE EXCEPTION TO THE LINE ABOVE, AND THAT IS DELIBERATE.
	   Emerald and red keep their fill on a dark ground because they are saturated: a
	   saturated fill reads as a fill on any ground. Slate-100 is not a colour, it is a
	   pale ground, so on slate-900 it stops being a quiet utility and becomes a glaring
	   white block -- louder than the primary action it is supposed to defer to. So the
	   FILL re-points here where the other two do not, and the label follows it to white.
	   Do not "fix" this back to match its neighbours (ALLM-7250). */
	--al-sys-color-accent-neutral: var(--al-ref-white-translucent);
	--al-sys-color-accent-neutral-line: var(--al-ref-slate-300);
	--al-sys-color-text-on-accent-neutral: var(--al-ref-white);
	--al-sys-color-accent-neutral-strong: var(--al-ref-white);
	--al-sys-color-accent-neutral-stronger: var(--al-ref-white);

	--al-sys-color-status-error: var(--al-ref-red-300);
	--al-sys-color-status-error-text: var(--al-ref-red-300);
	--al-sys-color-status-warning: var(--al-ref-amber-500);
	--al-sys-color-status-success: var(--al-ref-emerald-300);
	--al-sys-color-status-info: var(--al-ref-blue-500);

	/* A STATUS AS BARE TEXT NEEDS ITS OWN RUNG, WHICH IS WHY -text EXISTS AT ALL.

	   The -text rung is the status colour for a word standing on the ground, where the
	   solid is the colour for a mark, a fill or an edge. Only error carried a dark rung
	   here; the other three kept the light ground's deep inks and disappeared. Measured
	   on slate-900: the dashboard's own KPI deltas at 1.75:1, the Cycle Count status at
	   2.85:1 -- and on that row the DOT was already bright, because a dot reads the solid
	   and the word beside it reads this.

	   The solid cannot stand in for these. Warning proves it: amber-500 as a word is
	   5.41:1 on dark but only 3.19:1 on light, so one rung cannot serve both grounds.
	   That split is the whole reason the pair is two tokens rather than one. */
	--al-sys-color-status-success-text: var(--al-ref-emerald-300); /* 8.81:1 on slate-900 */
	--al-sys-color-status-warning-text: var(--al-ref-amber-300); /* 8.60:1 */
	--al-sys-color-status-info-text: var(--al-ref-blue-300); /* 7.02:1 */

	/* AND THE TINT IS A THIRD RUNG, for the same reason the -text rung exists.

	   The solid is a mark, -text is a word, and -tint is the GROUND a status sits on.
	   This scope re-pointed the first two and left the third, so all four tints stayed
	   at the light ramp's -50 while everything standing on them followed the ground to
	   white. A tint is the one rung where that is fatal rather than merely dim: the ink
	   moves and the ground does not, so the contrast does not shrink, it inverts.

	   ALLM-7286 is what that looks like. An expired batch card in POS grounds on
	   --al-danger-bg, which is this rung, and in dark it kept rgb(252,239,238) under
	   white text: 1.12:1, and 1.37:1 for the secondary ink, beside an ordinary card at
	   10.67:1. Reported as "unreadable", which understates it -- the row is invisible.

	   -700 is the rung the ramp already keeps for a heavier ground, and it is deep
	   enough to carry the same inks the rest of the dark scope uses. Measured against
	   white: 8.16, 6.06, 9.87, 8.72. One honest limit: secondary ink on amber-700 is
	   3.95:1, under AA for body copy, so a warning surface carrying muted text wants
	   the primary ink rather than a fourth rung invented here. */
	--al-sys-color-status-error-tint: var(--al-ref-red-700);
	--al-sys-color-status-warning-tint: var(--al-ref-amber-700);
	--al-sys-color-status-success-tint: var(--al-ref-emerald-700);
	--al-sys-color-status-info-tint: var(--al-ref-blue-700);

	/* THE EMPHASIS LADDER FLATTENS HERE, and this is the one place the dark scope has
	   to state something it would rather not.

	   -strong and -stronger mean "one rung more emphatic", and on a light ground more
	   emphatic is DARKER: an outline button's label deepens under hover and press to
	   hold its contrast as the veil lightens the ground beneath it. On a dark ground
	   that direction is exactly backwards — deepening walks the label into the
	   background. Left alone it does: pressing a Discard button on a dark panel drops
	   its label to emerald-700 on slate-900 and the word disappears.

	   The old code never showed this because .al-on-dark .al-btn-outline (0,2,0)
	   outranked .al-btn-outline:hover (0,1,1), so hover simply never fired inside the
	   dark scope. Removing that rule is what exposed it. It was always broken; it was
	   only unreachable.

	   The honest fix is a lighter rung to climb to, and the ramp does not have one —
	   there is nothing above emerald-300 or red-300. So the ladder flattens: the label
	   holds its resting value through hover and press, and the veil carries the state
	   on its own. A theme with a fuller ramp can simply point these somewhere real.
	   Same shape of gap as the missing slate rung between 700 and 900. */
	/* The ladder now CLIMBS on dark instead of flattening. It was pinned flat because
	   the ramp had nothing above 300 to climb to; 200 exists now, so hover and press
	   get lighter here exactly as they get darker on a light ground. */
	--al-sys-color-accent-primary-strong: var(--al-ref-emerald-200);
	--al-sys-color-accent-primary-stronger: var(--al-ref-emerald-200);
	--al-sys-color-accent-danger-strong: var(--al-ref-red-200);
	--al-sys-color-accent-danger-stronger: var(--al-ref-red-200);

	--al-sys-color-surface-page: var(--al-ref-slate-950); /* the page sits UNDER the cards, so on dark it
	     goes darker than the card rather than lighter — the opposite of the light
	     ground, and the reason it is a role rather than a fixed offset. */
	--al-sys-color-scrim: rgb(from var(--al-ref-black) r g b / 0.66); /* a 55% ink scrim over an already dark
	     page barely dims it, so the scrim deepens with the ground. */
	--al-sys-color-shadow-ink: var(--al-ref-black);
	--al-sys-elevation-sticky:
		0 1px 0 rgb(from var(--al-sys-color-shadow-ink) r g b / 0.5),
		0 2px 6px -2px rgb(from var(--al-sys-color-shadow-ink) r g b / 0.6);
	--al-sys-elevation-overlay:
		0 2px 4px rgb(from var(--al-sys-color-shadow-ink) r g b / 0.5),
		0 24px 60px -12px rgb(from var(--al-sys-color-shadow-ink) r g b / 0.75);

	/* THE ROW TINTS GO UP ON DARK, and they mix the LIGHT rung. A 10% emerald over a
	   dark row is very nearly nothing where the same 10% over white is plainly a tint,
	   so the percentages that read on one ground disappear on the other. Measured
	   before and after: the chosen row went from invisible to a 1.33 step. */
	--al-sys-color-row-tint-selected: color-mix(in srgb, var(--al-sys-color-accent-primary-line) 16%, transparent);
	--al-sys-color-row-tint-active: color-mix(in srgb, var(--al-sys-color-accent-primary-line) 40%, transparent);
}

/* -----------------------------------------------------------------------------
   AND BACK AGAIN.

   A ground is inherited, so everything inside .al-on-dark is dark until something
   says otherwise — including a white card nested in a dark bar, which is a normal
   thing to build and which rendered a white label on a white field for exactly as
   long as this file has existed. It was in the stress harness the whole time,
   labelled "already-known broken", because with dark written onto each control there
   was nothing to say otherwise WITH.

   Now there is. A ground owns this list of roles and nothing else, so a ground can be
   undone by pointing the same list back. The two blocks are key-for-key identical on
   purpose: if one grows a role, so does the other, and the pair is greppable.

   THE RULE THAT COMES WITH IT: if you paint a background, name the ground. A panel
   gets .al-on-dark or .al-on-light. Not naming it means inheriting the one outside,
   which is right far more often than it is wrong — but when it is wrong it is silent,
   and this is the thing that makes it sayable.
   ----------------------------------------------------------------------------- */
.al-on-light {
	--al-sys-color-surface: var(--al-ref-white);
	--al-sys-color-surface-control: var(--al-ref-white);
	--al-sys-color-surface-sunken: var(--al-ref-slate-50);
	--al-sys-color-surface-raised: var(--al-ref-white);
	--al-sys-elevation-raised:
		0 1px 2px rgb(from var(--al-sys-color-shadow-ink) r g b / 0.1),
		0 10px 28px -6px rgb(from var(--al-sys-color-shadow-ink) r g b / 0.22);

	--al-sys-color-text-primary: var(--al-ref-slate-900);
	--al-sys-color-text-secondary: var(--al-ref-slate-700);
	--al-sys-color-text-muted: var(--al-ref-slate-500);
	--al-sys-color-text-placeholder: var(--al-ref-slate-500);

	--al-sys-color-border: var(--al-ref-slate-200);
	--al-sys-color-border-control: var(--al-ref-slate-500);

	--al-sys-color-accent-primary-line: var(--al-ref-emerald-500);
	--al-sys-color-accent-danger-line: var(--al-ref-red-500);
	--al-sys-color-accent-primary-strong: var(--al-ref-emerald-600);
	--al-sys-color-accent-primary-stronger: var(--al-ref-emerald-700);
	--al-sys-color-accent-danger-strong: var(--al-ref-red-600);
	--al-sys-color-accent-danger-stronger: var(--al-ref-red-700);

	/* Neutral has to come back here, and it is the ONLY accent that does, for the same
	   reason it was the only one to re-point on dark: its FILL moved. Emerald and red
	   keep one fill on both grounds, so a light panel nested in a dark one inherits a
	   fill that was already right. Neutral's became white-translucent, which on a white
	   panel is nothing at all -- the button would vanish rather than merely look wrong.
	   Live case: LocationPickerModal renders inside the MenuBar's dark ground and wears
	   .al-on-light to get a light panel back (ALLM-7250). */
	--al-sys-color-accent-neutral: var(--al-ref-slate-100);
	--al-sys-color-accent-neutral-line: var(--al-ref-slate-600);
	--al-sys-color-accent-neutral-strong: var(--al-ref-slate-700);
	--al-sys-color-accent-neutral-stronger: var(--al-ref-slate-900);
	--al-sys-color-text-on-accent-neutral: var(--al-ref-slate-700);

	--al-sys-color-status-error: var(--al-ref-red-500);
	--al-sys-color-status-error-text: var(--al-ref-red-700);
	--al-sys-color-status-warning: var(--al-ref-amber-500);
	--al-sys-color-status-success: var(--al-ref-emerald-500);
	--al-sys-color-status-info: var(--al-ref-blue-500);

	--al-sys-color-row-tint-selected: color-mix(in srgb, var(--al-sys-color-accent-primary) 10%, transparent);
	--al-sys-color-row-tint-active: color-mix(in srgb, var(--al-sys-color-accent-primary) 20%, transparent);

	--al-sys-color-surface-page: var(--al-ref-slate-50);
	--al-sys-color-scrim: var(--al-ref-slate-900-translucent);
	--al-sys-color-shadow-ink: var(--al-ref-slate-900);
	--al-sys-elevation-sticky:
		0 1px 0 rgb(from var(--al-sys-color-shadow-ink) r g b / 0.06),
		0 2px 6px -2px rgb(from var(--al-sys-color-shadow-ink) r g b / 0.08);
	--al-sys-elevation-overlay:
		0 2px 4px rgb(from var(--al-sys-color-shadow-ink) r g b / 0.08),
		0 24px 60px -12px rgb(from var(--al-sys-color-shadow-ink) r g b / 0.32);
	color: var(--al-sys-color-text-primary); /* see .al-on-dark */

}

/* =============================================================================
   THE ROW TINTS FOR AN ENGINE WITHOUT color-mix().

   color-mix() is Chrome 111. The Dejavoo P18 POS terminals run an AOSP WebView
   pinned at Chrome 101 and it cannot be updated -- no Play Store, a factory system
   app at /product/app/webview, and one entry in the ROM provider allowlist.

   A custom property does NOT fall back the way a normal declaration does. The token
   parses fine and holds the unparseable value; the failure happens later, wherever
   var() substitutes it, and it takes that whole declaration with it. So a second
   --al-sys-color-row-tint-selected written above would not rescue anything. Only a
   guard that re-points the token does.

   This is scoped to the row tints alone because they are the only color-mix tokens
   in this file the POS actually consumes -- measured 2026-09-30, 3 uses in
   react/pos. The rest never reach a terminal.
   ============================================================================= */
@supports not (color: color-mix(in srgb, red 50%, transparent)) {
	:root {
		--al-sys-color-row-tint-selected: var(--al-ref-emerald-500-translucent-10);
		--al-sys-color-row-tint-active: var(--al-ref-emerald-500-translucent-20);
	}
	.al-on-dark {
		--al-sys-color-row-tint-selected: var(--al-ref-emerald-300-translucent-16);
		--al-sys-color-row-tint-active: var(--al-ref-emerald-300-translucent-40);
	}
	.al-on-light {
		--al-sys-color-row-tint-selected: var(--al-ref-emerald-500-translucent-10);
		--al-sys-color-row-tint-active: var(--al-ref-emerald-500-translucent-20);
	}
}
