/* ============================================================================
   TYPEFACE REPAIR — RETIRED, and kept as a note because the reasoning is worth
   more than the rule was.
   ============================================================================
   CoreUI hard-set a legacy typeface on .form-control, .btn-info and .btn-info2,
   and only _layout_fullscreen.njk loaded CoreUI, so the damage was confined to
   the eight auth views and highly visible on them: every login field and the
   "Log in" button itself. This block undid it with font-family: inherit.

   The auth layout now loads bootstrap.min.css instead, whose reboot already
   does `button,input,optgroup,select,textarea { font-family:inherit }`. There
   is nothing left to repair, so the rule went. Measured on the login screen
   before and after removing it: Public Sans at 14px either way.
   ==========================================================================*/


/* ============================================================================
   P4-T2 — BUTTON GEOMETRY.
   ============================================================================
   The colour layer for buttons was already done. This adds the SHAPE, which
   Bootstrap was still supplying: padding .375rem .75rem, font-size 1rem,
   weight 400. The design is 13px / 600 and roomier.

   >> WHERE THESE NUMBERS COME FROM. Not from grepping the prototype — that
   approach had 18 of its claims refuted on review. The prototype ships a
   support.js runtime and HYDRATES into a working app, so its buttons were
   rendered across 23 screens and read with getComputedStyle:
       node .specs-local/tools/design-measure.js buttons
   103 buttons -> 29 distinct looks. The design is hand-tuned per screen, so
   these are measured PLURALITIES applied uniformly. That is deliberate: a
   token system exists to normalise exactly this kind of drift.

   >> NO HEIGHT IS DECLARED, AND THAT IS NOT AN OVERSIGHT. Measured button
   heights vary WITHIN a single look (31px and 33px for the same style), which
   proves height is a consequence of padding + line-height rather than a
   designed value. Authoring a fixed height would be inventing a constraint the
   design does not have, and would break any button whose label wraps.

   >> VENDOR DOM IS EXCLUDED. ui-select emits
   "btn btn-default form-control ui-select-toggle" on ONE node from
   vendors/ui-select/select.min.js, so it is simultaneously a button and a
   field. It is a FIELD, and button geometry is the wrong answer for all of
   them. main.css:205 already owns .ui-select-toggle.btn but loads EARLIER than
   this file, so without the :not() below we would silently take it over.
   Those controls belong to P4-T4b.

   >> VARIANT MAPPING, recorded because half of it was already decided in silence:
     .btn-primary   -> accent solid    (226 uses)
     .btn-info      -> accent solid    (108 uses) — IDENTICAL to .btn-primary in
                      this file since before P4. Not a decision made here, but
                      one that had never been written down. Consequence: ~47% of
                      the app's 839 buttons render accent green, against roughly
                      20-32% in the design. Closing that needs markup edits to
                      encode a semantic the app never carried, so it is a
                      RECORDED PARITY GAP, not a carve-out. See tasks.md.
     .btn-secondary -> outlined        (190 uses)
     .btn-danger    -> destructive     (135 uses)
     .btn-success   -> accent solid    (23 uses). NOTE: --al-success-solid and
                      --al-accent-primary both resolve to #157a52, so this is
                      already the accent look. An earlier review called the
                      hover a "hue jump"; it is not — hover goes to #0f5f3f,
                      the same rung .btn-primary uses. The token NAMES cross
                      families, which is a maintainability wart with zero pixel
                      impact. Deliberately not chased in a reskin.
     .btn-warning   -> warning solid   (7 uses)
   ==========================================================================*/
/* `:not(.k-icon-button):not(.k-button-icon)` spares icon-only Kendo buttons.
   Grid row-action buttons render `.btn.btn-sm.btn-<color> …k-icon-button` with a
   fixed square vendor width; the padding + font-size here (border-box) shrank that
   square until the 16px glyph clipped to a ~2px sliver (P4-T2 regression, reported
   on the customer-notes + discounts grids). Excluding them lets Kendo keep its own
   square geometry; the `.btn-<color>` chip colours below still apply, so the red/
   green/blue chips are unchanged — only the glyph comes back. The guard is added to
   EVERY geometry tier below to keep the specificity balance the comment at .btn-sm
   describes. */
/* [PASS6-F13 / recipe #8] The confirm-dialog buttons join this rule rather than
   keeping their own copy of it. Recipe #8 ordered the bespoke confirm-dialog and
   save-bar systems "folded into the one .btn set"; the colours were folded in an
   earlier pass but the GEOMETRY never was, so 180 instances across all three
   surfaces stayed padding-sized at 41.5px, and 61px on the five where the label
   wrapped. They keep their own class (44 files emit `.al-confirm-btn`, and this is
   a reskin — chrome, not markup), but they no longer keep their own geometry. */
.btn:not(.ui-select-toggle):not(.ui-select-match-item):not(.k-icon-button):not(.k-button-icon),
.al-confirm .modal-footer .al-confirm-btn {
	/* [PASS6-F12] These three values were MEASURED off the old app, not chosen. The
	   recipe was silent on button type size, so tokens.css filled the gap with body
	   default and the drift became invisible. Recipe amendments 12/13/14 close it. */
	padding: 0 16px; /* [12] zero vertical — height does the sizing, not padding */
	font-size: var(
		--al-fs-md
	); /* [12] 14px, Jeff's call. An EXISTING rung: the
	   recipe demo's 13.5px was its own house body size, never a button decision, and
	   minting a half-pixel token for it would start a second type scale. */
	font-weight: var(--al-fw-semibold);
	line-height: 1;
	/* [PASS6-F13] Radius belongs HERE, stated, not inherited from Bootstrap's own
	   .btn. Found this the hard way: F13 removed `border-radius` from the confirm
	   rule on the assumption that joining this rule supplied it, and all 180 confirm
	   buttons went square. They were living off Bootstrap's default the whole time.
	   The recipe locks 8px for all kinds, all sizes, all states — so say so. */
	border-radius: var(--al-radius-md);
	white-space: nowrap; /* [14] locked — and it is what makes `height` safe below */
	/* [PASS6-F12 / amendment 14] `height`, NOT `min-height`. The comment this replaces
	   defended min-height so a wrapping label could grow; the recipe locks
	   `white-space: nowrap` precisely so that case cannot arise. min-height is the gap
	   five tiers escaped through, rendering at 39/40/44/47/48px. */
	height: var(--al-h-control);
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: 6px; /* icon+label spacing now that we're a flex container */
}

/* >> THE :not() CHAIN MUST MATCH THE BASE RULE'S LENGTH. <<
   `.btn:not(.ui-select-toggle):not(.ui-select-match-item)` is specificity
   (0,3,0). A tier written as `.btn-sm:not(.ui-select-toggle)` is only (0,2,0),
   so the BASE rule wins and every small button silently renders at default
   size. Caught by the gate on the first run: .btn-sm reported padding
   4px 8px -> 9px 16px instead of the small value. Equal specificity plus
   later source order is what actually makes these tiers work. */
.btn-sm:not(.ui-select-toggle):not(.ui-select-match-item):not(.k-icon-button):not(.k-button-icon),
.btn-group-sm > .btn:not(.ui-select-toggle):not(.ui-select-match-item):not(.k-icon-button):not(.k-button-icon) {
	padding: 0 11px; /* [13] */
	/* [PASS6-F12] 13px, one rung below the 14px default. The recipe demo shows 12.5px
	   on the sm tier, but 12.5 is not a rung in tokens.css and minting one would be the
	   exact half-pixel mistake amendment 12 exists to avoid. Flagged to Jeff. */
	font-size: var(--al-fs-base);
	height: var(--al-h-control-sm); /* [14][19] hard height, now a token */
}

/* The page-header CTA tier — measured 10px 18px / 700, 5 instances, the single
   largest cluster. --al-fw-bold's own note ("primary buttons DRIFT 600/700")
   predicted this split; measuring 103 rendered buttons confirmed it. */
.btn-lg:not(.ui-select-toggle):not(.ui-select-match-item):not(.k-icon-button):not(.k-button-icon),
.btn-group-lg > .btn:not(.ui-select-toggle):not(.ui-select-match-item):not(.k-icon-button):not(.k-button-icon) {
	padding: 0 22px; /* [13] */
	font-size: var(--al-fs-15); /* [12] 15px, one rung above the default */
	/* [PASS6-F12] 600, not bold. The recipe locks weight 600 across ALL kinds and ALL
	   sizes; the 700 this replaces was measured off 5 page-header CTAs in the old app,
	   which is the same measure-don't-decide pattern amendment 12 closes. */
	font-weight: var(--al-fw-semibold);
	height: var(--al-h-control-lg); /* [14] */
}

/* [PASS7-F20] VENDOR FALLBACK INK — stated BEFORE the variant blocks on purpose.
   =============================================================================
   Kendo renders plenty of buttons carrying no variant of its own (solid-base,
   flat-base, outline-base), and F17 gave them a token-driven label colour because
   their stock #212529 / #333 exist nowhere in tokens.css. That was right. What was
   wrong was WHERE it sat: 1100 lines further down, after every .btn-* variant.

   An element can wear both systems at once. On /admin/config/sell/price_groups the
   row actions are literally
       class="btn btn-sm btn-secondary k-button k-button-solid-base"
   and .k-button-solid-base, .btn-secondary and .btn-danger are ALL (0,1,0). Equal
   specificity is broken by source order, so the later rule won and this fallback
   repainted three buttons that had already been told what colour to be: emerald
   "Edit"/"Duplicate" and a white-on-red "Delete" all came out #3d4550.

   Moving it above the variants closes the whole class of collision by position, with
   NO change in specificity. Raising specificity instead — the obvious
   :not(.btn-primary):not(.btn-secondary)… chain — would have introduced a worse bug:
   it lifts this rule to (0,7,0), where it would beat its OWN hover at (0,2,0) and
   freeze the label colour on hover.

   This file already states that contract for .btn:disabled a few blocks down: "Stated
   before the variant blocks so a variant can still override if one ever genuinely
   needs to, rather than winning by accident of source order." A fallback belongs
   first; that is what makes it a fallback rather than an override. */
.k-button-solid-base,
.k-button-flat-base,
.k-button-outline-base {
	color: var(--al-text-body);
}

.btn-primary {
	color: var(--al-accent-on-primary);
	background-color: var(--al-accent-primary);
	border-color: var(--al-accent-primary);
}

/* ============================================================================
   [F4] INTERACTION STATE LAYER — one overlay for every button.
   ============================================================================
   Jeff's call: instead of a named hover and press colour per button kind, a
   translucent film fades in over whatever colour the button already is (8% hover,
   12% press). One rule covers both states for every button in every colour, so a
   new button kind needs no new tokens and cannot disagree with its neighbours.
   That disagreement is what he spotted on the POS queue screen.

   Implemented as ::after rather than a background swap so it layers over ANY fill
   (solid, outline, white) without knowing what that fill is, and sits under the
   label. `isolation:isolate` keeps the film inside the button's own stacking
   context. Buttons that carry their own ::after are excluded above. */
.btn::after {
	content: '';
	position: absolute;
	inset: 0;
	/* UNDER THE LABEL. The comment above has always said so and nothing implemented it:
	   generated content paints after in-flow content, so the film was over the label the
	   whole time. It went unnoticed because --al-state-layer is the label's own colour,
	   so the film was veiling white with white. isolation:isolate above is what makes a
	   negative z-index safe -- it keeps the film inside the button. */
	z-index: -1;
	border-radius: inherit;
	background: var(--al-state-layer);
	opacity: 0;
	transition: opacity var(--al-dur-base) var(--al-ease-standard);
	pointer-events: none;
}
/* A SELECT IS NOT A BUTTON, however it is marked up. ui-select emits .btn on BOTH the
   toggle and the clear affordance inside it, so the button film applied to both: the
   control darkened on hover, and hovering the clear painted a grey slab across the right
   end of the field, square-cornered against the field's own radius and overlapping the
   caret. The recipe's select answers a hover on its EDGE and leaves its fill alone.

   SCOPED TO THE CONTAINER, not to the toggle. It was written for the toggle alone, and
   the clear had the identical bug sitting next to it for exactly as long. Anything
   ui-select marks .btn is part of a field, not a button.

   Worth knowing how these survive: the film is painted by a pseudo-element, so comparing
   the element's own background-color reports white in every state and cannot see it. */
.ui-select-container .btn::after {
	content: none;
}

/* THE CLEAR IS A CLEAR, not a link. ui-select gives it .btn-link, whose bootstrap blue
   never showed because the glyph is a child <i> with its own colour -- so the anchor was
   carrying an off-brand colour that happened to be invisible. It is set anyway: an inert
   wrong value becomes a visible wrong value the moment someone puts a label in it.

   The mark itself was at FULL INK, the same strength as the value it sits beside, and
   competed with it. The recipe's clear is muted and strengthens under the pointer, which
   is the whole difference between "you may remove this" and "this is part of the value". */
.ui-select-container .btn-link {
	color: var(--al-sys-color-text-muted);
}
.ui-select-container .btn-link > .cil,
.ui-select-container .btn-link > i {
	color: var(--al-sys-color-text-muted);
	transition: color var(--al-dur-base) var(--al-ease-standard);
}
.ui-select-container .btn-link:hover > .cil,
.ui-select-container .btn-link:hover > i {
	color: var(--al-sys-color-text-primary);
}

/* A SINGLE SELECT'S VALUE IS ONE LINE. ui-select already gives .ui-select-match-text both
   `overflow: hidden` and `text-overflow: ellipsis`, and then leaves `white-space: normal`, which
   stops the ellipsis working: a value with a space in it breaks at the space instead, and the
   second line renders 3.8px below the control's bottom border. "Star TSP100 Cash Drawer" showed
   "Drawer" cut in half. A value with nothing to break at truncated correctly, so only some of
   them looked broken and it read as random rather than as one missing declaration. Measured on
   ALLM-7255 terminal Hardware. Excludes the multiple variant, whose match items are chips and
   are supposed to wrap. */
.ui-select-container:not(.ui-select-multiple) .ui-select-match-text {
	white-space: nowrap;
}
.btn {
	position: relative;
	isolation: isolate;
}

/* THE FILM'S COLOUR IS DECIDED BY THE GROUND IT SITS ON, which is the one thing that
   makes a single rule work across every button in every colour.

   OVER AN ACCENT FILL IT DARKENS. The fill is a known colour carrying a known label, and
   lightening it eats that label's contrast -- the button gets paler exactly as the white
   words on it need the ground to hold. This used to lighten, and a primary button went
   FURTHER toward white the harder you pressed it.

   OVER THE SURFACE IT IS THE INTENT. An outline button's ground is the canvas, so a
   neutral darkening film greys it out; its own intent deepens it into its own family
   instead, which is why a pressed Delete stays red rather than going slate.

   The two alphas differ for the same reason: over a fill the label only gains contrast as
   the ground deepens, over the surface the film moves the ground TOWARD the label, so the
   on-surface press has a ceiling the on-fill one does not. */
.btn-primary,
.btn-danger,
.btn-success,
.btn-info,
.btn-warning {
	--al-state-layer: var(--al-sys-color-veil-on-accent);
	--al-state-hover: var(--al-sys-state-hover-opacity-on-fill);
	--al-state-press: var(--al-sys-state-press-opacity-on-fill);
}
.btn-secondary,
.al-confirm .modal-footer .al-confirm-cancel {
	--al-state-layer: var(--al-accent-primary);
	--al-state-hover: var(--al-sys-state-hover-opacity-on-surface);
	--al-state-press: var(--al-sys-state-press-opacity-on-surface);
}
.btn:hover:not(:disabled):not(.disabled)::after {
	opacity: var(--al-state-hover);
}
.btn:active:not(:disabled):not(.disabled)::after {
	opacity: var(--al-state-press);
}

/* ============================================================================
   THE PRESS.

   Held down, .btn deepened its film and did nothing else, and the app's own buttons
   -- Unlock, Delete, Save, Discard, the carets, the confirm buttons -- did nothing at
   all: hover and press were the same pixels. Two separate gaps, one state.

   THE SCALE IS THE HALF THAT MAKES IT PHYSICAL. The recipe's words: "colour alone
   under-sells a press, and it reads on every fill." It is also the half that works on
   a button whose press is otherwise invisible, which is most of the list below.
   Transform costs no layout, so nothing around the button moves.

   EVERY SELECTOR CARRIES ITS OWN SUFFIX and the lists are not folded. A pseudo-class
   written once after a comma-separated list binds to the LAST selector only, so the
   other fifteen buttons took the pressed transform and the pressed film AT REST --
   permanently small, under a permanent veil. It looked deliberate enough to miss by
   eye; Playwright caught it refusing to click a login button that was no longer where
   its layout box said it was.
   ============================================================================ */
.btn:active:not(:disabled):not(.disabled),
.al-user-action:active:not(:disabled):not(.disabled),
.al-user-secondary:active:not(:disabled):not(.disabled),
.al-cfg-add:active:not(:disabled):not(.disabled),
.al-cfg-addsq:active:not(:disabled):not(.disabled),
.al-cfg-delete:active:not(:disabled):not(.disabled),
.al-cfg-new:active:not(:disabled):not(.disabled),
.al-cfg-iconbtn:active:not(:disabled):not(.disabled),
.al-cfg-editbtn:active:not(:disabled):not(.disabled),
.al-curbside__btn:active:not(:disabled):not(.disabled),
.al-login__btn:active:not(:disabled):not(.disabled),
.al-confirm .modal-footer .al-confirm-btn:active:not(:disabled):not(.disabled) {
	transform: scale(var(--al-sys-state-press-scale));
}

/* The film, for the buttons that never had one. Their HOVER is a background swap and
   stays exactly as it is -- this is press-only, so the press reads as a deeper version
   of the hover rather than a second answer competing with it. */
.al-user-action,
.al-user-secondary,
.al-cfg-add,
.al-cfg-addsq,
.al-cfg-delete,
.al-cfg-new,
.al-cfg-iconbtn,
.al-cfg-editbtn,
.al-curbside__btn,
.al-login__btn,
.al-confirm .modal-footer .al-confirm-btn {
	position: relative;
	isolation: isolate;
}
.al-user-action::after,
.al-user-secondary::after,
.al-cfg-add::after,
.al-cfg-addsq::after,
.al-cfg-delete::after,
.al-cfg-new::after,
.al-cfg-iconbtn::after,
.al-cfg-editbtn::after,
.al-curbside__btn::after,
.al-login__btn::after,
.al-confirm .modal-footer .al-confirm-btn::after {
	content: '';
	position: absolute;
	inset: 0;
	z-index: -1;
	border-radius: inherit;
	background: var(--al-state-layer);
	opacity: 0;
	transition: opacity var(--al-dur-base) var(--al-ease-standard);
	pointer-events: none;
}
.al-user-action:active:not(:disabled):not(.disabled)::after,
.al-user-secondary:active:not(:disabled):not(.disabled)::after,
.al-cfg-add:active:not(:disabled):not(.disabled)::after,
.al-cfg-addsq:active:not(:disabled):not(.disabled)::after,
.al-cfg-delete:active:not(:disabled):not(.disabled)::after,
.al-cfg-new:active:not(:disabled):not(.disabled)::after,
.al-cfg-iconbtn:active:not(:disabled):not(.disabled)::after,
.al-cfg-editbtn:active:not(:disabled):not(.disabled)::after,
.al-curbside__btn:active:not(:disabled):not(.disabled)::after,
.al-login__btn:active:not(:disabled):not(.disabled)::after,
.al-confirm .modal-footer .al-confirm-btn:active:not(:disabled):not(.disabled)::after {
	opacity: var(--al-state-press);
}

/* The film takes the button's OWN meaning, so a press deepens it into its own colour
   instead of greying it. Without this a delete button pressed toward ink and stopped
   reading as destructive at the moment it mattered most. */
.al-cfg-delete,
.al-user-action--danger {
	--al-state-layer: var(--al-danger-solid);
}
.al-user-secondary,
.al-cfg-add,
.al-cfg-addsq,
.al-cfg-new,
.al-curbside__btn,
.al-login__btn {
	--al-state-layer: var(--al-accent-primary);
}

/* ============================================================================
   [PASS6-F15 / amendment 15(b)] THE STANDALONE ICON BUTTON.

   Amendment 15 split a family that was never one thing. Of 78 captured icon-only
   buttons, 49 were dropdown carets and numeric spinners sized by their HOST control
   and 6 were Google Maps chrome — neither is a button, and both are now excluded
   from the taxonomy outright. Jeff caught that before a blanket square rule shipped:
   "it depends on the context. If some of those carets are inside of dropdowns, does
   that still work?" They are, and it does not — a 36x36 square cannot live inside a
   36px input.

   What is left is the real icon button: square at the tiers already locked, radius 8,
   transparent fill, muted glyph strengthening on hover.

   Defined on the selectors that EXIST rather than as a fresh .al-icon-btn nobody
   consumes. That mistake is already in this codebase once — .al-segmented was built
   correctly, wired to nothing, and sat unused (see F16).
   ============================================================================ */
.al-cfg-iconbtn,
.al-cfg-editbtn {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	flex: none;
	position: relative;
	border: 1px solid transparent;
	border-radius: var(--al-radius-md);
	background: transparent;
	color: var(--al-text-muted);
	cursor: pointer;
}
.al-cfg-iconbtn {
	width: var(--al-h-control); /* 36 — was a bespoke 34, same as .al-cfg-new was */
	height: var(--al-h-control);
}
.al-cfg-editbtn {
	width: var(--al-h-control-sm); /* 28 — a row-level affordance */
	height: var(--al-h-control-sm);
}

/* Hit target keyed to the RENDERED BOX, not the tier — Jeff's rule: "you only really
   need the larger hit target if it is a smaller icon like the X." A 28px glyph button
   is a 28px target without this; the expansion is invisible and costs no layout. */
.al-cfg-iconbtn::before,
.al-cfg-editbtn::before {
	content: '';
	position: absolute;
	left: 50%;
	top: 50%;
	width: max(100%, 44px);
	height: max(100%, 44px);
	transform: translate(-50%, -50%);
}

/* [PASS6-F14 / amendment 17] Disabled is ONE blanket rule, not a per-variant table.
   The recipe locks `opacity:.5` and nothing else, deliberately: a faded button still
   reads as the KIND of button it is. The per-variant swap this replaces repainted
   .btn-secondary's text and border to grey, so 12 disabled secondaries stopped
   looking like secondaries at all — an invented rule the recipe never asked for.
   Stated before the variant blocks so a variant can still override if one ever
   genuinely needs to, rather than winning by accident of source order. */
.btn:disabled,
.btn.disabled {
	opacity: 0.5;
	pointer-events: none;
}

/* [F4] the per-kind hover colours below are GONE — the state layer replaces them.
   Only the border/colour parts that are not a hover fill remain. */

.btn-primary:focus,
.btn-primary.focus {
	/* [PASS7-F24] The resting colours are RESTATED here, exactly as F19 had to do for
	   disabled. Bootstrap ships `.btn-{v}.focus,.btn-{v}:focus{{color;background-color;
	   border-color;box-shadow}}` at (0,2,0). This block used to declare box-shadow ALONE, so
	   the three colours fell through to our resting rule at (0,1,0) and Bootstrap outranked
	   it — every focused button repainted itself vendor blue/grey and held until blur.
	   Focus is a ring, not a recolour. Same specificity, so we win on load order. */
	color: var(--al-accent-on-primary);
	background-color: var(--al-accent-primary);
	border-color: var(--al-accent-primary);
	box-shadow: none;
}

/* [PASS7-F19] RESTORED. The comment that stood here said the removal was "harmless"
   because "the blanket rule does that job now by not changing colour at all", and that
   was simply wrong: the blanket `.btn:disabled` rule sets `opacity` and `pointer-events`
   and NO COLOUR, so it cannot hold anything off. Bootstrap ships
   `.btn-primary:disabled{background-color:#007bff}` at (0,2,0); our resting
   `.btn-primary` is (0,1,0). With this rule deleted, Bootstrap outranked us and every
   disabled primary in Admin repainted itself Bootstrap blue — measured live, 15/15,
   with our opacity:.5 sitting correctly on top of the wrong colour.

   F14 removed the protection and wrote a comment claiming the opposite. The lesson is
   not "restore the old rule": it is that a per-variant disabled block must RESTATE THE
   RESTING COLOURS (what amendment 17 actually asks for) rather than swap in new ones
   (what the pre-F14 secondary rule wrongly did). Same values as resting, stated at
   (0,2,0) so Bootstrap cannot reach past them. */
.btn-primary.disabled,
.btn-primary:disabled {
	color: var(--al-accent-on-primary);
	background-color: var(--al-accent-primary);
	border-color: var(--al-accent-primary);
}

/* [PASS6-F14 / amendment 16] These seven blocks pin the RESTING fill on :hover and
   :active, so the state layer is the only thing that moves on either.

   >> A PRE-EXISTING BUG, FOUND HERE, NOT CAUSED HERE. << Primary buttons were turning
   BOOTSTRAP BLUE on hover in the live product: rest rgb(21,122,82) -> hover
   rgb(0,105,217). Confirmed by measuring with this change stashed out, so it is not
   mine. A previous pass removed "the per-kind hover colours" on the grounds that the
   state layer replaced them, without checking what would win once ours were gone —
   and vendored bootstrap.min.css has .btn-primary:hover{background:#0069d9} at higher
   specificity than our .btn-primary. The layer was faithfully painting 8% black over
   blue.

   Nothing caught it because THE GALLERY ONLY CAPTURES RESTING STATE. An audit
   instrument that never hovers cannot see a hover bug.

   The rest of the note still applies: these cannot simply be deleted. They do two
   jobs — neutralise the vendor, and (previously) darken the fill. I deleted them for
   the second reason and only then discovered the first. */
/* superseded note: These seven :active blocks now pin the RESTING fill
   rather than the darker hover rung, so the state layer is the only thing that moves
   on press.

   They cannot simply be deleted, which I found out by deleting them: they are doing
   TWO jobs, and I had only noticed one. Besides darkening the fill, they suppress
   Bootstrap's own vendored `.btn-primary:…:active{background:#0062cc}`. With them
   gone, pressing a primary turned it BLUE. Keep the selector, neutralise the vendor,
   let the layer do the work. */
.btn-primary:not(:disabled):not(.disabled):hover,
.btn-primary:not(:disabled):not(.disabled):active,
.btn-primary:not(:disabled):not(.disabled).active,
.show > .btn-primary.dropdown-toggle {
	color: var(--al-accent-on-primary);
	background-color: var(--al-accent-primary);
	border-color: var(--al-accent-primary);
}

.btn-primary:not(:disabled):not(.disabled):active:focus,
.btn-primary:not(:disabled):not(.disabled).active:focus,
.show > .btn-primary.dropdown-toggle:focus {
	box-shadow: none;
}

/* SECONDARY = emerald outline (Jeff's pick from the button lab, 2026-07-30):
   white fill + emerald border/text — the outline partner to the solid primary. */
.btn-secondary {
	color: var(--al-accent-primary);
	background-color: var(--al-surface-card);
	border: 1px solid var(--al-accent-primary);
}

.btn-secondary:focus,
.btn-secondary.focus {
	/* [PASS7-F24] The resting colours are RESTATED here, exactly as F19 had to do for
	   disabled. Bootstrap ships `.btn-{v}.focus,.btn-{v}:focus{{color;background-color;
	   border-color;box-shadow}}` at (0,2,0). This block used to declare box-shadow ALONE, so
	   the three colours fell through to our resting rule at (0,1,0) and Bootstrap outranked
	   it — every focused button repainted itself vendor blue/grey and held until blur.
	   Focus is a ring, not a recolour. Same specificity, so we win on load order. */
	color: var(--al-accent-primary);
	background-color: var(--al-surface-card);
	border-color: var(--al-accent-primary);
	box-shadow: none;
}

/* [PASS7-F19] The F14 note here was half right and half wrong, and the wrong half
   shipped a bug. Right: the OLD per-variant swap repainted the text muted-grey and the
   border input-grey, stripping the emerald outline off every disabled secondary, so
   deleting it was correct. Wrong: "the blanket opacity:.5 above now carries it, and the
   button keeps its own colours at half strength." It does not. The blanket rule sets no
   colour, and Bootstrap's `.btn-secondary:disabled{background-color:#6c757d}` is (0,2,0)
   against our (0,1,0) resting rule — so disabled secondaries went SOLID BOOTSTRAP GREY,
   which is further from a secondary than the grey outline ever was.

   What amendment 17 asks for is the resting appearance at half strength. That needs the
   resting colours RESTATED at the disabled specificity, which is this rule. It is not a
   revival of the old swap: every value below is identical to the resting block above. */
.btn-secondary.disabled,
.btn-secondary:disabled {
	color: var(--al-accent-primary);
	background-color: var(--al-surface-card);
	border-color: var(--al-accent-primary);
}

.btn-secondary:not(:disabled):not(.disabled):hover,
.btn-secondary:not(:disabled):not(.disabled):active,
.btn-secondary:not(:disabled):not(.disabled).active,
.show > .btn-secondary.dropdown-toggle {
	color: var(--al-accent-primary);
	background-color: var(--al-accent-primary-tint);
	border-color: var(--al-accent-primary);
}

.btn-secondary:not(:disabled):not(.disabled):active:focus,
.btn-secondary:not(:disabled):not(.disabled).active:focus,
.show > .btn-secondary.dropdown-toggle:focus {
	box-shadow: none;
}

.btn-success {
	color: var(--al-accent-on-primary);
	background-color: var(--al-success-solid);
	border-color: var(--al-success-solid);
}

.btn-success:focus,
.btn-success.focus {
	color: var(--al-accent-on-primary);
	background-color: var(--al-success-solid);
	border-color: var(--al-success-solid);
	box-shadow: none;
}

.btn-success.disabled,
.btn-success:disabled {
	color: var(--al-accent-on-primary);
	/* [PASS7-F19] was --al-accent-primary while the resting block above uses
	   --al-success-solid. Both resolve to #157a52 today, so nothing looked wrong, but a
	   disabled success button was reading its colour from a DIFFERENT ROLE than the same
	   button at rest. Re-theme success away from emerald and only the disabled state
	   would stay green. Invisible today, wrong by construction, and exactly the kind of
	   thing the token layer exists to prevent — so it is conformed rather than left. */
	background-color: var(--al-success-solid);
	border-color: var(--al-success-solid);
}

.btn-success:not(:disabled):not(.disabled):hover,
.btn-success:not(:disabled):not(.disabled):active,
.btn-success:not(:disabled):not(.disabled).active,
.show > .btn-success.dropdown-toggle {
	color: var(--al-accent-on-primary);
	background-color: var(--al-accent-primary);
	border-color: var(--al-accent-primary);
}

.btn-success:not(:disabled):not(.disabled):active:focus,
.btn-success:not(:disabled):not(.disabled).active:focus,
.show > .btn-success.dropdown-toggle:focus {
	box-shadow: none;
}

.btn-info {
	color: var(--al-accent-on-primary);
	background-color: var(--al-accent-primary);
	border-color: var(--al-accent-primary);
}

.btn-info:focus,
.btn-info.focus {
	color: var(--al-accent-on-primary);
	background-color: var(--al-accent-primary);
	border-color: var(--al-accent-primary);
	box-shadow: none;
}

.btn-info.disabled,
.btn-info:disabled {
	color: var(--al-accent-on-primary);
	background-color: var(--al-accent-primary);
	border-color: var(--al-accent-primary);
}

.btn-info:not(:disabled):not(.disabled):hover,
.btn-info:not(:disabled):not(.disabled):active,
.btn-info:not(:disabled):not(.disabled).active,
.show > .btn-info.dropdown-toggle {
	color: var(--al-accent-on-primary);
	background-color: var(--al-accent-primary);
	border-color: var(--al-accent-primary);
}

.btn-info:not(:disabled):not(.disabled):active:focus,
.btn-info:not(:disabled):not(.disabled).active:focus,
.show > .btn-info.dropdown-toggle:focus {
	box-shadow: none;
}

.btn-info2 {
	color: var(--al-accent-on-primary);
	background-color: var(--al-info-solid);
	border-color: var(--al-info-solid);
}

.btn-info2:focus,
.btn-info2.focus {
	box-shadow: none;
}

.btn-info2.disabled,
.btn-info2:disabled {
	color: var(--al-accent-on-primary);
	background-color: var(--al-info-solid);
	border-color: var(--al-info-solid);
}

.btn-info2:not(:disabled):not(.disabled):hover,
.btn-info2:not(:disabled):not(.disabled):active,
.btn-info2:not(:disabled):not(.disabled).active,
.show > .btn-info2.dropdown-toggle {
	color: var(--al-accent-on-primary);
	background-color: var(--al-info-solid-hover);
	border-color: var(--al-info-solid-hover);
}

.btn-info2:not(:disabled):not(.disabled):active:focus,
.btn-info2:not(:disabled):not(.disabled).active:focus,
.show > .btn-info2.dropdown-toggle:focus {
	box-shadow: none;
}

.btn-warning {
	color: var(--al-text-strong);
	background-color: var(--al-warning-solid);
	border-color: var(--al-warning-solid);
}

.btn-warning:focus,
.btn-warning.focus {
	color: var(--al-text-strong);
	background-color: var(--al-warning-solid);
	border-color: var(--al-warning-solid);
	box-shadow: none;
}

.btn-warning.disabled,
.btn-warning:disabled {
	color: var(--al-text-strong);
	background-color: var(--al-warning-solid);
	border-color: var(--al-warning-solid);
}

.btn-warning:not(:disabled):not(.disabled):hover,
.btn-warning:not(:disabled):not(.disabled):active,
.btn-warning:not(:disabled):not(.disabled).active,
.show > .btn-warning.dropdown-toggle {
	color: var(--al-text-strong);
	background-color: var(--al-warning-solid);
	border-color: var(--al-warning-solid);
}

.btn-warning:not(:disabled):not(.disabled):active:focus,
.btn-warning:not(:disabled):not(.disabled).active:focus,
.show > .btn-warning.dropdown-toggle:focus {
	box-shadow: none;
}

.btn-danger {
	color: var(--al-accent-on-primary);
	background-color: var(--al-danger-solid);
	border-color: var(--al-danger-solid);
}

.btn-danger:focus,
.btn-danger.focus {
	color: var(--al-accent-on-primary);
	background-color: var(--al-danger-solid);
	border-color: var(--al-danger-solid);
	box-shadow: none;
}

.btn-danger.disabled,
.btn-danger:disabled {
	color: var(--al-accent-on-primary);
	background-color: var(--al-danger-solid);
	border-color: var(--al-danger-solid);
}

.btn-danger:not(:disabled):not(.disabled):hover,
.btn-danger:not(:disabled):not(.disabled):active,
.btn-danger:not(:disabled):not(.disabled).active,
.show > .btn-danger.dropdown-toggle {
	color: var(--al-accent-on-primary);
	background-color: var(--al-danger-solid);
	border-color: var(--al-danger-solid);
}

.btn-danger:not(:disabled):not(.disabled):active:focus,
.btn-danger:not(:disabled):not(.disabled).active:focus,
.show > .btn-danger.dropdown-toggle:focus {
	box-shadow: none;
}

/* ---- THE RING IS FOR THE KEYBOARD ----
   Every :focus block above sets box-shadow:none and the ring is re-applied here on
   :focus-visible, because a mousedown FOCUSES a button: pressing Validate painted a
   3px emerald ring around it and held it there until something else took focus. That
   is a focus indicator answering a press, and it was the loudest thing about the
   pressed state.

   The colour restatements in those blocks stay where they are. They are not about the
   ring -- bootstrap ships colour AND shadow together at (0,2,0), so a block that
   declared shadow alone let its recolour through and every focused button went vendor
   blue until blur. That still has to be beaten on plain :focus. Only the ring moves. */
.btn:focus-visible {
	box-shadow: var(--al-ring-focus-strong);
}

/* .btn-light and .btn-dark retired (P9-BTN5, recipe: no neutral/grey button).
   .btn-light had 0 uses; .btn-dark's uses were reclassed to .btn-secondary
   (grow Transfer / Print Tags; inventory Batch/Manage; metrc Resolve).
   Ink survives only as POS payment tiles, not as a button. */

/* ============================================================
   v2 radius + emerald focus (T5)
   Bootstrap's flat 4px defaults -> the DS radius scale; the blue
   focus glow -> the emerald ring. Kendo / ag-Grid controls keep
   their own look for now (folded into one grid style in P2).
   ============================================================ */
.btn,
.form-control,
.custom-select {
	border-radius: var(--al-radius-md);
}

/* ============================================================================
   P4-T3 — SECTION CARD.
   ============================================================================
   Measured by rendering the prototype across 23 screens (45 cards):
       node .specs-local/tools/design-measure.js cards
   Two properties are UNANIMOUS at 45/45 — 1px border, and box-shadow:none. The
   design's card is FLAT. 44/45 use #e4e7ec and white. Radius is 10px at 58%
   (then 12px, 8px), which --al-radius-xl already carries.

   The card was already at the right radius; what it was still inheriting from
   Bootstrap was a translucent-black border and a grey-on-grey header.

   >> PADDING DISCREPANCY, RECORDED NOT RESOLVED. --al-pad-card is 18px 20px and
   its note claims "23/26 agree, well settled". Rendering gives 16px 17px (13
   cards) and 16px 18px (7). The gap is 2-3px and my card detector is a
   heuristic that may be measuring the padded wrapper rather than the semantic
   body, so the established token WINS here. Not worth churning a settled value
   on a heuristic's word. Revisit if T4 makes it matter. */
.card {
	border-radius: var(--al-radius-xl);
	border-color: var(--al-border-default);
	background-color: var(--al-surface-card);
	box-shadow: none;
}

/* Bootstrap gives the header a grey tint (rgba(0,0,0,.03)) and a 3px radius that
   does not follow the card's 10px, so the corners visibly disagree. The design's
   header is the same white as the card, separated by the hairline alone. */
.card-header {
	background-color: transparent;
	border-bottom-color: var(--al-border-default);
	border-top-left-radius: var(--al-radius-xl);
	border-top-right-radius: var(--al-radius-xl);
	padding: var(--al-pad-card-head);
}

.card-footer {
	background-color: transparent;
	border-top-color: var(--al-border-default);
	border-bottom-left-radius: var(--al-radius-xl);
	border-bottom-right-radius: var(--al-radius-xl);
}

.card-body {
	padding: var(--al-pad-card);
}

/* ----------------------------------------------------------------------------
   FORM LAYOUT GRID — OPT-IN, deliberately NOT applied to .card-body.
   ----------------------------------------------------------------------------
   The Design Guide canvas claims the card body IS the form grid at "1fr 1fr,
   gap 18px". Rendering the PROTOTYPE — which is the authority here — says:
     - equal-width columns: CONFIRMED (4 of 5 measured form grids)
     - two columns: NOT universal. The customer detail form is THREE.
     - gap 18px: appears ZERO times. Measured column-gaps are 16px (x2), 14px,
       10px; row-gaps 14px (x2), 16px, 10px. Hence 14px 16px below, which is
       exactly what the largest form (customer detail) uses.

   >> WHY THIS IS NOT ON .card-body. <<
   Setting display:grid on .card-body would change LAYOUT for 169 card bodies
   app-wide, most of which contain Bootstrap .row/.col markup that grid would
   fight. That is a functional change with a large blast radius, and this is a
   reskin. So the grid is an opt-in class a view applies where a form actually
   wants it. --al-form-cols lets a 3-column form override without a new class.
   -------------------------------------------------------------------------- */
.al-form-grid {
	display: grid;
	grid-template-columns: repeat(var(--al-form-cols, 2), minmax(0, 1fr));
	gap: 14px 16px;
	align-items: start;
}

/* A field that must span the full form width regardless of column count. */
.al-form-grid > .al-field-wide {
	grid-column: 1 / -1;
}

/* ----------------------------------------------------------------------------
   P5-T-filtercard — LIST-PAGE FILTER CARD (design: Prototype:3696, the Retail
   Order History page). A white card wrapping a responsive grid of labeled
   filter fields. The fields themselves are already styled (P4-T4); this adds
   the container chrome, the auto-fit field grid, and the uppercase eyebrow
   labels. Reusable — consumed today by retail_order_history + add_inventory,
   and by the T11 list archetype later.

   Scope: only the two pages that HAND-ROLL a labeled-field bar can take this as
   a pure reskin. The dominant list pages inject a Kendo filter widget into a
   placeholder div (deliberately excluded from the field restyle, see :1093) —
   converting those is a markup/behaviour change, deferred to T11/Tier-3. */
.al-filter-card {
	background-color: var(--al-surface-card);
	border: 1px solid var(--al-border-default);
	border-radius: var(--al-radius-xl);
	padding: 16px 18px;
	margin-bottom: 16px;
}

/* Auto-fit field grid — cells reflow to fill the row (design:
   repeat(auto-fit, minmax(190px, 1fr)), gap 14/16). align-items:end so labels
   sit at the top and the controls — plus a trailing Apply button — share one
   baseline (this preserved the app's original .align-items-end intent). */
.al-filter-grid {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
	gap: 14px 16px;
	align-items: end;
}

/* Uppercase eyebrow labels for the filter fields (design: 11/700/.04em/#8a92a0).
   Scoped to the filter card so it does not touch other .form-label uses. Beats
   .form-label on specificity (0,1,1 vs 0,1,0). */
.al-filter-card label {
	display: block;
	margin-bottom: 6px;
	font-size: var(--al-fs-xs, 11px);
	font-weight: var(--al-fw-bold);
	letter-spacing: 0.04em;
	text-transform: uppercase;
	color: var(--al-accent-primary); /* [P9-CRD-ADM-EYE #3] filter-card eyebrow -> emerald (was ink-300 #8a92a0) */
}

.dropdown-menu {
	border-radius: var(--al-radius-lg);
}

/* ============================================================================
   P4-T7 — MODALS. Measured by rendering the prototype and OPENING modals (a
   closed modal computes styles but is not a measured modal — the T4b lesson).
   The design's dialog:
       container  #fff, radius 14px, shadow 0 24px 60px rgba(0,0,0,.28)
       header     padding 18px 22px, border-bottom 1px #eef0f3, title 16px/700
       body       padding 20px 22px
       footer     padding 14px 22px, border-top 1px #eef0f3, right-aligned, gap 10
       backdrop   rgba(20,25,35,.5)  (blue-ink, not Bootstrap's flat black)
   Every value already had a token — this task is wiring, not new tokens. Modal
   chrome is deliberately roomier than card chrome (18/20/14 vs the card's 18/20),
   so it uses its OWN --al-pad-modal-* tokens; do not stretch card padding over it. */
.modal-content {
	border-radius: var(--al-sys-radius-container);
	box-shadow: var(--al-sys-elevation-overlay);
	border: var(--al-sys-control-border-width) solid var(--al-sys-color-border);
}

.modal-header {
	padding: var(--al-sys-space-200) var(--al-sys-space-300);
	border-bottom: var(--al-sys-control-border-width) solid var(--al-sys-color-border);
}

.modal-title {
	font-size: var(--al-sys-font-size-400);
	font-weight: var(--al-fw-bold);
	color: var(--al-text-strong);
}

.modal-body {
	padding: var(--al-sys-space-200) var(--al-sys-space-300);
}

/* A HEADING INSIDE A DIALOG MUST NOT OUTRANK THE DIALOG'S OWN TITLE. Bootstrap's
   h1-h6 are page-scale, so "Metrc Endpoints Called:" came out 20px/500 under a
   17px/700 title — the section label read as the more important of the two, and it
   was lighter as well as bigger. Inside a modal these step DOWN from the title, and
   they are section labels, so they are set at the heading weight rather than the
   500 bootstrap gives them.

   Scoped to .modal-body so it cannot reach a heading on the page behind it. */
.modal-body h1,
.modal-body h2,
.modal-body h3,
.modal-body h4,
.modal-body h5,
.modal-body h6 {
	font-size: var(--al-sys-font-size-300);
	font-weight: var(--al-sys-font-weight-heading);
	line-height: var(--al-sys-line-height-heading);
	color: var(--al-sys-color-text-primary);
}

/* Bootstrap's rule is a raw rgba(0,0,0,.1); ours is the hairline every other
   divider in the app uses, so a rule inside a dialog matches the one under its
   header. */
hr {
	border-top-color: var(--al-sys-color-border);
}

/* .text-muted was still bootstrap's #6c757d — a warm grey next to the blue-ink
   secondary every label, hint and caption in the app already uses. 74 uses across
   34 views, all of them meaning the same thing, all of them the wrong grey.

   THE !important IS THE UTILITY'S OWN. Bootstrap ships .text-muted with it, so
   nothing short of !important replaces it, and scoping under an ancestor cannot.
   This is not beating a rule on specificity — it is the same utility in our colour. */
.text-muted {
	color: var(--al-sys-color-text-secondary) !important;
}

.modal-footer {
	padding: var(--al-sys-space-150) var(--al-sys-space-300);
	border-top: var(--al-sys-control-border-width) solid var(--al-sys-color-border);
	justify-content: flex-end;
	gap: var(--al-sys-space-100);
}

/* Bootstrap spaces footer children with per-child margins; the design uses a
   flex gap. Zero the child margins so the two do not compound into uneven gaps. */
.modal-footer > * {
	margin: 0;
}

/* The design's scrim is a dark blue-ink (--al-scrim = rgba(20,25,35,.5)), not
   Bootstrap's flat black. Set the colour (its own .5 alpha carries the dimming)
   and pin .show to opacity 1 so Bootstrap's opacity:.5 does not halve the alpha
   to ~.25 and wash the scrim out. */
.modal-backdrop {
	background-color: var(--al-sys-color-scrim);
}

.modal-backdrop.show {
	opacity: 1;
}

/* Kendo's Window and Dialog are the same shell as a Bootstrap modal, so they take the
   same container radius and the same scrim rather than the vendor's own. */
.k-window,
.k-dialog {
	border-radius: var(--al-sys-radius-container);
}
.k-overlay {
	background-color: var(--al-sys-color-scrim);
	opacity: 1;
}

/* Centre every modal vertically. The design centres its dialogs; Bootstrap
   top-aligns by default. Jeff's rule: centre as the general default, revisit
   only if a large modal reads worse centred.

   >> WHY NOT Bootstrap's .modal-dialog-centered. <<
   That relies on `min-height: 100%` on .modal-dialog resolving against the full-
   height .modal. Several config modals wrap .modal-dialog in a <form>
   (e.g. config_templates/inventory/brand.html:46), and the block <form> is only
   as tall as its content, so `100%` collapses and the dialog sticks to the top —
   even though the markup already carries .modal-dialog-centered. Centring on the
   .modal itself is wrapper-proof: whatever the single child is (.modal-dialog or
   a <form> around it) gets centred as a unit.

   `safe center` keeps it scroll-safe: when a modal is taller than the viewport it
   falls back to top-aligned so the header can't be clipped above the scroll,
   which plain `center` would do. Chrome (the app's target) supports it. */
.modal.show {
	display: flex !important;
	align-items: safe center;
	justify-content: center;
}

/* ============================================================================
   P5-T1 — CONFIRMATION DIALOG.  Design: Guide §10 + Prototype:3062-3072.
   ============================================================================
   The app's Bootstrap confirm/delete modals get a scoping hook `.al-confirm`
   plus a `--danger` (delete) or `--warning` (guarded) variant, and adopt the
   design: a 46px icon-circle over a centered title, a body that NAMES the
   record, and a Cancel-first footer of two full-width buttons. Filled red is
   the confirm action for DELETES only; guarded actions use a warning icon and
   a GREEN confirm (Guide §10:234). Builds on the P4-T7 frame above.

   >> Per-modal markup the surgery (P5-T1 batches) produces:
        <div class="modal fade al-confirm al-confirm--danger">   (--warning = guarded)
          <div class="modal-dialog"><div class="modal-content">
            <div class="modal-body">
              <div class="al-confirm-icon"><span class="material-symbols-outlined">delete</span></div>
              <div class="al-confirm-title">Delete this discount?</div>
              <div class="al-confirm-text"><b>{{ name }}</b> will be permanently removed. This can't be undone.</div>
            </div>
            <div class="modal-footer">
              <button type="button" class="al-confirm-btn al-confirm-cancel" data-dismiss="modal">Cancel</button>
              <button type="submit" class="al-confirm-btn al-confirm-action">Delete</button>
            </div>
          </div></div>
        </div>
   Confirm buttons carry `.al-confirm-btn` (NOT `.btn`) so button geometry does
   not double-apply. --warning uses the `warning` icon + verb-specific label. */
.al-confirm .modal-dialog {
	max-width: 384px;
}
/* a confirm that has to SHOW something -- a diff, a list of what will change -- rather than
   just ask. 384px is right for a question and wrong for two columns of evidence. */
.al-confirm--wide .modal-dialog {
	max-width: 720px;
}

/* Clip children to the frame's rounded corners. `.modal-content` carries the
   radius but no overflow, so an opaque per-page `.modal-body{background:#fff}`
   (left over in note.css / file.css from the old modals) paints a square that
   bleeds over the rounded top corners. Clipping here makes the frame win. */
.al-confirm .modal-content {
	overflow: hidden;
}

/* the design confirm has no header/X — icon + title live in the centered body */
.al-confirm .modal-header {
	display: none;
}

.al-confirm .modal-body {
	padding: 24px 24px 18px;
	text-align: center;
}

.al-confirm-icon {
	width: 46px;
	height: 46px;
	margin: 0 auto 14px;
	border-radius: 50%;
	display: flex;
	align-items: center;
	justify-content: center;
}

.al-confirm-icon .material-symbols-outlined {
	font-size: var(--al-fs-4xl, 24px);
}

.al-confirm--danger .al-confirm-icon {
	background-color: var(--al-danger-bg);
}
.al-confirm--danger .al-confirm-icon .material-symbols-outlined {
	color: var(--al-danger-fg);
}

.al-confirm--warning .al-confirm-icon {
	background-color: var(--al-warning-bg);
}
/* A FILL RUNG IS NOT AN INK, AND FOR TWO WEEKS THIS RULE SAID SO TO NOBODY. [ALLM-7295]
   The glyph takes --al-dialog-tone from recipe-controls.css:11431, and for warning that is
   amber-500, a rung meant for a fill or an edge. Measured on the real #timeoutModal in
   views/account/login.njk, which needs no session: #c38313 on #fdf3e3 is 2.91:1, under even
   the 3:1 a glyph has to clear.

   THE FIRST FIX NEVER APPLIED, AND THE SELECTOR IS WHY. It was written .al-confirm--warning
   with two dashes; every dialog in views/ spells it .al-confirm-warning with one, and the
   recipe defines the one-dash name at recipe-controls.css:11503. A two-dash selector cannot
   match a one-dash class, so the ticket reported 2.91 fixed to 5.51 while the page went on
   reading 2.91. Corrected here and re-measured on the same element: 5.51:1.

   ONLY WARNING IS TOUCHED, because only warning fails. Measured on the same dialog by
   swapping its tone class: info 4.95:1 and danger 4.47:1 both clear 3:1 on the tone alone.
   The neighbouring --warning/--danger/--alert rules in this file are dead for the same
   spelling reason, and correcting them would change two tones that pass today. Left alone
   deliberately; the ground here still comes from the recipe's tone-tint, which is what the
   5.51 was measured against.

   DARK IS NOT ANSWERED HERE. .al-on-dark .al-confirm--warning at line 3728 carries the same
   two-dash spelling, so the dark pairing ALLM-7292 recorded as settled cannot be applying
   either. That wants measuring on a surface that genuinely renders dark, not on this one. */
.al-confirm-warning .al-confirm-icon .material-symbols-outlined {
	color: var(--al-warning-fg);
}

/* The third dialog variant: alert/info, a single acknowledge,
   recipe Modals #8. CSS-ready; renders once a neutral prompt adopts .al-confirm--alert. */
.al-confirm--alert .al-confirm-icon {
	background-color: var(--al-info-bg, #eaf1fb);
}
.al-confirm--alert .al-confirm-icon .material-symbols-outlined {
	color: var(--al-info-fg, #2a6fdb);
}

.al-confirm-title {
	font-size: var(--al-sys-font-size-300);
	font-weight: var(--al-fw-bold);
	color: var(--al-text-strong);
	margin-bottom: 7px;
}

.al-confirm-text {
	font-size: 13.5px;
	line-height: 1.55;
	color: var(--al-text-muted);
}

/* the bold record name the dialog always states */
.al-confirm-text b {
	color: var(--al-text-body);
}

/* footer: no divider, two full-width buttons, Cancel first */
.al-confirm .modal-footer {
	padding: 0 24px 22px;
	border-top: none;
	flex-wrap: nowrap;
	gap: 10px;
}

/* Scoped `.al-confirm .modal-footer …` (0,0,3,0) so per-page modal CSS — e.g.
   customer/file.css's `.modal-footer button:first-of-type{background:#880000}`
   positional overrides left over from the old modals — can't repaint these. */
.al-confirm .modal-footer .al-confirm-btn {
	/* [PASS6-F13] GEOMETRY REMOVED — padding, font-size, font-weight and radius now
	   come from the shared tier rule above, which these selectors joined. What is left
	   is genuinely dialog-specific: the footer stretches its two buttons to fill.
	   Leaving even one geometry value here would silently re-fork the system, because
	   this rule is (0,3,0) and sits later in the file. */
	flex: 1;
	width: auto;
	margin: 0;
	font-family: inherit;
	cursor: pointer;
}
/* .al-confirm-btn is not a .btn, so the ring the button rules give does not reach it;
   button ring never applied (recipe Modals #6). Keyboard only, for the reason above:
   with :focus in the list, clicking Delete ringed the button under the pointer. */
.al-confirm .modal-footer .al-confirm-btn:focus {
	outline: none;
}
.al-confirm .modal-footer .al-confirm-btn:focus-visible {
	outline: none;
	box-shadow: var(--al-ring-focus);
}

/* [P9-BTN7] Cancel folds onto the standard Secondary (recipe #8): emerald
   outline, was a grey outline (text-body / border-input). Hover = emerald-100
   ghost fill, matching .btn-secondary (BTN2). */
.al-confirm .modal-footer .al-confirm-cancel {
	color: var(--al-accent-primary);
	background-color: var(--al-surface-card);
	border: 1px solid var(--al-accent-primary);
}
.al-confirm .modal-footer .al-confirm-cancel:hover {
	color: var(--al-accent-primary);
	background-color: var(--al-accent-primary-tint);
	border-color: var(--al-accent-primary);
}

/* Third, negative-but-not-dismiss action for 3-outcome confirms (approve / DENY /
   cancel — the admin email-change approval). Red outline: distinct from the grey
   Cancel outline and the filled action, fills red on hover. Only 3-button confirms
   use it; two-button confirms never render it. */
.al-confirm .modal-footer .al-confirm-deny {
	color: var(--al-danger-solid);
	background-color: var(--al-surface-card);
	border: 1px solid var(--al-danger-solid);
}
.al-confirm .modal-footer .al-confirm-deny:hover {
	color: var(--al-accent-on-primary);
	background-color: var(--al-danger-solid);
	border-color: var(--al-danger-solid);
}

/* the confirm action — RED filled for deletes, GREEN filled for guarded */
.al-confirm .modal-footer .al-confirm-action {
	/* THE FILL BELONGS ON THE BASE. It only ever lived on the --danger and --warning
	   variants, and every confirm in the app had one -- so the first plain .al-confirm
	   rendered white text on the framework's #f0f0f0 and the primary button was
	   unreadable. A variant re-points this; it does not supply it. */
	background: var(--al-accent-primary);
	color: var(--al-accent-on-primary);
	border: 1px solid transparent;
}
.al-confirm--danger .modal-footer .al-confirm-action {
	background-color: var(--al-danger-solid);
	border-color: var(--al-danger-solid);
}
.al-confirm--danger .modal-footer .al-confirm-action:hover {
	background-color: var(--al-danger-solid-hover);
	border-color: var(--al-danger-solid-hover);
}
.al-confirm--warning .modal-footer .al-confirm-action {
	background-color: var(--al-accent-primary);
	border-color: var(--al-accent-primary);
}
.al-confirm--warning .modal-footer .al-confirm-action:hover {
	background-color: var(--al-accent-primary-hover);
	border-color: var(--al-accent-primary-hover);
}

/* ============================================================================
   P4-T9 — ag-GRID CHROME.  Renders on the orders + cash grids (16-20 mounts).
   ============================================================================
   Measured: the design's data table has a muted-grey UPPERCASE header band over
   a hairline, and a 13px/400 body. ag-Grid v25 ships NO --ag-* custom properties
   (those arrived in v27), so every value is a hand-authored override.

       header  bg #f8f9fb, text #8a92a0 11px/700 uppercase +0.05em, border #e4e7ec
       body    13px/400 #161b24, row border #eef0f3, selected row #e9f4ef
   Ours was dark 13px non-uppercase header, 14px/500 body — restated to the above.

   >> DOUBLED CLASS IS DELIBERATE, NOT A TYPO. ag-theme-alpine.min.css loads
   AFTER this file (T0 put ag-Grid last on both layouts for consistency), so a
   plain `.ag-theme-alpine .ag-header-cell` (0,2,0) would lose to the vendor on
   load order. `.ag-theme-alpine.ag-theme-alpine ...` is (0,3,0) — it beats the
   vendor rule without re-opening the load order or reaching for !important.

   Row HEIGHT is left alone — ag-Grid sets it inline from a JS option, not CSS;
   changing it here is fragile and is a functional concern, not chrome.
   Cell horizontal padding is also left to the page (order_status.css pins it
   with !important); this task is colour/type/border, not spacing. */
.ag-theme-alpine.ag-theme-alpine .ag-header {
	background-color: var(--al-surface-sunken);
	border-bottom: 1px solid var(--al-border-default);
}

.ag-theme-alpine.ag-theme-alpine .ag-header-cell {
	/* THE HEADER IS SENTENCE CASE, the same call the Kendo block below already carries:
	   13px / 600 / secondary ink, which is what .al-grid and .al-table say. This block held
	   11px / 700 / uppercase at 0.05em in the faint ink, so two of the product's three grid
	   families disagreed with the third and with the recipe. Values are read off the sys
	   tokens rather than restated as numbers, so they cannot drift from .al-grid's. */
	color: var(--al-sys-color-text-secondary);
	font-size: var(--al-sys-font-size-200);
	font-weight: var(--al-sys-font-weight-control);
	letter-spacing: normal;
	text-transform: none;
}

.ag-theme-alpine.ag-theme-alpine .ag-row {
	color: var(--al-text-strong);
	font-size: var(--al-fs-base);
	font-weight: var(--al-fw-regular);
	border-bottom: 1px solid var(--al-surface-nested);
}

.ag-theme-alpine.ag-theme-alpine .ag-row-selected,
.ag-theme-alpine.ag-theme-alpine .ag-row-selected .ag-cell {
	background-color: var(--al-accent-primary-tint);
}

/* ============================================================================
   P4-T10 — BOOTSTRAP .table CHROME.  338 tables, on grow / cash / process / …
   ============================================================================
   Same design table chrome as ag-Grid (T9): light muted UPPERCASE header band,
   13px/400 body, hairline row borders. Ours had a DARK header (white on #343a40
   via .thead-dark) and 500-weight body. Bootstrap loads before this file, so
   plain selectors win — no doubled-class needed here (unlike ag-Grid).

   >> THE .table-dark ROWS ARE PROTECTED. The app uses `<tr class="table-dark">`
   for dark emphasis rows (9 in views/). Setting body text to dark ink globally
   would paint dark-on-dark and make them unreadable, so every body rule is
   scoped `tr:not(.table-dark)`. Header dark (.thead-dark / thead.table-dark) IS
   overridden to the light band — the design has no dark headers. */
/* `.table .thead-dark th` is included because Bootstrap's own rule for the dark
   header is (0,2,1) and beats a plain `.table thead th` (0,1,2) on the colour +
   background — caught by the gate: grow's dark header took the uppercase/size but
   kept its white-on-#343a40. Matching that selector (and loading after Bootstrap)
   wins it back. */
.table thead th,
.table .thead-dark th,
.table thead.table-dark th {
	/* Sentence case, off the same three sys tokens as ag-Grid above and Kendo below. One
	   look across all three grid families and the recipe, in one place each. */
	color: var(--al-sys-color-text-secondary);
	background-color: var(--al-surface-sunken);
	font-size: var(--al-sys-font-size-200);
	font-weight: var(--al-sys-font-weight-control);
	letter-spacing: normal;
	text-transform: none;
	border-top: 0;
	/* border-color as well as the bottom edge: this block absorbed a second, later
	   `.table thead th` rule that restated the whole band and recoloured Bootstrap's 2px
	   bottom border. Same selector, same intent, two copies free to drift -- and they had,
	   which is how a case fix here would have been silently overruled by the copy. */
	border-color: var(--al-border-default);
	border-bottom: 1px solid var(--al-border-default);
}

/* neutralise the dark thead's own element background for the band to read clean */
.table thead.thead-dark,
.table thead.table-dark {
	background-color: var(--al-surface-sunken);
}

/* body — normal rows only; .table-dark emphasis rows keep their own colours */
.table tbody tr:not(.table-dark) > td,
.table tbody tr:not(.table-dark) > th {
	border-top: 1px solid var(--al-border-default); /* [P9-TBL-HAIRLINE-ADM] one hairline #e4e7ec (was #eef0f3) */
}

.table tbody tr:not(.table-dark) > td {
	color: var(--al-text-strong);
	font-size: var(--al-fs-base);
	font-weight: var(--al-fw-regular);
}

.table-hover tbody tr:not(.table-dark):hover,
.table-hover tbody tr:not(.table-dark):hover > td {
	background-color: var(--al-surface-sunken); /* [P9-TBL-SEL-ADM-CORE] neutral hover #f8f9fb (was emerald-100 == selected) */
}

/* ============================================================================
   P4-T11 — KENDO GRID CHROME.  The tar pit, last. 9 grids on grow/cash/process/
   customer, ~7,350 vendor rule blocks, ZERO custom properties.
   ============================================================================
   Same design table chrome as ag-Grid (T9) and Bootstrap tables (T10) — one
   consistent data-table look across all three grid systems:
       header  #f8f9fb band, #8a92a0 11px/700 uppercase +0.05em, #e4e7ec hairline
       body    13px/400 #161b24, #eef0f3 row border
   Kendo shipped a #f5f5f5 header, #333 13px/500 body, and a #f5f5f5 zebra.

   >> NO ZEBRA, on purpose. Kendo stripes .k-alt at #f5f5f5. ag-Grid and the
   Bootstrap tables are un-striped (hairline borders do the row separation), so
   the .k-alt stripe is removed here for consistency across all three. If a dense
   grid later needs a scan aid, a far fainter --al-gray-50 stripe is the move,
   not Kendo's heavy grey.

   Kendo CSS loads BEFORE this file (T0 order), so plain selectors win on load
   order — no doubled-class needed, unlike ag-Grid.

   Grid FILTER cells and row HEIGHT are left to Kendo: T4c already excluded
   grid-embedded fields (`:not(.k-grid *)`), and height is Kendo's own metric.
   This is colour / type / border only. */
/* THE THIRD SELECTOR IS THE ONE THAT DOES ANYTHING. kendo.common declares
   `.k-filter-row th, .k-grid-header th.k-header { font-weight: 400 }`, which is (0,2,1)
   against this rule's (0,2,0), so the weight below was silently losing and the headers
   rendered regular while every other property here applied. Measured 400 on the Users grid.
   Matching Kendo's selector length is what lets later source order decide it — the same
   lesson the .btn tiers in this file already carry. */
/* THE VENDOR FIELD CHROME IS THREE LITERALS, AND ALL THREE OUTLIVE A THEME.

   bootstrap `.form-control` is #fff on #495057, its `.dropdown-menu` is #fff on #212529,
   and Kendo's `.k-input` is #fff on #333 with a #ccc edge. On a dark ground each becomes
   a white slab -- the ui-select popup on Item detail is white rows under white text, and
   a Kendo picker's selected value measured 1.37:1.

   Repointed at the recipe roles they were imitating. The GROUNDS are a no-op on light:
   surface-control and surface-raised are both white there, which is exactly what the
   vendor was painting. The INKS move on light, from three vendor greys to the recipe's
   one ink, which is the direction every conformed field has already gone. */
.form-control {
	background-color: var(--al-sys-color-surface-control);
	color: var(--al-sys-color-text-primary);
}

/* AND THE FOCUS STATE, WHICH IS THE HALF THAT WAS MISSING. [ALLM-7428] The rule above is
   (0,1,0) and bootstrap.min.css carries `.form-control:focus { background-color: #fff }` at
   (0,2,0), so on any focused field the vendor won and the control painted white -- in dark
   mode, a white box among dark ones. It went unseen because almost nothing here is focused
   while you look at it. The exception is a ui-select: opening one moves focus into its
   search input, so it is the one control that is ALWAYS focused when you are looking at it,
   and it was the only one reported.

   Measured on the running admin in dark, on the State field of Editing _Testing Location:
   the open search input read rgb(255,255,255) with rgb(73,80,87) ink while every sibling
   field composited to rgb(13,17,23) with white ink. After: transparent over the same dark
   ground, white ink, indistinguishable from its neighbours.

   .btn-default-focus is the same gap wearing different clothes: ui-select's own stylesheet
   paints the CLOSED control #EBEBEB while it holds focus, which is the pale grey in the
   second screenshot on that ticket. Both are the vendor answering a state our theme only
   answered at rest. */
.form-control:focus {
	background-color: var(--al-sys-color-surface-control);
	color: var(--al-sys-color-text-primary);
}
.btn-default-focus {
	background-color: var(--al-sys-color-surface-control);
	color: var(--al-sys-color-text-primary);
	border-color: var(--al-sys-color-border-control);
}
.dropdown-menu {
	background-color: var(--al-sys-color-surface-raised);
	color: var(--al-sys-color-text-primary);
	border-color: var(--al-sys-color-border);
}
.k-input,
.k-widget.k-input,
.k-autocomplete,
.k-picker {
	background-color: var(--al-sys-color-surface-control);
	color: var(--al-sys-color-text-primary);
	border-color: var(--al-sys-color-border-control);
}
/* AND AGAIN FOR EVERY STATE, because the rule above only wins while nobody is using the control.

   Kendo paints that same white FOUR times, and the rule above is only the first of them:

       .k-input, .k-widget.k-input                             (0,1,0)   the rule above
       .k-input.k-hover,  .k-input:hover,  .k-widget...        (0,2,0)   the two below
       .k-input.k-focus,  .k-input:focus,  .k-widget...        (0,2,0)
       .k-input:focus-within, .k-widget.k-input:focus-within   (0,2,0)

   So the re-point above holds at rest and loses the instant anyone touches the control. On a
   dark ground that is a white slab appearing under white text exactly when someone is looking
   at it: the Retail Areas multiselect on Config > Sell > Terminals measured 1:1 on its own
   chips, and the vendor combobox on /inventory/add does the same. Matching the vendor's chain
   is what lets source order decide it, the same trick the .btn tiers and the grid header in
   this file already use.

   >> ENUMERATE ALL FOUR, WHICH IS THE PART I GOT WRONG. The first version of this covered
   focus and focus-within and stopped, because those were the states I happened to be driving.
   Hover shipped still white and Jeff caught it on the screen. Grep the vendor sheet for every
   rule that sets the property, then cover the list -- do not fix the states you noticed.

   >> AND THE ROOTS ARE A SECOND AXIS. [ALLM-7161] All four states were enumerated and
   `.k-picker` was not: it is named in the resting rule above and was missing from both of
   these, so a DropDownList was correct until somebody touched it and then took Kendo's
   `.k-picker.k-hover { color: #333 }`. On the Items pager in dark the page-size value
   measured 17.26:1 at rest and 1.37:1 with its list open -- the number you are changing
   FROM disappears at the moment you go to change it. Cover the vendor chain on both axes:
   every state, and every root the vendor applies the property to. */
.k-input.k-focus,
.k-input:focus,
.k-input:focus-within,
.k-widget.k-input.k-focus,
.k-widget.k-input:focus,
.k-widget.k-input:focus-within,
.k-picker.k-focus,
.k-picker:focus,
.k-picker:focus-within,
.k-widget.k-picker.k-focus,
.k-widget.k-picker:focus,
.k-widget.k-picker:focus-within {
	background-color: var(--al-sys-color-surface-control);
	color: var(--al-sys-color-text-primary);
}
/* HOVER IS ITS OWN RULE so that it can carry the edge without touching the focused edge.
   Kendo's hover also darkens the border to a literal #aeaeae, and the recipe's answer to an
   input's hover is that there is not one: `.al-input:hover` does not exist. So hover holds the
   resting edge and changes nothing, which is the same call the grid header got under
   ALLM-7134 for the same reason -- conform to what the recipe already decided rather than
   invent a state for a vendor control that the app's own control does not have. */
.k-input.k-hover,
.k-input:hover,
.k-widget.k-input.k-hover,
.k-widget.k-input:hover,
.k-picker.k-hover,
.k-picker:hover,
.k-widget.k-picker.k-hover,
.k-widget.k-picker:hover {
	background-color: var(--al-sys-color-surface-control);
	color: var(--al-sys-color-text-primary);
	border-color: var(--al-sys-color-border-control);
}
/* the selected value and the placeholder inside those, which carry their own vendor ink */
.k-input-value-text,
.k-input-inner,
.k-input-inner::placeholder {
	color: inherit;
}

/* AND THE TRIGGER BUTTON, which carries its own vendor GROUND rather than its own ink.

   The block above re-points the ink of the input's children and the block above that
   re-points the input's ground in all four of its states. The button that opens the list is
   neither: it is a <button class="k-input-button k-button-solid-base"> INSIDE the field, and
   kendo.bootstrap.min.css gives .k-button-solid-base its own `background-color:#fff`. Our
   .k-button-solid-base rule up in the button section re-points only `color`. So on dark the
   glyph went to white and the ground stayed white -- measured 1:1, an invisible chevron in a
   white slab stuck to the end of every combobox. Five of them on Add Inventory alone.

   Transparent rather than a role, because the field behind it is already painted and a
   second opaque ground inside one control is what made this visible in the first place. It
   is also the vendor's own answer: six of the seven bootstrap themes in /vendors ship
   `.k-picker .k-input-button { background: 0 0 }` and the one we load is the only one that
   omits it. That rule would not have been enough here anyway -- a ComboBox is .k-input, not
   .k-picker, so both roots are named.

   All three states the vendor sets, not just the resting one. Rule 7135 was reopened for
   fixing three of kendo's four .k-input rules and stopping. */
/* THE BUSY STATE WAS ANNOUNCED AND NEVER DRAWN.

   Kendo already does the hard half: on a serverFiltering picker it sets
   aria-busy="true" on the input while the request is in flight, and swaps the trigger
   icon to .k-i-loading. It then paints that state as `background-image:
   url(Bootstrap/loading.gif)` -- a 10px raster, on the same element whose ::before
   still draws the chevron glyph. So the gif renders UNDERNEATH the chevron and the
   user sees nothing change. Measured mid-flight in both themes: aria-busy true, no
   visible indicator. 86 server-filtered pickers across 46 files.

   That is the recipe's own bargain inverted. The button's busy state exists so that
   "you cannot make it look like it is thinking without also telling a screen reader it
   is thinking"; here the screen reader is told and the screen says nothing.

   THE RING IS THE BUTTON'S, NOT A NEW ONE. Same construction as .al-btn[aria-busy], same
   --al-sys-spinner-ring-width, and al-spin is REUSED rather than re-declared -- the
   recipe says out loud that two identical @keyframes blocks are how a spinner and a
   progress ring end up at different speeds. currentColor, so it follows the trigger ink
   that the block below hands back to the theme, and no literal enters this file.

   Keyed off the vendor's own .k-i-loading rather than [aria-busy], because the attribute
   sits on the input and the icon is its sibling; matching the class Kendo already
   toggles keeps this to one selector and no :has().

   IT HAS TO UNDO OUR OWN CARET FIRST, and that is the part that is not obvious. The
   block further down dresses this same element as the recipe's chevron: 10px, painted
   with background-color and clipped to shape by a MASK. A mask clips the border too, so
   a ring drawn here without clearing it comes out as a chevron-shaped smear. The gif was
   never the only thing in the way.

   The selector mirrors that block's four families and adds .k-i-loading, because the
   caret rule is (0,4,0) and anything shorter loses to it silently -- the failure mode is
   a rule that reads correct, applies as written, and never wins. Back to the vendor's own
   16px slot, since 2px of ring on a 10px caret is a third of the circle. */
.k-combobox:not(.k-grid *) .k-input-button .k-icon.k-i-loading,
.k-dropdownlist:not(.k-grid *) .k-input-button .k-icon.k-i-loading,
.k-dropdowntree:not(.k-grid *) .k-input-button .k-icon.k-i-loading,
.k-multiselect:not(.k-grid *) .k-input-button .k-icon.k-i-loading {
	box-sizing: border-box;
	width: var(--al-sys-icon-size-md);
	height: var(--al-sys-icon-size-md);
	background-color: transparent;
	background-image: none;
	-webkit-mask: none;
	mask: none;
	border: var(--al-sys-spinner-ring-width) solid var(--al-select-caret, var(--al-sys-color-text-muted));
	border-right-color: transparent;
	border-radius: 50%;
	animation: al-spin var(--al-sys-motion-duration-loop) linear infinite;
}

.k-input .k-input-button,
.k-picker .k-input-button,
.k-input .k-input-button:hover,
.k-picker .k-input-button:hover,
.k-input .k-input-button.k-hover,
.k-picker .k-input-button.k-hover,
.k-input .k-input-button:active,
.k-picker .k-input-button:active,
.k-input .k-input-button.k-active,
.k-picker .k-input-button.k-active,
.k-input .k-input-button.k-selected,
.k-picker .k-input-button.k-selected {
	background-color: transparent;
}

/* A KENDO CHART IS SVG, AND ITS CHROME IS PRESENTATION ATTRIBUTES.

   I said twice that no CSS reaches this chart. That was wrong. It renders SVG and paints
   its own chrome with fill= and stroke= ATTRIBUTES, and an author stylesheet outranks a
   presentation attribute -- so a rule here reaches it, live, with no re-render when the
   theme flips.

   Matched on the literal, which is what keeps the DATA out of it: the series path carries
   the series colour, so a blanket `path { fill }` would repaint the bars. These literals
   come from the kendo.bootstrap theme pinned in /vendors, so they cannot drift without a
   deliberate Kendo upgrade; if one ever does, the chart falls back to its own white here,
   which is visible rather than broken.

   Dark-scoped because these ARE the vendor's light-theme values, not the recipe's -- an
   unscoped re-point would restyle the chart on a light ground too. */
.al-on-dark .k-chart path[fill='#fff'] {
	fill: var(--al-sys-color-surface);
}
.al-on-dark .k-chart text[fill='#232323'] {
	fill: var(--al-sys-color-text-secondary);
}
.al-on-dark .k-chart text[fill='#2b3142'] {
	fill: var(--al-sys-color-text-primary);
}
.al-on-dark .k-chart path[stroke='#dfdfdf'],
.al-on-dark .k-chart path[stroke='#8e8e8e'],
.al-on-dark .k-chart path[stroke='#000'],
.al-on-dark .k-chart path[stroke='#2b3142'] {
	stroke: var(--al-sys-color-border);
}

/* AG-GRID IS A WHOLE THEME, NOT A RULE. It draws every ground and edge from its own
   --ag-* variables and ships alpine's light values, so on a dark page it stayed a white
   slab with the recipe's light ink on top: header text at 1.53:1, cells at 1.00:1 --
   white on white, invisible. /order/order_status alone measured 54 slabs.

   Scoped to the dark ground on purpose. Every other vendor fix here is a no-op on light
   because the recipe role already resolves to the literal, but alpine's light defaults
   are NOT the recipe's values, so an unscoped re-point would restyle every ag-Grid in
   the product. ag-Grid is on its way out (Kendo grids + Bootstrap tables are the target),
   so this buys legibility on dark without chasing alpine parity. */
.al-on-dark .ag-theme-alpine {
	--ag-background-color: var(--al-sys-color-surface);
	--ag-foreground-color: var(--al-sys-color-text-primary);
	--ag-data-color: var(--al-sys-color-text-primary);
	--ag-secondary-foreground-color: var(--al-sys-color-text-secondary);
	--ag-disabled-foreground-color: var(--al-sys-color-text-muted);
	--ag-header-background-color: var(--al-sys-color-surface-sunken);
	--ag-header-foreground-color: var(--al-sys-color-text-secondary);
	--ag-subheader-background-color: var(--al-sys-color-surface-sunken);
	--ag-control-panel-background-color: var(--al-sys-color-surface-raised);
	--ag-odd-row-background-color: transparent;
	--ag-border-color: var(--al-sys-color-border);
	--ag-row-border-color: var(--al-sys-color-border);
	--ag-secondary-border-color: var(--al-sys-color-border);
	--ag-input-border-color: var(--al-sys-color-border-control);
	--ag-input-disabled-background-color: var(--al-sys-color-surface-sunken);
	--ag-row-hover-color: var(--al-sys-color-row-tint-selected);
	--ag-selected-row-background-color: var(--al-sys-color-row-tint-active);
	--ag-modal-overlay-background-color: var(--al-sys-color-scrim);
	--ag-alpine-active-color: var(--al-sys-color-accent-primary-line);
	--ag-chip-background-color: var(--al-sys-color-surface-sunken);
}

/* KENDO PAINTS ITS OWN GROUND AS A LITERAL, AND A LITERAL SURVIVES A THEME.

   `.k-widget { background-color: #fff }` and `.k-header { background-color: #f5f5f5 }`
   in the vendor sheet are the reason the light/dark switch left a white grid sitting on
   a dark page — the most repeated surface in the product, and the one place a vendor
   hex is loudest. Repointed at the recipe's surface roles.

   This is a no-op on a light ground: --al-sys-color-surface IS white and
   --al-sys-color-surface-sunken IS the grey the header already wears, so nothing moves
   until the ground goes dark. `.k-widget` rather than `.k-grid` on purpose — a dropdown
   popup and a calendar carry the same vendor white, and one rule is the whole class of
   problem. Both sit at (0,1,0) like the vendor's own, and this file loads after Kendo's.

   The grid's INNER grounds go transparent instead of being re-painted: the wrapper is
   only ever the ground its cells sit on, and a second opinion about that ground is how
   a translucent header rung ends up stacked on an opaque one and reads a shade off. */
.k-widget {
	background-color: var(--al-sys-color-surface);
}
.k-grid-header,
.k-grid-header-wrap,
.k-grid-footer,
.k-grid-footer-wrap,
.k-grid-content,
.k-grid-content-locked {
	background-color: transparent;
}
.k-grid-header .k-header,
.k-grid-header th,
.k-grid-header th.k-header {
	/* THE HEADER IS SENTENCE CASE, and the recipe already said so: 13px / 600 / secondary ink
	   at .al-grid and .al-table alike. This block was setting 11px / 700 / uppercase with
	   0.55px tracking in the faint ink, so the one place the design system had an opinion was
	   the one place it was overruled. Nothing else in the product shouts in caps.

	   Three selectors, because Kendo's own `.k-grid-header th.k-header` is (0,2,1). */
	font-size: var(--al-sys-font-size-200);
	font-weight: var(--al-sys-font-weight-control);
	color: var(--al-sys-color-text-secondary);
	text-transform: none;
	letter-spacing: normal;
	/* The band and its edge. Rewriting this block for the case change dropped both, and Kendo's
	   own #f5f5f5 and #ccc took over: a vendor grey where the recipe asks for its sunken
	   surface, and a darker rule than any other hairline in the product. */
	background-color: var(--al-sys-color-surface-sunken);
	border-color: var(--al-sys-color-border);
}
/* THE HEADER LINK HAS NO OPINION ABOUT ITS OWN INK. Kendo gives it a literal #333,
   which was invisible the moment the header band stopped being the vendor's white. The
   th above already states the ink for the whole band, so the link inherits it rather
   than carrying a second answer. Five selectors because the vendor's own reach `:link`
   and `:visited` at (0,3,0). */
.k-grid-header .k-link,
.k-grid-header .k-link:link,
.k-grid-header .k-link:visited,
.k-grid-header .k-link:hover,
.k-grid-header .k-link:active {
	color: inherit;
}
/* THE SORT MARK IS THE RECIPE'S CHEVRON, not a recoloured vendor glyph.

   Two bugs were stacked here. The selectors named `.k-sort-icon`, which THIS Kendo build
   never emits: the widget appends `<span class="k-icon k-i-sort-asc-sm">` to the header
   link, so kendo.bootstrap's own `.k-grid-header .k-i-sort-asc-sm { color: #428bca }` at
   (0,2,0) stood untouched and the sorted column carried a blue arrow. And even corrected it
   only recoloured a font glyph, so the app's mark and the design system's mark were
   different shapes.

   font-size:0 kills the ligature; the box then carries the recipe's chevron as a mask, which
   is how .al-grid-sort draws it, so both consumers render one glyph from one token. */
.k-grid-header .k-i-sort-asc-sm,
.k-grid-header .k-i-sort-desc-sm {
	font-size: 0;
	width: var(--al-sys-icon-chevron-size);
	height: var(--al-sys-icon-chevron-size);
	background: var(--al-sys-color-accent-primary-line);
	-webkit-mask: var(--al-sys-icon-chevron) center / contain no-repeat;
	mask: var(--al-sys-icon-chevron) center / contain no-repeat;
}
/* Ascending is the same chevron turned over, exactly as .al-grid-sort[data-dir='asc'] does. */
.k-grid-header .k-i-sort-asc-sm {
	transform: rotate(180deg);
}
/* The little 2 on a second sort column: a fact about the sort, not a thing you act on. */
.k-grid-header .k-link .k-sort-order {
	color: var(--al-sys-color-text-muted);
	font-size: var(--al-sys-font-size-100);
}

/* `.k-grid tr td` and not `.k-grid td`, for the same reason as the header above:
   kendo.bootstrap sets rgb(204,204,204) at (0,1,2) and this rule was (0,1,1), so every
   cell divider rendered in Kendo's grey while the header hairline above it was already
   the recipe's. One table, two different greys, which is what gave the columns their
   heavier rules. */
.k-grid tr td {
	color: var(--al-text-strong);
	font-size: var(--al-fs-base);
	font-weight: var(--al-fw-regular);
	border-color: var(--al-border-default); /* [P9-TBL-HAIRLINE-ADM] one hairline #e4e7ec (was #eef0f3) */
}

/* AND THE ROW NEEDS AN EDGE OF ITS OWN. [ALLM-7161]

   kendo.common sets `.k-grid td { border-width: 0 0 0 1px }` -- a LEFT edge and nothing
   else -- and the only rule in the vendor sheet that adds a bottom is scoped to
   `.k-pivot-layout`. So an admin grid draws five vertical rules through a row and nothing
   between that row and the next one: the eye is given every boundary except the one it
   needs to carry a name across to its quantity.

   The zebra block below turns the vendor stripe off and says why: "the #e4e7ec hairline
   dividers do the row separation". That decision was right and the hairline it defers to
   was never drawn, so the comment has been asserting a line nobody rendered. This is that
   line, and it is what .al-grid has always done -- border-bottom and border-left on every
   body cell, the same token for both, which is what stops a table's two axes disagreeing
   about their own colour.

   Width only: the colour comes from the rule above, so there is one hairline here and not
   a second description of it. tbody, so a footer or an edit form keeps the vendor's answer,
   and the last row gives its edge back to the container the way the recipe's does. Kendo's
   own `.k-grouping-row td { border-bottom-width: 0 }` is (0,2,2) against this (0,1,3) and
   still wins, which is correct -- a group header is not a row. */
.k-grid tbody tr > td {
	border-bottom-width: var(--al-sys-control-border-width);
}
.k-grid tbody tr:last-child > td {
	border-bottom-width: 0;
}

/* THE AGGREGATE ROW KEPT THE VENDOR'S GROUND AFTER WE REPLACED ITS INK. [ALLM-7192]

   `.k-widget` above turned the grid's own white off, and `.k-grid-footer` with it -- but
   `.k-grid-footer` is the wrapper DIV, and the totals live in the CELLS. kendo.bootstrap
   paints those separately, as a literal:

     .k-footer-template td, .k-group-footer td, .k-grouping-header { background: #f5f5f5 }
     .k-grouping-row td, td.k-group-cell                          { background: #f5f5f5 }

   Meanwhile `.k-grid tr td` above is (0,1,2) and hands every body cell --al-text-strong,
   which beats the vendor's own `.k-grid-footer td { color: #333 }` at (0,1,1). So we
   supply white ink, Kendo supplies a white ground, and neither rule can see the other:
   the aggregate row measured 1.09:1 on dark -- white on #f5f5f5, the numbers present and
   unreadable -- on Items > Inventory and on Retail Order History's TOTALS row.

   Pointed at the sunken surface because that is what the header band already wears, so
   one table is bracketed top and bottom by one grey instead of two. On light this moves
   #f5f5f5 to #f7f8f9, which is that same header grey and not a no-op -- small, deliberate,
   and in the direction of the table agreeing with itself. */
.k-footer-template td,
.k-group-footer td,
.k-grouping-row td,
.k-grid td.k-group-cell,
.k-grouping-header {
	background-color: var(--al-sys-color-surface-sunken);
}
/* AND THE GROUPING BAR HAS ITS OWN INK, which the cell rules above never reach: it is a
   <div>, not a td, so `.k-grid tr td` does not apply and kendo.bootstrap's `#333` on
   `.k-grouping-header` and on the group chip's `.k-link` stood. Left alone it would have
   turned invisible the moment the ground above went dark -- fixing the slab and breaking
   the sentence on it. Secondary ink, matching the header band: this is chrome telling you
   what you can do, not data. `.k-grid .k-grouping-header` for the edge, because the
   vendor's own border rule for it is (0,2,0). */
.k-grouping-header,
.k-grouping-header .k-link {
	color: var(--al-sys-color-text-secondary);
}

/* THE SAME LITERAL, A SECOND TIME, ON EIGHT MORE CONTROLS. [ALLM-7196] kendo.bootstrap
   paints this family #f5f5f5 in one declaration that has nothing to do with the grouping
   rule above:

     .k-drag-clue, .k-grid-header, .k-grouping-header, .k-header, .k-menu,
     .k-panelbar > .k-panelbar-header > .k-link, .k-progressbar, .k-state-highlight,
     .k-tabstrip, .k-tabstrip-items .k-item, .k-toolbar { background-color: #f5f5f5 }

   THE TICKET UNDERSTATED IT. It reported the toolbar as "the wrong ground, not an
   unreadable one", on the basis that the vendor's #333 stays legible on #f5f5f5. That is
   true of the vendor's own text and not of ours: the Export to Excel button on that
   toolbar takes the dark ground's white ink and measured 1:1 -- white on white, a button
   you cannot see -- on Insight > Real-Time > Current Inventory. The ink is already
   theme-aware; only the ground was stuck, which is why this is a ground fix and there is
   no colour rule underneath it.

   Pointed at the sunken surface, the same rung ALLM-7192 gave the grouping bar directly
   below it, so the two bands that bracket a grid agree instead of one being a white strip
   over the other.

   CONFIRMED RENDERING: .k-toolbar and .k-grid-toolbar, measured at 1072x38 on that report
   in both themes. The rest of the list is covered without having been caught in the act --
   I crawled twelve admin pages and never saw .k-menu, .k-tabstrip, .k-progressbar,
   .k-panelbar's header link, .k-drag-clue or .k-state-highlight render at all. They share
   one declaration with the two that are confirmed broken, so leaving them out would only
   mean waiting for the screen that shows one. .k-grid-header and .k-header are already
   answered elsewhere and are not repeated here. */
.k-toolbar,
.k-grid-toolbar,
.k-menu,
.k-tabstrip,
.k-tabstrip-items .k-item,
.k-progressbar,
.k-panelbar > .k-panelbar-header > .k-link,
.k-drag-clue,
.k-state-highlight {
	background-color: var(--al-sys-color-surface-sunken);
}

/* AND THE BAND ABOVE THE TOOLBAR IS A THIRD DECLARATION AGAIN. [ALLM-7196, reopened]
   The report header on Insight > Real-Time is a Kendo AppBar, and Kendo defaults an AppBar
   to its LIGHT variant, so `.k-appbar-light` ships a hard `#fff` ground with black ink.
   Once the toolbar and the grouping bar below it went dark it was the only opaque light
   ground left on the page -- measured at 754x53, rgb(255,255,255) on rgb(0,0,0) -- which is
   what Jeff meant by "content/report header still white on dark mode". Fixing the toolbar
   made it obvious rather than causing it.

   BOTH AXES, because this one owns its ink as well as its ground. The list above re-points
   backgrounds only, which is right for controls that inherit their colour; an AppBar sets
   black explicitly, so re-pointing the ground alone would have traded a white slab for
   black text on a dark one. That is the failure this file has now met four times from the
   other direction.

   `surface-sunken` rather than the card's own ground so the header band and the grid
   toolbar under it read as one piece of chrome, which is what they are. */
.k-appbar-light {
	background-color: var(--al-sys-color-surface-sunken);
	color: var(--al-sys-color-text-primary);
}

/* AND THE BUTTONS STANDING ON IT KEEP A WHITE OF THEIR OWN. [ALLM-7196] Fixing the
   toolbar's ground did not fix the toolbar, because `.k-button-solid-base` carries its own
   `background-color:#fff` from kendo.bootstrap -- a different declaration again -- while
   the ink follows the dark ground to white. Export to Excel measured 1:1 both before and
   after the ground fix, and twenty numeric spinners on the same report measured the same.

   This is the third root of one already-known defect. The existing block further up made
   `.k-input-button.k-button-solid-base` transparent when a combobox trigger went invisible
   for exactly this reason, and it named the two roots that screen had. A toolbar button and
   a spinner are the same vendor rule on roots nobody had reached yet.

   `--al-sys-color-surface-control` rather than the transparent that block used, because
   these two are real buttons rather than a trigger inside a painted field: on dark it IS
   transparent, so the toolbar shows through and the inherited ink is readable, and on light
   it is the white they already had, so nothing moves. The edge takes the control role in
   the same breath -- it was a literal #ccc, which is the same bug one property over.

   THE GRID FILTER CELL IS THE FOURTH ROOT. [ALLM-7178] Same vendor rule again, on the
   operator button that sits beside a column filter: measured 1.00:1 on dark, a blank white
   square on the dark grid, 4 of them on Discounts, and the same literal #ccc edge. It is a
   real button standing on the grid rather than a trigger inside a painted field, so it
   takes this block's treatment and not the transparent one above.

   FOUR ROOTS IS THE POINT NOW, not the fourth fix. Each has been found by someone meeting
   it on a screen, and the list only grows in the direction of wherever a person happened to
   look. Whether `.k-button-solid-base` should simply take these two roles everywhere is a
   real question with a real blast radius, and it is Jeff's rather than mine -- raised
   rather than taken. */
.k-toolbar .k-button-solid-base,
.k-spinner-increase.k-button-solid-base,
.k-spinner-decrease.k-button-solid-base,
.k-filtercell .k-button-solid-base {
	background-color: var(--al-sys-color-surface-control);
	border-color: var(--al-sys-color-border-control);
}
.k-grid .k-grouping-header {
	border-color: var(--al-sys-color-border);
}

/* Default link colour. Bootstrap makes bare <a> #007bff blue, but the design links
   entities + content in the accent (Prototype:402 — the customer name is #157a52).
   This is intentionally LOW specificity (0,0,1): every classed colour rule beats it,
   so nav (.al-nav-*), buttons (.btn), breadcrumb (.app-breadcrumb a), grid name links
   (.check-select), and the account menu keep their own colours. Only links with no
   colour rule of their own change — Kendo-grid entity links AND Angular-bound
   `{{ }}` links (which carry the auto-added .ng-binding class, so :not([class])
   could never reach them). That's why this is a bare `a`, not a:not([class]). */
/* THE RUNG, NOT JUST THE COLOUR. --al-accent-primary is the accent as a FILL, and the
   recipe splits fill from line precisely because a fill holds its value on a dark ground
   while a line lightens. A link is a line. Pointed at the fill it stayed emerald-500 on
   slate-900 and measured 3.24:1 -- and 1.87:1 over a hovered row -- across roughly 180
   grid links, the single most clicked piece of text in the app. --al-text-link is the
   alias named for this job and already chains to the line rung, so both grounds are
   emerald-500 on light and this moves nothing there. */
a {
	color: var(--al-text-link);
}
a:hover {
	color: var(--al-accent-primary-hover);
}

/* [F4] A text action does NOT underline on hover — it darkens (Jeff's call).
   This is the exact bug he reported: "my open orders button text underlines when I
   hover but remove queue doesn't — remove queue probably has it right." The cause
   is an <a href> nested INSIDE a <Button>, so the anchor picks up Bootstrap's
   default a:hover underline while a plain button has nothing to underline. Two
   controls side by side, different behaviour, for a markup reason nobody chose.
   Real navigational links keep their underline — that is an accessibility
   affordance, and is not what this rule touches. */
.btn a:hover,
.k-button a:hover,
button a:hover {
	text-decoration: none;
}

.k-grid tr.k-alt,
.k-grid .k-alt {
	background-color: transparent;
}

/* [P9-TBL-ZEBRA-ADM] neutralize the vendor Bootstrap .table-striped zebra (core-ui
   stripes odd rows rgba(0,0,21,.05), live on 14+ views). No zebra — the #e4e7ec
   hairline dividers do the row separation (recipe Tables #4). Same selector as core-ui
   so load order wins; no !important (Kendo status-row tints are on separate tables). */
.table-striped tbody tr:nth-of-type(odd) {
	background-color: transparent;
}

.k-grid tbody tr:hover,
.k-grid tbody tr:hover td {
	background-color: var(--al-surface-sunken); /* [P9-TBL-SEL-ADM-CORE] neutral hover #f8f9fb (was emerald-100 == selected) */
}

/* AND THE HEADER ROW IS NOT A ROW. [ALLM-7134]

   The rule above is scoped to tbody, which is correct. kendo.bootstrap ships
   `.k-grid tr:hover { background-color: #ebebeb }` and does NOT scope it, so the <tr>
   inside .k-grid-header was the one row left taking a vendor literal on hover. On a
   light ground that is a faint grey nobody notices. On a dark one the band went
   near-white under secondary ink: measured rgb(204,209,217) on rgb(235,235,235), 1.3:1,
   and on every Kendo grid in admin rather than only the dashboard the ticket named.

   Transparent rather than a dark hover tint, because the recipe already answered this:
   neither .al-table nor .al-grid gives a header any hover fill, both hover tbody tr
   only, and the sort mark is the affordance. A hover state here would be one the design
   system does not have. The th keeps its own sunken band, which is what shows through.

   (0,1,2) against the vendor (0,1,1), so no !important. thead as an element selector on
   purpose: it also covers .k-table-thead in the newer Kendo DOM without naming it. */
.k-grid thead tr:hover {
	background-color: transparent;
}

/* selected rows — Kendo paints the CELLS a blue (with white text) that outweighs a
   plain override (the <tr> takes emerald but each <td> stays blue), so force the cells
   + text. Covers legacy (tr/td), modern (.k-table-row/.k-table-td), and .k-state-selected.
   On-brand emerald tint, readable ink, emerald links. */
.k-grid tr.k-selected,
.k-grid tr.k-selected > td,
.k-grid td.k-selected,
.k-grid tr.k-state-selected,
.k-grid tr.k-state-selected > td,
.k-grid td.k-state-selected,
.k-grid .k-table-row.k-selected,
.k-grid .k-table-row.k-selected > .k-table-td,
.k-grid .k-table-td.k-selected {
	background-color: var(--al-accent-primary-tint) !important;
	color: var(--al-text-body) !important;
}
/* THE SAME RUNG THE BASE `a` RULE ALREADY MOVED TO. This one kept --al-accent-primary
   when that rule was repointed, so selecting a row put the accent as a FILL back on the
   most clicked text in the app -- emerald-500, which does not lighten on a dark ground.
   Measured on /inventory/ and /inventory/batches: 8.81:1 unselected, 3.24:1 selected. */
.k-grid tr.k-selected a,
.k-grid .k-table-row.k-selected a {
	color: var(--al-text-link) !important;
}
/* [P9-TBL-SEL-ADM-CORE] 3px emerald left border on the selected row (recipe Tables #3).
   Inset box-shadow on the first cell — no layout shift, survives border-collapse. */
.k-grid tr.k-selected > td:first-child,
.k-grid tr.k-state-selected > td:first-child,
.k-grid .k-table-row.k-selected > .k-table-td:first-child {
	box-shadow: inset 3px 0 0 0 var(--al-accent-primary) !important;
}

/* ============================================================================
   P5-BTN1 — KENDO PRIMARY BUTTONS.  kendo-button / grid-toolbar buttons with
   themeColor:'primary' (e.g. the "Create Rule" button) render kendo.bootstrap's
   blue #428bca; our button reskin only ever touched Bootstrap `.btn-*`. Repoint
   the Kendo primary solid/outline/flat fills to the emerald accent so widget
   buttons match. Kendo loads before this file, so plain `.k-*` selectors win.
   ============================================================================ */
.k-button-solid-primary {
	background-color: var(--al-accent-primary);
	border-color: var(--al-accent-primary);
	color: var(--al-accent-on-primary);
}
.k-button-solid-primary.k-hover,
.k-button-solid-primary:hover,
.k-button-solid-primary.k-active,
.k-button-solid-primary.k-selected,
.k-button-solid-primary:active {
	background-color: var(--al-accent-primary-hover);
	border-color: var(--al-accent-primary-hover);
	color: var(--al-accent-on-primary);
}
.k-button-solid-primary.k-focus,
.k-button-solid-primary:focus {
	border-color: var(--al-accent-primary);
	box-shadow: var(--al-ring-focus-strong);
}
/* [PASS6-F17] `.k-selected` added. It was in the outline-primary list eight lines
   below and missing from this one, so a SELECTED flat-primary fell through to Kendo's
   own coral #ff6358 — which was shipping live on Go Console's all-orders screen. One
   word, one selector list, an off-brand colour in production. The audit predicted this
   exactly; I only had to look. */
.k-button-outline-primary,
.k-button-flat-primary,
.k-button-flat-primary.k-hover,
.k-button-flat-primary.k-active,
.k-button-flat-primary.k-selected,
.k-button-flat-primary:hover {
	color: var(--al-accent-primary);
}
.k-button-outline-primary.k-hover,
.k-button-outline-primary:hover,
.k-button-outline-primary.k-active,
.k-button-outline-primary.k-selected,
.k-button-outline-primary:active {
	background-color: var(--al-accent-primary);
	border-color: var(--al-accent-primary);
	color: var(--al-accent-on-primary);
}
.k-button-outline-primary.k-focus,
.k-button-outline-primary:focus {
	box-shadow: var(--al-ring-focus-strong);
}

/* [PASS6-F17] THE NEUTRAL KENDO BUTTONS — the other 90.
   The bridge above only ever handled `-primary`, which the comment at the top of it
   says outright: "our button reskin only ever touched Bootstrap .btn-*". Everything
   Kendo renders as base or flat-base therefore kept its stock label colour —
   #212529 on 38 buttons, #333 on 3 — and NEITHER HEX EXISTS IN tokens.css.

   That is the part that matters for theming, more than the shade being slightly off:
   a colour with no token is invisible to the system. No sweep of var(--al-*) will
   ever find it, no re-point can reach it, and a dark theme would leave these labels
   dark-on-dark. They are 90 buttons the token layer simply does not know about. */
/* [PASS7-F20] The RESTING ink for these three moved UP, to just above the .btn-*
   variant blocks. It has to be stated before them, not after — see the note there.
   The hover below stays here: at (0,2,0) it outranks every variant's resting rule, so
   its position is not load-bearing. */
.k-button-flat-base.k-hover,
.k-button-flat-base:hover,
.k-button-solid-base.k-hover,
.k-button-solid-base:hover {
	color: var(--al-text-strong);
}

/* [PASS6-F17] Kendo's radius classes were never repointed. Kendo ships
   `.k-rounded-md{border-radius:.25rem}` — 4px — so every Kendo button sat off the
   locked 8px while the app's own buttons sat on it, on the same screen.
   tokens.css collapsed every radius rung to 8px specifically so this could not
   happen; it happened anyway, in a layer the token sweep does not read.
   Won on specificity (.k-button.k-rounded-md is 0,2,0 against Kendo's 0,1,0) rather
   than !important. `.k-rounded-full` is deliberately excluded: pill icon buttons are
   an open recipe question Jeff has not been asked yet. */
.k-button.k-rounded-sm,
.k-button.k-rounded-md,
.k-button.k-rounded-lg {
	border-radius: var(--al-radius-md);
}

/* ============================================================================
   P5-T5 — TOGGLE / SWITCH.  The app's on/off toggles are kendo-switch (101 in
   markup), NOT native checkboxes — the 100 native checkboxes are grid selectors
   and permission lists that must STAY checkboxes. So this is a Track-B vendor
   recolour, not a checkbox-to-switch conversion.
   ============================================================================
   Kendo ships the on-state in BOOTSTRAP BLUE (#428bca). The design's switch is
   emerald when on, grey (#cdd2db) when off. Kendo loads before this file, so
   plain selectors win. Two thumb class names exist across the app's Kendo
   versions (.k-switch-thumb and .k-switch-handle) — target both.

   >> SIZE — now matched (resize rules below the colour rules). The earlier pass
   left it as a deviation fearing the ON knob-travel was a fragile fixed transform;
   on inspection it is `calc(1em ± …)` off the switch's OWN font-size, so dropping
   font-size to 8px resizes cleanly and the knob still lands correctly. */
.k-switch-on .k-switch-track,
.k-switch-on:hover .k-switch-track,
.k-switch-on:focus .k-switch-track {
	background-color: var(--al-accent-primary);
	border-color: var(--al-accent-primary);
}

.k-switch-off .k-switch-track,
.k-switch-off:hover .k-switch-track,
.k-switch-off:focus .k-switch-track {
	background-color: var(--al-surface-neutral-strong); /* [P9-TGL-SW1-ADM #1] OFF track #d3d8e0 (was gray-500 #cdd2db) */
	border-color: var(--al-border-input);
}

.k-switch-on .k-switch-thumb,
.k-switch-on .k-switch-handle,
.k-switch-off .k-switch-thumb,
.k-switch-off .k-switch-handle {
	background-color: var(--al-accent-on-primary);
	border-color: var(--al-accent-on-primary);
}

/* SIZE — matched to the prototype (34x20 track, 16px knob, measured off the
   hydrated Prototype, not the guide, which was wrong at 38x22/18px). Kendo sizes
   the switch in `em` off its own font-size (track 5em = 60px at the default 12px;
   thumb 2em; the off/on thumb-wrap positions are calc(1em ± …) so they FOLLOW the
   em size). font-size:8px makes the thumb 2em = 16px and recomputes the travel;
   width/height are then pinned to 34x20. Verified: knob lands 3px from each edge
   in both states, no stranding. (Loads after Kendo -> source order wins.) */
.k-switch,
.k-switch.k-switch-md {
	font-size: 8px;
	width: 34px;
	height: 20px;
}
.k-switch .k-switch-track {
	width: 34px;
	height: 20px;
	/* kendo.bootstrap paints an `inset 0 0 0 1px #ccc` ring; the prototype pill is
	   clean fill only, so drop it (visible as a faint ring on the emerald ON). */
	box-shadow: none;
}
/* The prototype toggle is a BARE pill — state reads from knob position + track
   colour, not inline text — so hide Kendo's in-pill On/Off labels (which also
   can't render legibly at 8px). */
.k-switch .k-switch-label-on,
.k-switch .k-switch-label-off {
	display: none;
}

/* A RECIPE CONTROL BUILT ON <label>. Bootstrap's reboot gives every label
   margin-bottom:.5rem, which is right for a form label above a field and wrong for a
   control that IS a label -- the segmented control's chips are 22px inside a track that
   reserved 8px of dead space under each one, so the whole thing sat high and looked
   broken. The recipe uses a label on purpose: it wraps the radio, so the whole chip is
   the hit area and the keyboard reaches it. The vendor default is the odd one out.

   THIS LIST HAS NOW GROWN TWICE for the same reason, which is the interesting part. It
   started as the segment and the checkbox; the search box turned up sitting four pixels
   above the New button beside it, and measuring that found the switch carrying the same
   eight pixels nobody had noticed. Every recipe control that IS a label lands here
   eventually, because the reboot reaches all of them and none of them states a margin of
   its own. The real answer is each control stating its own margin, which is a recipe change
   nobody has asked for yet; until then, adding a name here is the fix and forgetting to is
   the bug. */
label.al-affix,
label.al-seg-item,
label.al-check,
label.al-switch,
label.al-field {
	margin-bottom: 0;
}

/* ---- CHECKBOX AND RADIO — the recipe's box, on every native input ----
   accent-color only tinted whatever box the browser felt like drawing, so one page
   carried four different checkboxes: 13px native on the locations tab, 16px in a
   config check-row, 17px in an outputs list, and 18px in the recipe. A tint is not a
   control. This is .al-check's box — same size, same radius, same masked mark, same
   hover and focus — reproduced on a native input so every screen gets it without
   touching markup.

   THE MARK IS A MASK, not a background image, so the tick's colour is a token.

   MARGINS ARE DELIBERATELY NOT SET. The recipe centres its box on the first line of
   its own label; in the app these inputs sit in flex rows, table cells and inline
   runs that already position them, and a margin here would fight all three.

   Kendo and ag-Grid draw their own and are excluded by class.

   SO DOES THE RECIPE. .al-check and .al-seg-item style their own input, and this rule
   outranks both -- three classes against one. Harmless where the boxes agree, and not
   harmless on the segmented control, whose radio is meant to be invisible and covers
   the whole chip: the skin drew it as a real radio dot beside the label. A recipe
   control owns its own parts, so its children are excluded here. The switch is on the
   list for the same reason and reached it late: it is a checkbox by type, and this rule
   was painting it 18px square with a 5px radius while its own track token said 34x20. */
input[type='checkbox']:not(.k-checkbox):not(.k-radio):not(.ag-checkbox-input):not(.al-seg-item > *):not(.al-check > *):not(.al-switch > *),
input[type='radio']:not(.k-checkbox):not(.k-radio):not(.ag-checkbox-input):not(.al-seg-item > *):not(.al-check > *):not(.al-switch > *) {
	--al-check-size: 18px;
	--al-check-radius: 5px;
	--al-check-border-width: var(--al-sys-control-border-width);
	--al-check-border-color: var(--al-sys-color-border-control);
	--al-check-fill: var(--al-sys-color-surface-control);
	--al-check-mark: var(--al-sys-color-text-on-accent);
	--al-check-mark-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M13.2 4.3 6.5 11 2.8 7.3' fill='none' stroke='%23000' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
	--al-check-selected-fill: var(--al-sys-color-accent-primary);
	--al-check-selected-border-color: var(--al-sys-color-accent-primary);
	--al-check-hover-border-color: var(--al-sys-color-accent-primary-line);

	appearance: none;
	-webkit-appearance: none;
	box-sizing: border-box;
	flex: none;
	position: relative;
	width: var(--al-check-size);
	height: var(--al-check-size);
	vertical-align: -4px; /* the inline case: "All Locations" sits beside its box, not on it */
	border: var(--al-check-border-width) solid var(--al-check-border-color);
	border-radius: var(--al-check-radius);
	background: var(--al-check-fill);
	cursor: pointer;
}
input[type='radio']:not(.k-checkbox):not(.k-radio):not(.ag-checkbox-input):not(.al-seg-item > *):not(.al-check > *):not(.al-switch > *) {
	border-radius: 50%;
}

/* THE TOUCH TARGET is bigger than the box. 18px is the right size and the wrong
   target, so the box grows an invisible one around itself and nothing moves. */
input[type='checkbox']:not(.k-checkbox):not(.k-radio):not(.ag-checkbox-input):not(.al-seg-item > *):not(.al-check > *):not(.al-switch > *)::before,
input[type='radio']:not(.k-checkbox):not(.k-radio):not(.ag-checkbox-input):not(.al-seg-item > *):not(.al-check > *):not(.al-switch > *)::before {
	content: '';
	position: absolute;
	inset: -3px;
}

input[type='checkbox']:not(.k-checkbox):not(.k-radio):not(.ag-checkbox-input):not(.al-seg-item > *):not(.al-check > *):not(.al-switch > *)::after,
input[type='radio']:not(.k-checkbox):not(.k-radio):not(.ag-checkbox-input):not(.al-seg-item > *):not(.al-check > *):not(.al-switch > *)::after {
	content: '';
	position: absolute;
	inset: -1px;
	background: var(--al-check-mark);
	-webkit-mask: var(--al-check-mark-image) center / contain no-repeat;
	mask: var(--al-check-mark-image) center / contain no-repeat;
	opacity: 0;
}
input[type='checkbox']:not(.k-checkbox):not(.k-radio):not(.ag-checkbox-input):not(.al-seg-item > *):not(.al-check > *):not(.al-switch > *):checked::after,
input[type='checkbox']:not(.k-checkbox):not(.k-radio):not(.ag-checkbox-input):not(.al-seg-item > *):not(.al-check > *):not(.al-switch > *):indeterminate::after,
input[type='radio']:not(.k-checkbox):not(.k-radio):not(.ag-checkbox-input):not(.al-seg-item > *):not(.al-check > *):not(.al-switch > *):checked::after {
	opacity: 1;
}
input[type='checkbox']:not(.k-checkbox):not(.k-radio):not(.ag-checkbox-input):not(.al-seg-item > *):not(.al-check > *):not(.al-switch > *):checked,
input[type='checkbox']:not(.k-checkbox):not(.k-radio):not(.ag-checkbox-input):not(.al-seg-item > *):not(.al-check > *):not(.al-switch > *):indeterminate,
input[type='radio']:not(.k-checkbox):not(.k-radio):not(.ag-checkbox-input):not(.al-seg-item > *):not(.al-check > *):not(.al-switch > *):checked {
	--al-check-fill: var(--al-check-selected-fill);
	--al-check-border-color: var(--al-check-selected-border-color);
}
/* INDETERMINATE is a real state here, not a nicety: the "All Locations" master box
   uses it to say "some of them", and the tooltip beside it documents the dash. */
input[type='checkbox']:not(.k-checkbox):not(.k-radio):not(.ag-checkbox-input):not(.al-seg-item > *):not(.al-check > *):not(.al-switch > *):indeterminate {
	--al-check-mark-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3.6 8h8.8' fill='none' stroke='%23000' stroke-width='2.2' stroke-linecap='round'/%3E%3C/svg%3E");
}
input[type='radio']:not(.k-checkbox):not(.k-radio):not(.ag-checkbox-input):not(.al-seg-item > *):not(.al-check > *):not(.al-switch > *):checked {
	--al-check-mark-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Ccircle cx='8' cy='8' r='3.4' fill='%23000'/%3E%3C/svg%3E");
}

input[type='checkbox']:not(.k-checkbox):not(.k-radio):not(.ag-checkbox-input):not(.al-seg-item > *):not(.al-check > *):not(.al-switch > *):not(:disabled):hover,
input[type='radio']:not(.k-checkbox):not(.k-radio):not(.ag-checkbox-input):not(.al-seg-item > *):not(.al-check > *):not(.al-switch > *):not(:disabled):hover {
	--al-check-border-color: var(--al-check-hover-border-color);
}
input[type='checkbox']:not(.k-checkbox):not(.k-radio):not(.ag-checkbox-input):not(.al-seg-item > *):not(.al-check > *):not(.al-switch > *):focus-visible,
input[type='radio']:not(.k-checkbox):not(.k-radio):not(.ag-checkbox-input):not(.al-seg-item > *):not(.al-check > *):not(.al-switch > *):focus-visible {
	outline: var(--al-sys-focus-ring-width) solid transparent;
	outline-offset: var(--al-sys-focus-hairline-width);
	box-shadow:
		0 0 0 var(--al-sys-focus-hairline-width) var(--al-sys-color-focus-hairline-light),
		0 0 0 calc(var(--al-sys-focus-hairline-width) + var(--al-sys-focus-ring-width)) var(--al-sys-color-focus-ring),
		0 0 0 calc(var(--al-sys-focus-hairline-width) * 2 + var(--al-sys-focus-ring-width)) var(--al-sys-color-focus-hairline-dark);
}
input[type='checkbox']:not(.k-checkbox):not(.k-radio):not(.ag-checkbox-input):not(.al-seg-item > *):not(.al-check > *):not(.al-switch > *):disabled,
input[type='radio']:not(.k-checkbox):not(.k-radio):not(.ag-checkbox-input):not(.al-seg-item > *):not(.al-check > *):not(.al-switch > *):disabled {
	opacity: var(--al-sys-state-disabled-opacity);
	cursor: default;
}
.k-checkbox:checked,
.k-checkbox.k-checked {
	background-color: var(--al-accent-primary);
	border-color: var(--al-accent-primary);
}
.k-radio:checked,
.k-radio.k-checked {
	border-color: var(--al-accent-primary);
}
.k-radio:checked::before {
	background-color: var(--al-accent-primary);
}

/* ============================================================================
   P5-T8 — RECORD HEADER (detail / editor pages).  Measured off the Prototype
   (disc.isEdit): a muted "← Back to {parent}" link, then a title row with the
   record name on the LEFT and record actions on the RIGHT. Delete RELOCATES here
   from the editor footer (a red-OUTLINE button, not a solid btn-danger). The
   in-page duplicate breadcrumb is replaced by this header; the topbar breadcrumb
   stays (they are not the same thing).
   ============================================================================ */
/* Consistent top breathing room ABOVE the header on every host — matches the old
   in-page breadcrumb's spacing and keeps the gap uniform whether the header sits
   at the top of the page (strain) or just under a tab strip (customer). Hosts
   should NOT add their own top padding for this (strain's main.pt-4 was removed). */
.al-record-back {
	/* Hosts write this as an <a> on some records and a <button> on others, and a button
	   arrives wearing 2px outset and a grey fill. A control that changes shape with the
	   element it lands on is not a control, so the chrome is stated, not inherited. */
	appearance: none;
	border: 0;
	background: none;
	padding: 0;
	font-family: inherit;
	display: inline-flex;
	align-items: center;
	gap: 5px;
	font-size: var(--al-fs-sm, 12.5px);
	font-weight: var(--al-fw-semibold);
	color: var(--al-text-muted);
	text-decoration: none;
	cursor: pointer;
	margin-bottom: 12px;
}
.al-record-back:hover {
	color: var(--al-text-body);
	text-decoration: none;
}
.al-record-back .material-symbols-outlined {
	font-size: 17px;
}

/* ============================================================================
   P5-CMP1 · Typography roles — reusable text styles built on the type tokens
   so screens stop re-declaring label / hint / eyebrow / numeric styling inline.
   Additive: nothing consumes these yet; screens opt in as they are reskinned.
   Specs track the shipped design (labels = semibold; eyebrow = .kpi_title).
   ============================================================================ */
.al-field-label {
	/* form field label. NB: name is .al-field-label, NOT .al-label — the sidebar
	   already owns .al-label for its nav-row labels, and colliding on it silently
	   overrode every nav row's colour. */
	font-size: var(--al-sys-font-size-100);
	font-weight: var(--al-sys-font-weight-heading);
	letter-spacing: 0.04em;
	line-height: var(--al-sys-line-height-body);
	text-transform: uppercase;
	font-family: var(--al-sys-font-family);
	color: var(--al-text-body-strong);
}
.al-hint {
	font-size: var(--al-fs-sm); /* 12.5px — helper text under a field */
	font-weight: var(--al-fw-regular);
	color: var(--al-text-strong); /* [P9-INP I5] dark hint #161b24 (was muted grey) */
	line-height: var(--al-lh-base);
}
.al-eyebrow {
	font-size: var(--al-fs-xs); /* 11px — uppercase section/group label */
	font-weight: var(--al-fw-semibold);
	letter-spacing: 0.03em;
	text-transform: uppercase;
	color: var(--al-text-muted);
}

.al-mono {
	font-family: var(--al-font-mono);
	font-variant-numeric: tabular-nums;
}

/* ============================================================================
   P5-CMP2 · Segmented control — canonical .al-segmented: the design's white
   bordered bar with an emerald active segment + hairline dividers between
   segments, inactive segments dark body text (not muted grey). Measured off the
   dashboard range toggle (the design's own analog, .range-toggle). The
   query-builder And/Or is a Kendo-specific grey-track variant that can converge
   onto this later. Additive: no consumers yet.
   ============================================================================ */
/* [P9-TGL-SEG1] "recede the others" (refinement #2): darker track, a white raised
   chip for the selected item, inactive text receded — contrast carries the selection,
   NOT an emerald fill. Values are the locked refinement spec. */
/* [PASS6-F16 / amendment 22] The dashboard range-toggle joins this rule instead of
   restating it. It was already a byte-identical hand-built copy — its own comment said
   "Matches the canonical .al-segmented" — which is the failure mode this card exists to
   end: the canonical control was built, documented, and then duplicated rather than
   used. Two copies agree until one of them is edited.

   Jeff asked the question that led here: "what do you call that control on the
   dashboard? Is that a button group? And if it is, should they not look the same?"
   They are both segmented controls, and now they are both THIS segmented control. */
.al-segmented,
.range-toggle,
.al-list-filter-card .k-filter .k-button-group {
	display: inline-flex;
	background-color: var(--al-surface-neutral); /* darker track #e4e7ec */
	border-radius: var(--al-radius-md);
	padding: 3px; /* inset so the selected chip floats on the track */
}
.al-segmented__item,
.filter-button,
.al-list-filter-card .k-filter .k-button-group .k-button {
	border: none;
	background-color: transparent;
	padding: 6px 15px;
	margin: 0;
	cursor: pointer;
	color: var(--al-text-faint); /* receded #8a92a0 */
	font-size: var(--al-fs-sm);
	font-weight: var(--al-fw-medium);
	line-height: 1;
	white-space: nowrap;
	border-radius: 6px;
}
.al-segmented__item:hover:not(.is-active),
.filter-button:hover:not(.active),
.al-list-filter-card .k-filter .k-button-group .k-button:not(.k-selected):hover {
	color: var(--al-text-strong); /* darken the receded text on hover, no fill */
}
/* Kendo emits joined radii on a button group (6px 0 0 6px / 0 6px 6px 0). A segmented
   control has a track with a floating chip, not two welded buttons, so the chip radius
   is uniform and the join is undone. This also retires the raw 6px literal the audit
   flagged in list-filter.css. */
.al-list-filter-card .k-filter .k-button-group .k-button,
.al-list-filter-card .k-filter .k-button-group .k-button.k-selected {
	border-radius: 6px !important; /* beats Kendo's own joined-radius rule, which is
	   emitted at higher specificity than a class selector can reach */
	box-shadow: none;
}
.al-segmented__item.is-active,
.filter-button.active,
.al-list-filter-card .k-filter .k-button-group .k-button.k-selected {
	background-color: var(
		--al-accent-primary
	); /* emerald raised chip (Jeff's call — refinement #2 selected state is emerald-fill + white text, on the recede track) */
	color: #fff;
	font-weight: 700;
	box-shadow: 0 2px 5px rgba(16, 22, 34, 0.18);
}

/* -------------------------------------------------------------------------
   THE CHIP TRAVELS HERE TOO. Same fix recipe-controls.css:3300 already made for
   .al-seg, which this family never received.

   js/shared/segmented.js arms all four tracks and writes --al-seg-x / -w / -h onto
   every one of them, but only .al-seg had a rule that reads them. On these three the
   numbers landed on nothing: the emerald was the ACTIVE ITEM'S OWN background, so it
   swapped between segments instead of sliding, and a swap makes the eye re-find the
   selection where a slide is followed.

   position:relative on the track is load-bearing, not decoration. offsetLeft is
   measured from the nearest positioned ancestor, and these tracks were position:static
   -- the dashboard's first segment measured 257px because it was being measured from
   <body>. The script's own comment claims "the track is position:relative", which was
   true of .al-seg and never of these.

   ::before rather than ::after so it paints UNDER the labels, and the items are given
   a stacking context of their own so their text stays above it. The transition is
   gated on .seg-armed, which the script adds two frames after first placement --
   ungated, every bar slides its chip in from the left on load and reads as a loading
   state. Width animates as well as transform because these segments are content-width
   (Today, Yesterday, WTD, MTD are four different lengths).

   A track the script never reaches keeps no .has-chip, so the item paints its own fill
   exactly as before. That is the fallback, not a second design. */
.al-segmented,
.range-toggle,
.al-list-filter-card .k-filter .k-button-group {
	position: relative;
}
.al-segmented.has-chip::before,
.range-toggle.has-chip::before,
.al-list-filter-card .k-filter .k-button-group.has-chip::before {
	content: '';
	position: absolute;
	top: 3px; /* the track's own padding, above */
	left: 3px;
	width: var(--al-seg-w);
	height: var(--al-seg-h);
	border-radius: 6px; /* the item radius above */
	background-color: var(--al-accent-primary);
	box-shadow: 0 2px 5px rgba(16, 22, 34, 0.18);
	transform: translateX(calc(var(--al-seg-x) - 3px));
	pointer-events: none;
}
.al-segmented.has-chip.seg-armed::before,
.range-toggle.has-chip.seg-armed::before,
.al-list-filter-card .k-filter .k-button-group.has-chip.seg-armed::before {
	transition:
		transform var(--al-dur-base) var(--al-ease-standard),
		width var(--al-dur-base) var(--al-ease-standard);
}
/* the labels ride above the travelling chip */
.al-segmented.has-chip .al-segmented__item,
.range-toggle.has-chip .filter-button,
.al-list-filter-card .k-filter .k-button-group.has-chip .k-button {
	position: relative;
	z-index: 1;
}
/* and the chosen item stops painting a second, stationary chip underneath it */
.al-segmented.has-chip .al-segmented__item.is-active,
.range-toggle.has-chip .filter-button.active,
.al-list-filter-card .k-filter .k-button-group.has-chip .k-button.k-selected {
	background-color: transparent;
	box-shadow: none;
}

/* ============================================================================
   KENDO'S "no rows" — the friendly treatment, on the grid's own class.

   THIS USED TO CLAIM .al-empty AS WELL, and .al-empty is a recipe control. Two
   definitions of one name, and this one won: a page using the recipe's empty state
   got its own package mark AND this block's inbox mark, one above the other. The
   recipe owns the name; this owns the vendor's.
   ============================================================================ */
.k-grid .k-grid-norecords {
	display: flex !important;
	/* WIDTH 100% PLUS PADDING IS WIDER THAN 100%. Content-box sizing made this cell 980px
	   of table plus 40px of its own padding, so it overflowed the scrollport by exactly that
	   40px: a horizontal scrollbar on a grid with nothing to scroll, and a box centred 20px
	   right of the content it sits in, which is why the icon never lined up with the message
	   under it. One property answers both. [ALLM-7262] */
	box-sizing: border-box;
	position: static; /* override Kendo's absolute-centered overlay box */
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: 10px;
	width: 100%;
	height: auto;
	padding: 44px 20px;
	border: 0; /* kill Kendo's default norecords box border */
	background: transparent;
	text-align: center;
}
.k-grid .k-grid-norecords::before {
	content: ''; /* [P9-TBL-EMPTY-ADM] thin-stroke inbox line-icon (recipe Tables #6),
	   was a 42px Material-Symbols FONT glyph. Mask keeps it token-coloured + font-free. */
	width: 30px;
	height: 30px;
	background-color: var(--al-text-faint);
	-webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='22 12 16 12 14 15 10 15 8 12 2 12'/%3E%3Cpath d='M5.45 5.11 2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z'/%3E%3C/svg%3E")
		center / contain no-repeat;
	mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='22 12 16 12 14 15 10 15 8 12 2 12'/%3E%3Cpath d='M5.45 5.11 2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z'/%3E%3C/svg%3E")
		center / contain no-repeat;
	order: 0; /* icon above the message */
}
.k-grid .k-grid-norecords-template {
	position: static;
	border: 0;
	padding: 0;
	background: transparent;
	order: 1;
	font-size: var(--al-fs-md);
	font-weight: var(--al-fw-medium);
	color: var(--al-text-muted);
	max-width: 340px;
}
/* THE BLOCK ABOVE HAS NEVER TAKEN EFFECT ON position, AND THAT IS THE WHOLE BUG. It says
   position: static at (0,2,0) and Kendo says absolute at (0,3,0) --
   .k-grid-content > .k-grid-norecords > .k-grid-norecords-template -- so Kendo wins by one
   class. The message has stayed an absolutely positioned overlay on every grid in the app:
   top 50%, margin -2em/-10em, sized 20em x 4em for a centring trick that predates the flex
   column above. So the icon stacked as a flex child and the message floated to the middle of
   the scrollport, 126px away from the icon that belongs to it.

   Matched here with one more real ancestor rather than !important, and Kendo's overlay
   geometry is unwound with it: the sizes existed only to centre the absolute box. [ALLM-7262] */
.k-grid .k-grid-content > .k-grid-norecords > .k-grid-norecords-template {
	position: static;
	top: auto;
	left: auto;
	margin: 0;
	width: auto;
	height: auto;
	line-height: var(--al-lh-base, 1.5);
}

/* P5-toast lives in main.css (its existing home, alongside the P0-T3 type rules
   it replaces) so there is one place for toast styling and no specificity fight
   with `.toast-<type> > .toast-header`. */

/* ============================================================================
   P5-chips — SELECTED CHIPS (Kendo multiselect).  The app's removable chips are
   Kendo `.k-chip` (45 kendo-multiselect hosts). Kendo ships a squared,
   translucent-grey box; the design (Design Guide §05:150) is a grey PILL.
   ============================================================================
       bg #f0f2f5 (~--al-gray-150), border 1px #e0e3e8 (~--al-gray-300),
       radius 20px (a pill), 12.5px/600 #27313c, padding 5px 8px 5px 13px,
       remove icon muted #8a92a0.
   Kendo loads before this file, so plain selectors win. Only the RESTYLE of the
   chip; the ui-select multiple chips (2 hosts) are left to T4b's ui-select work.
   The remove icon is recoloured/resized, NOT swapped to a Material Symbol —
   that is Kendo-internal render, out of a CSS pass. */
.k-chip {
	background-color: var(--al-surface-nested);
	border: 1px solid var(--al-border-default);
	border-radius: var(--al-radius-pill);
	color: var(--al-text-body-strong);
	font-size: var(--al-fs-sm);
	font-weight: var(--al-fw-semibold);
	padding: 5px 8px 5px 13px;
}

.k-chip .k-i-close,
.k-chip .k-icon,
.k-chip .k-chip-remove-action,
.k-chip .k-button-icon {
	color: var(--al-text-faint);
	font-size: var(--al-fs-15, 15px);
}

/* ============================================================================
   P5-CMP5 · Selected chips (ui-select multiple) — match the Kendo .k-chip grey
   pill above so a ui-select multiselect and a Kendo multiselect read identically
   (the ~2 ui-select-multiple hosts were left out of the original P5-chips pass).
   Selector carries an extra class so it beats main.css:251 / grow/index.css:6.
   The dashed "Add value" affordance is deferred: it needs widget-internal markup
   the real ui-select/Kendo controls don't expose without JS.
   ============================================================================ */
.ui-select-container.ui-select-multiple .ui-select-match-item {
	background-color: var(--al-surface-nested);
	border: 1px solid var(--al-border-default);
	border-radius: var(--al-radius-pill);
	color: var(--al-text-body-strong);
	font-size: var(--al-fs-sm);
	font-weight: var(--al-fw-semibold);
	padding: 4px 11px;
	line-height: 1.2;
	box-shadow: none;
}
.ui-select-container.ui-select-multiple .ui-select-match-item:hover {
	background-color: var(--al-surface-nested);
	color: var(--al-text-body-strong);
}
.ui-select-container.ui-select-multiple .ui-select-match-item .close {
	color: var(--al-text-faint);
	opacity: 1;
	text-shadow: none;
	font-size: var(--al-fs-15, 15px);
	margin-left: 7px;
}

/* ============================================================================
   P6 · text-info repoint — .text-info was the old CoreUI accent BLUE (#39f),
   used as the section/card heading colour across 31 screens (Time Clock cards,
   discount section titles, item/harvest/metrc detail headings, …). It was never
   repointed when btn-info went emerald. Bring it onto the emerald heading accent
   so headings stop reading off-brand blue. Colour only; !important + later source
   order beats core-ui's own !important rule.
   ============================================================================ */
.text-info {
	color: var(--al-text-heading-accent) !important;
}
a.text-info:hover,
a.text-info:focus {
	color: var(--al-accent-primary-hover) !important;
}
/* Sibling CoreUI text utilities were off-brand too — .text-primary was #321fdb
   (purple-blue!), and success/danger/warning were CoreUI's generic shades, not
   the brand palette. Repoint to the brand semantic tokens so accent + status
   text reads consistently app-wide (111 uses across 39 views). */
.text-primary {
	color: var(--al-accent-primary) !important;
}
.text-success {
	color: var(--al-success-fg) !important;
}
.text-danger {
	color: var(--al-danger-fg) !important;
}
.text-warning {
	color: var(--al-warning-fg) !important;
}

/* ============================================================================
   P5-menu — DROPDOWN POPUP (the app's floating menu).  There are ZERO Bootstrap
   .dropdown-menu in the app (0 in markup, 0 triggers); the real floating menu is
   the KENDO popup that opens from every dropdownlist / combobox / multiselect.
   ============================================================================
   Kendo ships a squared grey popup (#f5f5f5, radius 0, flat 5px shadow). The
   design's floating panel is a WHITE rounded card with a soft drop shadow:
       radius --al-radius-menu (9px), border #e4e7ec, bg #fff, --al-shadow-lg;
       item 13px #161b24; hover + selected -> emerald tint + accent text.
   Kendo loads before this file, so plain selectors win. Hover/selected classes
   differ across the app's Kendo versions (.k-hover/.k-state-hover,
   .k-selected/.k-state-selected) — target both. */
.k-popup,
.k-list-container {
	border-radius: var(--al-radius-menu);
	border: 1px solid var(--al-border-default);
	background-color: var(--al-surface-card);
	box-shadow: var(--al-shadow-lg);
	overflow: hidden;
}
/* THE FRAME WAS OURS AND THE LIST INSIDE IT WAS STILL THE VENDOR'S.

   `.k-list` is a CHILD of the popup, not the popup, and Kendo gives it its own
   `background-color:#fff; color:#333`. Nothing above reaches it, so the panel rendered as our
   dark card wrapped around a white slab, with the item ink correctly white on top of it --
   every option at 1:1, on every Kendo dropdownlist, combobox, autocomplete and multiselect in
   admin. Measured on Config > Sell > Terminals and on the vendor picker on /inventory/add.

   The ground is the popup's, not a second surface, so this cannot drift away from the frame
   it sits in. */
.k-list {
	background-color: var(--al-surface-card);
	color: var(--al-text-strong);
}

.k-list .k-list-item,
.k-popup .k-item,
.k-list-optionlabel {
	font-size: var(--al-fs-base);
	color: var(--al-text-strong);
	padding: 7px 12px;
}

/* [P9-DRP D4] selected = emerald tint + accent text.

   THE OPTION LABEL IS SELECTABLE TOO, and it was not on this list. Kendo renders a
   dropdown's optionLabel -- the "Select Calculation Method" placeholder row -- as
   .k-list-optionlabel, NOT as a .k-list-item, and it carries .k-selected whenever the
   widget has no value. So every picker in the app with a placeholder and nothing
   chosen opened on a row of vendor blue: rgb(66,139,202), measured, on a list whose
   other rows were already ours. The three lines above it size and ink the option
   label; only its states were missed. */
.k-list .k-list-item.k-selected,
.k-list .k-list-optionlabel.k-selected,
.k-popup .k-item.k-state-selected {
	background-color: var(--al-accent-primary-tint);
	/* THE INK IS THE ACCENT AS A *LINE*, AND THAT IS A DIFFERENT ROLE FROM THE FILL.
	   recipe-tokens.css:236 splits the two and says why: a fill is a ground in its own right
	   and must stay the same emerald on any canvas, while a line is ink laid ON the ground and
	   has to lighten when the ground goes dark. This was reading --al-accent-primary, the FILL
	   alias, as text: emerald-500 on a dark popup is 3.24:1. The -line role is emerald-300
	   there and measures 8.81:1. On light the two resolve to the same value, so nothing about
	   this row changes on a light ground. */
	color: var(--al-sys-color-accent-primary-line);
}
/* [P9-DRP D4] hover = distinct NEUTRAL grey (not the same emerald as selected) */
.k-list .k-list-item.k-hover,
.k-list .k-list-item:hover,
.k-list .k-list-optionlabel.k-hover,
.k-list .k-list-optionlabel:hover,
.k-popup .k-item.k-state-hover,
.k-popup .k-item:hover {
	background-color: var(--al-surface-sunken);
	color: var(--al-text-strong);
}
/* THE POPUP ROWS, matched to the recipe's select. Three things were off, and all three
   only exist while the panel is open, which is why measuring the closed control found
   none of them.

   The row text stays INK in every state. It was emerald on the selected row, which made
   the accent mean two things at once: "this is chosen" and "you can act on this". The
   tint says chosen; a tick says chosen; the words are just words.

   Selected is the accent at a tenth, hovered at a fifth. The vendor's grey hover had no
   relationship to anything else in the product. */
.ui-select-choices-row > span,
.ui-select-choices-row > a,
.ui-select-bootstrap .ui-select-choices-row > span {
	color: var(--al-sys-color-text-primary);
}
.ui-select-choices-row.active > span,
.ui-select-choices-row.active > a,
.ui-select-bootstrap .ui-select-choices-row.active > span {
	position: relative;
	background-color: var(--al-sys-color-row-tint-selected);
	color: var(--al-sys-color-text-primary);
}
/* The tick the recipe's picker draws with option::checkmark. A native pseudo-element is
   not available on a div-based popup, so the row draws it. */
/* The tick sits on the LEADING edge, where the recipe's native picker puts it, and every
   row indents by the same amount so the labels stay in one column whether ticked or not.
   A trailing tick reads as a status on the row; a leading one reads as the mark of which
   row you are on, which is what it is. */
.ui-select-bootstrap .ui-select-choices-row > span,
.ui-select-bootstrap .ui-select-choices-row > a,
.ui-select-choices-row > span,
.ui-select-choices-row > a {
	padding-left: calc(var(--al-sys-control-padding-x-md) + var(--al-sys-space-200));
	padding-right: var(--al-sys-control-padding-x-md);
}
.ui-select-choices-row.active > span::after,
.ui-select-choices-row.active > a::after {
	content: '✓';
	position: absolute;
	top: 50%;
	left: var(--al-sys-control-padding-x-md);
	translate: 0 -50%;
	color: var(--al-sys-color-accent-primary-line);
	font-weight: var(--al-sys-font-weight-control);
}
/* The chosen row carries weight as well as tint, so it is still findable in a long list
   once the panel has scrolled and the tick is off screen. */
.ui-select-choices-row.active > span,
.ui-select-choices-row.active > a,
.ui-select-bootstrap .ui-select-choices-row.active > span {
	font-weight: var(--al-sys-font-weight-control);
}
/* The panel clears the control rather than sitting flush against it: two edges touching
   read as one thicker edge, and the shadow has nothing to fall on. */
.ui-select-choices.dropdown-menu {
	margin-top: var(--al-sys-space-50);
	box-shadow: var(--al-sys-elevation-raised);
}
/* The wrapper, not the toggle. It greyed on hover while the toggle stayed white, so the
   fill showed through from underneath -- invisible while measuring the toggle alone. */
.ui-select-container:not(.ui-select-multiple) .ui-select-match:hover {
	background-color: transparent;
}
/* THE VENDOR PAINTS THE ROW AND WE PAINT ITS CHILD, SO OUR TINT LANDED ON #f5f5f5.
   vendors/ui-select/select.css:34 sets `.ui-select-choices-row:hover { background-color:
   #f5f5f5 }` on the LI. The rule below tints the SPAN inside it, and that tint is
   TRANSLUCENT -- so on dark it composited over the vendor's opaque light grey instead of
   over the popup's ground. Measured on the running app with a real pointer, dark forced
   before first paint: the hovered label sat at 1.09 against rgb(245,245,245), white on
   near-white, while resting and selected both read 10.67 against rgb(51,63,80).

   Clearing the row's own fill puts the tint back on the popup's ground and the hovered row
   returns to 10.67 -- the same figure as its neighbours rather than a number tuned for it.

   It is the row and not the span because only the row carries the vendor fill, and
   `.active` was never affected: a selected row has no vendor hover fill beneath it, which
   is why this looked like a hover-colour problem rather than a compositing one. [ALLM-7433] */
.ui-select-choices-row:hover,
.ui-select-bootstrap .ui-select-choices-row:hover {
	background-color: transparent;
}

.ui-select-choices-row:not(.active):hover > span,
.ui-select-choices-row:not(.active):hover > a,
.ui-select-bootstrap .ui-select-choices-row:not(.active):hover > span {
	background-color: var(--al-sys-color-row-tint-active);
	color: var(--al-sys-color-text-primary);
}

/* >> KILL THE BLUE FOCUS GLOW. Kendo's .k-focus item carries an inset blue
   box-shadow (rgb(50,118,177) 0 0 10px 3px inset). The emerald background above
   applies, but the blue glow sits on top — so a selected item read as blue, not
   green (caught by Jeff). The design has no such ring; clear it. */
.k-list .k-list-item.k-focus,
.k-list .k-list-item.k-selected.k-focus,
.k-list .k-list-optionlabel.k-focus,
.k-list .k-list-optionlabel.k-selected.k-focus,
.k-popup .k-item.k-state-focused,
.k-popup .k-item.k-state-selected.k-state-focused {
	box-shadow: none;
}

.form-control:focus,
.custom-select:focus {
	border-color: var(--al-accent-primary);
	box-shadow: var(--al-ring-focus);
}

/* ============================================================================
   NATIVE SELECTS.
   ============================================================================
   Text inputs used to be here too. They now wear .al-input and this file says
   nothing about them: one definition of a text field, in the recipe.

   >> ELEMENT-QUALIFIED ON PURPOSE. `select.form-control`, not `.form-control`.
   ui-select emits "btn btn-default form-control ui-select-toggle" on a SPAN at
   runtime, so the element qualifier excludes those 166 controls for free.
   Kendo likewise replaces its source element.

   >> THE DESIGN DECLARES FIELD HEIGHT. Measured selects have padding-top:0 and
   line-height:normal -- ~15px at 13px -- yet compute to 34px. Zero vertical
   padding plus a 15px line box cannot make 34px, so height is explicit.
   Buttons are the opposite (height varies within one look, so it is emergent
   there). The two components genuinely differ; do not unify them. */
select.form-control,
.custom-select {
	height: var(--al-h-input);
	/* Vertical 0 is required for the text to centre in the 34px border-box. */
	padding-top: 0;
	padding-bottom: 0;
	/* SAME TOKENS THE RECIPE INPUT USES. A select sits directly under a text field in
	   almost every form, and once the fields moved to .al-input the select's own 12px
	   left its text starting four pixels short of the field above it. Reading the value
	   off the same token is what keeps the column aligned; a literal here would be the
	   hand-match this pass exists to remove. */
	padding-left: var(--al-sys-control-padding-x-md);
	border-color: var(--al-border-input);
	/* BOOTSTRAP'S OWN #495057 was still winning here, so every value a user typed came
	   out three steps lighter than the label above it, which reads as disabled. Nothing
	   in the app had ever set this — it was the browser default all the way down. */
	color: var(--al-sys-color-text-primary);
	font-size: var(--al-sys-font-size-300);
}

/* ---- REQUIRED-AND-EMPTY (.incomplete) — an edge, not a fill ----
   This is the app's own signal and it is a good one: it marks the fields still owed
   before a record can be saved, and the tab strip carries a red underline so you can
   find the section that owes them. Only the paint changes. A pale red wash on every
   required field turned Create User into a pink form; the red edge says the same
   thing at the same glance and still reads when six of them are on screen.

   The chain is the base input rule's, plus .incomplete — that is what beats it,
   rather than the !important this used to carry. */
.al-input.incomplete,
textarea.form-control.incomplete,
select.form-control.incomplete,
.custom-select.incomplete,
.k-dropdownlist.incomplete,
.k-multiselect.incomplete,
.k-combobox.incomplete,
.k-autocomplete.incomplete,
.k-textbox.incomplete,
.ui-select-container.incomplete .ui-select-toggle,
div.incomplete input.form-control,
div.incomplete .al-input {
	border-color: var(--al-sys-color-status-error);
	background-color: var(--al-sys-color-surface-control);
}

/* THE UNIT-LABEL CARVE-OUT, and why .al-input is free where this file was not.
   Three patterns overlay a label INSIDE a field and reserve room for it with
   horizontal padding:
       .input-label-input    padding-right:50px   main.css:165          (29 uses)
       .qty-input            padding-right:76px   grow/index.css:11 and
                             duplicated in inventory/{index,manage,
                             metrc_import,production}.css               (13 uses)
       .payment-amount-input padding-left:25px
                             inventory/biotrack-invoice-payments-modal.css:73 (1)
   Each is a bare class at (0,1,0). THIS FILE loads last on both layouts, so any
   horizontal padding set here beat them and collapsed the reserved gap on 43
   fields -- which is why the input rule had to hold Bootstrap's 12px against the
   design's measured 11px.
   recipe-controls.css loads at layout line 43, BEFORE main.css at 48 and before
   the page block. Same specificity, earlier source, so .al-input's padding
   shorthand loses to every carve-out and the gap survives. Measured on the real
   thing before the swap: gap held at 50px, a twelve-digit value still stops
   before the unit label. */

/* .al-input INSIDE BOOTSTRAP'S .input-group. The container is the third party here,
   not the control: .input-group is display:flex and its children are sized by
   `.input-group > .form-control { flex:1 1 auto; width:1% }`. A recipe input is
   display:block/width:100%/flex:none, which in that container takes the whole row
   and pushes the addon out of it. This is the override layer doing its job -- the
   vendor container adapting the control -- not a property hand-matched onto it.

   The squared inner corners come with it: two things joined into one control only
   read as one if the shared edge is flat. That is right for a real .input-group-text
   addon. Where the neighbour is an al-btn with its own radius and border they do not
   join, and the answer there is to leave .input-group rather than square the button. */
.input-group > .al-input {
	position: relative;
	flex: 1 1 auto;
	width: 1%;
	min-width: 0;
	margin-bottom: 0;
}
.input-group > .al-input:not(:last-child) {
	border-top-right-radius: 0;
	border-bottom-right-radius: 0;
}
.input-group > .al-input:not(:first-child) {
	border-top-left-radius: 0;
	border-bottom-left-radius: 0;
}

/* The addon gets the TYPE RAMP, not a hard height. Bootstrap's .input-group is
   align-items:stretch, so once the addon's natural height drops below the
   field's definite 34px it stretches to meet it exactly. Pinning it to 34px too
   would work today but silently break the moment the field height changes —
   this version has no second number to keep in sync. */
.input-group-text {
	padding: 0 11px;
	font-size: var(--al-fs-base);
	line-height: normal;
	border-color: var(--al-border-input);
	background-color: var(--al-surface-addon); /* [P9-INP I6] #f4f6f8 addon fill (was framework grey) */
}

/* Selects are 4px taller than inputs in the design, and the extra room is the
   chevron well. The native arrow is replaced so the control matches on every
   platform — Windows, macOS and Linux all draw a different default. */
select.form-control,
.custom-select {
	height: var(--al-h-select);
	padding-right: 30px;
	background-color: var(--al-surface-card); /* [P9-DRP D1] closed select stays white-outline */
	appearance: none;
	-webkit-appearance: none;
	-moz-appearance: none;
	background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='none' stroke='%23157a52' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round' d='M3 4.5 6 7.5 9 4.5'/%3E%3C/svg%3E");
	background-repeat: no-repeat;
	background-position: right 11px center;
	background-size: 12px 12px;
}

/* A textarea gets NO height — it has to grow. This is the case the original
   "never author a height" instruction was really protecting, and it is handled
   by scoping rather than by giving up height everywhere. */
textarea.form-control {
	height: auto;
	/* the same tokens as the field beside it -- see the select above */
	padding: 9px var(--al-sys-control-padding-x-md);
	border-color: var(--al-border-input);
	font-size: var(--al-sys-font-size-300);
}

/* RESTING FILL IS THE CONTROL SURFACE — white, the same as the select beside it.
   This was a soft grey on text fields only, with selects left white, so a Security
   Role select and the New Password field under it sat in the same card wearing two
   different grounds. The recipe fills both from --al-sys-color-surface-control and
   that is the version that survives a form with both control types in one row.

   It also frees the sunken surface to mean one thing again. Read-only fields take
   that ground in the recipe, and they could not while every editable field was
   already wearing it. */
textarea.form-control {
	background-color: var(--al-sys-color-surface-control);
}
/* DISABLED IS THE SAME BLANKET EVERY OTHER CONTROL WEARS. It used to be a third
   grey plus muted text, which put three greys on one form — editable, disabled and
   read-only all within a few points of each other — and the muted colour lost to
   the base rule's ink anyway, so the declaration was doing nothing. Dimming says
   "not available" without inventing a surface, and the fill stays the control's own.
   The chain matches the base rule's so this wins where it needs to. */
textarea.form-control:disabled,
.form-control[disabled],
.custom-select:disabled,
select.form-control:disabled {
	opacity: var(--al-sys-state-disabled-opacity);
	background-color: var(--al-sys-color-surface-control);
}
/* INVALID IS AN EDGE, not a tint — the same call .incomplete got, for the same
   stated reason: the version that looks better with one error is not the one that
   survives six on a single form. Leaving the tint here would also have meant two
   error states in the app that look different from each other. */
.form-control.is-invalid,
.custom-select.is-invalid {
	border-color: var(--al-sys-color-status-error);
	background-color: var(--al-sys-color-surface-control);
}

/* ============================================================================
   P4-T4b — ui-select (166 controls).  THESE ARE FIELDS, NOT BUTTONS.
   ============================================================================
   ui-select swaps between TWO nodes, and both must be styled or the control
   changes size the instant you click it:
       resting  <span  class="btn btn-default form-control ui-select-toggle">
       open     <input class="form-control ui-select-search">
   Neither string appears anywhere in views/ — the vendor emits them at runtime
   from vendors/ui-select/select.min.js. That is why static analysis reported
   three different wrong counts for this family, and why T4a's gate was able to
   claim "0 vendor changes" while silently restyling all 166 search inputs.

   >> SCOPE. T4a excluded these deliberately, which left them at 16px beside
   13px native fields. A ui-select is a SELECT replacement, so the target is the
   native select: 38px tall, 13px, #d3d8e0, 7px radius. Height and radius
   already matched; the gap was type size and the arrow.

   >> HEIGHT MUST BE EXPLICIT, and this is the trap. The 38px these controls
   have today is not designed — it falls out of Bootstrap's
   `.form-control{height:calc(1.5em + .75rem + 2px)}` evaluated at 16px.
   Dropping the font to 13px re-evaluates that calc to 33.5px, so styling ONLY
   the type would silently shrink every ui-select 4.5px away from the native
   selects it sits beside. Pinning the height is what keeps them aligned.

   >> SPECIFICITY. main.css:205 `.ui-select-toggle.btn` is (0,2,0) and loads
   earlier. `.ui-select-container:not(.ui-select-multiple) .ui-select-toggle` is
   (0,3,0), so it wins on specificity rather than on load order alone.

   >> MULTI-SELECT IS EXCLUDED. 2 instances (views/admin/leafly.njk:27,
   public/js/admin/config_templates/sell/fees.html:356). Those render removable
   chips via .ui-select-match-item inside the control, so a fixed 38px would
   clip the moment a second chip wraps. They keep the vendor's auto height.

   >> The vendor's !important radius rules are NOT a fight. select.css:180-182
   is scoped to `.input-group > .ui-select-bootstrap > input.ui-select-search
   .form-control.direction-up` — an upward-opening ui-select inside an
   input-group. Narrow enough to leave alone. Likewise select.css:196's
   `text-align:left !important` is wanted: it undoes .btn's centring. */
.ui-select-container:not(.ui-select-multiple) .ui-select-toggle,
.ui-select-container:not(.ui-select-multiple) .ui-select-search {
	height: var(--al-h-select);
	font-size: var(--al-fs-base);
	border-color: var(--al-border-input);
	border-radius: var(--al-radius-md);
	line-height: normal;
	display: flex;
	align-items: center;
}

/* THE `.incomplete` ARM ABOVE IS INERT ON A UI-SELECT, and has been since the rule directly
   above landed. Both are (0,3,0) -- `:not()` contributes its argument's specificity, so
   `.ui-select-container.incomplete .ui-select-toggle` (line 2994) and
   `.ui-select-container:not(.ui-select-multiple) .ui-select-toggle` weigh the same -- and the
   one below wins on source order, 167 lines later in this same file, repainting the border
   input-grey.

   HALF-LIVE RATHER THAN INERT, and the distinction matters to anyone re-reading 2994: that arm
   sets border-color AND background-color, and the block below sets no background-color, so the
   background declaration has been applying all along. It just cannot be seen --
   --al-sys-color-surface-control resolves to #ffffff, which is what the control already was.
   Only the border was being stolen, and the border is the whole signal.

   ALLM-7529 is what that costs: Cycle Count refuses to submit with no field flagged, because
   check_incomplete() gates on a Reason the user has no way of knowing is missing.

   One more class rather than !important, so it stays beatable. MULTI-SELECT IS ALREADY FINE and
   is excluded here for that reason: the rule above does not match it, so the (0,3,0) arm still
   wins there. Measured on the create page: toggle rgb(93,112,139) before, rgb(192,70,63) after,
   which is the same #c0463f a native .al-input.incomplete beside it already paints. */
.ui-select-container.incomplete:not(.ui-select-multiple) .ui-select-toggle {
	border-color: var(--al-sys-color-status-error);
}

/* Replace the vendor's Bootstrap caret with the same chevron the native selects
   got in T4a, so a ui-select and a <select> in the same form are indistinguish-
   able. The caret is a CSS triangle the vendor positions absolutely
   (select.css:160-166, :199-203); hiding it and painting a background chevron
   is less invasive than trying to restyle a border-triangle into an arc. */
.ui-select-container:not(.ui-select-multiple) .ui-select-toggle > .caret,
.ui-select-container:not(.ui-select-multiple) > .ui-select-match > .caret {
	display: none;
}

/* THE CLEAR AND THE CHEVRON WERE ON TOP OF EACH OTHER. The vendor pins both to
   right:10px absolutely (select.css). We hide its caret and paint our own chevron at the
   control's padding rung, but the clear anchor kept the vendor's position -- so a
   ui-select with allow-clear rendered an x and a chevron in the same 10px, which reads as
   one broken glyph. The clear moves left of the chevron and gets a real hit target; the
   vendor's 10px-tall anchor was never one. */
.ui-select-container:not(.ui-select-multiple) .ui-select-toggle > a.btn {
	/* 24px: the smallest control tier is 28px and a 28px target inside a 36px frame has
	   nowhere to sit. Same reasoning, and same number, as the search field's clear.
	   No --al- prefix: that prefix means the design system, and this is one vendor
	   control's own geometry. */
	--uiselect-clear-size: 24px;
	--uiselect-clear-gap: var(--al-sys-space-100);
	position: absolute;
	top: 50%;
	right: calc(var(--al-sys-control-padding-x-md) + var(--al-select-caret-size, 10px) + var(--uiselect-clear-gap));
	translate: 0 -50%;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: var(--uiselect-clear-size);
	height: var(--uiselect-clear-size);
	padding: 0;
	border: 0;
	border-radius: var(--al-sys-radius-sm);
	background: none;
	color: var(--al-sys-color-text-muted);
	line-height: 1;
}
.ui-select-container:not(.ui-select-multiple) .ui-select-toggle > a.btn:hover {
	background: var(--al-sys-color-surface-sunken);
	color: var(--al-sys-color-text-primary);
}
/* the room BOTH of them need, or the value runs under the clear */
.ui-select-container:not(.ui-select-multiple) .ui-select-toggle.ui-select-toggle {
	padding-right: calc(
		var(--al-sys-control-padding-x-md) * 2 + var(--al-select-caret-size, 10px) + var(--al-sys-space-100) + 24px
	);
}

/* ui-select IS the recipe's select. Compared property by property against .al-select in
   the catalogue, at rest and under hover, focus and active, nine of fourteen properties
   disagreed -- all of them Bootstrap defaults arriving through .btn, none of them chosen.
   The caret is a MASK, not a background-image: the shape comes from the SVG and the
   colour from a token, so a theme can move it. As an image its colour was a hex baked
   into a data URI, which no sweep of var(--al-*) could ever have found. */
.ui-select-container:not(.ui-select-multiple) .ui-select-toggle {
	min-height: var(--al-sys-control-height-md);
	padding-right: calc(var(--al-sys-control-padding-x-md) * 2 + var(--al-select-caret-size, 10px));
	padding-left: var(--al-sys-control-padding-x-md);
	background-color: var(--al-sys-color-surface-control);
	color: var(--al-sys-color-text-primary);
	font-family: var(--al-sys-font-family);
	font-size: var(--al-sys-font-size-300);
	line-height: var(--al-sys-line-height-control);
}
/* ::before, not ::after: .btn::after is already the button system's state-layer veil,
   and reusing it put the caret behind an opacity:0 rule where it rendered as nothing. */
.ui-select-container:not(.ui-select-multiple) .ui-select-toggle::before {
	content: '';
	position: absolute;
	top: 50%;
	right: var(--al-sys-control-padding-x-md);
	width: var(--al-select-caret-size, 10px);
	height: var(--al-select-caret-size, 10px);
	translate: 0 -50%;
	background: var(--al-sys-color-text-muted);
	mask: var(--al-ref-icon-chevron-down) center / contain no-repeat;
	pointer-events: none;
}
/* The recipe's select edges to the accent on hover. ui-select had no hover rule at all,
   so the control sat inert while every other control in the product answered. */
.ui-select-container:not(.ui-select-multiple):not(.disabled) .ui-select-toggle:hover {
	border-color: var(--al-sys-color-accent-primary);
}

/* The focus ring already exists at main.css:217-225 via .btn-default-focus and
   is on the emerald token, so there is no blue -webkit-focus-ring to fight —
   contrary to the note this task was planned from. Left alone. */

/* ============================================================================
   P4-T4c — KENDO FIELDS.  99 widgets with no styling hook at all.
   ============================================================================
   Kendo REPLACES the source element with its own DOM and hides the original, so
   a class on the source never reaches anything visible. There are also ZERO
   custom properties in the three Kendo stylesheets we load (~7,354 rule blocks),
   so every value here is a hand-written override rather than a variable set.

   >> WHAT THE SURFACE ACTUALLY IS. The planning note said "296 Kendo fields,
   own budget, the tar pit". Measuring says otherwise, and the split matters:
       62 widgets ALREADY carry .form-control and were restyled by T4a
       99 widgets carry NO hook — this rule
      129 rendered .k-input hits are GRID filter chrome, which is T11's surface
   The pattern is per-widget-type, not random: comboboxes were given the class
   (57 of 59), dropdownlists never were (0 of 46), multiselects rarely (2 of 43).

   Concentrated in views/order/discount_forms/* and discount_detail.njk (49),
   public/js/admin/config_templates/sell/pos_configuration.html (15),
   views/customer/medical.njk (5), views/order/sale_limit2.njk (5).

   >> GRID CHROME IS EXCLUDED, deliberately. Kendo's grid filter cells and filter
   toolbar render the same .k-input class as a form field. They sit at 29px to
   keep the header row compact, and growing them to 34px would push every grid
   header taller — a change that belongs to T11 with the rest of grid chrome,
   not smuggled in here. `:not(.k-grid *)` is Selectors-4 and is fine on the
   browsers this app targets.

   >> NOT VERIFIABLE BY THE PARITY HARNESS. Like ui-select, essentially all of
   these render inside modals or on detail sub-routes, so the six harness pages
   see almost none of them. The gate cannot confirm this task. It was checked by
   navigating to a page that renders them; any future claim here needs the same. */
.k-picker:not(.k-grid *),
.k-textbox:not(.k-grid *),
.k-dropdownlist:not(.k-grid *),
.k-multiselect:not(.k-grid *),
.k-numerictextbox:not(.k-grid *),
.k-datepicker:not(.k-grid *),
.k-timepicker:not(.k-grid *),
.k-datetimepicker:not(.k-grid *) {
	min-height: var(--al-h-input);
	border-color: var(--al-border-input);
	border-radius: var(--al-radius-md);
	font-size: var(--al-fs-base);
}

/* [P9-DRP D3] emerald focus ring on Kendo pickers (they ship with none). */
.k-picker:not(.k-grid *).k-focus,
.k-textbox:not(.k-grid *).k-focus,
.k-dropdownlist:not(.k-grid *).k-focus,
.k-combobox:not(.k-grid *).k-focus,
.k-multiselect:not(.k-grid *).k-focus,
.k-numerictextbox:not(.k-grid *).k-focus,
.k-datepicker:not(.k-grid *).k-focus,
.k-timepicker:not(.k-grid *).k-focus,
.k-datetimepicker:not(.k-grid *).k-focus {
	border-color: var(--al-accent-primary);
	box-shadow: var(--al-ring-focus);
}

/* A multiselect with several chips must SCROLL, not hard-clip. Kendo fixes
   .k-multiselect at one row (its size class + the input-group flex pin the height)
   with overflow:hidden, so wrapped chips are cut off at the bottom — visible on
   discount filters with 6+ selections. Switching to overflow-y:auto keeps the
   compact one-row height (so the filter table stays aligned) but scrolls to the
   extra chips instead of slicing them. NOTE (Jeff): this deliberately does NOT
   expand the cell to show every selection at once — that is a separate,
   pre-existing limitation, recorded not fixed. This only stops the clipping. */
.k-multiselect:not(.k-grid *) {
	overflow-y: auto;
}

/* min-height, not height. A Kendo multiselect grows as chips are added — 43 of
   the 99 are multiselects — and a fixed height would clip the second row of
   chips the moment a user picks two things. Same reasoning that kept
   textarea and the ui-select multi variant on auto height. */

/* >> RECENTRE THE ARROW. Caught by eye, not by the gate. <<
   Kendo sizes its dropdown button for the widget's ORIGINAL 29px height, giving
   a 27px button. The widget is inline-flex/align-items:stretch, but the button
   carries its own height so stretch does not apply to it — it stays 27px and
   sits at the TOP of the 34px box we now ask for. Measured: 6px above the icon,
   12px below. The icon is correctly centred inside its button; the button is
   what is high.
   align-self:center on the button alone fixes it (9px/9px) without resizing
   anything, which is safer than forcing the button to stretch or flipping the
   widget's own align-items — either of which would also move the text and the
   chip rows in a multiselect.
   NOTE: this is a defect THIS TASK INTRODUCED. Growing a vendor control's box
   does not reflow the vendor's internal layout, and no computed-style gate will
   report "the arrow looks high" — it only reports values that changed, and the
   button's 27px did not change. Look at the screen.

   >> LIST EVERY WIDGET TYPE, NOT THE ONES YOU HAPPENED TO LOOK AT. The first
   version of this rule named .k-picker / .k-dropdownlist / the date-time family
   and MISSED .k-combobox — which is the single most common Kendo field in the
   app (59 instances) and the one on /inventory/add. Worse, comboboxes carry
   .form-control, so their box was grown to 34px by T4a and the off-centre arrow
   had been shipping since then. It went unnoticed because the verification page
   for T4c (discount detail) has no comboboxes on it. One page is not coverage. */
.k-picker:not(.k-grid *) > .k-input-button,
.k-dropdownlist:not(.k-grid *) > .k-input-button,
.k-combobox:not(.k-grid *) > .k-input-button,
.k-dropdowntree:not(.k-grid *) > .k-input-button,
.k-datepicker:not(.k-grid *) > .k-input-button,
.k-timepicker:not(.k-grid *) > .k-input-button,
.k-datetimepicker:not(.k-grid *) > .k-input-button,
.k-multiselect:not(.k-grid *) > .k-input-button {
	align-self: center;
}

/* A KENDO WRAPPER IS NOT A TEXT INPUT, even when it is wearing the class of one.

   `kendo-combo-box` on a `<select class="al-input">` copies EVERY class from the select
   onto the wrapper span Kendo builds, so the wrapper picks up .al-input. That class carries
   `display: block`, and Kendo's own `.k-input, .k-picker { display: inline-flex }` is the
   same specificity, (0,1,0) against (0,1,0), with recipe-controls.css loading later. The
   wrapper therefore stops being a row: the text box, the clear button and the caret stack
   vertically and the control renders at roughly double height with its caret underneath it.

   MEASURED on the Combine/Convert batch picker: wrapper 332x76 holding a 36px inner and a
   28px button, one above the other. 52 selects in views/ pair kendo-combo-box with
   .al-input, so this is not one dialog.

   The class belongs on the <select> — it is what makes the recipe style the control at all
   — so the wrapper's own display is restored here rather than the class being stripped out
   of 52 files. (0,2,0), which clears .al-input without an !important. */
.k-input.al-input,
.k-picker.al-input {
	display: inline-flex;
}
/* And again one level up, because .al-field has its own opinion about a span.

   A Kendo wrapper IS a span, and when the widget sits in a recipe field it is a DIRECT child
   of .al-field, where `.al-field > span:not(.al-affix) { display: block }` is (0,2,1) and
   beats the (0,2,0) above on type count. Same rule, same trap: it is what flattened the
   Areas combobox earlier in this rollout. (0,3,0) settles it. */
.al-field > .k-input.al-input,
.al-field > .k-picker.al-input {
	display: inline-flex;
	/* AND THE TYPOGRAPHY, which the display reset above left behind. The same label rule sets
	   six more properties, so a dressed wrapper rendered IDENTICALLY to a label: 12px/700,
	   uppercase, 0.48px tracking and a 6px margin, against the 14px/500 of a plain .al-input
	   on the same screen. That is not a subtle drift -- it uppercased the VALUE the control was
	   showing, so a Sales Type reading "Adult" rendered "ADULT".

	   Reset to what .al-input itself gives, not to initial: the wrapper IS the control, and the
	   only reason it loses is that .al-field > span is (0,2,1) against the control rule's (0,1,0).
	   font-weight and letter-spacing take inherit/normal because .al-input carries font: inherit;
	   line-height is deliberately NOT touched here, so the control keeps its own tier. [ALLM-7276] */
	margin-bottom: 0;
	font-size: var(--al-input-font-size);
	font-weight: inherit;
	letter-spacing: normal;
	text-transform: none;
	color: var(--al-input-text);
}

/* AND ONE BOX, NOT TWO. Kendo copies the select's classes onto the wrapper AND onto the
   inner input it builds, so .al-input's border, ground, radius and padding are drawn twice:
   measured, a 48px wrapper with a 1px edge and 16px padding holding a 36px inner with the
   same 1px edge and the same 16px padding. A box inside a box.

   Kendo's model is that the WRAPPER is the control: it is what carries the focus ring, the
   clear button and the caret, and every other rule in this file assumes it. So the inner is
   flattened back to the transparent text slot Kendo intends, and the wrapper keeps the
   recipe's input chrome. */
.k-input.al-input > .k-input-inner,
.k-picker.al-input > .k-input-inner {
	border: 0;
	border-radius: 0;
	background: transparent;
	padding: 0;
	min-height: 0;
	box-shadow: none;
	/* The text slot FLEXES; it does not claim the row. .al-input also carries width:100% on
	   pages that set it, and 100% of a flex line leaves nothing for the clear button and the
	   caret, so they wrapped to a second line under the text and the control rendered at
	   double height with its arrow beneath it. */
	flex: 1 1 auto;
	width: auto;
	min-width: 0;
}
/* The caret and the clear button keep their own size at the end of that row. */
.k-input.al-input > .k-input-button,
.k-picker.al-input > .k-input-button,
.k-input.al-input > .k-clear-value,
.k-picker.al-input > .k-clear-value {
	flex: none;
}


/* A DATE KEEPS ITS CALENDAR, TINTED. The mark in a picker's trigger says what OPENS, so
   the date and time family keep the vendor's own glyph and give up only its colour. The
   list openers used to be in this rule and are not: a chevron is not a tint, it is a
   different glyph, and it is written below.

   >> THIS RULE STILL REACHES NOTHING, and it is left that way ON PURPOSE. It names only
   .k-svg-icon, and this build ships the icon FONT -- `k-icon k-i-calendar`, measured by
   constructing one of every widget family on /inventory/add. Adding .k-icon here makes
   the tint apply for the first time, which would turn every date and time glyph in the
   app emerald in a commit whose subject is the chevron. It is also the wrong colour: the
   recipe's select, ui-select and now the Kendo list openers all draw their mark in
   --al-select-caret, and two colours for two kinds of trigger is not one product. Jeff's
   call, and it is in the handover. */
.k-datepicker:not(.k-grid *) .k-input-button .k-svg-icon,
.k-timepicker:not(.k-grid *) .k-input-button .k-svg-icon,
.k-datetimepicker:not(.k-grid *) .k-input-button .k-svg-icon {
	color: var(--al-accent-primary);
}

/* ============================================================================
   ONE CHEVRON, AND KENDO GETS IT TOO.

   A ui-select and a native <select> in the same form paint the SAME mark: the recipe's
   chevron, masked so the shape comes from --al-ref-icon-chevron-down and the colour from
   --al-select-caret. A Kendo combobox beside them painted the vendor's filled triangle in
   the page's primary ink -- three controls that open a list, two answers. Measured on
   /inventory/add, where two ui-selects sit directly above five comboboxes.

   THE GLYPH IS A FONT, NOT AN SVG, in this build. The rule above it named only
   .k-svg-icon and so never reached the single most common Kendo field in the app; that is
   why the triangle survived a task whose whole subject was this caret. Both are named
   here, and the font's ::before content is removed rather than merely recoloured -- a
   transparent glyph still claims its 16px and pushes the mask off centre.

   LIST OPENERS ONLY, and each named. .k-picker is not used: a Kendo date picker carries
   it too, and a chevron on a calendar field promises a list and delivers a month.
   ============================================================================ */
.k-combobox:not(.k-grid *) .k-input-button .k-icon::before,
.k-dropdownlist:not(.k-grid *) .k-input-button .k-icon::before,
.k-dropdowntree:not(.k-grid *) .k-input-button .k-icon::before,
.k-multiselect:not(.k-grid *) .k-input-button .k-icon::before {
	content: none;
}
.k-combobox:not(.k-grid *) .k-input-button .k-svg-icon > svg,
.k-dropdownlist:not(.k-grid *) .k-input-button .k-svg-icon > svg,
.k-dropdowntree:not(.k-grid *) .k-input-button .k-svg-icon > svg,
.k-multiselect:not(.k-grid *) .k-input-button .k-svg-icon > svg {
	display: none;
}
.k-combobox:not(.k-grid *) .k-input-button .k-icon,
.k-combobox:not(.k-grid *) .k-input-button .k-svg-icon,
.k-dropdownlist:not(.k-grid *) .k-input-button .k-icon,
.k-dropdownlist:not(.k-grid *) .k-input-button .k-svg-icon,
.k-dropdowntree:not(.k-grid *) .k-input-button .k-icon,
.k-dropdowntree:not(.k-grid *) .k-input-button .k-svg-icon,
.k-multiselect:not(.k-grid *) .k-input-button .k-icon,
.k-multiselect:not(.k-grid *) .k-input-button .k-svg-icon {
	/* the vendor's icon slot is 16px and sized by font-size; the mark is 10px, the same
	   number the recipe's select and ui-select both draw */
	width: var(--al-select-caret-size, 10px);
	height: var(--al-select-caret-size, 10px);
	min-width: 0;
	min-height: 0;
	background-color: var(--al-select-caret, var(--al-sys-color-text-muted));
	-webkit-mask: var(--al-ref-icon-chevron-down) center / contain no-repeat;
	mask: var(--al-ref-icon-chevron-down) center / contain no-repeat;
}

/* >> THE SPINNER IS THE OPPOSITE CASE — IT MUST STRETCH, NOT CENTRE. <<
   A numeric textbox's spinner is a vertical STACK of two buttons that divides
   whatever height it is given. Centring it collapsed it to its natural 16px, so
   each arrow became 8px and the pair rendered as a single diamond smudge
   instead of an up/down control. It has to fill the box so each half stays
   legible. Included explicitly rather than left to inherit, because the
   temptation is to lump it in with the dropdown buttons above — it is the same
   slot in the same widget family, and exactly the wrong answer. */
.k-numerictextbox:not(.k-grid *) > .k-input-spinner {
	align-self: stretch;
}

/* ============================================================================
   P4-T5 — STATUS PILLS.
   ============================================================================
   Measured by rendering the prototype: 75 pill instances, 15 colour variants.
       node .specs-local/tools/design-measure.js buttons

   The design's pill is a SOFT TINT WITH DARK TEXT. Bootstrap's badge is a
   SATURATED FILL WITH WHITE TEXT. That is the whole change — .badge-success
   goes from #28a745-on-white to #e9f4ef-with-#157a52-text.

   Geometry was also inconsistent in our app depending on the page: badges
   rendered at 160px radius / 15px / weight 700 on /process/, and 4px radius /
   10.5px on /order/order_status, because only some carry .badge-pill. The
   design uses one pill everywhere.

   >> THE TIER SPLIT IS REAL AND P2 ALREADY DOCUMENTED IT. Do not reach for the
   callout tokens here:
       amber  pill --al-warn-badge-bg #fbf1dd   vs callout --al-warning-bg #fdf6e3
       blue   pill --al-info-badge-bg #e8f0fb   vs callout --al-info-100  #eaf1fb
       danger pill fg --al-danger-700 #b23b39   vs --al-danger-fg #c0463f
   Each pairing is internally consistent in the design and it never mixes them.
   The measured pill values match the badge-tier tokens exactly, which is
   independent confirmation that P2's split was correct.

   Mapping (design counts in brackets):
     .badge-secondary -> grey  #f2f4f6 / #3d4550  [25 — the dominant pill]
     .badge-success   -> green #e9f4ef / #157a52  [21]
     .badge-warning   -> amber #fbf1dd / #9a6a12  [8]
     .badge-danger    -> red   #fbeceb / #b23b39  [7]
     .badge-info      -> blue  #e8f0fb / #2a6fdb  [4]
   Not mapped: the teal "Exchange" pill (#e5f1f1/#2a6f6f, 2 instances) has no
   Bootstrap variant to hang off, and inventing one would be a new component. */
/* >> [DEVIATION] THE PILL CARRIES A HAIRLINE BORDER. THE DESIGN'S DOES NOT. <<
   Decided by Jeff, 2026-07-20, after reviewing six treatments side by side at
   .specs-local/tools/parity/pill-options.html (option D).

   DESIGN:  0 of 75 measured pills have any border. Fill + text only.
   OURS:    same fill, same text, plus a 1px hairline.
   WHY:     at 9% tint the pill dissolves into a striped table row — which is
            where nearly all of ours live. The border holds the shape whatever
            is behind it. Note this deviates by ADDING, not by altering: every
            fill and text colour remains exactly the measured design value, so
            it is the smallest of the options considered.
   The border colours are NOT invented either — each is that hue's own triad
   border, which the design already uses on its callouts. So the deviation is
   "pills borrow the callout border", not "we made up five colours".

   >> DO NOT REVERT THIS TO MATCH THE DESIGN. A future parity pass WILL flag
   these borders as a difference. That is expected and intentional. Changing it
   back requires asking Jeff first. */
.badge {
	border-radius: var(--al-radius-pill);
	padding: var(--al-pad-badge);
	font-size: var(--al-fs-xs);
	font-weight: var(--al-fw-semibold);
	line-height: normal;
	border: 1px solid transparent;
}

.badge-secondary {
	color: var(--al-text-body);
	background-color: var(--al-surface-nested);
	border-color: var(--al-border-input);
}

.badge-success {
	color: var(--al-success-fg);
	background-color: var(--al-success-bg);
	border-color: var(--al-success-border);
}

/* [P9-STA-TRIAD-ADM] collapsed the badge tier onto the ONE triad per hue (recipe #3):
   warning badge -> warning triad (was the separate --al-warn-badge-* amber); danger fg
   -> --al-danger-fg #c0463f (was the pill-only 2nd red --al-danger-700 #b23b39). */
.badge-warning {
	color: var(--al-warning-fg);
	background-color: var(--al-warning-bg);
	border-color: var(--al-warning-border);
}

.badge-danger {
	color: var(--al-danger-fg);
	background-color: var(--al-danger-bg);
	border-color: var(--al-danger-border);
}

.badge-info {
	color: var(--al-info-fg);
	background-color: var(--al-info-bg); /* [P9-STA-TRIAD-ADM] info-100 (was 2nd-blue #e8f0fb) */
	border-color: var(--al-info-border);
}

/* .badge-primary was NOT in the prototype's badge vocabulary (secondary /
   success / warning / danger / info), so it fell back to Bootstrap's harsh
   solid blue — visible on the transfer "Open" status pill. Map it to the
   design's soft info badge (an active/in-progress state, distinct from the
   green success + grey secondary). Systemic: fixes every .badge-primary. */
.badge-primary {
	color: var(--al-info-fg);
	background-color: var(--al-info-bg); /* [P9-STA-TRIAD-ADM] info-100 (was 2nd-blue #e8f0fb) */
	border-color: var(--al-info-border);
}

/* .badge-pill is now redundant — the base .badge is already fully rounded. Left
   matching rather than removed, so the 1 markup use does not become a mismatch. */
.badge-pill {
	border-radius: var(--al-radius-pill);
}

/* ----------------------------------------------------------------------------
   ALERTS / CALLOUTS — 91 instances. The OTHER tier.
   ----------------------------------------------------------------------------
   These consume the CALLOUT triads, not the badge ones. P2 separated them for a
   reason and the pairings differ by a couple of hex digits each — see the pill
   block above. Alerts were still on raw Bootstrap (#f8d7da / #fff3cd / #d1ecf1).

   >> THE TWO OUT-OF-TRIAD VARIANTS. 18 alerts use classes the design has no
   equivalent for, so they were falling through to Bootstrap:
       .alert-secondary  14 uses
       .alert-primary     4 uses
   Reading what they actually contain settles it — both are INSTRUCTIONAL PROSE,
   not status:
       alert-primary   "Name cannot be a duplicate of any existing Alleaves items."
       alert-secondary "Select which strains and the number of each strain..."
                       "If this client requires additional workspaces..."
   So: alert-primary states a rule or constraint -> the INFO tier reads right.
   alert-secondary is plain guidance with no urgency -> NEUTRAL grey.
   Neither is a status colour, and painting them green or amber would invent a
   severity the copy does not have. Recorded rather than left unstyled. */
.alert {
	border-radius: var(--al-radius-lg);
}

.alert-success {
	color: var(--al-success-fg);
	background-color: var(--al-success-bg);
	border-color: var(--al-success-border);
}

.alert-danger {
	color: var(--al-danger-fg);
	background-color: var(--al-danger-bg);
	border-color: var(--al-danger-border);
}

/* Danger alert with a leading Material Symbol aligned to the message. Used by the
   Destroy Plant(s) confirm FORMS, which stay data-entry modals (required reason +
   METRC waste weights) and so can't use the centered .al-confirm dialog — this is
   the "hybrid" treatment: design danger colours + icon on the alert, keep the form.
   The glyph inherits .alert-danger's colour (--al-danger-fg). */
.alert.al-alert-icon {
	display: flex;
	align-items: center;
	gap: 10px;
}
.alert.al-alert-icon .material-symbols-outlined {
	font-size: var(--al-fs-3xl, 22px);
	flex-shrink: 0;
}

.alert-warning {
	color: var(--al-warning-fg);
	background-color: var(--al-warning-bg);
	border-color: var(--al-warning-border);
}

.alert-info,
.alert-primary {
	color: var(--al-info-fg);
	background-color: var(--al-info-bg);
	border-color: var(--al-info-border);
}

.alert-secondary {
	color: var(--al-text-body);
	background-color: var(--al-surface-nested);
	border-color: var(--al-border-default);
}

/* ON DARK A STATUS SURFACE CARRIES THE PRIMARY INK, NOT A TINTED ONE. [ALLM-7292]
   ALLM-7286 gave the four tint rungs a dark value, which fixed the ground. The ink above
   was already on the -300 rung, so a badge became -300 on -700: danger 2.76, warning 2.97,
   info 3.47, and only success cleared AA at 5.04.

   THE RECIPE HAD ALREADY ANSWERED THIS and the Bootstrap layer never followed. `.al-badge`
   pairs its own tint with `--al-badge-intent-text: var(--al-sys-color-text-primary)` — the
   primary ink. This is that same pairing, applied to the classes Bootstrap brought.

   NOT A TINTED INK ONE RUNG LIGHTER, because there is no rung that works: -200 does not
   exist for amber or blue, and on red, where it does, it reaches 4.16 — still under AA. The
   only tinted rung that clears 4.5 on all four is -50, which is very nearly white already.
   Measured with the primary ink: 8.16, 6.06, 9.87, 8.72.

   DARK ONLY. On light the pale tint under a deep tinted ink reads at 15:1 and is not what
   this ticket is about; the hue still comes from the ground either way. */
.al-on-dark .badge-success,
.al-on-dark .badge-warning,
.al-on-dark .badge-danger,
.al-on-dark .badge-info,
.al-on-dark .alert-success,
.al-on-dark .alert-danger,
.al-on-dark .alert-warning,
.al-on-dark .alert-info,
.al-on-dark .alert-primary {
	color: var(--al-text-body);
}
/* The confirm dialog's icon chip is the same pairing one size down: a tint ground with a
   tinted glyph on it. A glyph is a MARK rather than body text, so its bar is 3:1 — and
   danger sat at 2.76, under even that. */
.al-on-dark .al-confirm--danger .al-confirm-icon .material-symbols-outlined,
.al-on-dark .al-confirm--warning .al-confirm-icon .material-symbols-outlined,
.al-on-dark .al-confirm--alert .al-confirm-icon .material-symbols-outlined {
	color: var(--al-text-body);
}

/* Order-type label on the orders grid: a Material Symbol + muted text, matching
   the design (which renders type as icon + #6b7480 label, never a pill — only the
   status column gets a pill). Paired with order_status.js, which stopped emitting
   badge-<colour> for type. The icon inherits currentColor, so it stays muted with
   the text. */
.order-type {
	display: inline-flex;
	align-items: center;
	gap: 6px;
	color: var(--al-text-muted);
	font-size: var(--al-fs-base);
}

.order-type__icon {
	font-size: 17px;
	line-height: 1;
	flex: none;
}

/* =========================================================================
   SAVE BAR — shell coupling only. The control lives in recipe-controls.css.
   ========================================================================= */
.al-savebar {
	--al-savebar-inset-start: var(--al-sidebar-w);
	--al-savebar-layer: var(--al-z-sticky); /* our shipped rung, not the recipe's 100 */
}
.app-shell:has(.app-sidebar.is-rail) .al-savebar {
	--al-savebar-inset-start: var(--al-sidebar-w-rail);
}

/* ============================================================================
   P6-ADMIN1 · User Profile (Prototype:145-213) — identity header (avatar + name
   + role pill + email) over two cards (Personal information / Security) + the
   dirty-gated .al-savebar. .al-card-title / .al-card-sub / .al-datalist are
   reusable across the P6 card screens.
   ============================================================================ */
.al-profile {
	max-width: 1060px;
}
.al-profile-head {
	display: flex;
	align-items: center;
	gap: 16px;
	margin-bottom: 22px;
}
.al-profile-avatar {
	flex: none;
	width: 56px;
	height: 56px;
	border-radius: var(--al-radius-circle);
	background: var(--al-accent-interactive); /* #1e9268 (Prototype:147) */
	color: var(--al-accent-on-primary);
	display: flex;
	align-items: center;
	justify-content: center;
	font-size: var(--al-fs-2xl); /* 20px */
	font-weight: var(--al-fw-semibold);
	text-transform: uppercase;
}
.al-profile-id {
	min-width: 0;
}
.al-profile-name {
	font-size: var(--al-fs-2xl); /* 20px */
	font-weight: var(--al-fw-bold);
	color: var(--al-text-strong);
}
.al-profile-meta {
	display: flex;
	align-items: center;
	gap: 10px;
	flex-wrap: wrap;
	margin-top: 5px;
}
.al-profile-role {
	font-size: var(--al-fs-xs); /* 11px */
	font-weight: var(--al-fw-semibold);
	color: var(--al-accent-primary);
	background: var(--al-accent-primary-tint);
	border: 1px solid var(--al-accent-primary-border);
	border-radius: var(--al-radius-pill);
	padding: 2px 10px;
}
.al-profile-email {
	font-size: var(--al-fs-sm); /* 12.5px */
	color: var(--al-text-faint);
}
.al-profile-grid {
	display: grid;
	grid-template-columns: 1fr 1fr;
	gap: 18px;
	align-items: start;
}
/* A CARD BESIDE ANOTHER IS NOT A CARD UNDER ONE. `.al-card + .al-card` is the recipe's gap
   between STACKED cards (recipe-controls.css:5233) and it still matches when a grid puts the
   pair side by side, so Security started 24px below Personal information -- measured at 182
   against 158. `align-items: start` does not save it, because it aligns the MARGIN box.
   FOURTH COPY. customer.css, harvest_detail.css and plant_detail.css carry the first three
   and every one of their comments says the same thing: this belongs in one place rather than
   in a per-grid override. Filed as its own ticket, because the one place is the recipe. */
.al-profile-grid > .al-card + .al-card {
	margin-top: 0;
}
@media (max-width: 900px) {
	.al-profile-grid {
		grid-template-columns: 1fr;
	}
}

/* reusable card heading + subtitle (P6 card screens) */

/* detail-page section heading — the design's 16px/700 card/section title
   (Prototype:434). Replaces oversized Bootstrap <h3> section headers. */
.al-section-title {
	font-size: var(--al-fs-lg); /* 16px */
	font-weight: var(--al-fw-bold);
	color: var(--al-text-strong);
	margin: 0;
}
/* Detail-screen section headings used oversized emerald <h2/h4/h5 class="text-info">
   (Item Availability, Metrc Fields, Process Inputs/Outputs, discount Options/Filter…).
   Bring them to the 16px section-title size; they keep their emerald text-info colour.
   One rule across every detail screen. */
h2.text-info,
h4.text-info,
h5.text-info,
h6.text-info {
	font-size: var(--al-fs-lg); /* 16px — was h2 ~32 / h4 ~24 / h5 ~20 */
	font-weight: var(--al-fw-bold);
}
/* Page titles — bare <h2> (Add Inventory, Physicians, Cash Management, Shipping/
   Receiving, Vendors…) rendered at Bootstrap's oversized 2rem/32px, redundant beside
   the breadcrumb. Bring them to the prototype's 20px record/page-title size. Canonical;
   the .text-info section-heading h2 above keeps its 16px via specificity. */
h2 {
	font-size: var(--al-fs-2xl); /* 20px, was 32px */
	font-weight: var(--al-fw-bold);
	color: var(--al-text-strong);
}
.al-card-sub {
	font-size: var(--al-fs-12); /* 12px */
	color: var(--al-text-faint);
	margin: 4px 0 18px;
}

/* ============================================================
   Detail-screen section cards (P7 card-wrapper pass)
   The prototype wraps each detail tab's content in per-section white
   cards with an emerald uppercase eyebrow title — NOT one card per tab.
   (Prototype item detail L597-622: "Item Details" + "Item Availability".)
   Card chrome = the design's flat card, verbatim: #fff / 1px #e4e7ec /
   10px / 18px 20px, no shadow. Shared across item/batch/customer/plant/
   harvest details so it's one class, applied per section.
   ============================================================ */
.al-detail-grid {
	display: grid;
	grid-template-columns: 1.15fr 1fr;
	gap: 20px;
	align-items: start;
}
@media (max-width: 900px) {
	.al-detail-grid {
		grid-template-columns: 1fr;
	}
}
.al-detail-card {
	background-color: var(--al-surface-card);
	border: 1px solid var(--al-border-default);
	border-radius: var(--al-radius-xl);
	padding: var(--al-pad-card); /* 18px 20px */
}
.al-detail-eyebrow {
	font-size: var(--al-fs-xs); /* 11px */
	font-weight: var(--al-fw-bold);
	letter-spacing: var(--al-ls-wide); /* .06em */
	text-transform: uppercase;
	color: var(--al-accent-primary);
	margin-bottom: 14px;
}
/* Space between stacked rows-of-cards, and a flex-column stack for tabs whose
   sections stack vertically (e.g. Product = settings card + pricing card). */
.al-detail-grid + .al-detail-grid {
	margin-top: 20px;
}
.al-detail-stack {
	display: flex;
	flex-direction: column;
	gap: 20px;
}

/* reusable read-only label/value grid */
.al-datalist {
	display: grid;
	grid-template-columns: 140px 1fr;
	row-gap: 15px;
	column-gap: 12px;
	font-size: var(--al-fs-base); /* 13px */
}
.al-dl-key {
	color: var(--al-text-muted);
}
.al-dl-val {
	color: var(--al-text-body-strong);
	font-weight: var(--al-fw-medium);
	word-break: break-word;
}
.al-dl-val--empty {
	color: var(--al-text-placeholder);
	font-weight: var(--al-fw-regular);
}

/* =============================================================================
   TWO RECIPE CONTRACTS THE APP DOES NOT MEET, BRIDGED HERE RATHER THAN IN 169 FILES.

   Both are markup shapes that predate the recipe. The recipe is right and the app is
   wrong, and rewriting every site by hand is a large mechanical diff with a real chance
   of dropping an ng-model. These two rules make the legacy shape render the way the
   correct shape does; a site rewritten properly keeps working, because neither rule
   matches the recipe's own markup.
   ========================================================================== */

/* 1. A COMPOSITE FIELD IS A ROW. .al-field-composite is the FRAME and .al-field-control
      is the row inside it; the app uses the frame AS the row and puts Bootstrap's
      .input-group-prepend in it. The frame is display:block, so the $ affix and its input
      stack into two full-width boxes. Measured on the item detail cost field: a 1291px
      affix sitting on top of a 1290px input.

      130 of the app's 132 composites are missing .al-field-control, and 81 of those hold
      an .input-group-prepend, which is what this matches. */
.al-field-composite:has(> .input-group-prepend) {
	display: flex;
	align-items: center;
	gap: var(--al-sys-control-gap);
	min-width: 0;
}
.al-field-composite:has(> .input-group-prepend) > .al-input,
.al-field-composite:has(> .input-group-prepend) > .al-select,
.al-field-composite:has(> .input-group-prepend) > .al-textarea {
	flex: 1 1 0;
	width: auto;
	min-width: 0;
}

/* 2. A CHECKBOX AND ITS WORDS ARE ONE LINE. The recipe wraps: <label class="al-check">
      <input><span>text</span></label>. The app puts al-check on the INPUT and leaves the
      <label for> as a sibling, so .al-check-set -- a flex column -- makes each of them its
      own row and the words land 18px under the box. Measured on five of six sampled.

      88 sites across the app carry the sibling shape, 26 of them on item detail. 67 of the
      68 affected sets hold exactly one pair, so turning the set into a row is safe; the one
      that holds two wraps rather than misaligning. */
.al-check-set:has(> input.al-check) {
	flex-direction: row;
	align-items: flex-start;
	/* NOWRAP, and this is the whole difference between a row and a pair of rows. With wrap,
	   a label longer than its container -- a location name inside a narrow table cell -- broke
	   to the next line and landed back under its own box, which is the defect this rule
	   exists to fix. The label wraps its TEXT instead. */
	flex-wrap: nowrap;
	gap: var(--al-check-gap, 9px);
}
.al-check-set:has(> input.al-check) > label {
	margin-bottom: 0;
	min-width: 0;
	line-height: 1.45;
}

/* THE SAME TWO RULES FOR AN ENGINE WITH NO :has(). This stylesheet reaches the POS, and the
   POS terminals run an AOSP WebView pinned at Chrome 101 while :has() is Chrome 105 -- so up
   there both rules above are discarded WHOLE and their elements lose their layout: a field
   composite stops being a row, and a checkbox stops sharing a line with its words.

   @supports not selector(:has(*)) fires ONLY where :has() is missing, so every modern browser
   keeps the precise rules above and nothing here can reach them. The :has() conditions are what
   separate app markup from conformed recipe markup, and they cannot be expressed without it, so
   these apply to both. That is the deliberate trade: a conformed composite is already a row, and
   being told so twice costs nothing, against every unconformed one breaking.

   .app-shell:has(.app-sidebar.is-rail) above is NOT mirrored here on purpose -- POS renders no
   .app-shell at all (0 occurrences in react/pos), so it can never match on the old engine. */
@supports not selector(:has(*)) {
	.al-field-composite {
		display: flex;
		align-items: center;
		gap: var(--al-sys-control-gap);
		min-width: 0;
	}
	.al-field-composite > .al-input,
	.al-field-composite > .al-select,
	.al-field-composite > .al-textarea {
		flex: 1 1 0;
		width: auto;
		min-width: 0;
	}
	.al-check-set {
		flex-direction: row;
		align-items: flex-start;
		flex-wrap: nowrap;
		gap: var(--al-check-gap, 9px);
	}
	.al-check-set > label {
		margin-bottom: 0;
		min-width: 0;
		line-height: 1.45;
	}
}

/* THE CLEAR SITS ON THE VALUE'S LINE. ui-select renders it as a Bootstrap .btn.btn-xs
   with pull-right, and inside the toggle's flex row that button overflowed the 36px
   control by 6px at the top -- measured on every ui-select on item detail. Bootstrap's
   own padding and line-height are what push it; the toggle is already a flex row, so
   centring it and letting it size to its glyph is the whole fix. */
.ui-select-bootstrap > .ui-select-match > .ui-select-toggle {
	align-items: center;
	gap: var(--al-sys-space-100);
}
/* MATCHED ON THE VENDOR'S OWN SELECTOR, because that is what it takes. select.css says
   .ui-select-bootstrap .ui-select-toggle > a.btn { position:absolute; height:10px;
   right:10px; margin-top:-2px } and then -6px again lower down -- (0,3,1), against the
   (0,2,0) of .ui-select-container .btn-link. The clear was six pixels above the value it
   sits beside on every ui-select in the app. The toggle is already a centred flex row, so
   taking the button out of absolute positioning is the whole fix. */
.ui-select-bootstrap .ui-select-toggle > a.btn {
	position: static;
	align-self: center;
	height: auto;
	right: auto;
	margin: 0;
	padding: 0 4px;
	line-height: 1;
}

/* ==========================================================================
   .al-grid-natural — a grid keeps the column widths it declares.

   Kendo shares leftover width between every column that declares one, so five
   declared widths meant a wide screen grew all five by the same proportion. On
   Items at 1920 that was +41% each: Item went 340px -> 482px to hold a longest
   name of 142px, and the eye crossed ~350px of nothing between the name and its
   category on every row. The slack is not wanted by any column -- 913px of it on
   Items, 986px on Cycle Count, 1855px on Strains -- so distributing it evenly
   puts a void in every gap rather than giving room to something that needs it.

   Under this class the columns stay at their declared widths and the leftover
   collects to the right of the last one, where nothing follows it. Narrow screens
   are untouched: max-content never shrinks below the declared total, so a grid
   too wide for its container still overflows and scrolls exactly as before.

   OPT-IN, one grid at a time. It changes what a page looks like on a wide monitor,
   so a grid takes it when somebody has looked at that grid.
   ========================================================================== */
.al-grid-natural {
	width: -moz-max-content;
	width: max-content;
	max-width: 100%;
}
.al-grid-natural .k-grid-header table,
.al-grid-natural .k-grid-content table {
	width: -moz-max-content;
	width: max-content;
}
