/* THE HOST LINKS THE FONTS, NOT THIS FILE.
 *
 * This file is a build input only. sources.json names it, build.js strips any font-loading at-rule
 * on the way into ClientUi/dist, and dist is what both hosts load (Voyager's SharedUiBundle includes
 * ~/Content/ClientUi/datablaze.ui.css; m2mNet links the copied artifact). build.js also throws if
 * that at-rule's keyword survives ANYWHERE in the text, comments included, so it is never written
 * here.
 *
 * A host adopting this package links Poppins and Roboto (the data face) in its own head. See
 * SharedUiDelivery.cs for the rest of the adoption notes.
 */

:root {
    /* ------------------------------------------------------------------------------------------
       THE STYLE GUIDE'S OWN VOCABULARY.

       docs/style/STYLE_GUIDE.md is the specification these values come from, and this block is the
       only place they are written down. Everything below resolves back to one of these rather than
       repeating a hex.

       THE BRAND BAND. --dz-accent and the values after it are the Datablaze brand values. A theme
       redeclares, on :root, each hook it needs: accent, on-accent, hover, active, 300, 50, ring and
       the two dark hooks. Nothing in the band is derived in CSS, because a tenant's hover may be a
       different hue from its accent (COBRANDING.md); no use site repeats #e45325.
       ------------------------------------------------------------------------------------------ */
    --dz-font-family: 'Poppins', 'Roboto', 'Segoe UI', Arial, sans-serif;
    /* THE DATA FACE. The guide pairs two families: Poppins "sets everything that is interface" and
       Roboto "sets data values - identifiers, addresses, timestamps, log lines, table body cells,
       metric readouts". A label is chrome and a value is what someone came to read, so the two are
       separated by face rather than by weight.

       KNOWN DEVIATION: .db-field's value is set in Poppins, where the guide's field table specifies
       "400 14px Roboto". Re-facing it is a separate pass; pointing it at this token is then a
       one-line change. */
    --dz-font-family-data: 'Roboto', 'Segoe UI', Arial, sans-serif;

    /* WL-20a fix round 1 (ClickUp 86894yg24), Brad's decision A77: --dz-grid-data-font-family is
       deliberately NOT declared here. Read by 02-grid-shell.css as
       `var(--dz-grid-data-font-family, var(--dz-font-family))` -- the sanctioned undeclared-with-
       inline-fallback pattern (--dz-card-header-control-height, further down, is the other one;
       this file's own token-fallback gate, stylesheet-tokens.harness.js, forbids an inline fallback
       for any token :root DOES declare, exactly to stop a second, ungoverned copy of a real
       default from drifting). Every grid on every untouched tenant must keep rendering its data
       columns in Poppins (the interface font, the fallback's own value) on ship day --
       --dz-font-family-data above already defaults to Roboto, so this token exists precisely so
       grid data cells are NOT wired to it directly. A later item's compiler writes
       --dz-grid-data-font-family only when a tenant's profile sets typography.data; until then the
       fallback resolves it to the interface font, same as before this token existed. */

    --dz-accent: #e45325;
    /* What sits ON a filled accent. A token because it is not white for every cobrand: the login
       page's CSG/Periscope accent resolves to #FBBF52, which takes dark text (m2mNet computes the
       same answer server-side as LoginTheme.AccentInk). Without this hook a light-accent cobrand
       gets an invisible tick in the grid's selection checkbox. */
    --dz-on-accent: #fff;
    --dz-accent-hover: #c8431a;
    --dz-accent-active: #a9361f;
    --dz-accent-300: #f0916f;
    --dz-accent-50: #fcede7;
    --dz-accent-ring: rgba(228, 83, 37, 0.4);
    /* The wash on the dark ground: a near-white wash on a dark card is a light patch, not a tint. */
    --dz-accent-dark-50: #3a2018;
    /* The focus ring on the dark ground, stronger so it still reads against the dark card. */
    --dz-accent-dark-ring: rgba(228, 83, 37, 0.55);

    /* FOCUS, AS ITS OWN BAND.
     *
     * Focus defaults to the brand because the design asks for it (Login.dc.html declares
     * `#login input:focus { border-color: #E45325 }`). A cobrand may want focus neutral without
     * giving up the accent elsewhere, so focus rules read these rather than --dz-accent.
     *
     * Three levels, each falling through to the one above:
     *   --dz-focus-color / --dz-focus-ring  - every focus affordance in the product.
     *   --dz-control-focus-*                - text editors only.
     *   --dz-grid-focus-color               - the grid's focused-cell overlay only.
     * A tenant that wants no brand on focus at all redeclares the first pair and stops. */
    --dz-focus-color: var(--dz-accent);
    --dz-focus-ring: var(--dz-accent-ring);
    --dz-control-focus-color: var(--dz-focus-color);
    --dz-control-focus-ring-color: var(--dz-focus-ring);
    --dz-grid-focus-color: var(--dz-focus-color);
    /* The tab strip's keyboard ring, through --dz-focus-ring rather than the brand band, so a tenant
       who repoints focus to house vocabulary moves this ring too. */
    --dz-tabs-focus-ring: var(--dz-focus-ring);

    /* THE REST OF THE BRAND SURFACE, on the same principle as the focus band: every construct that
     * resolves the accent names its OWN token, defaulting to the accent. A cobrand can say "not
     * here" about any single place the brand appears without editing a rule; overriding --dz-accent
     * alone still moves all of them.
     *
     * COBRANDING.md's warning applies to each: the ink on a filled brand shape is its own token
     * because it is not light for every tenant. --dz-selection-ink is that hook here. */

    /* Anything that says "this one is chosen": checked and indeterminate checkboxes in the grid and
       in the filter popup, and the density radio. The segmented control's fill sits one step down
       the ramp - see below. */
    --dz-selection-color: var(--dz-accent);
    --dz-selection-ink: var(--dz-on-accent);
    /* The UNCHECKED checkbox's fill: the card, so a light theme draws the plain white box (owner
       direction). A dark band repoints it to its sunken tone so the box separates from a dark row. */
    --dz-checkbox-fill: var(--dz-card);
    /* THE TICKED CHECKBOX everywhere (PP-D82, Cole: "Cobrand everywhere"): forms, detail pages, toolbars,
       filter lists and script-built boxes read the same selection colour the grid's box does, so there
       is one cobrand checkbox. Checked and indeterminate fill and border are --dz-selection-color and
       the tick is --dz-selection-ink, so a cobrand that repoints the selection pair moves every box.
       Unchecked, hover, focus, disabled and readonly are untouched. It used to be the plain card box
       with a house-blue tick (owner direction, 2026-10-07), which is what an untouched tenant saw.

       The tick on the fill, for the default accent #e45325 with white: 3.78:1 (the grid's own box,
       and above the 3:1 floor for a graphical object). A custom accent brings its own ink with it
       (--dz-on-accent, derived by the compiler): #1565c0 with white 5.75:1, #654321 with white 8.83:1,
       light #ffd600 with black 14.87:1. */
    --dz-checkbox-checked-fill: var(--dz-selection-color);
    --dz-checkbox-checked-border: var(--dz-selection-color);
    --dz-checkbox-tick-color: var(--dz-selection-ink);
    /* A111 (ClickUp 86894yg24, WL-33, Brad's ruling): when a tenant customizes Primary's own
       BACKGROUND, WhitelabelCompiler.EmitCheckboxAndRadioFollowPrimary repoints the three tokens
       above to var(--dz-button-primary-background)/-border-color/-color -- pure aliases, never
       re-derived, the same shape EmitSubmitFollowsPrimary already uses for Submit. An untouched
       tenant (or an ink/hover-only customization) never reaches that call, so these three keep
       the selection-colour values above (PP-D82; before it, the plain card box and link-blue tick) --
       "There is NO new setting." A Primary-background customization also moves --dz-accent to the
       same colour, so since PP-D82 this follow is redundant for the colour and is kept as the tenant's
       explicit statement about Primary.

       RADIO (PP-D83, Cole: "THESE SHOULD ALL BE COLORED THE SAME"): a checked radio is painted like a
       checked checkbox. The RING is filled with the selection colour and bordered with it, and the DOT is
       the selection ink (its own 5px border is the ring's fill), so every selected radio is the brand's
       and a cobrand that repoints the selection pair moves checkboxes and radios together. It used to be
       dx.light's white ring around a #337ab7 dot, which WL-33 round 2 kept for every untouched tenant
       by leaving the ring tokens undeclared and reading each with an inline fallback; the default is now
       declared here and the fallbacks are gone (22-devextreme-adapter.css answers each state).
       The dot on the ring, the checkbox's tick pairing: default accent #e45325 with white 3.78:1; the
       WL-33 profiles' #1565c0 with white 5.75:1, #654321 with white 8.83:1, light #ffd600 with black
       14.87:1 (a dot disc inside a ring is a graphical object, 3:1 floor).
       When Primary's background is customized the same gate repoints all four to Primary's fill,
       border, ink and focus ring, as it does the checkbox trio. */
    --dz-radio-checked-fill: var(--dz-selection-color);
    --dz-radio-checked-border: var(--dz-selection-color);
    --dz-radio-checked-dot-color: var(--dz-selection-ink);
    /* A keyboard-focused CHECKED radio's ring: its edge now matches its fill, so the package focus ring
       is the cue (the Insert Link button's and 07-button-roles.css's treatment). The compiler repoints it
       to Primary's focus ring with the rest of the radio set. */
    --dz-radio-checked-focus-ring: 0 0 0 var(--dz-control-focus-width) var(--dz-control-focus-ring-color);
    /* NATIVE (non-DevExtreme) checkbox/radio pickers -- the Invite Link edit page's Link Type radios,
       its Plans/Roles/Marketplace Items checkbox lists, and its Marketplace Required checkbox all
       render a plain HTML <input>, which has no DevExtreme icon for the rules above to repaint.
       `accent-color` is the native equivalent (25-choice-cards.css's own `.db-choice-card
       input[type="radio"]` already uses it, for the always-brand-coloured choice-card construct).
       Since PP-D82/PP-D83 every native checkbox and radio takes --dz-selection-color (22-devextreme-
       adapter.css); this hook is the gate's, repointed to Primary's fill with the DevExtreme tokens
       above, and the Invite Link page's own rule still reads it. */
    --dz-native-control-accent: var(--dz-selection-color);
    /* THE SEGMENTED CONTROL. The chosen option is FILLED WITH THE TENANT'S ACCENT and the others are
       white (owner direction), so a cobrand's toggles carry its brand.
     *
     * ONE STEP DOWN THE ACCENT RAMP, for the same reason as --dz-interactive-primary below (AA wins):
     * the label is 13px SemiBold, and white on --dz-accent measures 3.78:1 against a 4.5:1 floor,
     * while on --dz-accent-hover it is 4.91:1. That is the pair --dz-button-action-selected-* draws,
     * and the ink is --dz-on-accent because a light tenant accent needs dark ink.
     *
     * Its own tokens rather than --dz-selection-* directly, so a tenant that wants the neutral
     * reading (a white option, ink text) sets --dz-segmented-selected to var(--dz-card) and
     * --dz-segmented-selected-ink to var(--dz-ink) without moving the checkboxes. */
    --dz-segmented-track: var(--dz-card);
    --dz-segmented-selected: var(--dz-accent-hover);
    --dz-segmented-selected-ink: var(--dz-on-accent);
    /* The track's outline. In a .db-card-header the track and the band behind it can be the same
       tone, so this line is what draws the control (15-segmented.css); a theme whose hairline is too
       quiet for that repoints it here. */
    --dz-segmented-border: var(--dz-hairline);
    /* The unselected-option HOVER tone (15-segmented.css), a third tone of its own -- distinct from
       both the track and the selected fill (deliberately NOT --dz-sunken/--dz-card: whitelabel WL-27
       derives a tenant-specific hover strictly between those two once Surface is customized; an
       untouched tenant keeps this page-tone alias exactly). */
    --dz-segmented-hover: var(--dz-page);
    /* The ground under a selected row in the layout panel, and the bar at its leading edge. */
    --dz-selection-tint: var(--dz-accent-50);
    /* The bar DevExtreme draws while a column is being dragged to a new position. */
    --dz-grid-separator-color: var(--dz-accent);
    /* A column whose funnel is filtered. The unfiltered one is --dz-body and is not brand at all. */
    --dz-grid-funnel-active-color: var(--dz-accent);
    /* The metric tile's hover edge. */
    --dz-card-hover-border-color: var(--dz-accent);
    /* The layout panel's per-row action chip on hover. */
    --dz-layout-action-hover-color: var(--dz-accent);
    /* The usage bar's normal band. The 300 step rather than the accent, so it reads as data rather
       than as an action. */
    --dz-usage-normal-color: var(--dz-accent-300);

    /* THE LOGO'S YELLOW, named with its restriction attached. The design system: "Flame hues
       (yellow #FDD501, red #ED1C24) belong to the logo and, sparingly, data-viz - not UI chrome."
       The --dz-status-* roles below remain the answer for anything reporting a state.
       The sanctioned exception is a FILL BEHIND INK: #fdd501 is 1.6:1 on white and cannot carry text
       or a border, but ink #31302f on #fdd501 measures 11.9:1. The Billing Center mock uses it that
       way only: one 16px info chip per row. */
    --dz-flame-yellow: #fdd501;
    /* THE INK THAT FILL CARRIES, a literal rather than var(--dz-ink) on purpose. The yellow does not
       change with the theme, so neither may the ink on it: the dark theme flips --dz-ink to
       near-white, and white on #fdd501 is 1.6:1 (measured on Billing Center's note chip). The light
       value is --dz-ink's, so light mode is unchanged. */
    --dz-on-flame-yellow: #31302f;
    /* THE INK ON A BRIGHT FILL THAT IS NOT THE YELLOW, the same literal for the same reason: Endpoint
       Dashboard's Offline tile keeps its #ed891f in both themes, where white measured 2.56:1 and this
       measures 5.14:1. Its own hook, so repointing the yellow's ink never moves this. */
    --dz-on-bright-fill: #31302f;

    /* The neutral spine. Nine steps, ink to card, and the interface is carried by these rather
       than by colour. */
    --dz-ink: #31302f;
    --dz-subhead: #4c4948;
    --dz-body: #666261;
    /* The guide's Meta is #918d8b, which is 3.29:1 on --dz-card. This token paints real 11px text
       (.db-layout-panel__caption, the grid's no-data text) where WCAG AA wants 4.5:1. #797573 is the
       same hue at 4.56:1 - the smallest darkening that clears the floor with a margin (#7a7674 lands
       on 4.4957). A deliberate deviation: the guide's legibility floor outranks its swatch. Pinned
       by datablaze.ui.contract.harness.js. */
    --dz-meta: #797573;
    /* An editor's placeholder. Meta by default; its own hook so a theme can move meta text without
       making a placeholder read like a typed value. */
    --dz-placeholder-color: var(--dz-meta);
    --dz-border: #c4c0be;
    --dz-hairline: #e2dfdd;
    --dz-sunken: #f0eeec;
    --dz-page: #f8f7f5;
    /* WL-20a (ClickUp 86894yg24, A71): the SURFACE every card, grid and editor sits on -- white,
       deliberately NOT derived from --dz-page-canvas, because A71's owner ruling is that a card/
       grid/editor surface does NOT follow a tenant's page-background choice: --dz-page-canvas and
       this token are siblings, not parent and child. Today's readers (--dz-card below, and
       --dz-control-background further down) already resolved to exactly this value under their own
       names; this is the single hook the WL-19 audit's gap G7/G12/G21 asked for, so a later item's
       compiler can move every one of them from one place. See the audit's "Surfaces" item for the
       reasoning and the full reader list. */
    --dz-surface: #fff;
    --dz-card: var(--dz-surface);
    /* WL-38 (ClickUp 86894yg24): THE PLATE A THIRD-PARTY LOGO SITS ON. A carrier or vendor mark is
       third-party artwork drawn for a white ground (dark strokes on a transparent PNG), so it sits
       on a fixed light plate in EVERY theme. DELIBERATE EXCEPTION to the rule that every :root
       token is restated for dark: voyager.darkmode.css and a tenant's Surface must NOT repoint
       this one, because the artwork does not change with the theme, only the ground behind it
       would. Not derived from --dz-card/--dz-surface for the same reason. The edge is its own
       token because the plate has to read against a dark card as well as a light one: a
       mid grey clears the 3.0:1 non-text floor on the plate and on a dark card. Callers: the
       logo-plate component (38-logo-plate.css), the island SIM card mark and the Marketplace
       shipping carrier logo. NOT for a tenant's own brand logo, which is the tenant's choice. */
    --dz-logo-plate: #fff;
    --dz-logo-plate-border: #8f8b89;
    /* WL-18a (ClickUp 86894yg24): the app canvas behind every card/grid, outside the navbar and
       side menu (SharedMvc/Content/Site.css #app-content) -- the whitelabel palette's own
       `pageBackground` hook. Deliberately its OWN property rather than --dz-page above: --dz-page
       already has real paint (grid pager/hover/alternate-row backgrounds) at its own default of
       #f8f7f5, so pointing #app-content at it would repaint grid chrome as a side effect of a
       tenant setting a page background and would give an unset profile #f8f7f5, not white. */
    --dz-page-canvas: #fff;

    /* Links are deep teal so they never compete with the accent. Underline on hover only. */
    --dz-link: #0a6188;
    --dz-link-hover: #063a52;

    /* ------------------------------------------------------------------------------------------
       WHITELABEL SHELL HOOKS (ClickUp 86894yg24, WL-1). One custom property per
       WhitelabelProfile.palette key (WebApps/Voyager/Voyager/Scripts/island/contracts/
       whitelabel-profile.schema.json), each defaulting to TODAY'S rendered value so an inactive
       profile -- every tenant, until WL-4 activates one -- repaints nothing. The full map, which
       rule reads each one, and the ones nothing reads yet, are in ClientUi/contract/
       ui.manifest.json's "whitelabelHooks" block; read that alongside this comment.

       accent, accentHover, onAccent, buttonPrimaryBackground/-Hover, linkHover and
       typography.interface/data all reuse tokens already declared above (--dz-accent,
       --dz-accent-hover, --dz-on-accent, --dz-interactive-primary/-hover, --dz-link-hover,
       --dz-font-family(-data)) and are not repeated here. brandOnActions is not a single
       property: the compiler (WL-3/4) repoints --dz-interactive-primary/-hover/-active/-ring to
       the neutral commit scale (--dz-interactive-neutral/-hover/-active/-ring) and
       --dz-button-primary-color/-hover-color to var(--dz-ink) when false, and leaves all six
       alone -- their existing accent defaults, unchanged -- when true. See the manifest entry
       for the full six-property list. ------------------------------------------------------- */

    /* NAV = the 52px top bar (Site.css's .navbar-dark / .voyager-navbar), NOT the rail below it.
       An earlier pass at this pair read voyager.chrome.css's rail comment ("this bar is #24292a
       in BOTH themes") and cited the RAIL's own ink, #c4c0be -- the wrong surface. Corrected here
       against the bar's real selectors: Site.css's `.navbar-dark` background and its
       `a.nav-link` / `.white-text` text colour. --dz-nav-background keeps the same VALUE either
       reading gives, since the rail and the bar share #24292A today; --dz-nav-ink is the one that
       actually changes, from #c4c0be to #ffffff. */
    --dz-nav-background: #24292A;
    --dz-nav-ink: #ffffff;
    /* WL-1 round 2 (DESIGN.md A18): wired into Site.css's `.navbar-dark` as
       `border-bottom: var(--dz-nav-rule-width, 0px) solid var(--dz-nav-rule, transparent)`.
       Zero width is provably invisible AND layout-neutral -- a 0-width border occupies no box
       space regardless of colour -- reproducing the schema's own "Absent = none" exactly until a
       tenant sets BOTH properties. */
    --dz-nav-rule: transparent;
    --dz-nav-rule-width: 0px;

    /* MENU = the rail's flyout, wired at the DECLARATION of Site.css's
       --color-primary-bg/--color-text/--color-hover-bg/--color-hover-text rather than at
       #sidebar/.flyout's own use sites. Covenant's live custom.css redeclares those four names at
       :root (its RV category) and wins there on source order today; wrapping the DECLARATION
       keeps that override winning exactly as it does now. Wrapping the use sites instead would
       have put Covenant's override on the LOSING side of a var() fallback the moment a profile
       ever activates -- the compiler would declare --dz-menu-background at :root, and Covenant's
       own --color-primary-bg redeclaration would stop reaching #sidebar entirely. See Site.css
       itself for the four lines this feeds; --color-hover-bg/--color-hover-text also feed the
       RAIL's own :hover/.hovered treatment, so the two surfaces share one hover pair on purpose. */
    --dz-menu-background: #24292A;
    --dz-menu-ink: #ffffff;
    --dz-menu-hover-background: #D3D3D3;
    --dz-menu-hover-ink: #24292A;
    /* WL-1 round 2 (DESIGN.md A18): wired into Site.css's rail-hover and flyout-link-hover rules
       as `box-shadow: inset 3px 0 0 0 var(--dz-menu-indicator, transparent)`. An inset shadow
       rather than a border, per the hard constraint ("never a border that adds width") -- a
       box-shadow never contributes to layout size, so it is invisible AND layout-neutral by
       construction, not merely by choosing transparent. */
    --dz-menu-indicator: transparent;
    /* WL-1 round 2: wired into Site.css's `.flyout h5` as
       `background-color: var(--dz-menu-header-background, transparent)`. Transparent is today's
       real computed value (the h5 has no ground of its own; it shows --dz-menu-background through
       it), so this default is invisible until a tenant sets it explicitly -- at which point the
       header strip gets its OWN ground, layered over the flyout's. */
    --dz-menu-header-background: transparent;

    /* HEADER = the surface card/panel headers (13-detail-segments-card-header.css's
       .db-card-header/.db-card__header and 14-carrier-badge.css's legacy .rounded-header) use.
       Reads Sunken ground and Ink text off the spine today, same as the grid header pair below --
       but as of WL-24 FIX ROUND 1 (PM ruling D1) that is two INDEPENDENT reads of the same spine
       value, not one shared hook: grid/tree-list column headers (--dz-grid-header-background/-color
       below) read --dz-sunken/--dz-ink DIRECTLY, not this pair, so a tenant's headerBackground/
       headerInk move card headers and .rounded-header but NOT a grid's column-header row. See that
       token's own comment for why.

       Tab strips are NOT wired to this pair (DESIGN.md A18 confirms this stays open):
       04-tabs.css's underline-tab redesign (2026-09-18) deliberately paints `.dx-tabs-wrapper {
       background-color: transparent }` -- a different value than this pair's #f0eeec default, on
       purpose, so the tab strip reads as lighter than the header above it. Forcing one hook over
       both would mean either a mismatched default per surface or visibly filling in a strip the
       redesign emptied on purpose; Covenant's own TB rules stay in customCss via the migrator
       instead (per A18). */
    --dz-header-background: var(--dz-sunken);
    --dz-header-ink: var(--dz-ink);
    /* WL-20a fix round 2 (ClickUp 86894yg24), D2: --dz-header-hover-background is DELIBERATELY NOT
       declared at :root any more. It was `var(--dz-header-background)`, fixed at :root's own value
       forever -- so `.db-grid--card`, which at the time redeclared --dz-grid-header-background to
       --dz-page for its own lighter header strip, could not make the hover token follow along: every
       card grid (Top Data Users, every account profile card) stepped from its own #f8f7f5 header to
       the FULL-PAGE grid's #f0eeec hover on every tenant, profile or not, and redeclaring it again on
       .db-grid--card would have blocked a tenant's own real headerHoverBackground setting from
       reaching card grids (reopening WL-19 G2).
       The fix is the same undeclared-with-fallback pattern --dz-grid-data-font-family already uses
       (00-tokens.css line ~37, A77): 02-grid-shell.css's hover rule reads
       `var(--dz-header-hover-background, var(--dz-grid-header-background))`. An untouched tenant
       gets no declaration anywhere, so the fallback wins and resolves to --dz-grid-header-background's
       own default -- hover equals rest everywhere, nothing flashes. A tenant whose profile sets
       headerHoverBackground gets it compiled onto :root as a real declaration (WhitelabelCompiler.cs
       EmitColor), which the fallback never even evaluates, so it still reaches every grid including
       card ones. See contract/ui.manifest.json's light.headerHoverBackground entry, updated to match.
       WL-24 (ClickUp 86894yg24), Brad A100: `.db-grid--card` no longer redeclares
       --dz-grid-header-background at all (02-grid-shell.css), so the "PER ARCHETYPE" resolution this
       paragraph used to describe collapsed to one value for every archetype -- the fallback mechanism
       itself needed no change, since it was always "resolve whatever --dz-grid-header-background
       resolves to for this grid", archetype or no.

       WL-24 FIX ROUND 1, PM ruling D1: --dz-grid-header-background no longer reads
       --dz-header-background at all (it reads --dz-sunken directly, see that token's own comment), so
       an untouched tenant's hover fallback now resolves to Sunken regardless of headerBackground --
       correct, since the resting ground no longer follows it either. A tenant's own explicit
       headerHoverBackground still reaches every grid exactly as before: it compiles onto :root as its
       own real declaration, independent of this chain. */

    /* WL-1 round 2 (DESIGN.md A18): popup title bars. `.dx-popup-title`'s only OTHER
       package-level rule (22-devextreme-adapter.css) is gated behind `html[data-db-theme]`,
       which the dark toggle keeps un-set in production today, so it never actually paints a
       popup. In hosts without the package (Atlas, Olympus) the rule that wins is Site.css's
       `.dx-popup-wrapper .dx-popup-normal div.dx-popup-title` / `.dx-dialog div.dx-popup-title`,
       MEASURED (both by cascade analysis and by reading DevExtreme's own dx.light.css, which this
       out-specifies) at #485254 background / #fff ink -- the same pre-2026 slate the old tab
       design used, never migrated to Sunken/Ink. Its own pair, not a repoint of
       --dz-header-background/-ink, because the two values genuinely differ today; the compiler
       sets BOTH this pair AND the header pair from headerBackground/headerInk, so one palette key
       still moves both surfaces for a tenant. In Voyager 36-legacy-popup.css wins instead: its
       ROOT (0,8,0) out-ranks those Site.css rules (at most (0,5,1)), so there this pair no longer paints a
       legacy popup title. It still paints the user-switcher popover and the mobile DateBox title,
       which ROOT excludes, and every popup title in Atlas and Olympus. */
    --dz-popup-title-background: #485254;
    --dz-popup-title-ink: #fff;

    /* TIER SHADES. THE "GR" category's column-tier0..3 shading -- a REAL, live paint site
       (corrected in round 2: Site.css has carried `.dx-header-row td.column-tier0..3` /
       `.dx-data-row td.column-tier1,3` all along; voyager.plan.index.js and
       voyager.plan.editbundle.js assign the classes today via `cssClass = 'column-tier' +
       (rank % 4)`; connectus's own legacy custom.css targets the identical selectors). Defaults
       are today's exact header-row literals -- tier0/2 and tier1/3 happen to share a value today,
       but each gets its OWN property so a tenant can move all four independently. The DATA/
       freespace row tint (#ebeeef, tier1/3 only) is NOT hooked -- no second schema slot exists to
       read it from; left a literal, flagged as a follow-up. */
    --dz-tier-0: #485254;
    --dz-tier-1: #3d4648;
    --dz-tier-2: #485254;
    --dz-tier-3: #3d4648;

    /* WL-1 round 2 (DESIGN.md A18): Site.css's `body a:hover` -- the wide legacy "LH" surface
       migrated tenant custom.css actually targets, which --dz-link-hover (declared above, a
       DIFFERENT default) must never be reused for -- see that rule's own comment. The compiler
       sets BOTH this property and --dz-link-hover from the same palette.linkHover key. */
    --dz-site-link-hover: #fe8300;

    /* WL-1 round 2 (DESIGN.md A18): the rail's own resting icon colour
       (voyager.chrome.css .primary-nav .nav-item), which that file's own comment argues against a
       DARK-MODE token for (no ground to adapt to, both themes share #24292a) -- an argument about
       light/dark parity, not about a tenant hook, so it does not forbid this one. Its own name
       rather than a repoint of --dz-menu-ink: the compiler derives it from menuInk ONLY when a
       tenant sets menuInk, and leaves it unset otherwise, so a tenant that never touches menuInk
       repaints nothing here. Default unchanged. */
    --dz-rail-ink: #c4c0be;

    /* CHART SERIES. Every current reader is server-rendered Razor
       (`Identity.VoyagerThemeJson?.ChartColorPalette?.ColorN`, Home/Index.cshtml and
       Home/Multi.cshtml) or the island's own literal list (island.css) -- neither resolves a
       --dz-* token today, so wiring either is view/island work, not a CSS hook. Declared with
       WhitelabelDefaults.Charts.Series's own order so a future reader and the platform-tier
       default agree from day one. */
    --dz-chart-series-1: #E75224;
    --dz-chart-series-2: #C0C0C0;
    --dz-chart-series-3: #444444;
    --dz-chart-series-4: #1BA1D5;
    --dz-chart-series-5: #FFD800;

    /* CATEGORY SCALE. Twelve colours that tell one KIND of record from another and say nothing else -
       not a state, not a label, not the brand. The first reader is the board card's marker
       (32-board.css), where Plan/EditBundle marks each plan with its billing strategy so plans that
       may share a pool read alike. Values are the bundle editor's own, moved here unchanged: -1 is
       the default strategy's orange, -2 to -12 the rest in the order the page cycled them. Each is
       its own property so a theme can move one that collides with its brand without moving the
       others. --dz-category-none marks a record with no category: Meta, a neutral that cannot be
       mistaken for one of the twelve.
       NOT EVERY STEP CLEARS 3:1, the non-text floor, against the card: -11 (#b4bec4) and -12 are
       under it on white, and -10 (#385362) under it on the dark card. A marker is never the only
       carrier of its category (the board card puts the category in its title), so this is
       recorded rather than fixed by moving colours the product already ships. */
    --dz-category-1: #fe8300;
    --dz-category-2: #6abe55;
    --dz-category-3: #f99a2c;
    --dz-category-4: #e45049;
    --dz-category-5: #c076dd;
    --dz-category-6: #257cbd;
    --dz-category-7: #3bc5de;
    --dz-category-8: #65ea9b;
    --dz-category-9: #f972c9;
    --dz-category-10: #385362;
    --dz-category-11: #b4bec4;
    --dz-category-12: #efd32c;
    --dz-category-none: var(--dz-meta);

    /* THE BOARD (32-board.css): lanes of cards a user drags between. A lane is a sunken well, so the
       cards on it read as lifted; a card is a card, with the grid card's hairline and shadow. */
    --dz-board-lane-background: var(--dz-sunken);
    --dz-board-lane-radius: var(--dz-radius-md);
    --dz-board-lane-title-font-size: 16px;
    --dz-board-lane-title-font-weight: 600;
    --dz-board-card-background: var(--dz-card);
    --dz-board-card-border-color: var(--dz-hairline);
    --dz-board-card-shadow: var(--dz-grid-card-shadow);
    /* Things you press or grab are sm: a board card is picked up. */
    --dz-board-card-radius: var(--dz-radius-sm);
    --dz-board-card-meta-color: var(--dz-meta);
    /* D4 (ClickUp 86894yg24), extending A105: this paints on the lane's own --dz-board-lane-background
       (var(--dz-sunken) above), not the card -- meta measures under 4.5:1 there, the same sunken-ground
       shortfall A105 found for the switch label. Body clears it, same fix. */
    --dz-board-empty-color: var(--dz-body);

    /* The radius scale, by the element's ROLE and never by a neighbouring control. Two numbers
       plus dialogs: things you press or type into are sm, things that draw a border around
       content are md. A flat 10px on a button traces to a placeholder that predates this scale. */
    /* The radius scale, by the element's ROLE and never by a neighbouring control: things you press
       or type into are sm, things that draw a border around content are md, plus dialogs. */
    --dz-radius-xs: 4px;
    --dz-radius-sm: 6px;
    --dz-radius-md: 10px;
    --dz-radius-xl: 24px;
    --dz-radius-pill: 999px;

    /* Spacing steps from an 8px base, with a 4px half-step allowed where 8 is too coarse. Anything
       off this lattice is not a spacing value. */
    --dz-space-half: 4px;
    --dz-space-1: 8px;
    /* The guide's 12px step - the gap the Subscribers mock puts between every control in both
       toolbar rows. Named for its place between 1 and 2 rather than renumbering the scale. */
    --dz-space-1half: 12px;
    --dz-space-2: 16px;
    --dz-space-3: 24px;
    --dz-space-4: 32px;

    /* THE PAGE CHROME BAND - the spacing that is not inside a grid row or a field cell.
     *
     * Named rather than written as literals so the compact band in 20-density.css is ONE list of
     * numbers, and a surface that reads one of these follows the density automatically. Every entry
     * is spacing; a control's size is never on the band (owner decision; see the band's header). */
    --dz-page-toolbar-margin-bottom: var(--dz-space-2);
    /* A view's framed panel box (.gridbox on the dashboards); 20-density.css steps it to 6px. */
    --dz-panel-padding: 10px;
    --dz-card-header-min-height: 49px;
    --dz-card-body-padding: var(--dz-space-2);
    /* .db-card-header / .db-card__header's padding and gap (13-detail-segments-card-header.css).
     *
       --dz-card-header-control-height is deliberately NOT declared here. Every reader of it takes a
       `, 32px` fallback rather than a bare var(); declaring it on :root too would be the
       inline-fallback-on-a-:root-token drift the token gate polices, and that gate names this token
       as the legitimate case because it is scoped, not :root's. */
    --dz-card-header-padding: var(--dz-space-1) var(--dz-space-1half);
    --dz-card-header-gap: var(--dz-space-2);
    /* .db-detail-segments__add's padding (13-detail-segments-card-header.css). */
    --dz-detail-segments-add-padding: var(--dz-space-1half);
    /* .db-usage's gap (16-card-usage-udf.css). */
    --dz-usage-gap: var(--dz-space-2);
    /* A DASHBOARD'S STRIPS ARE TUNED TIGHTER THAN A DETAIL PAGE'S.
     *
     * A dashboard shows seven or eight panels at once, so its card headers give a control 5px of air
     * where .db-card-header gives 12.5px - a deliberate second density, shared by Voyager's
     * Home/Index and Account/Dashboard.
     *
     * THE TWO NUMBERS MOVE TOGETHER. The padding sets the air around a control and the floor sets
     * the height of a strip holding only a title; change one alone and the two kinds of strip stop
     * agreeing. 5 + a 31px control + 4 + the strip's 1px bottom rule = 41 (.db-card--dense,
     * 13-detail-segments-card-header.css), and the floor is the same 41. */
    --dz-dashboard-header-min-height: 41px;
    --dz-dashboard-header-padding-y: 5px;
    /* The side air of a .db-card--dense strip: tighter than a detail card header's, as the
       vertical air is, so a dashboard title and its controls keep their width on a narrow panel. */
    --dz-dashboard-header-padding-x: 10px;
    --dz-dashboard-header-control-height: 31px;
    /* DevExtreme's own numbers, restated so they can move: the grid toolbar's 10px bottom margin and
       the pager's 9px padding above and below its page buttons. Measured from the widget's
       rendering, not chosen here; they change nothing at the default density. */
    --dz-grid-toolbar-margin-bottom: 10px;
    --dz-grid-pager-padding-y: 9px;

    /* A disabled control drops to this ONCE, never through a container (it compounds per nested
       widget). Form editors and checkboxes show the state through their surface (29-form.css). */
    --dz-disabled-opacity: 0.45;

    /* A109 (ClickUp 86894yg24, WL-32): a DIFFERENT disabled shape -- a translucent wash drawn OVER a
       whole grid by a positioned sibling element (Voyager's User/Edit.cshtml: the Add/Edit User
       page's Preset/Permissions grids), rather than an opacity fade of the grid's own paint. The
       default is the EXACT literal that view's own <style> block used to hard-code (same value in
       both themes there, unconditionally -- voyager.darkmode.css restates it identically for the
       same reason). A customized Surface derives its own wash (WhitelabelCompiler
       .DeriveGridDisabledOverlay) so a dark-Surface tenant's disabled grid fades toward ITS OWN
       tone instead of the platform's fixed near-white. */
    --dz-grid-disabled-overlay-background: rgba(245, 245, 245, 0.75);

    /* ------------------------------------------------------------------------------------------
       Role tokens. Each one resolves a value from the spine above.
       ------------------------------------------------------------------------------------------ */
    --dz-surface-hover: var(--dz-page);
    --dz-surface-active: var(--dz-sunken);
    --dz-control-radius: var(--dz-radius-sm);
    /* The fill of a text editor, dropdown or date box (22-devextreme-adapter.css). WL-20a (ClickUp
       86894yg24): repointed from --dz-sunken to --dz-surface. --dz-sunken was never actually what a
       LIGHT-mode text box rendered -- the adapter this token feeds is gated behind
       html[data-db-theme], which only dark mode set, so a light editor always showed dx.light.css's
       own #fff regardless of this declaration. Un-gating the adapter (see that file's own header
       comment) makes this token reach light mode for the first time, and --dz-surface's own #fff
       default reproduces dx.light's #fff exactly, so nothing repaints for an untouched tenant. Its
       own hook rather than a bare --dz-surface reference because an editor often sits in a Sunken
       card header, where a theme may want it inset instead of flush. */
    --dz-control-background: var(--dz-surface);
    /* The backdrop behind a popup (dx's .dx-overlay-shader, painted by 22-devextreme-adapter.css under
       a theme). The default is dx.light's own wash, recorded so a theme that sets nothing keeps it;
       a dark theme needs a dark scrim, because a white wash over a dark page lightens it. */
    --dz-overlay-scrim: rgba(255, 255, 255, 0.8);
    /* The backdrop behind the navigation drawer, which is the sidebar rail opened off-canvas on a
       narrow window (Voyager's voyager.chrome.css, section 8). Not --dz-overlay-scrim: that one
       is a white wash in the light theme, and a white wash beside a dark drawer reads as the
       page fading out rather than as the drawer coming forward. A dark dim works over either
       theme's page, so one value serves both. */
    --dz-drawer-scrim: rgba(0, 0, 0, 0.45);
    --dz-control-focus-width: 2px;
    /* Every interactive control on a page is 40px, and the paint fills the target. The mocks size
       every control the same and the page toolbar's icon buttons and search box are 40px, so a
       shorter button beside them would step at the top and bottom of the row. 40px clears the
       WCAG 2.5.8 AA minimum of 24px; it is below the 44px of 2.5.5 AAA. */
    --dz-interactive-target-size: 40px;
    /* The glyph in a square icon control (.db-icon-button, .db-icon-dropdown): Body, one step lighter
       than a labelled button's Ink text, at rest and on hover. An Ink glyph made the toolbar icons
       the loudest thing in the header. The role still paints the box - ground, border, hover,
       focus. */
    --dz-icon-control-glyph-color: var(--dz-body);
    /* The compact control, for a button inside chrome that is not the page's own - a fixed-height
       header strip, a grid cell. 24px is WCAG 2.5.8's Target Size (Minimum) floor exactly, which is
       why it stops there: a control that fits by becoming untappable has not fitted. */
    --dz-control-compact-height: 24px;
    /* A MENU POPUP'S FLOOR. A dxDropDownButton sizes its popup to the BUTTON unless told otherwise,
       so an icon button's menu renders as ellipsis ("Export Selected" became 36px under a 40px
       icon). The popup rule sizes a menu to its widest item, so this floor only stops a short menu
       collapsing: 120px, a labelled trigger's width. It was 200px, which drew a three-word menu
       far wider than its items (owner direction, 2026-10-07). */
    --dz-menu-min-width: 120px;
    /* A GUARD rather than a measurement - see the popup rule for why a shrink-to-fit menu needs one.
       360px is well clear of the longest item shipping ("Export Selected" at 125px) and narrow
       enough that a menu hitting it stays inside the viewport at the narrowest supported width. */
    --dz-menu-max-width: 360px;
    --dz-control-visual-height: 40px;
    /* Commit green. The positive role draws from this scale so there is a single green in the
       palette.

       THE PRIMARY ROLE DOES NOT READ THIS - see --dz-interactive-primary below, which resolves to
       the cobrand. */
    --dz-interactive-positive: #2e7d32;
    --dz-interactive-positive-hover: #286e2c;
    --dz-interactive-positive-active: #215b24;
    --dz-interactive-positive-ring: rgba(46, 125, 50, 0.4);
    /* The destructive action role: a meaning colour, so it holds across tenant themes. */
    --dz-interactive-danger: #d23a2c;
    --dz-interactive-danger-hover: #b52f24;
    --dz-interactive-danger-active: #96271f;
    --dz-interactive-danger-ring: rgba(210, 58, 44, 0.4);

    /* THE NEUTRAL COMMIT SCALE, which Save, Apply and Submit draw from.
     *
     * The directive is a light grey for save/submit. Submit has its own scale rather than repainting
     * the green, so positive keeps meaning "an affirmative action whose intent should be visually
     * explicit". A Save button takes the submit role, not the primary one.
     *
     * Ink rather than white on top: MEASURED, --dz-ink on this fill is 10.0:1, where white would be
     * 1.4:1. This is the case COBRANDING.md warns about - never assume a foreground still works on a
     * different fill.
     *
     * It does not read as disabled: disabled is --dz-disabled-opacity over the control's OWN paint,
     * so a disabled grey button is visibly a faded grey button. */
    --dz-interactive-neutral: #dcd8d4;
    --dz-interactive-neutral-hover: #cdc8c4;
    --dz-interactive-neutral-active: #bdb8b4;
    --dz-interactive-neutral-ring: rgba(102, 98, 97, 0.35);
    --dz-button-padding-y: 8px;
    /* 18px, from the mocks - "Bulk Actions" on Subscribers and "New" on Billing Center are both
       0 18px in a 40px box. */
    --dz-button-padding-x: 18px;
    --dz-button-content-gap: 8px;
    /* 14px, which DevExtreme sets on .dx-button. The DevExtreme adapter reads this token too, so
       native .db-button--native elements and DevExtreme buttons agree. */
    --dz-button-font-size: 14px;
    --dz-button-font-weight: 400;

    /* THE STANDARD BUTTON ROLE (WL-20a, ClickUp 86894yg24). Action, Cancel, Disclosure and Chrome
       below each independently read --dz-ink/--dz-card/--dz-border/--dz-surface-hover/
       --dz-surface-active today -- correct, since those ARE the neutral spine -- but there was no
       single place a tenant setting could land that moves all four together the way buttonPrimary*
       moves .db-button--primary (WL-19 audit, item 7 / G14-G16: "Bulk Actions, toolbars, dialogs").
       These seven properties are that place. A SHARED PARENT ABOVE Action and Chrome, deliberately
       NOT a repoint of Chrome onto Action's own tokens: Chrome's own comment below still applies
       ("point any line below at an Action token and the role has no reason to exist") -- Chrome
       reads this shared neutral, the same as Action does, rather than reading Action itself, so the
       two stay independently addressable while a tenant setting can still move both. Today's
       defaults resolve the exact spine values every role already used, so nothing repaints until a
       later item's compiler writes to them. */
    --dz-button-standard-color: var(--dz-ink);
    --dz-button-standard-background: var(--dz-card);
    --dz-button-standard-border-color: var(--dz-border);
    --dz-button-standard-hover-color: var(--dz-ink);
    --dz-button-standard-hover-background: var(--dz-surface-hover);
    --dz-button-standard-hover-border-color: var(--dz-border);
    --dz-button-standard-active-background: var(--dz-surface-active);

    /* An OUTLINED button draws its border from the neutral border scale, NOT from its own text token.
       Action is the only role outlined by default; a FILLED role (Submit, Next, Positive, Danger,
       Action+selected) takes a darker shade of its own fill.

       Tracking the text token (#31302f) renders a near-black box on a different scale from the
       search box and layout picker beside it. Sharing --dz-border also keeps a themed Action role
       from tinting chrome: a brand colour recolours the label and focus ring and leaves the outline
       neutral. The High contrast theme darkens this one token and every outline follows it. */
    --dz-button-action-color: var(--dz-button-standard-color);
    --dz-button-action-background: var(--dz-button-standard-background);
    --dz-button-action-border-color: var(--dz-button-standard-border-color);
    --dz-button-action-hover-color: var(--dz-button-standard-hover-color);
    --dz-button-action-hover-background: var(--dz-button-standard-hover-background);
    --dz-button-action-hover-border-color: var(--dz-button-standard-hover-border-color);
    --dz-button-action-active-background: var(--dz-button-standard-active-background);
    --dz-button-action-focus-ring: var(--dz-focus-ring);
    --dz-button-action-selected-color: var(--dz-on-accent);
    --dz-button-action-selected-background: var(--dz-accent-hover);
    --dz-button-action-selected-border-color: var(--dz-accent-hover);
    --dz-button-action-selected-hover-background: var(--dz-accent-active);
    /* WL-22 fix round 3 (PM ruling, ClickUp 86894yg24): a SEPARATE ink for the selected toggle's
       hover/pressed fill (above) -- it and the REST fill (--dz-button-action-selected-background,
       accent-hover) are two different steps of a tenant's accent ramp, so one shared ink cannot always
       be WCAG-readable on both (a common mid-tone accent has white narrowly fail the lighter REST fill
       while black narrowly fails the darker HOVER/PRESSED fill, or the reverse -- there is no ink that
       clears both at once in that case). Defaults to the SAME value as -selected-color, so an untouched
       tenant's hover/pressed ink is byte-identical to today. */
    --dz-button-action-selected-hover-color: var(--dz-button-action-selected-color);

    /* The trigger that OPENS a group of actions, as opposed to the actions inside it.

       Its own token set because the two move in opposite directions (the request: "some blue style
       on the drop down but neutral for the actual action buttons"). The defaults resolve the same
       neutral scale Action does; the role makes the trigger addressable in the Theme Lab and in a
       theme without dragging the menu entries along. */
    --dz-button-disclosure-color: var(--dz-button-standard-color);
    --dz-button-disclosure-background: var(--dz-button-standard-background);
    --dz-button-disclosure-border-color: var(--dz-button-standard-border-color);
    --dz-button-disclosure-hover-color: var(--dz-button-standard-hover-color);
    --dz-button-disclosure-hover-background: var(--dz-button-standard-hover-background);
    --dz-button-disclosure-hover-border-color: var(--dz-button-standard-hover-border-color);
    --dz-button-disclosure-active-background: var(--dz-button-standard-active-background);
    --dz-button-disclosure-focus-ring: var(--dz-accent-ring);

    /* CANCEL, AS ITS OWN ROLE. It resolves the same neutrals as Action; the point is the seam, so a
     * cobrand can restyle dismissal without repainting every supporting button. */
    --dz-button-cancel-color: var(--dz-button-standard-color);
    --dz-button-cancel-background: var(--dz-button-standard-background);
    --dz-button-cancel-border-color: var(--dz-button-standard-border-color);
    --dz-button-cancel-hover-color: var(--dz-button-standard-hover-color);
    --dz-button-cancel-hover-background: var(--dz-button-standard-hover-background);
    --dz-button-cancel-hover-border-color: var(--dz-button-standard-hover-border-color);
    --dz-button-cancel-active-background: var(--dz-button-standard-active-background);
    --dz-button-cancel-focus-ring: var(--dz-focus-ring);

    /* The inline pencil beside an editable value (`.db-edit-affordance` in
       09-edit-affordance-action-menu.css), registered in ui.manifest.json's `surfaces` block as a
       role.

       Ghost by design - ground and border stay transparent in every state, because the design draws
       a bare stroke glyph. THE FOCUS RING IS DELIBERATELY NOT PART OF THIS BAND: on an icon-only
       control the ring is all a keyboard user gets, so the rule applying this role leaves
       --dz-button-focus-ring to the band it is layered over (Action's, today). */
    --dz-button-edit-affordance-color: var(--dz-body);
    --dz-button-edit-affordance-background: transparent;
    --dz-button-edit-affordance-border-color: transparent;
    --dz-button-edit-affordance-hover-color: var(--dz-ink);
    --dz-button-edit-affordance-hover-background: transparent;
    --dz-button-edit-affordance-hover-border-color: transparent;
    --dz-button-edit-affordance-active-background: transparent;

    /* A control belonging to the GRID rather than to the page - Export, the layout gear, the column
       chooser. It sits beside a search box and a layout picker and has to read as furniture.

       THE DEFAULTS RESOLVE THE NEUTRAL SCALE, NOT --dz-button-action-*, AND THAT IS THE ROLE.
       Chrome is the one button surface a brand colour on Action cannot reach; point any line below
       at an Action token and the role has no reason to exist. It still follows the neutral scale, so
       the High contrast theme darkens its border with everything else. */
    --dz-button-chrome-color: var(--dz-button-standard-color);
    --dz-button-chrome-background: var(--dz-button-standard-background);
    --dz-button-chrome-border-color: var(--dz-button-standard-border-color);
    --dz-button-chrome-hover-color: var(--dz-button-standard-hover-color);
    --dz-button-chrome-hover-background: var(--dz-button-standard-hover-background);
    --dz-button-chrome-hover-border-color: var(--dz-button-standard-hover-border-color);
    --dz-button-chrome-active-background: var(--dz-button-standard-active-background);
    /* The focus ring stays the accent. A neutral ring on neutral chrome would be invisible to the
       keyboard user who needs it. */
    --dz-button-chrome-focus-ring: var(--dz-focus-ring);

    /* THE PRIMARY ACTION on a page: the accent, filled, with white text.
     *
     * The style guide: "Orange means 'this is the primary action'; if links were orange, every ICCID
     * in a grid of ten thousand rows would shout as loudly as the one button that matters." Billing
     * Center's "New" measures #e45325 on 0 18px in a 40px box at 400 14px with a 6px radius; the
     * Login page's one filled button is the same #e45325.
     *
     * A ROLE, NOT A SHAPE. Every other toolbar dropdown is declared `chrome` (Endpoints' Export),
     * `action` (the report exports) or `disclosure` (Subscriber detail's menu), and none takes this
     * colour. `button.toolbar.primary` has one caller - Billing Center's New.
     *
     * ONE STEP DOWN THE ACCENT RAMP (owner ruling, 2026-09-30: AA wins). White on --dz-accent was
     * 3.78:1 under a 14px/400 label; on --dz-accent-hover it is 4.91:1, the pair
     * --dz-button-action-selected-* already used. Hover and active step down with it, and active
     * mixes the accent's own bottom step toward black because --dz-accent-active was the end of the
     * ramp. Every step is the tenant's accent ramp, so a cobrand still paints its own primary, and a
     * tenant's buttonPrimaryBackground still redeclares the alias outright.
     *
     * Its own alias rather than --dz-accent directly, and separate from Submit: they are two
     * decisions, and collapsing them would make flipping one flip the other. */
    --dz-interactive-primary: var(--dz-accent-hover);
    --dz-interactive-primary-hover: var(--dz-accent-active);
    --dz-interactive-primary-active: color-mix(in srgb, var(--dz-accent-active) 80%, #000);
    --dz-interactive-primary-ring: var(--dz-accent-ring);

    /* WL-1 (ClickUp 86894yg24): repointed from a bare #fff literal to var(--dz-on-accent) --
       pixel-identical today (--dz-on-accent's own default is #fff) and it is what lets
       buttonPrimaryInk (the schema key this property IS the hook for) follow the same
       ink-over-a-brand-fill rule COBRANDING.md states for every other filled-accent surface: the
       CSG/Periscope login already needs dark text on a light accent, and this button role would
       otherwise go on reading white regardless. */
    --dz-button-primary-color: var(--dz-on-accent);
    --dz-button-primary-background: var(--dz-interactive-primary);
    --dz-button-primary-border-color: var(--dz-interactive-primary);
    --dz-button-primary-hover-color: var(--dz-on-accent);
    --dz-button-primary-hover-background: var(--dz-interactive-primary-hover);
    --dz-button-primary-hover-border-color: var(--dz-interactive-primary-hover);
    --dz-button-primary-active-background: var(--dz-interactive-primary-active);
    /* WL-22 fix round 4 (PM ruling, ClickUp 86894yg24): PRESSED gets its OWN ink, the same split the
       selected toggle's hover/pressed ink already got in round 3 (--dz-button-action-selected-hover-
       color, above) -- round 3 had hover and pressed share ONE property
       (--dz-button-primary-hover-color) via a minimax pick, which still failed BOTH floors at once for
       some accents (e.g. #ff9800: no single ink clears the hover fill and the pressed fill together).
       01-button-base.css's `.db-button--primary` block maps this onto the role-agnostic
       --dz-button-active-color that 07-button-roles.css's `.dx-state-active`/`:active` rules read
       instead of --dz-button-hover-color. Defaults to the hover ink -- an untouched tenant's pressed
       text stays byte-identical to today, which already read --dz-button-hover-color. */
    --dz-button-primary-active-color: var(--dz-button-primary-hover-color);
    --dz-button-primary-focus-ring: var(--dz-interactive-primary-ring);

    --dz-button-submit-color: var(--dz-ink);
    --dz-button-submit-background: var(--dz-interactive-neutral);
    --dz-button-submit-border-color: var(--dz-interactive-neutral-hover);
    --dz-button-submit-hover-color: var(--dz-ink);
    --dz-button-submit-hover-background: var(--dz-interactive-neutral-hover);
    --dz-button-submit-hover-border-color: var(--dz-interactive-neutral-active);
    --dz-button-submit-active-background: var(--dz-interactive-neutral-active);
    --dz-button-submit-focus-ring: var(--dz-interactive-neutral-ring);
    /* WL-31 (ClickUp 86894yg24, Brad's ruling 2026-10-08, option b): PRESSED gets its own ink,
       the same split --dz-button-primary-active-color (above) already got in WL-22 round 4 --
       without this, a tenant whose Primary pressed ink differs from Primary's hover ink (the
       whole reason that property exists) would alias onto Submit's HOVER ink for the pressed
       state instead (.db-button's own fallback, 01-button-base.css: --dz-button-active-color:
       var(--dz-button-hover-color)), reproducing the exact minimax failure round 4 fixed.
       Defaults to the hover ink -- an untouched tenant's pressed text stays byte-identical to
       today, which already read --dz-button-submit-hover-color for this state. */
    --dz-button-submit-active-color: var(--dz-button-submit-hover-color);

    /* THE ANSWER ROLE (PP-D76): the button that gives a declining answer, a No beside a Yes. It is a
     * real answer, so it must read as neither Cancel (back out: the white standard button) nor the
     * Yes (the commit: the Submit grey), and the owner picked a tinted step one under Submit: the
     * sunken fill, bordered in the commit scale's resting grey. Neutral scale only and never the
     * brand, like Submit (COBRANDING.md Layer A), with its own tokens so a tenant can repoint it.
     * Hover steps up to the commit scale's resting grey, one step under Submit's hover.
     *
     * MEASURED on the footer ground (--dz-dialog-footer-background, --dz-page): --dz-ink on the
     * fill is 11.38:1, on the hover fill 9.29:1 and on the pressed fill 7.94:1, all over the 4.5:1
     * text floor. The fill alone is 1.08:1 against the footer and the border 1.32:1; Submit's own
     * fill measures the same 1.32:1 and its border 1.55:1, Cancel's border 1.69:1. None of the
     * neutral buttons clears 3:1 as a shape, so the label carries the control, as it does there. */
    --dz-button-answer-color: var(--dz-ink);
    --dz-button-answer-background: var(--dz-sunken);
    --dz-button-answer-border-color: var(--dz-interactive-neutral);
    --dz-button-answer-hover-color: var(--dz-ink);
    --dz-button-answer-hover-background: var(--dz-interactive-neutral);
    --dz-button-answer-hover-border-color: var(--dz-interactive-neutral-hover);
    --dz-button-answer-active-background: var(--dz-interactive-neutral-hover);
    --dz-button-answer-focus-ring: var(--dz-interactive-neutral-ring);

    /* Next keeps its blue. #2563eb / #1d4ed8 / #1e40af is an off-palette ramp with no design
       source, but the harness asserts Next must NOT resolve the accent - a deliberate decision no
       mockup overturns (the Login page's only Next renders disabled). Open question, not a value
       to guess at. */
    --dz-button-next-color: #fff;
    --dz-button-next-background: #2563eb;
    --dz-button-next-border-color: #1d4ed8;
    --dz-button-next-hover-color: #fff;
    --dz-button-next-hover-background: #1d4ed8;
    --dz-button-next-hover-border-color: #1e40af;
    --dz-button-next-active-background: #1e40af;
    --dz-button-next-focus-ring: rgba(37, 99, 235, 0.4);

    --dz-button-positive-color: #fff;
    --dz-button-positive-background: var(--dz-interactive-positive);
    --dz-button-positive-border-color: var(--dz-interactive-positive-hover);
    --dz-button-positive-hover-color: #fff;
    --dz-button-positive-hover-background: var(--dz-interactive-positive-hover);
    --dz-button-positive-hover-border-color: var(--dz-interactive-positive-active);
    --dz-button-positive-active-background: var(--dz-interactive-positive-active);
    --dz-button-positive-focus-ring: var(--dz-interactive-positive-ring);

    --dz-button-danger-color: #fff;
    --dz-button-danger-background: var(--dz-interactive-danger);
    --dz-button-danger-border-color: var(--dz-interactive-danger-hover);
    --dz-button-danger-hover-color: #fff;
    --dz-button-danger-hover-background: var(--dz-interactive-danger-hover);
    --dz-button-danger-hover-border-color: var(--dz-interactive-danger-active);
    --dz-button-danger-active-background: var(--dz-interactive-danger-active);
    --dz-button-danger-focus-ring: var(--dz-interactive-danger-ring);

    --dz-dropdown-color: var(--dz-ink);
    --dz-dropdown-background: var(--dz-card);
    --dz-dropdown-border-color: var(--dz-border);
    --dz-dropdown-hover-background: var(--dz-surface-hover);
    --dz-dropdown-active-background: var(--dz-surface-active);
    --dz-dropdown-focus-ring: var(--dz-accent-ring);
    --dz-dropdown-content-padding: 8px;
    --dz-dropdown-toolbar-width: 180px;
    --dz-dropdown-trigger-width: 36px;
    /* THE DISCLOSURE CARET on a dropdown BUTTON, and its gap from the label, measured off the
       Billing Center mock's New control: a 13px chevron in a `display:inline-flex; gap:8px` span.
       Between the grid's 12px group caret and 14px row caret, because it sits beside 14px button
       text rather than inside a row. */
    --dz-dropdown-caret-size: 13px;
    --dz-dropdown-caret-gap: var(--dz-space-1);

    /* A customer label is a coloured outline over a 10% wash of its own colour, at 12px, with the
       label colour carried by the text as well as the border. This is the Subscribers mock, chosen
       over the guide by the owner.

       The guide asks for a 2px outline at 14px with body-grey text, on contrast grounds: label
       yellow #a8940a measures 3.04:1 on white (just over the 3:1 floor for a meaningful shape) and
       2.84 on the page tint, where it fails. revising-the-style-guide.md calls the colour and the
       2px weight "one fix in two halves".

       The trade is taken deliberately: at 1px the lightest two of the ten label colours - yellow and
       mint - are hard to pick out, harder still on a tinted surface. The wash is what makes the chip
       findable at this weight. If those two are a real problem in use, darken them; that does not
       require going back to 2px.

       --dz-badge-background stays `transparent` here: the wash is a colour-mix of the label's OWN
       accent, which only exists on the element, so it is declared in voyager.badges.css on
       .db-badge--label. A browser without color-mix() falls back to this transparent and gets the
       plain outline. */
    --dz-badge-font-weight: 500;
    --dz-badge-padding-y: 3px;
    --dz-badge-padding-x: 8px;
    --dz-badge-radius: var(--dz-radius-sm);
    --dz-badge-neutral-accent: #918d8b;
    --dz-badge-surface: #fff;
    --dz-badge-color: var(--dz-body);
    --dz-badge-background: transparent;
    --dz-badge-border-width: 1px;
    --dz-badge-font-size: 12px;
    --dz-badge-line-height: 1.3;
    --dz-badge-gap: 6px;
    /* The outline chip's size: 14px rather than a label chip's 12px, as both mocks draw them. A
       label is metadata the row carries; a status answers "what is this row", so it sits at the
       row's own text size. */
    --dz-badge-outline-font-size: 14px;
    /* How much of the accent washes the fill. One value so every outline chip tints by the same
       amount whatever colour it carries. */
    --dz-badge-outline-tint: 10%;

    /* The selection block's gap and weight, measured off the Subscribers mockup and off the scales
       on purpose: 22px is not an 8px-grid step and 700 is heavier than any other control text. The
       mockup draws "N selected · Assign Labels · Transfer" that way because the row appears only
       when a selection exists and has to read as a state change rather than toolbar furniture.
       Named so the two values move together. */
    --dz-selection-actions-gap: 22px;
    --dz-selection-actions-font-weight: 700;

    /* A DETAIL FIELD - the micro-label over its value, measured off the detail mocks. The label is
       12px, below the guide's 14px interface size, because it names the thing below it rather than
       being text to read. */
    --dz-field-label-font-size: 12px;
    --dz-field-label-font-weight: 600;
    --dz-field-label-gap: 4px;
    /* The height of a field cell in a detail grid, so every row of them agrees.
     *
     * 12px padding + a 16px label + a 6px gap + a 22px value line + 12px padding = 68. The value
     * line is 22px because the tallest thing that legitimately sits on it is a carrier badge; the
     * edit affordance is capped to 20px. Without a floor the rows measure 65 / 74 / 86 depending on
     * content.
     *
     * 54px at compact - 20-density.css's compact band is the only place this moves. */
    --dz-field-cell-min-height: 68px;
    /* A .db-field-list row's padding: the detail grid cell's 12px, so a field reads the same in
       either layout. 6px at compact, in 20-density.css's band. */
    --dz-field-list-cell-padding: 12px;
    --dz-field-value-font-size: 14px;
    --dz-field-value-font-weight: 500;

    /* A FORM FIELD - a detail field whose value is an editor (29-form.css). Its label reads the
       three --dz-field-label-* tokens above, so a settings page and a detail page name a field the
       same way.
     *
     * Help text is the guide's "Small text. Table cells, helper text, metadata": 14px regular in
     * Body. The vertical rhythm is the lattice: 16px between fields, 24px above a section title.
     *
     * THE STATE COLOURS ARE HOOKS over the status scale's danger and warning accents, so a tenant
     * who moves that scale moves the form with it. dx.light paints these states with its own
     * #d9534f family; 29-form.css repoints each rule that sets one.
     *
     * THE REQUIRED MARK IS THE DANGER ACCENT, the one an invalid field already uses, so "required" and
     * "this field is required" are one colour. Body read as grey beside the ink labels, and dx.light's
     * #c9302c is a literal no tenant can repoint. */
    --dz-form-field-gap: var(--dz-space-2);
    --dz-form-section-gap: var(--dz-space-3);
    --dz-form-section-title-font-size: 14px;  /* the card title's size: a section is a titled region of one card */
    --dz-form-section-title-font-weight: 600;
    --dz-form-help-color: var(--dz-body);
    --dz-form-help-font-size: 14px;
    --dz-form-message-font-size: 13px;  /* one step under help: it sits on the same line slot and must not outweigh the value */
    --dz-form-required-color: var(--dz-status-danger-accent);  /* red, the same accent as an invalid field: 5.70:1 */
    --dz-form-invalid-color: var(--dz-status-danger-accent);
    /* The focus ring of an invalid editor, at the same 40% as the brand ring (--dz-accent-ring) and
       the danger button's ring, but mixed from the hook so it follows a repointed scale. */
    --dz-form-invalid-ring: color-mix(in srgb, var(--dz-form-invalid-color) 40%, transparent);
    --dz-form-warning-color: var(--dz-status-warning-accent);
    --dz-form-actions-gap: var(--dz-space-1);

    /* A NOTIFICATION - the toast datablaze.ui.notify raises (30-notify.css).
     *
     * The kind colours are HOOKS over the status scale, like the form's. They colour the toast's
     * leading EDGE, not its fill: white on the warning accent is 4.14:1, under the 4.5:1 floor for
     * 14px text, so dx.light's solid fill with white words is a defect for any tenant. On the card
     * surface the words are ink and pass for every kind. The edge is 4px because that reads as a
     * colour at a glance on a ~40px toast; the hairline border is the card's own. */
    --dz-notify-success-color: var(--dz-status-active-accent);
    --dz-notify-info-color: var(--dz-status-info-accent);
    --dz-notify-warning-color: var(--dz-status-warning-accent);
    --dz-notify-error-color: var(--dz-status-danger-accent);
    --dz-notify-edge-width: 4px;
    --dz-notify-font-size: 14px;
    /* A CODE BLOCK - the preformatted structured text datablaze.ui.codeBlock.json writes
     * (37-code-block.css), on the card ground with a hairline border.
     *
     * Key, string and number are HOOKS over the status text hooks, not the status accents, because
     * those hooks already hold the measured dark value (voyager.darkmode.css lifts each one) and are
     * the text answers the floor needed. On the card, light / dark: key (info) 5.77 / 6.34,
     * string (active) 4.89 / 5.83, number (warning) 5.76 / 7.25. The literal (true/false/null) is
     * the neutral meta text, 4.56 / 14.35: a status hue would read as a verdict, and red in a log
     * viewer reads as an error. They were measured on the card and not on --dz-sunken: the active
     * green is 4.23:1 on the light sunken tone, under 4.5:1. A theme that repoints a role keeps it
     * on the card's contrast. */
    --dz-code-key-color: var(--dz-info-text-color);
    --dz-code-string-color: var(--dz-active-text-color);
    --dz-code-number-color: var(--dz-warning-text-color);
    --dz-code-literal-color: var(--dz-meta);
    --dz-code-font-family: ui-monospace, 'Cascadia Mono', Consolas, 'Courier New', monospace;
    --dz-code-font-size: 12px;
    --dz-code-max-height: 24rem;
    /* A CHOICE TILE - one saved thing picked from a row of them (31-choice-tile.css), first drawn
     * for Account Edit's saved payment methods.
     *
     * SELECTED reads from the selection pair, which is how "this one is chosen" reads everywhere
     * else in the palette, so a tenant that repoints selection repoints the tile with it. The edge
     * is 2px because a 1px change of colour on a 200px card does not read at a glance; it is drawn
     * inset so choosing a tile never moves its content.
     *
     * THE MARKER IS INK, NOT THE ACCENT. It borrows the eyebrow's size, weight and tracking, but the
     * eyebrow's accent ink is 3.78:1 on white at 11px, under the 4.5:1 floor, and the tile's selected
     * ground is tinted besides. The page this replaced drew a white-on-green pill: the same failure.
     *
     * MUTED is a tile that does not fit the row's current filter (a saved ACH account while the
     * payment type is Credit Card). It stays readable and clickable - choosing it is how the viewer
     * switches - so it is an opacity step, not the disabled one. */
    --dz-choice-tile-width: 200px;
    --dz-choice-tile-min-height: 54px;
    --dz-choice-tile-gap: 10px;
    --dz-choice-tile-padding: 8px 12px;
    --dz-choice-tile-radius: var(--dz-radius-sm);
    --dz-choice-tile-border-color: var(--dz-border);
    --dz-choice-tile-hover-border-color: var(--dz-card-hover-border-color);
    --dz-choice-tile-background: var(--dz-card);
    --dz-choice-tile-selected-edge-width: 2px;
    --dz-choice-tile-selected-border-color: var(--dz-selection-color);
    --dz-choice-tile-selected-background: var(--dz-selection-tint);
    --dz-choice-tile-muted-opacity: 0.55;
    --dz-choice-tile-title-color: var(--dz-ink);
    --dz-choice-tile-title-font-size: 13px;
    --dz-choice-tile-title-font-weight: 600;
    --dz-choice-tile-meta-color: var(--dz-meta);
    --dz-choice-tile-meta-font-size: 12px;
    --dz-choice-tile-marker-color: var(--dz-ink);
    /* The tile's rename and delete: the row-action glyphs at the size the tile's 13px title sets,
       body ink at rest and ink on hover, as the edit affordance does beside a value. */
    --dz-choice-tile-action-glyph-size: 13px;
    --dz-choice-tile-action-color: var(--dz-body);
    --dz-choice-tile-action-hover-color: var(--dz-ink);
    --dz-choice-tile-add-color: var(--dz-body);
    --dz-choice-tile-add-hover-background: var(--dz-surface-hover);
    /* THE CHOICE LIST - a dense radio or checkbox list (25-choice-cards.css, the --list variant),
     * first drawn for Voyager's call-plan picker.
     *
     * The checked row reads the selection pair, the same "this one is chosen" the grid and the
     * choice cards use: a tint under the row and a 3px bar inset on its leading edge, so the row is
     * marked without its content moving. Hooks rather than the pair read directly, so a tenant can
     * repoint the list alone.
     *
     * A TAG IS TEXT IN ITS KIND'S COLOUR inside a currentColor outline, at 11px. Info reads the info
     * role as text. Warning does not read the warning accent: it is 4.14:1 on white, under the 4.5:1
     * floor at this size, so it reads the darker warning mark ground, 5.76:1. */
    --dz-choice-list-selected-background: var(--dz-selection-tint);
    --dz-choice-list-selected-marker: var(--dz-selection-color);
    --dz-choice-list-tag-info-color: var(--dz-info-text-color);
    --dz-choice-list-tag-warning-color: var(--dz-warning-text-color);
    /* Meta text (a fact label, a none value, the aside caption) on a CHECKED row. --dz-meta is 4.00:1
       on the light selection tint, under the 4.5:1 floor at 11-12px; body ink is 5.28:1 there. */
    --dz-choice-list-selected-meta: var(--dz-body);
    /* THE DIALOG (35-dialog.css, datablaze.ui.dialog). The header shares the body's ground and a
     * hairline beneath it sets the title off; a sheet's footer, and a popup's while its body scrolls,
     * sits on the page tint under its own rule, which separates the actions (a popup whose body fits
     * is flat, PP-D79). Both rules use --dz-dialog-rule. Meta ink (the chips) is
     * --dz-meta, 4.56:1 on the white surface; it is never painted on the page ground (the rail, the
     * footer), where it falls to 4.3:1, so each of those has its own meta hook. */
    --dz-dialog-background: var(--dz-surface);
    --dz-dialog-radius: var(--dz-radius-md);
    /* Measured from the approved dialog prototype: a wide soft lift and a tight contact edge, both in
       the ink's hue rather than black. */
    --dz-dialog-shadow: 0 24px 64px rgba(49, 48, 47, 0.22), 0 2px 6px rgba(49, 48, 47, 0.08);
    /* The page dimmed in the ink's hue, as the approved dialog prototype draws it, but darker: 0.6
       against the prototype's 0.42. It replaces dx.light's white wash under
       a dialog and under any unported popup that 36-legacy-popup.css matches (not a popover,
       tooltip, editor pop-up, header-filter or filter-builder menu); other overlays keep
       --dz-overlay-scrim. */
    --dz-dialog-scrim: rgba(49, 48, 47, 0.6);
    /* The dim a busy dialog wears: a load panel's shader inside a dialog (35-dialog.css). The same ink
       hue as the scrim, at 0.35 against its 0.6, because the dialog already sits on a dimmed page and
       this only has to say "not live" over its body and footer; the load panel's own card stays opaque
       on top, so "Loading..." keeps its ink-on-card contrast whatever this value is. The dark theme
       sets its own (voyager.darkmode.css): the ink's grey is lighter than the dark dialog and would
       lift it. */
    --dz-dialog-busy-shade: rgba(49, 48, 47, 0.35);
    --dz-dialog-rule: var(--dz-hairline);
    /* How far the lead paragraph that opens a dialog body pulls up into the body's top padding (PP-D78).
       The paragraph's 20px line box carries 3-4px of half-leading above its glyphs, so equal padding
       looked top-heavy; the trim is that half-leading, 4px, one half step of the spacing scale. */
    --dz-dialog-lead-trim: var(--dz-space-half);
    --dz-dialog-title-ink: var(--dz-ink);
    --dz-dialog-meta-ink: var(--dz-meta);
    /* Running text inside a dialog (a step's lead line): body ink, 6.03:1 on the
       white surface; the dark theme sets --dz-body to its ink. */
    --dz-dialog-body-ink: var(--dz-body);
    --dz-dialog-rail-background: var(--dz-page);
    --dz-dialog-step-current: var(--dz-ink);
    --dz-dialog-step-current-ink: var(--dz-surface);
    --dz-dialog-sheet-grabber: var(--dz-border);
    /* Secondary ink on the rail's page ground (a step's number and value, a step not yet current):
       body ink, 5.63:1 on the light page; the dark theme sets --dz-body to its ink. */
    --dz-dialog-rail-meta-ink: var(--dz-body);
    /* The footer's ground, the page tint, and its summary's ink: body ink, 5.63:1 on the light page
       (--dz-meta is 4.3:1 there). The dark theme needs no override: --dz-page is #171615 there and
       --dz-body is its ink. */
    --dz-dialog-footer-background: var(--dz-page);
    --dz-dialog-footer-meta-ink: var(--dz-body);
    /* The ring of a step's number that is neither done nor current. */
    --dz-dialog-step-border: var(--dz-border);
    /* A done step's number: filled in the selection colour with a check in its ink, so done reads
       apart from the hollow to-do ring and the ink-filled current step in both themes. The check is
       a graphic, not text, so it needs 3:1 against the fill, which the selection pair carries. */
    --dz-dialog-step-done: var(--dz-selection-color);
    --dz-dialog-step-done-ink: var(--dz-selection-ink);
    /* The gap between the carrier's coloured square and the carrier name: the mock draws 8px here
       and 6px between label chips, so it is not --dz-badge-gap. */
    --dz-carrier-mark-gap: 8px;

    /* A GLYPH BADGE - a solid coloured square carrying a two-letter mark, then the value as
       ordinary row text. The carrier square's shape one step larger (26/6/11 against 22/5/10),
       because it reports a state rather than naming a vendor: the Billing Center mock puts it in the
       Status column, where the eye lands, with the carrier square deliberately quieter. The gap is
       wider than --dz-badge-gap because the mark and its label are one unit.
     *
     * The mark is the only place the status colour appears, as a FILL. The fills are the guide's
     * roles, each publishing a figure above 4:1, so they are legible either way round. The invoice
     * vocabulary's gold (#f2b705) is not: 1.9:1 as text on white and no better under white text. */
    --dz-badge-glyph-size: 26px;
    --dz-badge-glyph-radius: 6px;
    --dz-badge-glyph-font-size: 11px;
    --dz-badge-glyph-font-weight: 600;
    --dz-badge-glyph-gap: 8px;
    --dz-badge-glyph-foreground: #ffffff;
    /* THE MARK'S OWN FILL, for a role whose accent is too light to carry the white letters. The mark reads
       --dz-badge-mark-background first and the accent after it, so a role darkens only its square:
       the accent keeps painting every other status use. */
    --dz-badge-mark-warning-background: #8a5d00;  /* white 5.76:1; the warning accent #a87200 is 4.14:1 */
    --dz-badge-mark-neutral-background: #6e6e6e;  /* white 5.10:1; MultiCarrier's #808080 was 3.95:1 */
    --dz-badge-mark-datablaze-background: #c8431a;  /* white 4.91:1; the Datablaze carrier's #e85224 is 3.71:1. A carrier identity, so a literal, not the tenant accent */
    --dz-badge-mark-rogers-background: #e00000;  /* white 5.04:1; Rogers' #ff0000 is 4.00:1 with white and 3.29:1 with ink */

    /* THE SAME SQUARE AT NOTE SIZE, for a mark that annotates a row rather than reporting its
       state. Transcribed from the Billing Center mock's info chip:
     *
     *     <span title="Info" style="display:inline-flex;align-items:center;justify-content:center;
     *       width:16px;height:16px;border-radius:4px;background:#FDD501;color:#31302F;
     *       font:700 10px/1 Poppins,sans-serif;cursor:pointer;flex:none">i</span>
     *
     * so 16/4/10/700 against the status square's 26/6/11/600. The weight goes up as the size comes
     * down on purpose: one lowercase letter at 10px needs the extra stroke to read. Same rule, four
     * tokens overridden - see .db-badge--note. */
    --dz-badge-note-size: 16px;
    --dz-badge-note-radius: 4px;
    --dz-badge-note-font-size: 10px;
    --dz-badge-note-font-weight: 700;

    /* Status is a 9px dot beside SemiBold ink text, and the dot is the only place a status colour
       appears. Five roles; every Voyager lifecycle class maps onto one in voyager.badges.css.

       TWO TOKENS, AND THE SPLIT IS THE POINT. --dz-status-marker-size is how big a status marker
       is anywhere; --dz-status-dot-size is the BADGE's marker, which `.db-grid` sets to 0 because
       inside a grid the word is the information. Markers that are not badges - the usage dot on
       Subscriber Detail - read the marker size, so they keep their dot inside a grid. */
    --dz-status-marker-size: 9px;
    --dz-status-dot-size: var(--dz-status-marker-size);

    /* THE EYEBROW. An all-caps SemiBold micro-label with wide tracking, in the accent - the one
       place accent orange appears on type, because an eyebrow labels a region rather than naming
       an action. STYLE_GUIDE.md's "the accent is not valid for body-size text" is about body copy;
       this is 11px chrome.

       Used by the layout panel's caption and a row-menu section heading, which land on different
       DevExtreme elements and cannot share a selector. 8/12/4 lines the heading's left edge up with
       the 12px of the menu items under it, and the panel caption with its rows. A caller needing
       different padding redeclares this token rather than respelling the number.

       THE ACCENT ITSELF, BY OWNER RULING (2026-10-07). #e45325 is 3.78:1 on the card, under AA for
       11px text. The accent's active step (6.4:1) was tried and read as a different, darker orange;
       the owner ruled the eyebrow keeps the cobrand colour, a known exception to the floor. */
    --dz-eyebrow-padding: 8px 12px 4px;
    --dz-eyebrow-color: var(--dz-accent);
    --dz-eyebrow-font-size: 11px;
    --dz-eyebrow-font-weight: 600;
    --dz-eyebrow-letter-spacing: 0.12em;
    --dz-eyebrow-line-height: 1.2;

    /* THE SEARCH BOX'S TEXT INSET, one number for three sites: .db-page-toolbar__search, the grid's
       injected %dx%-search-panel, and the placeholder's ::before, which DevExtreme pads separately
       from the input. If they disagree the placeholder sits away from the text typed over it. */
    --dz-search-input-padding-x: 14px;
    /* Read twice here and once in Voyager's badge bundle, each with a 14px fallback; declared so the
       three agree by construction. */
    --dz-grid-row-font-size: 14px;
    --dz-status-active-accent: #34803a;  /* guide: Active 4.89:1 */
    --dz-status-danger-accent: #c0301f;  /* guide: Terminated 5.70:1, and off the logo hue */
    --dz-status-warning-accent: #a87200;  /* guide: Pending/throttled 4.14:1 */
    --dz-status-info-accent: #35699a;  /* guide: Info 5.77:1 */
    /* ON-STATUS INK (ClickUp 86894yg24, WL-20b round 2, A73/A87): the ink a caller puts ON a status
       accent used AS A FILL (not the dot-plus-text pattern most status reads use, where ink is always
       --dz-ink and the accent never carries text). Each of these four is MEASURED as whichever of
       white/black scores the higher WCAG contrast ratio against its own accent above (true relative
       luminance, not Rec. 601 luma -- see the warning case below for why that distinction is not
       academic here): active white 4.89:1 (black 4.29:1), danger white 5.70:1 (black 3.68:1), info
       white 5.77:1 (black 3.64:1) -- white wins and clears 4.5:1 for all three. The warning accent is
       the one case where white does NOT win: white is only 4.14:1 (under the 4.5:1 floor) but black
       measures 5.07:1 -- this is exactly why the Endpoint "offline" tile (warning accent) now reads
       black text (A87), not the white this block used to hold on. Each tenant's whitelabel compiler
       re-derives its OWN on-status ink the SAME way (whichever of #fff/#000 scores the higher WCAG
       contrast against that tenant's own accent) whenever that status colour is customized -- a
       DIFFERENT rubric from onAccent's own "auto" (Rec. 601 luma, deliberately not WCAG, see that
       token's own comment), which stays luma-based; these four here are only the PLATFORM tier's own
       (measured, not derived) answers, and WCAG math is why the floor is always met: the higher of
       white/black against any opaque colour is never below ~4.583:1 (CarrierBadgePalette.Foreground's
       own doc comment proves the same bound for its own, separate use of this math). */
    --dz-on-status-active: #fff;
    --dz-on-status-danger: #fff;
    --dz-on-status-info: #fff;
    --dz-on-status-warning: #000;  /* white is 4.14:1, under the 4.5:1 floor; black is 5.07:1, the higher of the two (A87) */
    /* THE DANGER ROLE AS TEXT: a destructive menu item, an inline error. A hook rather than the accent
       read directly, because text and fill part company on a dark ground: the accent is 2.89:1 as
       text on the dark card, and lifting the accent itself would also lighten every danger fill and
       mark. So the dark theme repoints only this. */
    --dz-danger-text-color: var(--dz-status-danger-accent);
    /* THE ACTIVE ROLE AS TEXT (Endpoint Detail's "1.0 GB Remaining"), the same split for the same
       reason: the accent is 3.37:1 as text on the dark card. */
    --dz-active-text-color: var(--dz-status-active-accent);
    /* THE INFO ROLE AS TEXT (Billing Center's revision chips, where the outline chip's accent is its
       text), the same split again: the accent is 2.62:1 as text on the dark grid. */
    --dz-info-text-color: var(--dz-status-info-accent);
    /* THE WARNING ROLE AS TEXT (the choice list's warning tag). Not the warning accent: #a87200 is
       4.14:1 on white, under the 4.5:1 floor for small text. #8a5d00 keeps its hue and is 5.76:1 on
       white and 5.05:1 on the light selection tint. The dark theme lifts it. */
    --dz-warning-text-color: #8a5d00;
    /* A GRID'S LOAD ERROR ("Unexpected server response"), a filled band. dx.light fills it #e89895 under white,
       2.24:1; the danger accent carries white at 5.70:1 and reads as the same failure a danger mark does. */
    --dz-grid-error-background: var(--dz-status-danger-accent);
    --dz-grid-error-color: #fff;
    --dz-status-inactive-accent: var(--dz-body);  /* guide: Inactive/Suspended 6.03:1 */
    /* Demo and Test are informational, so they share the Info role. Not the accent: the guide says
       "accent orange, status colours and label colours each have one job and do not borrow each
       other's". The dot carries the role and the word carries the meaning. */
    --dz-status-demo-accent: var(--dz-status-info-accent);
    --dz-status-color: var(--dz-ink);
    --dz-status-background: transparent;
    --dz-status-border-width: 0;
    --dz-status-radius: 0;
    --dz-status-padding-y: 0;
    --dz-status-padding-x: 0;
    --dz-status-font-size: 14px;
    --dz-status-font-weight: 600;
    --dz-status-line-height: 1.4;
    /* Literal, not an alias of --dz-badge-gap: every host that renders a status badge overrides
       --dz-status-gap outright, so this is the themeable value and there is no second one. */
    --dz-status-gap: 8px;
    --dz-status-marker-radius: 50%;

    /* ------------------------------------------------------------------------------------------
       WL-10a WHITELABEL EDITOR CONSTRUCTS (ClickUp 86894yg24). Seven hooks for four new components -
       23-contrast-chip.css, 24-callout.css, 27-unsaved-chip.css and 28-preview-bar.css each carry
       the reasoning for their own use of these; nothing else in the package reads them today.
       Every one of them is its OWN named hook rather than a rule reading an existing token
       directly, per COBRANDING.md - a tenant repointing "the branding editor's warning callout"
       must never also move every other reader of --dz-status-warning-accent. ------------------- */
    --dz-contrast-chip-ok-marker: var(--dz-status-active-accent);
    --dz-contrast-chip-fail-marker: var(--dz-status-danger-accent);
    --dz-callout-info-marker: var(--dz-status-info-accent);
    --dz-callout-warning-marker: var(--dz-status-warning-accent);
    /* Callout note line ink. --dz-meta is about 4.0:1 on the callout washes, under the 4.5:1 floor
       at 12px; body ink is about 5.3:1. Same reasoning as --dz-choice-list-selected-meta below. */
    --dz-callout-note-ink: var(--dz-body);
    --dz-unsaved-chip-marker: var(--dz-status-warning-accent);
    /* Between the body-level actions menu (1600, above) and the hover tip (1700, above) - a fixed
       bottom pill has to sit over ordinary page chrome and any body-level menu the page also
       shows, but a hover tip explaining one of the pill's own buttons must still draw over it, so
       this sits strictly between the two rather than at either endpoint. */
    --dz-preview-bar-layer: 1650;

    /* Shared data-grid primitives. Archetype classes swap the header and row-state treatments while
       keeping the shell, spacing, and interaction language consistent.

       The header bar is Sunken rather than the Page tint docs/style/data-grids.md names. The grid
       sits in a white card on a white page, where #f8f7f5 against #ffffff rows is close to
       invisible. #f0eeec is what the Subscribers mock draws, and revising-the-style-guide.md
       settles it: "where the two differ on a value that only exists in the product - container
       corners, row density, the size of text inside a grid - take the mockup's number, because that
       is him drawing the actual surface." */
    --dz-grid-surface: var(--dz-card);
    --dz-grid-border-color: var(--dz-hairline);
    /* The line under the column headers is a full Border, not the Hairline the card and the row
       separators use. The mock's header cells carry `border-bottom: 1px solid #c4c0be` over #e2dfdd
       row separators - the header rule is the heavier of the two. */
    --dz-grid-header-rule-color: var(--dz-border);
    /* Where one column ends and the next begins, shown only while the pointer is in the header.
       Same value as the rule under the header: one answer to "how heavy is a grid's own line". */
    --dz-grid-header-edge-color: var(--dz-border);
    --dz-grid-pager-background: var(--dz-page);
    /* The pager's current page and page-size chips: one neutral step under the bar, so the current
       page reads on it (dx.light drew #d4d4d4 on the same bar). */
    --dz-grid-pager-selected-background: var(--dz-hairline);
    --dz-grid-row-color: var(--dz-ink);
    --dz-grid-row-alternate-background: var(--dz-page);
    /* The header filter funnel, matched to --dz-grid-expand-caret-size so the two pieces of chrome
       on one header row are the same size (the Billing Center mock draws 13px).

       Its COLOUR deliberately departs from the mock: the mock's unfiltered funnel is #918D8B, which
       MEASURES 2.84:1 against --dz-grid-header-background (#F0EEEC) and fails the 3.0:1 floor for a
       meaningful non-text shape. --dz-body is 5.21:1 with the same intent - a funnel quieter than
       the caption beside it. */
    --dz-grid-funnel-size: 14px;
    /* THE HEADER AND ROW-STATE GROUND, one set for every archetype AND every embedding -- standalone
       or inside a card, ported (`.db-grid`) or not (00-tokens.css's own unported skin further down
       reads these same names directly).

       WL-24 (ClickUp 86894yg24), Brad A100: this used to differ by archetype -- `.db-grid--card`
       redeclared --dz-grid-header-background to --dz-page for a lighter header, and
       16-card-usage-udf.css separately did the same thing for ANY grid merely sitting inside a
       `.db-card`/`.db-detail-card`, ported or not. Brad's walkthrough named the gap directly: the
       Subscriber Details Logs card's column-header row stayed near-white (#F8F7F5) while the rest of
       the card themed. A100 is "every header row and footer reads ONE surface-step token" -- a step
       darker than Surface, which is what Sunken already is -- so there is no more per-archetype
       override of this token, and nothing redeclares it for "inside a card" either. Per-archetype
       override of OTHER grid tokens (the header RULE colour, for one) is still exactly this
       mechanism -- redeclare the token on the archetype class -- see `.db-grid--card` in
       02-grid-shell.css.

       DECLARED AT :root AND THAT IS LORE-BEARING. The unported-grid skin further down reads these
       names directly, so they have to resolve on a grid that carries no `.db-grid` class at all;
       `color: var(--undeclared)` is invalid at computed-value time and drops in silence. See the
       comment above that rule - it shipped broken that way once. */
    /* WL-1 (ClickUp 86894yg24): repointed from var(--dz-sunken)/var(--dz-ink) to
       --dz-header-background/--dz-header-ink -- same resolved value at the time (that pair's own
       default IS --dz-sunken/--dz-ink), and it let headerBackground/headerInk (the schema keys that
       pair is for) move the grid's column headers in step with card/panel headers and
       .rounded-header.

       WL-24 FIX ROUND 1 (ClickUp 86894yg24), PM ruling D1 -- REVERSES the paragraph above. A100's own
       words are "a step darker than the Surface (the package sunken tone)", not "the header strip's
       colour": the grid header ground is a SURFACE STEP, not a repaint of headerBackground. The
       adversary measured why the two cannot be the same hook: a tenant's headerBackground/headerInk
       is a PAIR (DESIGN.md A31, ink and ground move together) chosen for the card-header/.rounded-
       header/popup-title surfaces, with no promise it also contrasts against whatever ink a grid
       header separately carries -- WL-24's own earlier round had already split the ink onto --dz-ink
       (next line), so a tenant pair like #1a7a3c/#ffe600 left dark ink on a green ground, 2.44:1.
       Back on var(--dz-sunken) directly, matching --dz-grid-header-color below (--dz-ink): the pair
       the grid header resolves to is ALWAYS the neutral spine's own sunken/ink, which the package
       guarantees readable, in every theme, regardless of what a tenant sets headerBackground/headerInk
       to. WL-26 (the item that derives the sunken step from a tenant's Surface) changes what
       --dz-sunken itself resolves to, which is how a tenant still moves this -- through Surface, not
       through the header pair. Same resolved value today for an untouched tenant (--dz-header-
       background's own default already WAS --dz-sunken), so nothing repaints. */
    --dz-grid-header-background: var(--dz-sunken);
    --dz-grid-header-color: var(--dz-ink);
    --dz-grid-row-hover-background: var(--dz-page);
    --dz-grid-row-selected-background: var(--dz-sunken);
    --dz-grid-row-padding-y: 12px;
    --dz-grid-header-padding-y: 12px;
    /* The grid container is a white card with a hairline border. Both mocks draw it and
       docs/style/data-grids.md specifies it: 1px hairline, 10px radius, one low shadow. */
    /* THE METRIC TILE. A big number over what it counts, in a card.
       36px is Account/Dashboard's value. The label is 16px rather than the 12px field label because
       this pair is read across a room, not scanned down a column - a metric is the inverse of
       .db-field, value first and label under it. */
    --dz-metric-value-font-size: 36px;
    --dz-metric-label-font-size: 16px;
    /* The tile floor. 150px is the legacy fixed height; as a MINIMUM it keeps a row of tiles level
       when one carries a delta block and its neighbours do not, without capping a tile whose label
       wraps at a narrow width. */
    --dz-metric-min-height: 150px;
    --dz-grid-card-radius: var(--dz-radius-md);
    --dz-grid-card-shadow: 0 1px 3px rgba(49, 48, 47, 0.06);
    /* Row height lands at 50px with 12px cell padding. DevExtreme's own row metrics match
       neither, so both need an explicit override - see .db-grid's rowsview rules. */
    --dz-grid-row-height: 50px;
    --dz-grid-cell-padding-x: 12px;
    /* The two command cells that open a row: the selection checkbox and the row-action trigger.
       Each is its content plus its own side padding. DevExtreme's defaults are 70px and 85-100px,
       wide enough that the checkbox floats in an empty column.

       36px is the Billing Center mock's checkbox cell. The mock spends 140px ahead of Status: 36 to
       the checkbox and 104 to ONE cell holding the row menu, the caret and the info flag, where
       DevExtreme insists on a separate cell per command.

       KNOWN LIMIT. Under `table-layout: fixed` a column's USED width comes from the colgroup, which
       DevExtreme writes from the COLUMN's resolved width - so a rule on the cell only wins where the
       column is `auto` and the widget measures the cell. MEASURED on Billing Center, whose leading
       columns are FIXED: the cell computes min-width 36px and the column, colgroup and rendered cell
       all stay at 40. Overriding this property and calling updateDimensions() does not move it, nor
       does columnOption() alone; only columnOption inside beginUpdate/endUpdate followed by
       updateDimensions does. Making this authoritative everywhere means declaring the selection
       column's width in the grid's column config - a contract decision, not a stronger selector. */
    --dz-grid-select-cell-width: 36px;
    /* The select cell's own side padding, separate from --dz-grid-cell-padding-x (12px, measured
       for text) because it has to move with the cell width and checkbox size to keep the box
       centred. */
    --dz-grid-select-cell-padding-x: 12px;
    /* THE FILTER ROW, which is its own row and not the bottom of the header band.
     *
     * 8px side padding puts 16px between two adjacent boxes, which is what makes them separate
     * objects rather than a strip of joined inputs.
     *
     * 8 and not the mock's 12, the block's one deviation. The mock's narrow filter-row cells are all
     * command cells it leaves empty. Ours has narrow DATA columns with an editor: Billing Center's
     * icon-only Subtype column is 30px, where 12px a side leaves a 6px box and 8px leaves 14px. The
     * real fix there is the column (a search box on a single-glyph column is not usable at any
     * width), and that is the host's call.
     *
     * The BACKGROUND is what makes it a row at all. The mock draws #FCFBFA against #F0EEEC on the
     * header above; ours takes --dz-page, the spine's step between Card and Sunken, rather than
     * adding a tenth neutral. Without it the filter row inherits the header's Sunken and the two
     * merge into one tall band.
     *
     * 13px text, one step under the row's 14px, because a filter is furniture rather than content. */
    --dz-grid-filter-editor-height: 32px;
    --dz-grid-filter-editor-font-size: 13px;
    --dz-grid-filter-cell-padding-y: 8px;
    --dz-grid-filter-cell-padding-x: 8px;
    --dz-grid-filter-row-background: var(--dz-page);
    /* The width DevExtreme reserves at the editor's left edge for its filter-operation glyph. Not a
       free choice: the widget hard-codes a 30px absolutely positioned menu there and pads the input
       32px to clear it, so this exists to be subtracted from the box rather than to be tuned. */
    --dz-grid-filter-operation-width: 28px;
    /* A109 (ClickUp 86894yg24, WL-32, Brad): the group-by drop target's own ground, split out as its
       own named property for the PORTED (.db-grid) archetype only (19-grid-selection.css) -- same
       default as the filter row above, same reason: an unnamed `var(--dz-page)` literal there cannot
       be told apart from "the compiler never touched this" by anything that wants to derive it from a
       customized Surface. The LEGACY (unported) group panel keeps reading --dz-sunken directly
       (22-devextreme-adapter.css) -- already Surface-derived since A96/A100, untouched by this item. */
    --dz-grid-group-panel-background: var(--dz-page);

    /* THE HOVER TIP.
     *
     * 1700, not the mock's 90, because this bubble lives on <body> rather than inside the control:
     * it competes with DevExtreme's overlay layer (from 1500) and the package's body-level actions
     * menu (1600). Above both, because a tooltip is transient and answers a pointer already on the
     * thing it labels. Why it is body-level at all is in the header of datablaze.ui.tip.js.
     *
     * 13px text and 8/10px padding, one step up from the mock's 12px and 6/8px, because on these
     * call sites the tip IS the label: the glyph it explains carries no text.
     *
     * WHITE, a DELIBERATE DEPARTURE FROM THE MOCKS, which draw every [data-tip] as #31302F with
     * white text. Owner decision, made after seeing the dark bubble in place. A white bubble on a
     * white row takes its edge from the mock's one white floating surface, the row-action menu
     * (.rowmenu-pop): a 1px --border-default hairline and a 0 8px 24px rgba(49,48,47,.16) shadow. */
    --dz-tip-layer: 1700;
    --dz-tip-font-size: 13px;
    --dz-tip-padding-y: 8px;
    --dz-tip-padding-x: 10px;
    --dz-tip-background: var(--dz-card);
    --dz-tip-color: var(--dz-ink);
    --dz-tip-shadow: 0 8px 24px rgba(49, 48, 47, 0.16);

    /* A FLOATING SURFACE - a popup menu that is not the tip: the row-action popout
       (.db-action-menu__popup), the header filter's list (.dx-header-filter-menu) and the legacy
       row menu (.themed-context-menu). One step lighter than --dz-tip-shadow on purpose: the tip sits
       above DevExtreme's overlay layer and has to read through it; these sit inside it. */
    --dz-surface-float-shadow: 0 6px 18px rgba(49, 48, 47, 0.12);
    /* THE COACHMARK (39-coachmark.css): the fly-out that points at one control. */
    --dz-coachmark-surface: var(--dz-card);  /* the fly-out's ground, and its arrow's fill */
    --dz-coachmark-ink: var(--dz-ink);  /* the title and message words */
    --dz-coachmark-border: var(--dz-hairline);  /* the fly-out's edge, and its arrow's */
    --dz-coachmark-shadow: var(--dz-surface-float-shadow);  /* the lift off the page */
    --dz-coachmark-edge: 1px;  /* the width of the fly-out's edge and of the video frame's, so the frame can subtract it exactly */
    --dz-coachmark-max-width: 320px;  /* how wide the message may run before it wraps */
    --dz-coachmark-meta-ink: var(--dz-meta);  /* the step counter's words ("1 of 3"); measured 4.56:1 on the card (--dz-coachmark-surface) */
    --dz-coachmark-media-aspect: 4 / 3;  /* the shape of the video frame, held before the video loads */
    --dz-coachmark-media-radius: var(--dz-control-radius);  /* the corners of the video frame */
    /* The toolbar overflow menu's shadow. Off the neutral palette and heavier than
       --dz-surface-float-shadow; kept as it renders pending a design call on unifying them. */
    --dz-toolbar-overflow-shadow: 0 6px 18px rgba(24, 50, 70, 0.18);
    /* The flush-content dropdown's popup shadow (08-dropdown-editor.css). */
    --dz-dropdown-shadow: 0 8px 24px rgba(49, 48, 47, 0.14);
    /* The master-detail expander's column and glyph. Narrower than the select column because a
       caret is chrome rather than a control the eye looks for, but still a real hit target.

       This ONE number sizes EVERY expand column a grid has, including the per-level indents a
       GROUPED grid adds ahead of the caret. MEASURED: DevExtreme resolves `width: auto` across all
       its expand columns to a single value - the MAX of whatever CSS offers them - and writes it
       into the colgroup, where `table-layout: fixed` beats any rule on the cells. Probed on Billing
       Center: indents at 44px moved BOTH columns to 44px; indents at 16px left both at the
       expander's width. A separate indent token would be inert.

       22px leaves 4px either side of the 14px caret mask and is the smallest number that reads as
       pressable next to a 36px checkbox cell. A grouped grid pays it twice (Billing Center's left
       gutter is 44px), which is why it is not larger. */
    --dz-grid-expand-cell-width: 22px;
    /* 14px, the mock's, as a MASK SIZE (see the caret rule near .dx-datagrid-group-closed). A 2.5px
       stroked chevron does not read at 12px - the same reason .db-icon--check is 16px while the
       rest of that set sits at 15px. */
    --dz-grid-expand-caret-size: 14px;
    /* The GROUP row's caret: the same artwork one step smaller. The mock draws 12px on the
       "Bill Month: ..." group header and 14px on the row that opens a sub-table: a group caret
       belongs to the heading's type scale, the row caret is a control on a row of 14px text. */
    --dz-grid-group-caret-size: 12px;
    /* A cell an editing grid will not edit, beside cells it will (33-grid-states.css). Meta: still
       legible data, one step back. In the dark theme Meta is the ink (no grey text on dark, owner
       direction 2026-09-29), so there the text cursor on the editable cells carries the difference. */
    /* A button's attention dot (07-button-roles.css): something to look at before using it. The
       cobrand accent (owner call 2026-10-07): it is a nudge, not a warning, so it takes the tenant's
       colour rather than the warning scale. Its own hook, so a tenant whose accent collides with
       something beside it can repoint the dot alone. */
    --dz-button-attention-color: var(--dz-accent);
    --dz-grid-readonly-cell-color: var(--dz-meta);
    /* A row heading the rows beneath it (a pool above its plans). The page step: below the card in
       the light theme and below it again in the dark, so it reads as a band in both. */
    --dz-grid-parent-row-background: var(--dz-page);
    /* A parent row's hover and selected grounds: darker steps of the same neutral ramp, ending on
       Hairline. The ordinary hover IS the page step, so a parent row lit with it would not change at
       all, and the ordinary selected ground would erase the band. */
    --dz-grid-parent-row-hover-background: color-mix(in srgb, var(--dz-hairline) 65%, var(--dz-card));
    --dz-grid-parent-row-selected-background: var(--dz-hairline);
    /* A row whose record no longer counts (33-grid-states.css): Meta, as the read-only cell. */
    --dz-grid-inactive-row-color: var(--dz-meta);
    /* A plain data table's total row (34-table.css): the header band, so a section total in a table
       reads as the same strip a grid's header and a card's header draw. 16px marks it as the figure
       the rows under it add up to. */
    --dz-table-total-background: var(--dz-header-background);
    --dz-table-total-ink: var(--dz-header-ink);
    --dz-table-total-font-size: 16px;
    /* DevExtreme draws its own box rather than a native input (22px by default, which overflowed
       the cell and let `text-overflow: ellipsis` put a literal "..." beside every checkbox). The
       mock's native input is 14px. */
    --dz-grid-checkbox-size: 14px;
    --dz-grid-action-cell-width: 22px;
    /* The row-action trigger is three drawn dots rather than a glyph. 3px dots at a 2.5px gap
       stack to 14px, which is what the mock draws. */
    --dz-grid-row-action-dot-size: 3px;
    --dz-grid-row-action-dot-gap: 2.5px;
    /* An icon a row carries as its own action - the trash and undo in an editable grid's buttons
       column. The row's ink (owner decision): link blue read as a link to follow rather than a thing
       to do to the row. Its own hook so a theme can move it without moving the row text. */
    --dz-grid-row-action-icon-color: var(--dz-grid-row-color);
    /* A row action's glyph box (edit, delete). 18px is the Font Awesome box those buttons drew at,
       MEASURED on Profile's UDF grid, so every pinned 72px buttons column still fits its two
       icons. */
    --dz-grid-row-action-icon-size: 18px;

    /* One padding for every row in the layout panel - stacked buttons and the name editor alike - so
       nothing in the panel ends up a few pixels narrower than its neighbours.

       The horizontal 12px is the panel's own rhythm: .db-layout-panel__caption pads 8px 12px 4px
       and a picker's saved-layout row pads 12px on the left, so captions, list and this block share one
       left edge. In the gear menu the saved layouts are items: their box takes the same 12px inset
       (--dz-layout-item-inset) and the name starts one item padding inside it
       (--dz-layout-item-pad), the same left edge as a Layout action's glyph and a Density radio, so
       there the name is deliberately inside the captions' edge. */
    --dz-layout-action-padding: 0 var(--dz-space-1half) 6px;
    /* Gap between the name box and the button row, and between the two buttons. */
    --dz-layout-editor-gap: 6px;
    /* Width of the overlaid per-item action icons. One value drives both the overlay strip and the
       hover padding that gives the name cell's space back, so they cannot disagree - and the padding
       must never be the smaller, or a transparent strip of the action cell hangs over the name and
       swallows the clicks that select a layout.

       Sized to the icons rather than DevExtreme's 80px two-icon minimum: the chips are 26px at 3px
       margins, so they occupy 64px, and on a 162px panel a wider strip truncates every name the
       moment the pointer arrives. 68px keeps 2px of clearance either side of the pair. */
    --dz-layout-item-actions-width: 68px;

    /* A ROW IN THE GEAR MENU (PP-D81): every item - the Density radios, the Layout actions, a saved
       layout - is one 36px row, so the menu keeps one rhythm. The Layout rows were 40px buttons with
       6px beneath (a 46px pitch) and the Density rows carried Bootstrap's 8px label margin (also 46),
       against the saved layouts' 36. */
    --dz-layout-item-height: 36px;
    /* The item's box is inset from the panel's content edge by this much on both sides, so a hover
       highlight is a rounded box with air round it rather than a full-bleed band. It is the panel's
       12px rhythm: captions pad 12px, and the box starts where their text does. */
    --dz-layout-item-inset: var(--dz-space-1half);
    /* Padding between an item's box edge and its content: a Layout action's glyph, a Density radio, a
       saved layout's name. Box inset plus this is the one left edge they share (22px from the panel's
       content edge). */
    --dz-layout-item-pad: 10px;
}

/* A bare `.db-button` has NO default role, deliberately. Each role class is the only thing that sets
   the eight --dz-button-* tokens, so a roleless button resolves `color`, `background-color` and
   `border-color` against nothing in the base rules (`.dx-button.db-button`,
   `button.db-button--native`, ...) and looks visibly unfinished instead of silently rendering as
   Action. Every caller pairs `.db-button` with an explicit role class. */

/* WL-22 fix round 4 (PM ruling, ClickUp 86894yg24): the PRESSED ink's generic default -- every role
   but Primary still paints its pressed/active state in the same ink as hover, exactly as every role
   did before this property existed, so this is the ONE declaration all of them fall through to.
   `.db-button--primary`, below, is the only role that overrides it (with
   --dz-button-primary-active-color, 00-tokens.css); same specificity as this rule, later in this same
   file, so it wins on a primary button without this default needing to know Primary exists. */
.db-button {
    --dz-button-active-color: var(--dz-button-hover-color);
}

.db-button--action {
    --dz-button-color: var(--dz-button-action-color);
    --dz-button-background: var(--dz-button-action-background);
    --dz-button-border-color: var(--dz-button-action-border-color);
    --dz-button-hover-color: var(--dz-button-action-hover-color);
    --dz-button-hover-background: var(--dz-button-action-hover-background);
    --dz-button-hover-border-color: var(--dz-button-action-hover-border-color);
    --dz-button-active-background: var(--dz-button-action-active-background);
    --dz-button-focus-ring: var(--dz-button-action-focus-ring);
}

/* A control belonging to the GRID rather than to the page - Export, and anything else that acts on
   the table itself instead of on the records in it. The toolbar already draws this line structurally:
   the collapse folds the `before` group and deliberately leaves the `after` group alone, because
   hiding a grid's own controls inside a menu about the page is the wrong grouping. This is that same
   line in paint.

   The point of the role is what its band does NOT read. Every default in --dz-button-chrome-* (at
   :root) resolves the neutral component scale rather than --dz-button-action-*, so a brand colour on
   the Action role cannot reach it: identical to Action until somebody themes Action. The band makes
   "restyle only the grid's own controls" a token edit and lets a tenant theme address chrome.
   Repoint a line below at an Action token and the role has nothing left to be. */
.db-button--chrome {
    --dz-button-color: var(--dz-button-chrome-color);
    --dz-button-background: var(--dz-button-chrome-background);
    --dz-button-border-color: var(--dz-button-chrome-border-color);
    --dz-button-hover-color: var(--dz-button-chrome-hover-color);
    --dz-button-hover-background: var(--dz-button-chrome-hover-background);
    --dz-button-hover-border-color: var(--dz-button-chrome-hover-border-color);
    --dz-button-active-background: var(--dz-button-chrome-active-background);
    --dz-button-focus-ring: var(--dz-button-chrome-focus-ring);
}

/* The trigger of a collapsed action group: the toolbar's "Actions" button, and the same control on a
   page that has no grid above it - Subscriber Detail's connection actions.

   Unlike chrome, which must stay furniture, a disclosure trigger is a legitimate thing to want
   coloured: it is the affordance that says there is more here. The entries it opens keep whatever
   role their own buttons declare, so colouring the trigger does not colour them. */
.db-button--disclosure {
    --dz-button-color: var(--dz-button-disclosure-color);
    --dz-button-background: var(--dz-button-disclosure-background);
    --dz-button-border-color: var(--dz-button-disclosure-border-color);
    --dz-button-hover-color: var(--dz-button-disclosure-hover-color);
    --dz-button-hover-background: var(--dz-button-disclosure-hover-background);
    --dz-button-hover-border-color: var(--dz-button-disclosure-hover-border-color);
    --dz-button-active-background: var(--dz-button-disclosure-active-background);
    --dz-button-focus-ring: var(--dz-button-disclosure-focus-ring);
}

.db-button--primary {
    --dz-button-color: var(--dz-button-primary-color);
    --dz-button-background: var(--dz-button-primary-background);
    --dz-button-border-color: var(--dz-button-primary-border-color);
    --dz-button-hover-color: var(--dz-button-primary-hover-color);
    --dz-button-hover-background: var(--dz-button-primary-hover-background);
    --dz-button-hover-border-color: var(--dz-button-primary-hover-border-color);
    --dz-button-active-background: var(--dz-button-primary-active-background);
    /* WL-22 fix round 4 (PM ruling, ClickUp 86894yg24): PRESSED reads its OWN ink now, not hover's --
       see 00-tokens.css's own comment on --dz-button-primary-active-color. */
    --dz-button-active-color: var(--dz-button-primary-active-color);
    --dz-button-focus-ring: var(--dz-button-primary-focus-ring);
}

.db-button--cancel {
    --dz-button-color: var(--dz-button-cancel-color);
    --dz-button-background: var(--dz-button-cancel-background);
    --dz-button-border-color: var(--dz-button-cancel-border-color);
    --dz-button-hover-color: var(--dz-button-cancel-hover-color);
    --dz-button-hover-background: var(--dz-button-cancel-hover-background);
    --dz-button-hover-border-color: var(--dz-button-cancel-hover-border-color);
    --dz-button-active-background: var(--dz-button-cancel-active-background);
    --dz-button-focus-ring: var(--dz-button-cancel-focus-ring);
}

.db-button--submit {
    --dz-button-color: var(--dz-button-submit-color);
    --dz-button-background: var(--dz-button-submit-background);
    --dz-button-border-color: var(--dz-button-submit-border-color);
    --dz-button-hover-color: var(--dz-button-submit-hover-color);
    --dz-button-hover-background: var(--dz-button-submit-hover-background);
    --dz-button-hover-border-color: var(--dz-button-submit-hover-border-color);
    --dz-button-active-background: var(--dz-button-submit-active-background);
    /* WL-31 (ClickUp 86894yg24): PRESSED reads its OWN ink now, the same repoint
       --dz-button-primary-active-color got in WL-22 round 4 -- see 00-tokens.css's own comment
       on --dz-button-submit-active-color. */
    --dz-button-active-color: var(--dz-button-submit-active-color);
    --dz-button-focus-ring: var(--dz-button-submit-focus-ring);
}

/* A declining answer, a No beside a Yes (PP-D76). Its own role so it reads as neither Cancel nor the
   commit; see 00-tokens.css for the tinted neutral defaults. Pressed ink falls through to the hover
   ink, the generic default at the top of this file. */
.db-button--answer {
    --dz-button-color: var(--dz-button-answer-color);
    --dz-button-background: var(--dz-button-answer-background);
    --dz-button-border-color: var(--dz-button-answer-border-color);
    --dz-button-hover-color: var(--dz-button-answer-hover-color);
    --dz-button-hover-background: var(--dz-button-answer-hover-background);
    --dz-button-hover-border-color: var(--dz-button-answer-hover-border-color);
    --dz-button-active-background: var(--dz-button-answer-active-background);
    --dz-button-focus-ring: var(--dz-button-answer-focus-ring);
}

.db-button--next {
    --dz-button-color: var(--dz-button-next-color);
    --dz-button-background: var(--dz-button-next-background);
    --dz-button-border-color: var(--dz-button-next-border-color);
    --dz-button-hover-color: var(--dz-button-next-hover-color);
    --dz-button-hover-background: var(--dz-button-next-hover-background);
    --dz-button-hover-border-color: var(--dz-button-next-hover-border-color);
    --dz-button-active-background: var(--dz-button-next-active-background);
    --dz-button-focus-ring: var(--dz-button-next-focus-ring);
}

.db-button--positive {
    --dz-button-color: var(--dz-button-positive-color);
    --dz-button-background: var(--dz-button-positive-background);
    --dz-button-border-color: var(--dz-button-positive-border-color);
    --dz-button-hover-color: var(--dz-button-positive-hover-color);
    --dz-button-hover-background: var(--dz-button-positive-hover-background);
    --dz-button-hover-border-color: var(--dz-button-positive-hover-border-color);
    --dz-button-active-background: var(--dz-button-positive-active-background);
    --dz-button-focus-ring: var(--dz-button-positive-focus-ring);
}

.db-button--action.db-button--selected {
    --dz-button-color: var(--dz-button-action-selected-color);
    --dz-button-background: var(--dz-button-action-selected-background);
    --dz-button-border-color: var(--dz-button-action-selected-border-color);
    /* WL-22 fix round 3 (PM ruling, ClickUp 86894yg24): see 07-button-roles.css's
       .db-button--notification-unread/--pressed, same repoint and same reasoning. */
    --dz-button-hover-color: var(--dz-button-action-selected-hover-color);
    --dz-button-hover-background: var(--dz-button-action-selected-hover-background);
    --dz-button-hover-border-color: var(--dz-button-action-selected-border-color);
    --dz-button-active-background: var(--dz-button-action-selected-hover-background);
}

.db-button--danger {
    --dz-button-color: var(--dz-button-danger-color);
    --dz-button-background: var(--dz-button-danger-background);
    --dz-button-border-color: var(--dz-button-danger-border-color);
    --dz-button-hover-color: var(--dz-button-danger-hover-color);
    --dz-button-hover-background: var(--dz-button-danger-hover-background);
    --dz-button-hover-border-color: var(--dz-button-danger-hover-border-color);
    --dz-button-active-background: var(--dz-button-danger-active-background);
    --dz-button-focus-ring: var(--dz-button-danger-focus-ring);
}

/* The grid shell carries an archetype class from datablaze.ui.grid.

   WHICH ARCHETYPES PAINT, AND WHICH DELIBERATELY DO NOT:

     full-page   THE BASE. It is what the :root header and row-state tokens already say.
     embedded    NO BLOCK: it does not differ from the base.
     picker      NO BLOCK, same reason.
     card        ITS OWN BLOCK, below, for the header RULE colour only. WL-24 (ClickUp 86894yg24),
                 Brad A100: the card archetype's header BACKGROUND no longer differs from the base --
                 every header row, ported or not, standalone or inside a card, reads the one
                 --dz-grid-header-background chain (today's Sunken). See that rule's own comment.
     detail      NO RULES ON PURPOSE. Its promise -- "owns no page furniture at all: no search
                 box, no header funnels, no pager" (datablaze.ui.grid.js) -- is BEHAVIOURAL and the
                 JS shell delivers it by not rendering those parts. It takes the base paint.

   If an archetype ever needs paint it gets a block here; do not add an empty one for symmetry, and
   do not introduce a per-archetype token band for a value nothing overrides. */
.db-grid {
    color: var(--dz-grid-row-color);
    background: var(--dz-grid-surface);
}

/* The CARD archetype USED TO take a lighter header (the mockup's Recent Connections card: strip
   #F0EEEC / --dz-sunken, table header a step lighter at #F8F7F5 / --dz-page) -- the reasoning being
   that the card's own --dz-sunken strip above it already carries the title, so a second Sunken band
   would read as one grey block with a rule through it.

   WL-24 (ClickUp 86894yg24), Brad A100 (from his walkthrough: the Subscriber Details Logs card's
   header row stayed near-white while the card header/body themed) OVERRULES this: every grid's
   column-header row and footer, standalone or embedded in a card, reads ONE surface-step token --
   --dz-grid-header-background, which still defaults to Sunken (:root, 00-tokens.css) -- so this
   archetype no longer redeclares it. The header RULE still steps down to Hairline rather than the
   base archetype's heavier Border: a card's own hairline-bordered body reads lighter company than a
   standalone grid's full border, and A100 did not ask for that to change.

   Keyed on the ARCHETYPE, not `.db-card .db-grid`: a descendant selector paints by whatever DOM the
   grid happens to sit in. The archetype is the host's own statement about the surface, and it also
   decides the search box, the funnels and the pager. */
.db-grid--card {
    --dz-grid-header-rule-color: var(--dz-hairline);
}

/* %dx% IS NOT A CSS FEATURE: tools/build.js expands it at build time into one copy of the rule per
   widget family, so `%dx%-header-panel` becomes the DataGrid selector and the TreeList one. Write
   the token form, never the expanded pair - a hand-written pair drifts the moment a selector is
   added to only one half. */

/* DevExtreme needs this header-panel for search/group/layout slots, but it does not need to look like
   a second card above the grid. Keep the panel in the layout and flatten its paint. */
.db-grid .dx-datagrid-header-panel,
.db-grid .dx-treelist-header-panel,
.db-grid .dx-datagrid-header-panel .dx-toolbar,
.db-grid .dx-treelist-header-panel .dx-toolbar {
    padding: 0;
    background: transparent;
    border: 0;
}

/* The gap between that toolbar and the column headers, restated so the density can tighten it
   (10px default, 4px compact). The toolbar's controls keep their size either way (20-density.css). */
.db-grid .dx-datagrid-header-panel .dx-toolbar,
.db-grid .dx-treelist-header-panel .dx-toolbar {
    margin-bottom: var(--dz-grid-toolbar-margin-bottom);
}

/* The grid's typeface. dx.light.css sets the family on .dx-widget, so without this every cell
 * renders in "Helvetica Neue" whatever --dz-font-family says.
 *
 * Deliberately NOT a blanket `.db-grid .dx-widget`: the row-action icon and DevExtreme's own glyphs
 * are icon fonts set by class, and a family that reaches them turns a control into a letter. */
.db-grid,
.db-grid .dx-datagrid,
.db-grid .dx-treelist,
.db-grid .dx-datagrid-header-panel .dx-toolbar,
.db-grid .dx-treelist-header-panel .dx-toolbar,
.db-grid .dx-datagrid-headers .dx-header-row > td,
.db-grid .dx-treelist-headers .dx-header-row > td,
.db-grid .dx-datagrid-pager,
.db-grid .dx-treelist-pager {
    font-family: var(--dz-font-family);
}

/* A CELL VALUE IS DATA, not chrome -- an identifier, a timestamp, a log line -- so it is ADDRESSABLE
 * separately from the interface font every other grid surface above reads (00-tokens.css's own
 * guide quote: "a label is chrome and a value is what someone came to read"). WL-20a (ClickUp
 * 86894yg24), item 9 / WL-19 audit G13, G23, G29. Higher specificity than the catch-all `.db-grid
 * %dx%` rule above (3 steps against 2), so it wins without needing !important.
 *
 * WL-20a fix round 1 (ClickUp 86894yg24), Brad's decision A77: this does NOT read
 * --dz-font-family-data directly. That token already defaults to Roboto (the guide's data face),
 * so every grid on every tenant would have shipped with its data columns switched from Poppins to
 * Roboto on ship day, whether or not that tenant's profile ever set typography.data. Reading the
 * dedicated --dz-grid-data-font-family hook instead, whose own default CHAINS to the interface
 * font (see its :root declaration), means nothing repaints until a later item's compiler writes a
 * tenant's typography.data into that hook. */
.db-grid .dx-datagrid-rowsview .dx-data-row > td,
.db-grid .dx-treelist-rowsview .dx-data-row > td {
    font-family: var(--dz-grid-data-font-family, var(--dz-font-family));
}

/* The grid is a white card with a hairline border and one low shadow.
 *
 * The card wraps the TABLE - column headers, rows, pager - and not the toolbar above it, as both
 * mocks draw. DevExtreme puts the header panel inside .dx-datagrid alongside the headers and the
 * rowsview, so no single element spans exactly those three parts and the border is assembled
 * across them: headers take the top and the top corners, the rowsview takes the sides, and
 * whichever of the rowsview or the pager is last takes the bottom edge and the shadow.
 *
 * A browser without :has() keeps a rounded rowsview bottom above a square pager - visibly
 * imperfect, not broken. */
.db-grid .dx-datagrid-headers,
.db-grid .dx-treelist-headers {
    border: 1px solid var(--dz-grid-border-color);
    border-radius: var(--dz-grid-card-radius) var(--dz-grid-card-radius) 0 0;
}

.db-grid .dx-datagrid-rowsview,
.db-grid .dx-treelist-rowsview {
    border-right: 1px solid var(--dz-grid-border-color);
    border-bottom: 1px solid var(--dz-grid-border-color);
    border-left: 1px solid var(--dz-grid-border-color);
    border-radius: 0 0 var(--dz-grid-card-radius) var(--dz-grid-card-radius);
    box-shadow: var(--dz-grid-card-shadow);
}

/* `~` and not `+`. MEASURED: DevExtreme inserts four hidden elements between the rowsview and the
   pager - an overlay, the column chooser popup and two `dx-hidden` divs - so an adjacent-sibling
   selector never matches, and the rowsview's rounded bottom stacks on the pager's. */
.db-grid .dx-datagrid-rowsview:has(~ .dx-datagrid-pager),
.db-grid .dx-treelist-rowsview:has(~ .dx-treelist-pager),
.db-grid .dx-datagrid-rowsview:has(~ .dx-datagrid-total-footer),
.db-grid .dx-treelist-rowsview:has(~ .dx-treelist-total-footer) {
    border-bottom: 0;
    border-radius: 0;
    box-shadow: none;
}

.db-grid .dx-datagrid-pager,
.db-grid .dx-treelist-pager,
.db-grid .dx-datagrid-total-footer,
.db-grid .dx-treelist-total-footer {
    border-right: 1px solid var(--dz-grid-border-color);
    border-bottom: 1px solid var(--dz-grid-border-color);
    border-left: 1px solid var(--dz-grid-border-color);
    border-radius: 0 0 var(--dz-grid-card-radius) var(--dz-grid-card-radius);
    box-shadow: var(--dz-grid-card-shadow);
}

/* A CARD GRID DRAWS NO CARD OF ITS OWN, because the card around it already drew one. Without this
 * a rounded box sits inside the panel's rounded box, two borders between them and a shadow across
 * the seam (MEASURED on the dashboard's Top Data Users).
 *
 * On the ARCHETYPE rather than the container class, so it holds whatever the container is called
 * (the dashboard's `.gridbox`, for one). The `.db-card` / `.db-detail-card` container rules are
 * only for a hand-written grid inside one.
 *
 * The header keeps its BOTTOM border: it is the rule between the column headings and the rows, not
 * part of the box, and the design draws it. */
.db-grid--card .dx-datagrid-headers,
.db-grid--card .dx-treelist-headers {
    border-width: 0 0 1px 0;
    border-radius: 0;
}

.db-grid--card .dx-datagrid-rowsview,
.db-grid--card .dx-treelist-rowsview,
.db-grid--card .dx-datagrid-pager,
.db-grid--card .dx-treelist-pager,
.db-grid--card .dx-datagrid-total-footer,
.db-grid--card .dx-treelist-total-footer {
    border: 0;
    border-radius: 0;
    box-shadow: none;
}

/* ...AND A GRID UNDER A LEGACY HEADER STRIP IS A CARD'S CONTENT TOO, whatever archetype it declares.
 *
 * A grid cannot declare `.db-grid--card` just to lose its corners: the archetype also decides state
 * scope, search, filter and paging. The account dashboard's Transactions and Endpoints grids sit in
 * `.gridbox` + `.rounded-header` cards; `#dgInvoices` is DbEmbedded because it needs a parent-scoped
 * state key plus Filter, Page and Selection. Without this, their header band's rounded top stacks
 * flush under the strip's own. So the key is POSITION: a grid after a card-header strip inside the
 * same box is that card's content.
 *
 * `~` AND NOT `+`: an unnamed wrapper div usually sits between the strip and the grid. The two
 * selectors cover the grid being that sibling and the grid being inside it.
 *
 * DIRECTION KEEPS THIS SAFE (checked): a following-sibling combinator cannot reach backwards or
 * outwards. Account/ApiConnections declares a DbFullPage grid BEFORE its strip, and
 * CallBlocking/Statistics closes its strip's `.gridbox` before the grid opens; neither matches.
 *
 * RADIUS ONLY. The top border stays: `.rounded-header` draws no bottom edge and both the strip and
 * the column-header band are Sunken, so that hairline is the only thing separating title from
 * column headings. A bottom border on the strip would be tidier and is not this rule's to make -
 * eleven views carry that class. */
.rounded-header ~ .db-grid .dx-datagrid-headers,
.rounded-header ~ .db-grid .dx-treelist-headers,
.rounded-header ~ * .db-grid .dx-datagrid-headers,
.rounded-header ~ * .db-grid .dx-treelist-headers {
    border-radius: 0;
}

/* ...but a grid INSIDE a master-detail row is not a card. Every rule above reaches the nested grid
 * by descent, so without this a sub-table renders as a second card inside the first; a sub-table is
 * part of the row that opened it.
 *
 * The mock's expanded row is a label gutter with one hairline down its right edge, then a bare
 * `<table border-collapse: collapse>` with no border, radius or shadow. The header band keeps its
 * Sunken background and the rows keep their hairlines.
 *
 * Borders are dropped per edge so a host that wants one back can add it without fighting a
 * shorthand.
 *
 * `:where(:not(...))` keeps the selector at the specificity it had: the one master that is not a
 * sub-table's host is the detail-only one, just below. */
.db-grid:where(:not(.db-grid--detail-only)) .dx-master-detail-cell .dx-datagrid-headers,
.db-grid:where(:not(.db-grid--detail-only)) .dx-master-detail-cell .dx-treelist-headers,
.db-grid:where(:not(.db-grid--detail-only)) .dx-master-detail-cell .dx-datagrid-rowsview,
.db-grid:where(:not(.db-grid--detail-only)) .dx-master-detail-cell .dx-treelist-rowsview,
.db-grid:where(:not(.db-grid--detail-only)) .dx-master-detail-cell .dx-datagrid-pager,
.db-grid:where(:not(.db-grid--detail-only)) .dx-master-detail-cell .dx-treelist-pager,
.db-grid:where(:not(.db-grid--detail-only)) .dx-master-detail-cell .dx-datagrid-total-footer,
.db-grid:where(:not(.db-grid--detail-only)) .dx-master-detail-cell .dx-treelist-total-footer {
    border: 0;
    border-radius: 0;
    box-shadow: none;
}

/* ...EXCEPT WHEN THE MASTER IS ONLY A HOST: `.db-grid--detail-only` is the host's own statement that
 * it shows its one row's detail grid as the page's grid, its master row hidden (Plans, which lists a
 * bundle's plans and hides the bundle row when there is a single bundle). The grid in the row is then
 * the page's only table, so it is a full-page grid, not a sub-table:
 *
 *   - the master draws no frame of its own (its rows view and the rest of its parts, named as the
 *     host's own children so the nested grid is not reached);
 *   - the row's cell lends no inset, rule or shade to the grid it holds;
 *   - the nested grid is left out of the sub-table opt-out above, so it takes the card rules by
 *     descent like any full-page grid.
 *
 * Toggled by the host's script as the row count changes, in both directions; nothing is drawn by
 * the page. */
.db-grid--detail-only > * > .dx-datagrid-headers,
.db-grid--detail-only > * > .dx-treelist-headers,
.db-grid--detail-only > * > .dx-datagrid-rowsview,
.db-grid--detail-only > * > .dx-treelist-rowsview,
.db-grid--detail-only > * > .dx-datagrid-pager,
.db-grid--detail-only > * > .dx-treelist-pager,
.db-grid--detail-only > * > .dx-datagrid-total-footer,
.db-grid--detail-only > * > .dx-treelist-total-footer {
    border: 0;
    border-radius: 0;
    box-shadow: none;
}

/* THE :root STEP is what lets `border: 0` win on the top edge. The host's Site.css draws a square
 * top rule on this cell at (0,4,1), and this chain without :root is (0,4,0). :root needs no
 * data-db-theme, so an unthemed host gets the same rule (33-grid-states takes the same step). */
:root .db-grid--detail-only > * > .dx-datagrid-rowsview .dx-master-detail-row > .dx-master-detail-cell,
:root .db-grid--detail-only > * > .dx-treelist-rowsview .dx-master-detail-row > .dx-master-detail-cell {
    padding: 0;
    border: 0;
}

/* A LABELLED SECTION INSIDE A MASTER-DETAIL ROW - `.dz-md-section` holding a `.dz-md-section-title`
 * and the grid it names ("Payments", the revisions chain). Shared by Billing Center and the Account
 * Dashboard's expanded transaction row.
 *
 * The title is parked in the left gutter - the run of the row's command columns - left-aligned and
 * vertically centred against the whole sub-table, with one hairline down its right edge where the
 * table starts (the Billing Center mock's flex pair: a label gutter, then the table). The box spans
 * the FULL indent (left: -indent, width: indent) so its border lands exactly on the table's left
 * edge. `top: 0; bottom: 0` with a flex centre, not a line height: a one-line block pinned to the
 * top of a 200px panel reads as a heading that has come loose.
 *
 * --dz-grid-detail-indent is not a token. It is the width of the command columns, which varies -
 * DevExtreme adds an indent column per GROUP LEVEL - so the host measures it and writes it on the
 * grid (syncDetailIndent in voyager.invoice.index.js). The fallback is the ungrouped width, which is
 * what a host that does not measure (the Dashboard) renders. */
.dz-md-section {
    position: relative;
}

/* The gap between two sections only when a VISIBLE one precedes this one. `.dz-md-section +
   .dz-md-section` would also match a hidden (display:none) payments section and give a revisions-only
   panel a phantom top gap, so the host toggles the class when it knows. */
.dz-md-revisions.dz-md-has-preceding {
    margin-top: 16px;
}

.dz-md-section-title {
    position: absolute;
    top: 0;
    bottom: 0;
    left: calc(0px - var(--dz-grid-detail-indent, 80px));
    box-sizing: border-box;
    display: flex;
    align-items: center;
    justify-content: flex-start;
    width: var(--dz-grid-detail-indent, 80px);
    padding: 0 12px;
    border-right: 1px solid var(--dz-hairline);
    color: var(--dz-body);
    font-size: var(--dz-field-label-font-size);
    font-weight: var(--dz-field-label-font-weight);
    line-height: 1.3;
    white-space: nowrap;
}

/* The gutter takes the row hover while a row of ITS section is hovered, so the highlight does not
 * stop at the table's left edge.
 *
 * Pure CSS works because DevExtreme renders the whole detail content ONCE, in the fixed table's
 * detail cell (the scrolling twin is empty), so the title and the hovered row share an ancestor.
 *
 * Keyed on the nested row's .dx-state-hover, the class the shell's own hover rule reads, rather than
 * the section's :hover - pointing at the section's column headers is not hovering a row. */
.db-grid[data-db-grid-hover="true"] .dz-md-section:has(.dx-data-row.dx-state-hover) > .dz-md-section-title {
    background-color: var(--dz-grid-row-hover-background);
}

/* THE HEADER SKIN FOR GRIDS THAT ARE NOT ON THE CONTRACT.
 *
 * This paints the header of EVERY DevExtreme grid in the host, so the look lands without touching a
 * view or asking anything of a grid's constructor. Unported grids carry per-grid logic a port would
 * have to re-validate; a repaint has nothing to re-validate.
 *
 * BOTH WIDGETS via %dx%. Accounts, Products, Order Edit, Home/Multi and the label and APN lists are
 * TreeLists - 14 Razor views. The old dark header comes from Site.css's
 * `.themed-treelist .dx-treelist-headers .dx-header-row`, three classes deep but not !important, so
 * this wins on priority rather than specificity.
 *
 * THE HEADER TOKENS MUST BE DECLARED AT :root. On an unported grid there is no .db-grid, and
 * `color: var(--undeclared)` is invalid at computed-value time - the declaration drops in silence
 * and the text inherits the old white, invisible on the light header. See the matching note where
 * the tokens are declared.
 *
 * Kept at ONE class of specificity so the two-class, also-!important .db-grid rules that follow
 * still win. Those resolve the same value as a pure specificity step. Do not "deduplicate" the pair.
 *
 * This is a SKIN, not the contract: colour, the rule under the header and the weight. Nothing
 * structural - no padding, row metrics or cell borders - because those interact with per-grid
 * column and template logic only a real port should take on. */
/* !important: beats Site.css `.themed-treelist .dx-treelist-headers .dx-header-row` (0,3,0) and its `.dx-col-fixed` header chain (0,6,0); one class cannot outrank them. */
.dx-datagrid-headers,
.dx-treelist-headers,
.dx-datagrid-headers .dx-header-row,
.dx-treelist-headers .dx-header-row,
.dx-datagrid-headers .dx-header-row > td,
.dx-treelist-headers .dx-header-row > td,
.dx-datagrid-content-fixed .dx-header-row > td,
.dx-treelist-content-fixed .dx-header-row > td {
    color: var(--dz-grid-header-color) !important;
    background-color: var(--dz-grid-header-background) !important;
}

/* !important: beats Site.css `.themed-treelist .dx-treelist-headers { border-bottom: none }` at (0,2,0); one class cannot outrank it. */
.dx-datagrid-headers,
.dx-treelist-headers {
    border-bottom: 1px solid var(--dz-grid-header-rule-color) !important;
}

.dx-datagrid-headers .dx-header-row > td,
.dx-treelist-headers .dx-header-row > td {
    font-weight: 600;
}

/* The sort arrow and the header-filter glyph are near-white (drawn for a dark header) and vanish on
   the tinted one. DevExtreme paints its icons on a child element, so the cell colour does not reach
   them. */
/* !important: beats Site.css `.themed-treelist .dx-treelist .dx-header-filter:not(.dx-header-filter-empty)` at (0,4,0) against this (0,3,0). */
.dx-datagrid-headers .dx-header-row .dx-sort,
.dx-treelist-headers .dx-header-row .dx-sort,
.dx-datagrid-headers .dx-header-row .dx-header-filter,
.dx-treelist-headers .dx-header-row .dx-header-filter {
    color: var(--dz-grid-header-color) !important;
}

/* !important: beats Site.css's (0,6,0) `.themed-treelist ... .dx-header-row .dx-col-fixed` header paint, and outranks the one-class !important skin above. */
.db-grid .dx-datagrid-headers,
.db-grid .dx-treelist-headers,
.db-grid .dx-datagrid-headers .dx-header-row,
.db-grid .dx-treelist-headers .dx-header-row,
.db-grid .dx-datagrid-headers .dx-header-row > td,
.db-grid .dx-treelist-headers .dx-header-row > td,
.db-grid .dx-datagrid-content-fixed .dx-header-row > td,
.db-grid .dx-treelist-content-fixed .dx-header-row > td {
    color: var(--dz-grid-header-color) !important;
    background-color: var(--dz-grid-header-background) !important;
}

/* The guide asks for the header tint "with a rule under it"; without one a light header and a white
   row read as one block. */
/* !important: equal specificity to Site.css `.themed-treelist .dx-treelist-headers` (0,2,0),
   which may load later. */
.db-grid .dx-datagrid-headers,
.db-grid .dx-treelist-headers {
    border-bottom: 1px solid var(--dz-grid-header-rule-color) !important;
}

/* !important: beats Site.css `.themed-treelist .dx-treelist-headers .dx-treelist-table .dx-row > td { border: none }` at (0,4,1) against this (0,3,1). */
.db-grid .dx-datagrid-headers .dx-header-row > td,
.db-grid .dx-treelist-headers .dx-header-row > td {
    padding-top: var(--dz-grid-header-padding-y);
    padding-bottom: var(--dz-grid-header-padding-y);
    border-color: transparent !important;
    font-weight: 600;
}

/* !important: outranks this sheet's own `border-color: transparent !important` on header cells. */
.db-grid[data-db-grid-column-lines="true"] .dx-datagrid-headers .dx-header-row > td,
.db-grid[data-db-grid-column-lines="true"] .dx-treelist-headers .dx-header-row > td {
    border-right-color: var(--dz-grid-border-color) !important;
}

/* WL-20a (ClickUp 86894yg24), item 3 / WL-19 audit G2: --dz-header-hover-background had zero
 * readers -- the header skin above forces its resting background with !important in every state,
 * so nothing ever painted a hover. A sortable column header IS a control (it sorts on click and
 * carries a header filter), unlike a static card/panel header strip, so it earns its own hover
 * paint. :focus-visible alongside :hover for the rare case a header cell is reachable by keyboard
 * (DevExtreme's own sort/filter affordances are, even though the td itself is not a native
 * button). !important to beat the resting skin's own !important two rules above.
 *
 * WL-20a fix round 2, D2: the fallback is `var(--dz-grid-header-background)`, not the resting
 * skin's literal colour -- --dz-header-hover-background is no longer declared at :root at all (see
 * 00-tokens.css), so an untouched tenant falls all the way through to the SAME token the resting
 * rule above reads, archetype and all. WL-24 (ClickUp 86894yg24), Brad A100: every archetype now
 * shares the ONE --dz-grid-header-background chain (the `.db-grid--card` divergence this comment
 * used to describe is gone, see that rule's own comment), so hover already equalled rest everywhere
 * before this fix and continues to. A tenant whose profile sets headerHoverBackground gets it
 * compiled onto :root as a real declaration, which wins outright and never even reaches this
 * fallback. */
.db-grid .dx-datagrid-headers .dx-header-row > td:hover,
.db-grid .dx-treelist-headers .dx-header-row > td:hover,
.db-grid .dx-datagrid-headers .dx-header-row > td:focus-visible,
.db-grid .dx-treelist-headers .dx-header-row > td:focus-visible {
    background-color: var(--dz-header-hover-background, var(--dz-grid-header-background)) !important;
}

/* WHERE THE COLUMN EDGES ARE, WHILE YOU ARE LOOKING FOR ONE.
 *
 * With column lines off (the shell default) nothing shows where a resize boundary is, and
 * DevExtreme's `columns-separator` stays TRANSPARENT until a drag starts. So hovering anywhere in the
 * header row reveals every column edge; leaving it takes them away, keeping the resting grid clean.
 *
 * AN INSET SHADOW RATHER THAN A BORDER, because a border participates in layout: adding one on
 * hover re-runs the column widths and shifts every header by a pixel. A shadow paints inside the
 * box the cell already has. */
.db-grid .dx-datagrid-headers .dx-header-row > td,
.db-grid .dx-treelist-headers .dx-header-row > td {
    box-shadow: inset -1px 0 0 0 transparent;
    transition: box-shadow 120ms ease-out;
}

/* The edge is a promise that a boundary is worth dragging, so it is withheld in three cases:
 *
 * - :not(:last-child) - there is no column after the final edge, and the card's border is there.
 * - :not([class*="dx-command"]) - a command column has no caption and does not resize. Billing
 *   Center's lead block is five command cells (selection, one indent per group level, the expander,
 *   two `buttons` columns), which would otherwise show four dividers over 122px of empty space.
 *   The substring key is the one the divider rule at the end of this file documents.
 * - a grid that cannot resize at all. DevExtreme renders the `columns-separator` only when
 *   resizing is on (MEASURED: zero on the dashboard's Top Data Users, which declares it off), so
 *   :has() reads the answer from the DOM rather than a class a host would forget. A browser
 *   without :has() keeps the edge on a fixed grid - visibly imperfect, not broken. */
.db-grid:has(.dx-datagrid-columns-separator) .dx-datagrid-headers:hover .dx-header-row > td:not(:last-child):not([class*="dx-command"]),
.db-grid:has(.dx-treelist-columns-separator) .dx-treelist-headers:hover .dx-header-row > td:not(:last-child):not([class*="dx-command"]) {
    box-shadow: inset -1px 0 0 0 var(--dz-grid-header-edge-color);
}

/* The separator DevExtreme parks on the boundary - the actual grab target. It keeps the vendor's
 * transparent resting state (the `-transparent` modifier says "not now"; overriding it strands a bar
 * wherever the pointer last was) and takes the accent only once live: this is feedback for something
 * the viewer is doing, not a fact about the record. The vendor default is Bootstrap's #337ab7. */
.db-grid .dx-datagrid-columns-separator:not(.dx-datagrid-columns-separator-transparent),
.db-grid .dx-treelist-columns-separator:not(.dx-treelist-columns-separator-transparent) {
    background-color: var(--dz-grid-separator-color);
}

/* The divider where the fixed columns end. DevExtreme paints it 2px #ddd from a rule naming
   `.dx-treelist-container .dx-datagrid-rowsview` - the two families crossed - so neither family's
   own rules reach it, and on a dark page it is a bright bar down the grid. Takes the colour of every
   other grid line. Both sides: a left-pinned column ends on the cell's left edge, a right-pinned one
   (an action column) on its right. A grid without `.db-grid` is covered by 22-devextreme-adapter.css. */
/* !important: beats dx.light `.dx-datagrid .dx-datagrid-rowsview .dx-data-row td.dx-pointer-events-none` at (0,4,1) against this (0,2,1). */
.db-grid td.dx-pointer-events-none {
    border-left-color: var(--dz-grid-border-color) !important;
    border-right-color: var(--dz-grid-border-color) !important;
}

.db-grid .dx-datagrid-rowsview,
.db-grid .dx-treelist-rowsview {
    background: var(--dz-grid-surface);
    border-color: var(--dz-grid-border-color);
}

/* Rows are 50px with 12px padding on all four sides.
 *
 * The height is explicit because padding alone cannot get there: 14px text at 1.4 is 19.6px, which
 * with 12px top and bottom lands at 44, not 50. On a table cell `height` is a minimum, so a wrapped
 * cell still grows instead of clipping. */
.db-grid .dx-datagrid-rowsview .dx-data-row > td,
.db-grid .dx-treelist-rowsview .dx-data-row > td {
    height: var(--dz-grid-row-height);
    padding-top: var(--dz-grid-row-padding-y);
    padding-bottom: var(--dz-grid-row-padding-y);
    padding-right: var(--dz-grid-cell-padding-x);
    padding-left: var(--dz-grid-cell-padding-x);
    color: var(--dz-grid-row-color);
    border-top-color: var(--dz-grid-border-color);
}

/* ...except in a card list, where a row is one whole card. The card is as tall as its content, so
 * neither the row floor nor the row padding applies. A host that lays the cell out as a flex item
 * (Group View's two-up cards) turns that floor into a fixed height and clips the card to it.
 * MEASURED: 30px at Compact against a 645px card. */
.db-grid.db-grid--cardlist .dx-datagrid-rowsview .dx-data-row > td,
.db-grid.db-grid--cardlist .dx-treelist-rowsview .dx-data-row > td {
    height: auto;
    padding: 0;
}

.db-grid .dx-datagrid-headers .dx-header-row > td,
.db-grid .dx-treelist-headers .dx-header-row > td {
    padding-right: var(--dz-grid-cell-padding-x);
    padding-left: var(--dz-grid-cell-padding-x);
}

/* A MARKER COLUMN: a cell that is one dot or glyph, under a header that is only its indicators.
 * Written by datablaze.ui.grid.markerColumn (src/grid.columns.js), which also sets the column's width,
 * minWidth and an empty header template - this is the paint half of that constructor, not a class
 * to spell by hand.
 *
 * THE GUTTERS ARE THE WIDTH. The column's floor is its HEADER: an empty caption still carries the
 * 18px sort-and-filter indicator group. With 12px gutters that is 12 + 18 + 12 = 42; at 4px it is
 * 4 + 18 + 4 = 26, and markerColumn declares 28 for width and minWidth, where the centred funnel
 * sits in the middle. DevExtreme puts a column's cssClass on header and data cells alike. Fixed at
 * 4px in every density: the column is already at its floor. */
.db-grid .dx-datagrid-rowsview .dx-data-row > td.db-grid-marker-cell,
.db-grid .dx-treelist-rowsview .dx-data-row > td.db-grid-marker-cell,
.db-grid .dx-datagrid-headers .dx-header-row > td.db-grid-marker-cell,
.db-grid .dx-treelist-headers .dx-header-row > td.db-grid-marker-cell {
    padding-right: 4px;
    padding-left: 4px;
}

    /* AND THE FUNNEL SITS IN THE MIDDLE, because there is no caption beside it. MEASURED on
       Subscribers' Online column: the caption element is 0px wide under an empty header template and
       STILL contributes its 3px right margin, and the funnel has its own 4px left margin - 7px off
       centre in a 20px content box. Centring the column itself is markerColumn's job (alignment is a
       column option; a text-align rule loses to the value DevExtreme writes on the cell). */
    /* Written from `.dx-header-row > td` so they out-rank the shared header rules that give every
       caption and funnel a left margin; a shallower selector lost to them (MEASURED: the funnel kept its
       4px and the empty caption its 3px, so a sorted marker overflowed by 7px). */
    .db-grid .dx-datagrid-headers .dx-header-row > td.db-grid-marker-cell .dx-datagrid-text-content,
.db-grid .dx-treelist-headers .dx-header-row > td.db-grid-marker-cell .dx-treelist-text-content {
        margin-right: 0;
        margin-left: 0;
    }

    .db-grid .dx-datagrid-headers .dx-header-row > td.db-grid-marker-cell .dx-header-filter,
.db-grid .dx-treelist-headers .dx-header-row > td.db-grid-marker-cell .dx-header-filter {
        margin-left: 0;
    }

    /* THE FUNNEL LEADS AND THE SORT ARROW FOLLOWS (owner direction). DevExtreme writes the arrow first;
       with no caption beside them, that pushed the funnel aside the moment the column was sorted.
       Funnel first keeps the control the viewer reaches for in the same place, and the arrow reads as
       state after it. */
    .db-grid .dx-datagrid-headers .dx-header-row > td.db-grid-marker-cell .dx-column-indicators,
.db-grid .dx-treelist-headers .dx-header-row > td.db-grid-marker-cell .dx-column-indicators {
        display: inline-flex;
        align-items: center;
    }

    .db-grid .dx-datagrid-headers .dx-header-row > td.db-grid-marker-cell .dx-column-indicators .dx-header-filter,
.db-grid .dx-treelist-headers .dx-header-row > td.db-grid-marker-cell .dx-column-indicators .dx-header-filter {
        order: -1;
    }

    .db-grid .dx-datagrid-headers .dx-header-row > td.db-grid-marker-cell .dx-column-indicators .dx-sort,
.db-grid .dx-treelist-headers .dx-header-row > td.db-grid-marker-cell .dx-column-indicators .dx-sort {
        margin-left: 4px;
    }

/* ...except the cell holding a TreeList's select-all checkbox, which needs its left padding back.
   TREELIST ONLY: a DataGrid gives select-all its own command column, while a TreeList puts the
   checkbox INSIDE the first header cell, absolutely positioned at DevExtreme's fixed 21px, and
   clears it with `.dx-treelist-headers .dx-header-row > .dx-treelist-select-all { padding-left: 44px }`.
   The padding rule above is deeper than that, so without this the caption renders UNDER the
   checkbox (MEASURED on Accounts).

   44px is DevExtreme's number, kept rather than derived from our padding token, because what it
   clears is DevExtreme's own 21px offset. The 14px after it is the indent box every row's first cell
   keeps before its text, so the caption lines up with the rows' (PP-D72, audit row A-C: Accounts'
   picker measured header text 14px left of the rows'; 19-grid-selection.css has the same 14px for the
   TreeLists without a checkbox, and takes this cell over where the select column hides it). */
.db-grid .dx-treelist-headers .dx-header-row > td.dx-treelist-select-all {
    padding-left: calc(44px + 14px);
}

/* The identifier column is the row's link, and the ONLY link in the row: deep teal, weight 500, no
   underline at rest and underlined on hover. This overrides Bootstrap's #007bff, which is
   off-palette and the mid blue the guide rejects for failing 4.5:1 at grid size; #0a6188 clears it
   at 6.82.

   .dx-link is excluded on purpose - that is DevExtreme's own command-column anchor, which is the
   row-action trigger rather than a link to a record. */
.db-grid .dx-datagrid-rowsview .dx-data-row a:not(.dx-link),
.db-grid .dx-treelist-rowsview .dx-data-row a:not(.dx-link) {
    color: var(--dz-link);
    font-weight: 500;
    text-decoration: none;
}

.db-grid .dx-datagrid-rowsview .dx-data-row a:not(.dx-link):hover,
.db-grid .dx-treelist-rowsview .dx-data-row a:not(.dx-link):hover,
.db-grid .dx-datagrid-rowsview .dx-data-row a:not(.dx-link):focus,
.db-grid .dx-treelist-rowsview .dx-data-row a:not(.dx-link):focus {
    color: var(--dz-link-hover);
    text-decoration: underline;
}

/* Selection checkboxes take the accent. DevExtreme draws its own box rather than a native input,
   so `accent-color` - which is what the mock uses - reaches nothing here; the checked state has to
   be painted directly. The unchecked box keeps the neutral border scale. */
.db-grid .dx-checkbox .dx-checkbox-icon {
    width: var(--dz-grid-checkbox-size);
    height: var(--dz-grid-checkbox-size);
    border-color: var(--dz-border);
    border-radius: var(--dz-radius-xs);
    font-size: calc(var(--dz-grid-checkbox-size) - 4px);
}

.db-grid .dx-checkbox.dx-checkbox-checked .dx-checkbox-icon,
.db-grid .dx-checkbox.dx-checkbox-indeterminate .dx-checkbox-icon {
    color: var(--dz-selection-ink);
    background-color: var(--dz-selection-color);
    border-color: var(--dz-selection-color);
}

.db-grid .dx-checkbox.dx-checkbox-indeterminate .dx-checkbox-icon::before {
    background-color: var(--dz-on-accent);
}

/* Wrapped cells break inside a word when the word alone is wider than the cell.

   "Wrap cell text" only removes dx-datagrid-nowrap, so the cell breaks at spaces. An unbroken
   token - an ICCID, an APN, a serial, a URL - still renders as one line and spills or clips.

   Deliberately NOT conditioned on the option: overflow-wrap has no effect while white-space is
   nowrap, so this is inert until DevExtreme allows wrapping, and it does not depend on where
   DevExtreme hangs its nowrap class.

   break-word rather than anywhere: both break the same way under DevExtreme's fixed table layout,
   and break-word leaves min-content alone for any grid that is not fixed.

   The trade: a 19-digit ICCID in a narrow column breaks mid-number, harder to read and select than
   one line but better than cut off - and the reason this stays tied to the wrap option. */
.db-grid .dx-datagrid-rowsview .dx-data-row > td,
.db-grid .dx-treelist-rowsview .dx-data-row > td {
    overflow-wrap: break-word;
}

/* Header cells never wrap, so they never break inside a word either.

   DevExtreme applies its wrap to header text too; with break-word a one-word column name shreds
   ("Labels" as "La be ls" down three lines in a 19px header). The style guide settles it: header
   cells carry `white-space: nowrap` "so column names never wrap". A wrapped column name is a narrow
   COLUMN, and the fix for that is the column width. */
.db-grid .dx-datagrid-headers .dx-header-row > td,
.db-grid .dx-treelist-headers .dx-header-row > td,
.db-grid .dx-datagrid-headers .dx-header-row > td > .dx-datagrid-text-content,
.db-grid .dx-treelist-headers .dx-header-row > td > .dx-treelist-text-content {
    white-space: nowrap;
}

.db-grid[data-db-grid-alternating-rows="true"] .dx-datagrid-rowsview .dx-row-alt > td,
.db-grid[data-db-grid-alternating-rows="true"] .dx-treelist-rowsview .dx-row-alt > td {
    background-color: var(--dz-grid-row-alternate-background);
}

/* .dx-state-hover, NOT :hover. A grid with any fixed column renders its rows TWICE - in
   .dx-datagrid-content-fixed and in the scrolling content - and the pointer is over only one, so
   :hover leaves a seam at the fixed-column boundary. DevExtreme mirrors the .dx-state-hover CLASS
   onto both halves; keying on it makes them agree (the unmatched half otherwise stays on dx.light's
   rgba(0,0,0,.04)). */
/* !important: beats dx.light's hover rule, whose :not() chain makes it (0,9,1) against this (0,5,1). */
.db-grid[data-db-grid-hover="true"] .dx-datagrid-rowsview .dx-data-row.dx-state-hover > td,
.db-grid[data-db-grid-hover="true"] .dx-treelist-rowsview .dx-data-row.dx-state-hover > td {
    background-color: var(--dz-grid-row-hover-background) !important;
}

/* Selecting a row must not change its ink.
 *
 * The adversary is dx.light.css's
 * `.dx-datagrid-rowsview .dx-selection.dx-row:not(.dx-row-focused) > td` at (0,4,1) - the :not()
 * argument counts - which sets #333 and beats our general cell rule at (0,3,1). This selector is
 * (0,4,1) too, so it ties and wins on source order: datablaze.ui.css loads after the theme. */
/* !important: beats dx.light's selected-row `:hover > td.dx-focused` at (0,6,1), which would paint the focused cell white. */
.db-grid .dx-datagrid-rowsview .dx-selection.dx-row > td,
.db-grid .dx-treelist-rowsview .dx-selection.dx-row > td,
.db-grid .dx-datagrid-rowsview .dx-selection.dx-row:hover > td,
.db-grid .dx-treelist-rowsview .dx-selection.dx-row:hover > td {
    background-color: var(--dz-grid-row-selected-background) !important;
    color: var(--dz-grid-row-color);
}

/* A parent row (.db-grid-row--parent, 33-grid-states.css) under the two rules above. Being
 * !important, they replace its band rather than layer over it: a hovered parent row took the page
 * step it already had and did not change, and a selected one lost the band. These restate both as
 * darker steps of the band, one class more specific than the rules they answer. */
.db-grid[data-db-grid-hover="true"] .dx-datagrid-rowsview .dx-data-row.db-grid-row--parent.dx-state-hover > td,
.db-grid[data-db-grid-hover="true"] .dx-treelist-rowsview .dx-data-row.db-grid-row--parent.dx-state-hover > td {
    background-color: var(--dz-grid-parent-row-hover-background) !important;
}

.db-grid .dx-datagrid-rowsview .dx-selection.dx-row.db-grid-row--parent > td,
.db-grid .dx-treelist-rowsview .dx-selection.dx-row.db-grid-row--parent > td,
.db-grid .dx-datagrid-rowsview .dx-selection.dx-row.db-grid-row--parent:hover > td,
.db-grid .dx-treelist-rowsview .dx-selection.dx-row.db-grid-row--parent:hover > td {
    background-color: var(--dz-grid-parent-row-selected-background) !important;
}

/* The row-action trigger is a vertical three-dot menu.
 *
 * Every grid builds it the same way - a dxDataGrid button column whose icon is 'fa fa-cog' - so the
 * glyph is decided in a dozen view scripts and Razor columns no stylesheet can reach. Overriding
 * ::before from one rule changes them all and out-specifies Font Awesome's single-class rule without
 * touching a caller. Both mocks draw three dots; a gear promises "settings for this grid" rather
 * than "things I can do to this row".
 *
 * Colour is Body rather than the dark slate the toolbar icon rule pins, so the trigger sits at the
 * weight of the row text instead of competing with the identifier link. */
.db-grid .dx-command-edit .dz-actionIcon {
    /* Restores what the cell's font-size: 0 took away. The dots are drawn from
       --dz-grid-row-action-dot-size rather than from the font, so this matters only for anything
       a caller puts inside the anchor. */
    font-size: var(--dz-grid-row-font-size);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 18px;
    height: 18px;
    color: var(--dz-body);
}

    /* The construct's trigger is a div[role=button], not a link, so the browser draws no pointer
     * for it. Hand-built cog links keep the pointer their anchor already gives them. */
    .db-grid .dx-command-edit div[role="button"].dz-actionIcon {
        cursor: pointer;
    }

    /* ...and it stays Body on a SELECTED row.
     *
     * The anchor carries .dx-link, and dx.light.css sets
     * `.dx-datagrid-rowsview .dx-selection > td .dx-link { color: inherit }` at (0,3,1), beating the
     * rule above's (0,3,0). `inherit` would hand the dots the row's Ink, but the trigger is
     * deliberately one step lighter, so the value is restated. Naming the theme's
     * .dx-selection > td shape makes this (0,4,1) and wins outright. */
    .db-grid .dx-datagrid-rowsview .dx-selection > td .dz-actionIcon,
.db-grid .dx-treelist-rowsview .dx-selection > td .dz-actionIcon {
        color: var(--dz-body);
    }

/* A row's own icon actions - the trash and undo an editable grid puts in its buttons column - in the
 * row's ink, not in link blue.
 *
 * Each is an anchor carrying .dx-link. dx.light.css paints `.dx-datagrid .dx-link` link blue at
 * (0,2,0) and `.dx-datagrid-rowsview .dx-selection > td .dx-link` inherit at (0,3,1), so the same
 * icon differs between plain and ticked rows. Both shapes are answered here, the selected one named
 * the way the theme names it so it wins on weight.
 *
 * Two kinds of .dx-link are left alone: the three-dot trigger (.dz-actionIcon), a step lighter on
 * purpose, and a masked .db-icon, whose ink the layout panel's chips depend on (10-layout-panel.css). */
.db-grid .dx-datagrid-rowsview .dx-command-edit .dx-link:not(.dz-actionIcon):not(.db-icon),
.db-grid .dx-treelist-rowsview .dx-command-edit .dx-link:not(.dz-actionIcon):not(.db-icon),
.db-grid .dx-datagrid-rowsview .dx-selection > td.dx-command-edit .dx-link:not(.dz-actionIcon):not(.db-icon),
.db-grid .dx-treelist-rowsview .dx-selection > td.dx-command-edit .dx-link:not(.dz-actionIcon):not(.db-icon) {
    color: var(--dz-grid-row-action-icon-color);
}

/* A ROW ACTION - edit or delete, declared through the row-action vocabulary (manifest `rowAction`;
 * .DbRowEdit() / .DbRowDelete() in Razor, datablaze.ui.grid.rowAction in script). It is a masked
 * .db-icon, so the rule above leaves it alone and this one gives it the same ink and its own box:
 * the glyph's default size is the inline affordance's 13px, and a row button is 18.
 *
 * NOT scoped to .db-grid. The class only exists where a helper wrote it, on a host that adopted the
 * package, so this reaches unported grids too (the Account Dashboard's Edit UDFs popup) and every
 * edit and delete draws the same. */
.db-icon.db-row-action {
    --db-icon-size: var(--dz-grid-row-action-icon-size);
}

.dx-datagrid-rowsview .dx-command-edit .dx-link.db-row-action,
.dx-treelist-rowsview .dx-command-edit .dx-link.db-row-action,
.dx-datagrid-rowsview .dx-selection > td.dx-command-edit .dx-link.db-row-action,
.dx-treelist-rowsview .dx-selection > td.dx-command-edit .dx-link.db-row-action {
    color: var(--dz-grid-row-action-icon-color);
}

/* Three drawn dots, not a font glyph: the mock draws 3px circles at a 2.5px gap, which no font's
 * ellipsis reproduces.
 *
 * One dot plus two box-shadows rather than three elements, because ::before is the only box this
 * rule owns - the element is written by view scripts as `icon: 'fa fa-cog'` in DevExtreme's markup.
 * The empty content also clears whatever glyph the caller's fa-* class set.
 *
 * currentColor so the dots follow the element's colour - Body here, and what a cobrand sheet or a
 * hover rule would change. */
.db-grid .dx-command-edit .dz-actionIcon::before {
    content: "";
    /* display:block is load-bearing: width and height do nothing on an inline ::before, so the dot
       would render at zero size. Flex is not an option because DevExtreme pins
       `display: inline-block` on this anchor from a five-class selector, so the element is not
       reliably a flex container; a block child with auto side margins centres under either.
       7.5px + 3px + 7.5px is the 18px box; the two shadow dots overflow it, keeping the hit area
       the same 18px square. */
    display: block;
    width: var(--dz-grid-row-action-dot-size);
    height: var(--dz-grid-row-action-dot-size);
    margin: 7.5px auto;
    background-color: currentColor;
    border-radius: 50%;
    box-shadow:
        0 calc(-1 * (var(--dz-grid-row-action-dot-size) + var(--dz-grid-row-action-dot-gap))) 0 currentColor,
        0 calc(var(--dz-grid-row-action-dot-size) + var(--dz-grid-row-action-dot-gap)) 0 currentColor;
}

/* TOUCH HIT AREAS, PHONES ONLY: the row's checkbox and its action trigger reach 44px.
 *
 * The glyphs are 14px (the checkbox) and 18px (the three dots, an edit or delete icon), which a
 * fingertip cannot reliably land on. The paint stays exactly as drawn; only the area that answers a
 * tap grows, through an empty ::after that bleeds past the control with no background, border or
 * colour. Nothing moves: an absolutely positioned child takes no space. A masked row-action icon
 * grows its own box instead (padding taken back by negative margins), because its mask clips the
 * hit test of anything bleeding past it.
 *
 * ONE CONDITION FOR EVERY PHONE-ONLY RULE IN THIS PACKAGE, and it is this literal:
 * `(max-width: 824.98px) and (pointer: coarse)`. Voyager swaps its sidebar for a drawer below 825px
 * (Scripts/voyager.navdrawer.js), which is what "a phone" means here, and 824.98 is the same edge
 * without the 825px viewport itself. This package cannot read Voyager's `html.nav-drawer-mode`
 * class, so it states the width instead. `pointer: coarse` keeps a narrowed desktop window with a
 * mouse on the desktop rendering. Both must hold, so at 825px and up, or with a mouse, none of these
 * rules match and the sheet renders exactly as before. A custom property cannot be read in a media
 * condition, hence the literal, repeated in 09, 19, 21 and 22. tests/wl21-adversary.harness.js
 * teaches this exact spelling as a layout-only width condition: a block here that sets paint throws.
 *
 * WHY THE CELLS CHANGE TOO. DevExtreme clips a body cell (`overflow: hidden`), and the selection cell
 * is 32px wide on a phone and the action cell 24px (a view declares 40 and 32 for a desktop;
 * grid.columns.js fitCommandColumnsToPhone narrows them, because the identity column needs the room),
 * so a 44px target would be cut to the cell. Horizontal overflow is therefore let out of a command
 * cell, vertical stays clipped to the row (the row is 50px, so 44 fits, and a clipped bleed cannot
 * lengthen the scroll area). The bleed lands in the next cell's own padding or empty side space,
 * never on its content. Two exceptions keep taps where they belong:
 *   - A TRAILING action cell stays clipped on both axes: its right-hand bleed would run past the
 *     table and grow a horizontal scroll. It keeps the cell's width and gains the 44px height.
 *   - An action cell holding SEVERAL icons (edit beside delete) is widened to 44px per icon
 *     (grid.columns.js) and spaces its icons so each target is its own 44px square, never covering
 *     its neighbour.
 * A compact grid's 30px row clips the height to the row.
 *
 * WIDTH, WHERE TWO TARGETS SIT SIDE BY SIDE. Every target keeps its 44px HEIGHT. Selection beside
 * a row menu (Subscribers: checkbox 9-23px, dots anchor 35-53px, measured from the selection
 * cell's left edge) cannot keep 44px of WIDTH each: their centres are 28px apart, so two 44px
 * targets would overlap by 16px and the later cell would take every tap in the overlap. The
 * checkbox takes the cell's left edge to 6px right of the box (29px wide), and the row menu takes
 * from there to 13px right of its anchor (37px wide). */
@media (max-width: 824.98px) and (pointer: coarse) {
    .db-grid .dx-datagrid-headers .dx-header-row > td.dx-command-select,
.db-grid .dx-treelist-headers .dx-header-row > td.dx-command-select,
    .db-grid .dx-datagrid-rowsview .dx-data-row > td.dx-command-select,
.db-grid .dx-treelist-rowsview .dx-data-row > td.dx-command-select,
    .db-grid .dx-datagrid-rowsview .dx-data-row > td.dx-command-edit:not(:last-child),
.db-grid .dx-treelist-rowsview .dx-data-row > td.dx-command-edit:not(:last-child) {
        overflow-x: visible;
        overflow-y: clip;
    }

    /* THE IDENTITY COLUMN PADS 10px, NOT 12, while a phone grid fits. Written by
       datablaze.ui.grid.adaptColumnsToPhone (src/grid.phonecolumns.js), which puts this class on the
       column it keeps whole and takes it off again when fitting ends. MEASURED on an iPhone at 360px,
       Subscribers in Fit: the ICCID cell is 196px with 12 + 12 of padding and a 20-digit ICCID's text
       is 173.2px, so it needs 197.2 and ellipsises by about 1px. 12 -> 10 frees 4px (every 19-digit
       ICCID already fit). DevExtreme puts a column's cssClass on header and data cells alike.
       min() keeps the density: Compact's 6px (--dz-grid-cell-padding-x) is already under 10 and stays,
       while the 12px default steps down to 10. Four classes and a tag, so it out-ranks the shell's
       three-class cell padding above in every density, and the card-header rule in 16. Layout only. */
    .db-grid .dx-datagrid-rowsview .dx-data-row > td.db-grid-phone-identity-cell,
.db-grid .dx-treelist-rowsview .dx-data-row > td.db-grid-phone-identity-cell,
    .db-grid .dx-datagrid-headers .dx-header-row > td.db-grid-phone-identity-cell,
.db-grid .dx-treelist-headers .dx-header-row > td.db-grid-phone-identity-cell {
        padding-right: min(10px, var(--dz-grid-cell-padding-x));
        padding-left: min(10px, var(--dz-grid-cell-padding-x));
    }

    /* The 32px column (fitCommandColumnsToPhone) holds the 14px box with 9px either side. The cell
       rule below pins 36px with 12px sides, which would leave 8px of content for the box and push
       it off-centre to the right. The filter row is named for the reason that rule gives. */
    .db-grid .dx-datagrid-headers .dx-header-row > td.dx-command-select,
.db-grid .dx-treelist-headers .dx-header-row > td.dx-command-select,
    .db-grid .dx-datagrid-headers .dx-datagrid-filter-row > td.dx-command-select,
.db-grid .dx-treelist-headers .dx-treelist-filter-row > td.dx-command-select,
    .db-grid .dx-datagrid-rowsview .dx-data-row > td.dx-command-select,
.db-grid .dx-treelist-rowsview .dx-data-row > td.dx-command-select {
        width: 32px;
        min-width: 32px;
        max-width: 32px;
        padding-right: 9px;
        padding-left: 9px;
    }

    /* 14px wide at 9px from the cell's left edge: 9 to the left reaches the edge itself, and 21 to
       the right makes the 44 (12px of it past the cell, into the next cell's padding). */
    .db-grid .dx-datagrid-headers .dx-header-row > td.dx-command-select .dx-checkbox,
.db-grid .dx-treelist-headers .dx-header-row > td.dx-command-select .dx-checkbox,
    .db-grid .dx-datagrid-rowsview .dx-data-row > td.dx-command-select .dx-checkbox,
.db-grid .dx-treelist-rowsview .dx-data-row > td.dx-command-select .dx-checkbox {
        position: relative;
    }

    .db-grid .dx-datagrid-headers .dx-header-row > td.dx-command-select .dx-checkbox::after,
.db-grid .dx-treelist-headers .dx-header-row > td.dx-command-select .dx-checkbox::after,
    .db-grid .dx-datagrid-rowsview .dx-data-row > td.dx-command-select .dx-checkbox::after,
.db-grid .dx-treelist-rowsview .dx-data-row > td.dx-command-select .dx-checkbox::after {
        content: "";
        position: absolute;
        inset: -15px -21px -15px -9px;
    }

    /* Beside a row menu: stops 6px right of the box, where the row menu's target starts. */
    .db-grid .dx-datagrid-headers .dx-header-row > td.dx-command-select:has(+ td.dx-command-edit) .dx-checkbox::after,
.db-grid .dx-treelist-headers .dx-header-row > td.dx-command-select:has(+ td.dx-command-edit) .dx-checkbox::after,
    .db-grid .dx-datagrid-rowsview .dx-data-row > td.dx-command-select:has(+ td.dx-command-edit) .dx-checkbox::after,
.db-grid .dx-treelist-rowsview .dx-data-row > td.dx-command-select:has(+ td.dx-command-edit) .dx-checkbox::after {
        inset: -15px -6px -15px -9px;
    }

    .db-grid .dx-command-edit .dz-actionIcon {
        position: relative;
    }

    /* Vertical only: 18px + 13 + 13 = 44. */
    .db-grid .dx-command-edit .dz-actionIcon::after {
        content: "";
        position: absolute;
        inset: -13px 0;
    }

    /* Alone in its cell, so the sides grow too. */
    .db-grid .dx-command-edit .dz-actionIcon:only-child::after {
        inset: -13px;
    }

    /* Right of a selection cell, the left side stops 6px short of the anchor (the 29px the
       checkbox keeps) and the 18px anchor reaches 13px right: 6 + 18 + 13 = 37. */
    .db-grid .dx-datagrid-rowsview .dx-data-row > td.dx-command-select + td.dx-command-edit .dz-actionIcon:only-child::after,
.db-grid .dx-treelist-rowsview .dx-data-row > td.dx-command-select + td.dx-command-edit .dz-actionIcon:only-child::after {
        inset: -13px -13px -13px -6px;
    }

    /* A ROW ACTION GROWS ITS OWN BOX, NOT AN ::after. It is a masked .db-icon (18-icon-cell.css), and
       Chromium clips hit testing on a masked element to the element's own box, so an ::after bleeding
       past it never takes a tap (measured: taps 20px off the icon landed on the cell). Padding makes
       the box 18 + 13 + 13 = 44 while the glyph stays 18: content-box sizing keeps the width at the
       glyph's, and mask-origin sizes and centres the artwork in that content box. The negative
       margins take the padding back out of the layout, so the cell and the row (50px) are unchanged.
       The same splits as the drawn dots above: alone, the full 44 square; right of a selection cell,
       6px on the left (6 + 18 + 13 = 37).
       Seven classes, through the table and row, because dx.light.css pins this link's
       `padding: 0; margin: 0 3px` from `%dx% %dx%-content %dx%-table .dx-row
       .dx-command-edit-with-icons .dx-link` (six), which out-ranked the five-class spelling. */
    .db-grid .dx-datagrid-rowsview .dx-datagrid-table .dx-row .dx-command-edit .dx-link.db-row-action,
.db-grid .dx-treelist-rowsview .dx-treelist-table .dx-row .dx-command-edit .dx-link.db-row-action {
        box-sizing: content-box;
        padding: 13px;
        margin: -13px;
        -webkit-mask-origin: content-box;
        mask-origin: content-box;
    }

    .db-grid .dx-datagrid-rowsview .dx-datagrid-table .dx-data-row > td.dx-command-select + td.dx-command-edit .dx-link.db-row-action:only-child,
.db-grid .dx-treelist-rowsview .dx-treelist-table .dx-data-row > td.dx-command-select + td.dx-command-edit .dx-link.db-row-action:only-child {
        padding-left: 6px;
        margin-left: -6px;
    }

    /* Sharing a cell (edit beside delete): grid.columns.js gives the column 44px per action on a
       phone, and the padding is kept in the layout across, so each 44 square sits beside the next.
       Neighbouring targets meet and never overlap. */
    .db-grid .dx-datagrid-rowsview .dx-datagrid-table .dx-row .dx-command-edit .dx-link.db-row-action:not(:only-child),
.db-grid .dx-treelist-rowsview .dx-treelist-table .dx-row .dx-command-edit .dx-link.db-row-action:not(:only-child) {
        margin-right: 0;
        margin-left: 0;
    }

    /* A GROUP ROW'S CAPTION WRAPS. It is one cell across the row holding the group's caption and its
       summaries, and dx.light.css clips every body cell to one line: `%dx%-rowsview .dx-row > td {
       overflow: hidden; text-overflow: ellipsis }` at (0,2,1), under the grid's `nowrap` (white-space
       is inherited from the rowsview). MEASURED on an iPhone at 390px, Billing Center grouped by Bill
       Month: "Bill Month: Sep 2026 (12 invoices, Balance: $687,165.39, Total: $703,817.23)" needs 535px
       in a 306px cell, so everything after "Bill Month: Sep 2026 (12" was an ellipsis. `normal` lets the
       text break at its spaces and `clip` drops the ellipsis that a wrapped line no longer needs. Four
       classes and a tag, (0,4,1), against the theme's (0,2,1). Layout and text only: the row's colour,
       weight and rules are the shell's and are not touched. */
    .db-grid .dx-datagrid-rowsview .dx-group-row > td.dx-group-cell,
.db-grid .dx-treelist-rowsview .dx-group-row > td.dx-group-cell {
        white-space: normal;
        text-overflow: clip;
    }
}

/* The command cells that open a row.
 *
 * Sized to their content plus padding, so the checkbox sits as far from the card's left edge as the
 * ICCID below it sits from the divider. DevExtreme's theme gives 70px for a selection column and
 * 85-100px for a command column; pinning width, min-width and max-width is what beats the theme's
 * min-width.
 *
 * THE CHECKBOX CELL IS THE BILLING CENTER MOCK'S, transcribed:
 *
 *     <td style="padding:12px; ... vertical-align:middle; width:36px">
 *       <input type="checkbox" style="width:14px;height:14px;accent-color:var(--db-orange);margin:0">
 *
 * so 36px wide, 12px of padding, a 14px box, and the brand orange when ticked (the checked rule
 * paints that through --dz-accent). If it needs to change, change the sample.
 *
 * The painted column is 24px narrower than the mock's: the mock is `table-layout: auto` with no
 * box-sizing reset, so its 36px is a CONTENT width (36 + 12 + 12 = 60). DevExtreme's table is fixed
 * and this package is border-box, so 36 is the whole column; the checkbox still lands 12px from the
 * card's edge.
 *
 * ALL THREE ROW TYPES, the filter row included. DevExtreme sizes a command column as the MAX of what
 * CSS offers every cell in it and writes that into the colgroup (see --dz-grid-expand-cell-width), so
 * an unnamed filter-row cell keeps dx.light.css's 70px min-width and that becomes the column's
 * width. */
.db-grid .dx-datagrid-headers .dx-header-row > td.dx-command-select,
.db-grid .dx-treelist-headers .dx-header-row > td.dx-command-select,
.db-grid .dx-datagrid-headers .dx-datagrid-filter-row > td.dx-command-select,
.db-grid .dx-treelist-headers .dx-treelist-filter-row > td.dx-command-select,
.db-grid .dx-datagrid-rowsview .dx-data-row > td.dx-command-select,
.db-grid .dx-treelist-rowsview .dx-data-row > td.dx-command-select {
    width: var(--dz-grid-select-cell-width);
    min-width: var(--dz-grid-select-cell-width);
    max-width: var(--dz-grid-select-cell-width);
    padding-right: var(--dz-grid-select-cell-padding-x);
    padding-left: var(--dz-grid-select-cell-padding-x);
    /* And no text, which is not cosmetic: DevExtreme leaves a whitespace text node beside the
       checkbox, and the cell is 2px short of its content (36px border-box against 12 + 14 + 12), so
       `text-overflow: ellipsis` renders that whitespace as a literal "..." next to every tick box.
       The tick glyph is sized by .dx-checkbox-icon's own font-size, so it is unaffected. */
    font-size: 0;
}

/* A SELECT-ALL ON AN EMPTY GRID STAYS HIDDEN, INCLUDING AFTER SOMEONE CLICKS WHERE IT WOULD BE.
 *
 * DevExtreme hides its select-all on an empty grid with .dx-state-invisible and aria-hidden rather
 * than not rendering it, then DROPS that state on the first pointer interaction (MEASURED on the
 * Account Dashboard's empty Transactions grid). Without this, clicking the blank cell grows a tick
 * box that ticks nothing.
 *
 * Keyed on the grid's OWN rows view, `> * >` rather than a descendant, because a master-detail grid
 * contains other grids: an empty nested payments grid would otherwise take the select-all off the
 * OUTER grid. The unnamed step is the widget family container, so the rule serves both families.
 *
 * Display rather than a state class: the class is DevExtreme's to remove, and it removes it. */
.db-grid:has(> * > .dx-datagrid-rowsview.dx-empty) .dx-datagrid-headers .dx-command-select .dx-checkbox,
.db-grid:has(> * > .dx-treelist-rowsview.dx-empty) .dx-treelist-headers .dx-command-select .dx-checkbox {
    display: none;
}

/* Keyed on the class, NOT nth-child: on a grouped grid the action cells come after one indent cell
   per group level (Billing Center's are the fourth and fifth children). A command-edit cell is an
   icon cell wherever it lands.

   The filter row is named for the reason the selection rule above gives. */
.db-grid .dx-datagrid-headers .dx-header-row > td.dx-command-edit,
.db-grid .dx-treelist-headers .dx-header-row > td.dx-command-edit,
.db-grid .dx-datagrid-headers .dx-datagrid-filter-row > td.dx-command-edit,
.db-grid .dx-treelist-headers .dx-treelist-filter-row > td.dx-command-edit,
.db-grid .dx-datagrid-rowsview .dx-data-row > td.dx-command-edit,
.db-grid .dx-treelist-rowsview .dx-data-row > td.dx-command-edit {
    width: var(--dz-grid-action-cell-width);
    min-width: var(--dz-grid-action-cell-width);
    max-width: var(--dz-grid-action-cell-width);
    /* NO horizontal padding, unlike every other cell.
     *
     * The cell is 22px and the row-action anchor 18px, so 12px of side padding pushes the anchor
     * off-centre to the right. The anchor is the click target and stays 18px (already below the
     * 24px WCAG 2.5.8 minimum), so the padding gives. The mockup draws only the 3px dots with no
     * target around them; we keep the target and centre it in the full cell. */
    padding-right: 0;
    padding-left: 0;
    text-align: center;
}

/* THE WHITESPACE TEXT NODE, collapsed - ONLY on a cell built from `buttons`.
 *
 * DevExtreme leaves a whitespace text node beside the action anchor, so the anchor centres against
 * "anchor + a space" and the dots sit 2.4px left of centre - visible in a 22px column. The package
 * draws that trigger as dots, so font-size 0 costs nothing there.
 *
 * The scope matters: Billing Center renders FONT AWESOME icons in a CellTemplate command cell (the
 * yellow MRO warning, the stale-draft exclamation), and a font glyph at font-size 0 is 0x0.
 * -with-icons is DevExtreme's marker for a command column built from `buttons`, so a CellTemplate
 * cell keeps its inherited size. The checkbox cell's tick is sized by .dx-checkbox-icon, not by the
 * cell. */
.db-grid .dx-datagrid-headers .dx-header-row > td.dx-command-edit-with-icons,
.db-grid .dx-treelist-headers .dx-header-row > td.dx-command-edit-with-icons,
.db-grid .dx-datagrid-rowsview .dx-data-row > td.dx-command-edit-with-icons,
.db-grid .dx-treelist-rowsview .dx-data-row > td.dx-command-edit-with-icons {
    font-size: 0;
}

/* THE DIVIDER between a row's commands and its data - the vertical rule the mock draws immediately
 * left of Status, on the header, the filter row and every data row alike.
 *
 * Keyed on the BOUNDARY rather than any one command cell: whatever leading commands a grid has - a
 * checkbox, grouping indents (one dx-command-expand per group level), action cells - exactly one
 * cell is "the first one that is not a command". A TRAILING command column is preceded by a data
 * cell, so this cannot reach it.
 *
 * [class*="dx-command"] is a substring match, worth rechecking if DevExtreme renames anything: it
 * matches dx-command-select, dx-command-expand, dx-command-edit, dx-command-drag and
 * dx-command-adaptive - every command cell the widget emits.
 *
 * The three row types are listed rather than a bare descendant: a group row's colspan cell also
 * follows an indent cell, and a rule across it would draw a stray tick through a band the mock
 * draws unbroken. */
/* !important: outranks this sheet's own `border-color: transparent !important` on header cells and dx.light's (0,4,1) pointer-events cell border. */
.db-grid .dx-datagrid-headers .dx-header-row > [class*="dx-command"] + td:not([class*="dx-command"]),
.db-grid .dx-treelist-headers .dx-header-row > [class*="dx-command"] + td:not([class*="dx-command"]),
.db-grid .dx-datagrid-headers .dx-datagrid-filter-row > [class*="dx-command"] + td:not([class*="dx-command"]),
.db-grid .dx-treelist-headers .dx-treelist-filter-row > [class*="dx-command"] + td:not([class*="dx-command"]),
.db-grid .dx-datagrid-rowsview .dx-data-row > [class*="dx-command"] + td:not([class*="dx-command"]),
.db-grid .dx-treelist-rowsview .dx-data-row > [class*="dx-command"] + td:not([class*="dx-command"]) {
    border-left: 1px solid var(--dz-grid-header-rule-color) !important;
}

/* A CARD: a white panel with a hairline edge, soft corners and one low shadow, optionally opened
 * by a Sunken header strip.
 *
 * Every mock but the login page is built from these - the field grid on Subscriber Detail, the Map
 * and Recent Connections panels, the usage summary on Endpoint Detail - and the metrics are the
 * same ones the grid card draws, because it IS this object with a table in it.
 *
 * overflow: hidden is what keeps a square-cornered child - a header strip, a table's first row -
 * from poking out through the rounded corner. It is the one declaration here that is structural
 * rather than decorative, and the reason a card must not be the element that scrolls; put the
 * overflow on the body instead.
 *
 * .db-detail-card is the same object under an older name; the tokens are the ones the grid card
 * reads (--dz-grid-border-color IS --dz-hairline, --dz-grid-card-shadow IS the design's 0 1px 3px).
 *
 * The edge is the HAIRLINE (#e2dfdd) and not Border (#c4c0be), and the two are not
 * interchangeable: the design reserves the stronger line for the rule UNDER the header strip, so
 * the card recedes and the strip reads as attached to the grid rather than floating above it. */
.db-card,
.db-detail-card {
    background: var(--dz-card);
    border: 1px solid var(--dz-grid-border-color);
    border-radius: var(--dz-grid-card-radius);
    box-shadow: var(--dz-grid-card-shadow);
    overflow: hidden;
}

.db-card__body {
    padding: var(--dz-card-body-padding);
}

/* A DASHBOARD-DENSITY CARD's body takes the view panel's box rather than the card's, so seven or
 * eight panels on one page keep the tighter rhythm the dashboards were laid out in. The header half
 * of .db-card--dense sits beside .db-card-header (13-detail-segments-card-header.css). */
.db-card--dense > .db-card__body {
    padding: var(--dz-panel-padding);
}

/* A FLUSH BODY holds a grid, tree list, tile view or map that draws its own edge, so padding would
 * show as a gutter inside the card. It must beat both padded bodies: the bare name ties
 * .db-card__body at (0,1,0) and the dense form ties the dense body at (0,2,0), and this rule comes
 * after both, so every tie goes to flush. */
.db-card__body--flush,
.db-card--dense > .db-card__body--flush {
    padding: 0;
}

/* A METRIC TILE: one number, what it counts, and optionally the control that opens it.
 *
 * This is a card, so it does not repaint one - a caller writes `class="db-card db-metric"` and the
 * edge, radius and shadow come from the rule above. What this adds is the LAYOUT, the type and the
 * hover. One shared rule, because Account/Dashboard, Account/_SubscriberDetails (a partial both
 * dashboards render) and Home/Multi must measure the same.
 *
 * It is the INVERSE of .db-field, deliberately. A field is a micro-label with its value under it,
 * for scanning a column of 45 of them; a metric is the value first with its label under it, for
 * reading four of them across a panel at a glance. Same two parts, opposite rank, so they are two
 * components rather than one with a modifier.
 *
 * A flex column centres value and label, so the height is a floor rather than a cage; do not
 * position them with offsets inside a fixed box. Colours are tokens so the tile follows dark mode
 * and the tenant accent.
 *
 * THE VALUE'S COLOUR IS STATED ON THE WRAPPER, NEVER ON THE NUMBER ITSELF, and callers must keep
 * that split. Account/Dashboard's amount-due figure is painted green or red by Site.css's
 * .positive-value / .negative-value, which the page's script toggles on an inner <span>. Both that
 * utility and this rule are one class, so if this colour ever lands on the same element it ties on
 * specificity and source order decides - and datablaze.ui.css loads after Site.css, so this would
 * win and the figure would silently stop reporting whether money is owed. Inherited colour loses to
 * any direct rule, which is why the wrapper is the right element. */
.db-metric {
    display: flex;
    /* DOES NOT GROW, and that is what keeps a row of tiles level. The cell around it is a flex
       column holding the tile and the control under it, so a growing tile would absorb the
       leftover in every cell that has no control - and the tiles would end up different heights
       precisely because their ACTIONS differ, which is the one thing a viewer should not read
       anything into. Every tile is its own floor and the controls line up across the row. */
    flex: 0 0 auto;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: var(--dz-space-1);
    /* A MINIMUM, not a height - see --dz-metric-min-height. */
    min-height: var(--dz-metric-min-height);
    /* The containing block for the absolute delta in a subscriber-count tile's corner; without it
       the delta positions against #mainbox and does not scroll with its tile. */
    position: relative;
    padding: var(--dz-space-2);
    text-align: center;
}

    .db-metric__value {
        color: var(--dz-ink);
        /* The DATA face, because the guide lists "metric readouts" among the things Roboto sets and
           this is the readout. Its label below stays Poppins - that is the pairing working, and
           this component is the smallest place to demonstrate it. */
        font-family: var(--dz-font-family-data);
        font-size: var(--dz-metric-value-font-size);
        font-weight: 400;
        line-height: 1.1;
    }

    .db-metric__label {
        color: var(--dz-body);
        font-family: var(--dz-font-family);
        font-size: var(--dz-metric-label-font-size);
        line-height: 1.3;
    }

        /* A LINK IN A LABEL IS A LINK. Account/Dashboard's "Last Invoice:" label ends in an anchor
           to the invoice PDF, which otherwise falls through to Bootstrap's `a { color: #007bff }`.
           That is the wrong blue twice over: the guide puts links on deep teal so they never
           compete with the accent, and #007bff measures 3.99:1 on the card against the 4.5:1 floor
           this size of text has to clear. --dz-link is 6.7:1, and lifts to a readable tint in dark
           mode.

           Scoped to the label rather than fixing Bootstrap's global `a`, which is a far larger
           decision than this component gets to make. */
        .db-metric__label a {
            color: var(--dz-link);
        }

        .db-metric__label a:hover,
        .db-metric__label a:focus {
            color: var(--dz-link-hover);
            text-decoration: underline;
        }

    /* The corner block: a period-over-period delta beside the figure it qualifies. Only the
       subscriber-count tiles carry one. */
    .db-metric__aside {
        position: absolute;
        top: var(--dz-space-1);
        right: var(--dz-space-1half);
        color: var(--dz-body);
        font-size: 13px;
        line-height: 1.3;
        text-align: center;
    }

    /* The delta's second line: the period's activations and cancellations side by side under the
       net change. Both spans are coloured by .positive-value / .negative-value from script, so
       this states only weight and spacing and leaves colour to them. */
    .db-metric__aside-row {
        display: flex;
        justify-content: center;
        gap: var(--dz-space-half);
        font-weight: 600;
    }

    /* The co-brand, and only on the EDGE. A border is one of the three uses COBRANDING sanctions
       for the accent - "valid as a fill, a border and a focus ring, not as body-size text" - and
       the edge is the right weight here because a tile is not a button: it has an action under it
       rather than being one, so the hover says the tile is live without offering to be pressed. A
       token, so every tenant accent (Periscope's yellow, CM Tech's red) reaches it.

       Stating the whole border rather than border-color keeps the tile from shifting by a pixel if
       a caller ever gives .db-card a different width. */
    .db-metric:hover {
        border: 1px solid var(--dz-card-hover-border-color);
    }

/* THE ROW OF TILES, and the reason it is a component rather than a `display: flex` in a view: the
 * NUMBER OF TILES IS NOT FIXED. Account/Dashboard draws four or five depending on two permission
 * checks, _SubscriberDetails draws four. `flex: 1 1 0` with a basis of 0 makes every tile take an
 * equal share of whatever is left, so both counts fill the row and neither needs a width. */
.db-metric-row {
    display: flex;
    flex-wrap: wrap;
    align-items: stretch;
    gap: var(--dz-space-1half);
    width: 100%;
}

    /* One tile plus the control that opens it, as a column, so the controls line up across the row
       however tall the tiles are. A cell with no control is still a cell - the Amount Due tile has
       no button on most accounts - and it keeps its share of the row rather than collapsing.

       200px IS DELIBERATELY BELOW THE POINT WHERE A LABEL WRAPS, and the number is the decision.
       MEASURED with a Range over the text: the longest label in either caller is "Suspended
       Subscribers" at 188.3px, so with the tile's 32px of side padding and its 1px edges a cell
       needs 222.3px to hold it on one line. Flooring at 222 instead would make the ROW wrap sooner
       - five tiles plus their gaps would stop fitting at about 1160px of container - and a wrapped
       row means an orphaned tile centred under the others. A label on two lines inside a 150px tile costs nothing; there is vertical
       room and the value above it is unaffected. So the label gives first and the row holds. */
    .db-metric-row__cell {
        display: flex;
        flex: 1 1 0;
        flex-direction: column;
        min-width: 200px;
    }

    /* The control under a tile. It is full-width because the tile above it is, and the two read as
       one column only while their edges agree. */
    .db-metric-row__action {
        margin-top: var(--dz-space-1);
    }

/* A TAB STRIP inside a card, on the same ranks every other header on the page uses.
 *
 * Opt-in by class, because DevExtreme's tab classes are not namespaced: a bare `.dx-tab` rule hits
 * every tab control the page renders, including any inside a popup not yet opened. Naming a class
 * means a view says which strip it means.
 *
 * THE STRIP IS AN UNDERLINE, drawn from the style guide's own Tabs component
 * (assets/samples/style-guide, "Underline tabs"): "For page-level sections. 3px Dark Gray active
 * indicator. All labels are Dark Gray; the active one is SemiBold, inactive Regular." Its component
 * draws a flex row with a 4px gap over a 1px subtle hairline, each tab a borderless button at
 * 12px/16px padding, and the indicator as an inset 3px bar with a 3px top radius. It is NOT a folder
 * tab (Sunken strip, white open tab): the guide describes no such control.
 *
 * Every value resolves to a token: --dz-ink is the sample's --text-strong (#31302f) and
 * --dz-hairline is its --border-subtle (#e2dfdd).
 *
 * ONE DELIBERATE DEPARTURE: the label stays 14px, not the export's 16px --fs-body. The export is a
 * style-guide document and 16px is its reading size; this is a dense operational product whose
 * chrome runs 14, which the seven product mockups all draw. Density is ours and is invariant.
 *
 * THE ACTIVE INDICATOR IS DARK GREY AND NOT THE ACCENT - the guide's rule, not a judgement made
 * here. docs/style/STYLE_GUIDE.md: "An underline tab, where one is used instead of chips, takes a
 * 3px dark grey active indicator, and a switch in its on state is dark grey. Outside those two,
 * orange stays with the primary action." The design-system export's readme says orange and is stale
 * on this point (its own Tabs component paints var(--text-strong)); recorded in the style skill's
 * CONFLICTS.md as item 6. .db-segmented spends no accent either. */
.db-tabs .dx-tabs-wrapper {
    background-color: transparent;
    border-radius: 0;
    /* The sample's 4px between tabs. The wrapper is already display:flex in this theme. */
    gap: var(--dz-space-half);
    /* THE STRIP IS A LINE, NOT A BOX, and saying so takes three overrides because dx.light.css
       draws the box three different ways.
     *
       ONE: `.dx-tabpanel-tabs-position-top .dx-tabs-wrapper { border-block: 1px solid #ddd }` is
       TOP AND BOTTOM in one property, so a border-bottom of our own replaces half of it and leaves
       a #ddd rule ABOVE the labels. `border-block` here, not `border-bottom`, so both edges are
       ours and the top is explicitly none. */
    border-block: 1px solid var(--dz-hairline);
    border-top: 0;
}

    /* TWO: the wrapper's own ::after is a full bordered rectangle - `inset: -1px 0` with
       `border-block` AND `border-inline` in #ddd - overlaid on the strip. It carries no content and
       no other job, so it goes entirely rather than being unpicked edge by edge. */
    .db-tabs .dx-tabs-wrapper::after {
        display: none;
    }

    /* THREE: on focus the theme repaints the strip's bottom edge Bootstrap blue -
       `.dx-tabpanel-tabs-position-top.dx-state-focused .dx-tabs-wrapper { border-block-end: 1px
       solid #337ab7 }` at (0,3,0), which out-specifies the (0,2,0) above. Two classes on the widget plus the wrapper ties that at (0,3,0) and wins on source order,
       which is asserted by datablaze.ui.delivery.harness.js. */
    .db-tabs.dx-state-focused .dx-tabs-wrapper {
        border-bottom-color: var(--dz-hairline);
    }

.db-tabs .dx-tab {
    background-color: transparent;
    border: 0;
    border-radius: 0;
    /* A TAB IS THE WIDTH OF ITS LABEL. dx.light.css says `.dx-tab { flex-basis: 100%;
       min-width: max-content }` and `.dx-tabs-expanded .dx-tab { flex-grow: 0; flex-basis: 100% }`,
       so in a flex row every tab asks for the whole line and they shrink to equal shares (five
       tabs at a uniform 140px on the account dashboard). `0 0 auto` leaves min-width doing what it
       is for. */
    flex: 0 0 auto;
    padding: var(--dz-space-1half) var(--dz-space-2);
    /* ALL LABELS ARE INK, active and inactive alike, as the sample states. Weight carries the
       selection instead of colour, so the indicator is the only other signal. */
    color: var(--dz-ink);
    /* The indicator below is absolute against the tab. */
    position: relative;
}

    /* THE LABEL IS COLOURED TWO ELEMENTS DOWN, and naming only the span does not reach it.
     *
     * dx.light.css colours the INTERMEDIATE div - `.dx-tab .dx-tab-text { color: #333 }` and again
     * `.dx-tab.dx-tab-selected .dx-tab-text` - so a span set to inherit alone inherits #333 from
     * its parent rather than Ink from the tab.
     *
     * (0,4,0) by naming .dx-tab-content, which the theme's own selected-arm rule at (0,3,0) then
     * cannot tie. Winning on source order would work today and quietly stop working the day this
     * file is registered before the theme. */
    .db-tabs .dx-tab .dx-tab-content .dx-tab-text,
    .db-tabs .dx-tab .dx-tab-text-span {
        color: inherit;
        font-family: var(--dz-font-family);
        font-size: 14px;
        font-weight: inherit;
    }

    /* ::before, NOT ::after, and this is a hard constraint rather than a preference. dx.light.css
       draws the tab's own KEYBOARD focus ring on ::after -
       `.dx-tabs.dx-state-focused .dx-tab.dx-state-focused::after { border: 1px solid ... }` - so an
       indicator on that element fights the focus ring for one box: the ring ends up drawn around
       the 3px bar instead of around the tab. Taking ::before leaves their element alone.

       THE INDICATOR, as the sample draws it: inset from each edge rather than spanning the tab, 3px
       tall, and radiused only at the top so it reads as a bar rising out of the hairline instead of
       a border on the box. `bottom: -1px` parks it ON the wrapper's hairline, which is what makes
       the active tab look joined to the line rather than floating above it.
     *
       Declared on EVERY tab and merely transparent when unselected, so the selection costs no
       layout and the labels cannot shift as it moves. */
    .db-tabs .dx-tab::before {
        content: "";
        position: absolute;
        left: var(--dz-space-1);
        right: var(--dz-space-1);
        bottom: -1px;
        height: 3px;
        border-radius: 3px 3px 0 0;
        background-color: transparent;
    }

    /* Hover is the one thing the export does not specify: its component transitions `color` and
       then never varies it, so the drawn state has no hover at all. A pointer target with no
       feedback is worse than a quiet one, so an unselected tab previews the indicator in the
       hairline tone - the same element, a tone that cannot be mistaken for the Ink of the real
       selection, and no ground change to repaint the strip. */
    .db-tabs .dx-tab:not(.dx-tab-selected).dx-state-hover::before {
        background-color: var(--dz-hairline);
    }

    .db-tabs .dx-tab.dx-state-hover,
    .db-tabs .dx-tab.dx-state-active {
        background-color: transparent;
    }

    /* ::after IS DEVEXTREME'S DECORATION BOX, AND IT DRAWS IN FOUR STATES, all named here at once:
     *
         .dx-tab::after                 -> the base element
         .dx-tab.dx-state-hover::after  { border-block: 1px solid #ddd }         <- top AND bottom
         .dx-tab.dx-state-active::after { border-block + border-inline #d8d8d8 } <- a full box
         .dx-tab.dx-tab-selected::after { border-block + border-inline #ddd }    <- a box, at REST
     *
       Left alone, hover draws a rule ABOVE the label, press draws a rectangle, and the SELECTED tab
       carries a permanent open-bottomed box (the tabpanel rule adds `border-block-end: none`) - a
       folder-tab outline. The sample gives a tab no box in any state.
     *
       The element is kept rather than display:none'd, because the keyboard ring rides on it. Only
       the colours go. */
    .db-tabs .dx-tab::after,
    .db-tabs .dx-tab.dx-state-hover::after,
    .db-tabs .dx-tab.dx-state-active::after,
    .db-tabs .dx-tab.dx-tab-selected::after,
    .db-tabs.dx-state-focused .dx-tab.dx-state-focused::after {
        border-color: transparent;
    }

    /* Both arms, so hovering the open tab does not repaint it as an unselected one. */
    .db-tabs .dx-tab.dx-tab-selected,
    .db-tabs .dx-tab.dx-tab-selected.dx-state-hover {
        background-color: transparent;
        /* SemiBold (600) is the sample's own weight for the active tab, against Regular for the
           rest: with no ground and no colour change carrying the selection, the weight has to be
           legible as a difference. */
        font-weight: 600;
    }

        .db-tabs .dx-tab.dx-tab-selected::before {
            background-color: var(--dz-ink);
        }

    /* DevExtreme draws its own moving indicator under the strip in this theme. It would sit beside
       the one above and read as two rules of different widths chasing each other. */
    .db-tabs .dx-tabs-indicator {
        display: none;
    }

    /* TWO FOCUS RINGS IN BOOTSTRAP'S LINK BLUE, which this product paints nothing else with:
     * dx.light.css states #337ab7 on both the focused tab's ring and the focused PANEL's border.
     *
     * The tab's ring is kept and repointed at the accent ring, which COBRANDING sanctions as one of
     * the accent's three valid uses and which a tenant can move.
     *
     * The PANEL's border is suppressed instead of recoloured. DevExtreme focuses the multiview
     * wrapper as a side effect of a tab click, so it would paint a stray rule down the content's
     * edge on every click, around a box the user never navigates to. */
    /* THE ONE STATE THAT STILL PAINTS: a KEYBOARD focus. DevExtreme adds .dx-state-focused on a
       mouse click as well as on an arrow key, so a ring keyed on that class alone boxes the label
       every time anyone clicks - which the sample never does. :focus-visible is the browser's own
       answer to that distinction, and the widget root is what takes DOM focus, so it is asked
       there.
     *
       (0,5,0) deliberately, so it beats the blanket above without depending on source order - the
       two would otherwise tie at (0,4,0), since :has() takes the specificity of its argument. */
    .db-tabs.dx-state-focused:has(:focus-visible) .dx-tab.dx-state-focused::after {
        border-color: var(--dz-tabs-focus-ring);
    }

    .db-tabs.dx-state-focused .dx-multiview-wrapper.dx-state-focused {
        border-color: transparent;
    }

    /* AND AT REST. dx.light draws the panel's border in TWO rules - a base
     * `.dx-tabpanel .dx-multiview-wrapper` at #ddd, and the focused one above - so suppressing only
     * the focused one leaves a box painted on load that vanishes on the first tab click.
     *
     * .db-tabs.dx-tabpanel rather than .db-tabs alone: both classes sit on the widget root, so this
     * is (0,3,0) against dx.light's (0,2,0) and wins on weight rather than on load order.
     *
     * Nothing is lost by taking it away. The tab content owns its own edges - a grid draws its own
     * card, and a page toolbar above one deliberately draws none - so a frame around the whole
     * destination is a second box drawn around boxes, and the strip above it already says where the
     * destination begins. */
    .db-tabs.dx-tabpanel .dx-multiview-wrapper {
        border-color: transparent;
    }

/* A GRID THAT IS THE WHOLE PANE STARTS BELOW THE STRIP, NOT ON IT.
 *
 * With the frame gone (above), nothing separates the strip from the first thing in the pane. A
 * pane that holds a panel - the account dashboard's .gridbox - draws its own edge, so meeting the
 * strip reads as a box under a heading. A grid placed straight in the pane has no edge above its
 * toolbar, so its search box would sit on the strip's rule (the monthly invoice editor). Direct
 * child only, which leaves the panel-in-pane case alone. */
.db-tabs.dx-tabpanel .dx-multiview-item-content > .db-grid {
    margin-top: var(--dz-space-1half);
}

/* AN OVERFLOWING STRIP SHOWS ONLY THE ARROW THAT CAN MOVE IT.
 *
 * A strip with more destinations than fit (the monthly invoice editor's, once SMS and Voice
 * sections exist) takes DevExtreme's showNavButtons, and DevExtreme leaves the arrow at the end
 * already reached on screen, disabled. It toggles dx-state-disabled live as the strip scrolls, so
 * hiding the disabled one leaves exactly the direction that still does something. */
.db-tabs .dx-tabs-nav-button.dx-state-disabled {
    display: none;
}

/* ON A PHONE A TAB IS STILL THE WIDTH OF ITS LABEL, WHEN THE STRIP SCROLLS.
 *
 * The rule above (`flex: 0 0 auto` at (0,2,0)) holds while the tabs fit. DevExtreme measures them
 * (Tabs._isItemsWidthExceeded) and, when their widths sum to more than the strip, wraps the wrapper
 * in `.dx-tabs-scrollable` - and dx.light.css then says `.dx-tabs:not(.dx-tabs-vertical)
 * .dx-tabs-scrollable .dx-tab { flex-basis: 100% }` at (0,4,0), which out-ranks the rule above and
 * puts every tab at the full width of the wrapper (shrink stays 0 from the rule above, so they do not
 * share it). Warehousing's three tabs ("Warehouses", "Inventory", "Units of Measure") do not fit a
 * 328px strip at 360px, so it scrolls, and each tab measured 374px (MEASURED on an iPhone): one tab
 * filled the screen and the other two sat off it.
 * Nothing else in this package or in Voyager's own sheets sets a width on the tab. A desktop strip
 * is not touched: it has the room, and one that does overflow keeps the wide tabs it has today, which
 * come from the package's own `flex: 0 0 auto` rule above and which this block does not change.
 *
 * `.db-tabs` sits on the Tabs root for a page strip and on the TabPanel root, above the Tabs, for a
 * pane strip, hence the two spellings. Each is (0,5,0) against the theme's (0,4,0), so it wins on
 * weight rather than on load order. Layout only: the one condition this package uses for a phone,
 * which tests/wl21-adversary.harness.js teaches as a layout-only width condition. */
@media (max-width: 824.98px) and (pointer: coarse) {
    .db-tabs.dx-tabs:not(.dx-tabs-vertical) .dx-tabs-scrollable .dx-tab,
    .db-tabs .dx-tabs:not(.dx-tabs-vertical) .dx-tabs-scrollable .dx-tab {
        flex: 0 0 auto;
    }
}


/* A DETAIL FIELD: a micro-label with its value under it.
 *
 * Both detail mocks are built out of these - 45 of them on Subscriber Detail alone - and the style
 * guide states the shape directly: "Field labels in detail panels are an uppercase micro-label
 * above the value." The label is Body at 12px/600 and the value is ink at 14px/500, so the pair
 * reads as one unit and the value is the part that carries weight.
 *
 * Two deliberate departures from the mocks:
 *
 * - The value is set in Poppins, like the label: both halves take --dz-font-family. The guide
 *   specifies Roboto for data values, so this is a known deviation, tracked by
 *   --dz-font-family-data in 00-tokens.css.
 * - The guide says the label is uppercase; the mocks render it in title case ("Last Seen", "Buy
 *   Plan", "Account Name"). Label casing only exists on this surface, so the mock wins and nothing
 *   here transforms the text. A caller that wants uppercase asks for it.
 *
 * An absent value is --empty: the same plain text one step lighter, never a grey pill. "N/A" is the
 * absence of a value, not a status, and painting it as a chip gives it the same visual weight as a
 * real one. */
.db-field {
    display: block;
}

.db-field__label {
    display: block;
    margin-bottom: var(--dz-field-label-gap);
    color: var(--dz-body);
    font-family: var(--dz-font-family);
    font-size: var(--dz-field-label-font-size);
    font-weight: var(--dz-field-label-font-weight);
    line-height: 1.3;
}

.db-field__value {
    display: block;
    color: var(--dz-ink);
    font-family: var(--dz-font-family);
    font-size: var(--dz-field-value-font-size);
    font-weight: var(--dz-field-value-font-weight);
    line-height: 1.4;
}

.db-field__value--empty {
    color: var(--dz-body);
    font-weight: 400;
}

/* A value that is prose (a description): it wraps and keeps the line breaks it was typed with. */
.db-field__value--prose {
    white-space: pre-wrap;
    overflow-wrap: anywhere;
}

/* A FIELD LIST: a record's fields stacked one per row in a card too narrow for the detail grid
 * (Group View's Summary). It sits in a flush card body, so the hairlines between rows run edge to
 * edge; the cell takes the detail grid's padding (Subscriber Detail's 12px, 6px at compact) so a
 * field reads the same in either layout. The last row draws no rule, where the card's edge is. */
.db-field-list > .db-field {
    padding: var(--dz-field-list-cell-padding);
    border-bottom: 1px solid var(--dz-hairline);
}

.db-field-list > .db-field:last-child {
    border-bottom: none;
}

/* A value that links somewhere (a parent record, a phone number, an email) reads as a link. */
.db-field-list .db-field__value a {
    color: var(--dz-link);
    text-decoration: none;
}

.db-field-list .db-field__value a:hover {
    color: var(--dz-link-hover);
    text-decoration: underline;
}

/* A labels field (badge.js renderLabelsField): the chips carry their own paint, so the cell drops
   its wash and padding, wraps, and leaves room beside it. The element is also a .data-content, and
   host views still paint that class as a value pill in their own <style> blocks. */
/* !important: on background-color it beats those host `.data-content` rules, whatever their
   specificity; on the other three it restores the precedence the inline style it replaces had. */
.db-field--labels {
    background-color: transparent !important;
    padding: 0 !important;
    white-space: normal !important;
    max-width: 70% !important;
}

/* THE INHERITED-VALUE HINT (ClickUp 86894yg24, WL-10a). A caption under a field, one step quieter
 * than the field's own label: "Inherited from Covenant Telecom", "Platform default (not
 * inherited)". The whitelabel branding editor is the first caller - most of its fields say where
 * the value they are showing actually came from, which is metadata about the field rather than
 * part of its value.
 *
 * THE ONE PART OF .db-field NOT REGISTERED IN contract/ui.manifest.json - flagged rather than
 * silently matched, because .db-field/__label/__value never were either (they predate the
 * manifest's `surfaces` block and this item does not retrofit them). Registering only the new part
 * of an unregistered family would say a decision was made about the other two that was not; this
 * stays undeclared alongside them until a pass registers the whole family at once.
 *
 * --dz-meta AT THE LABEL SIZE, NOT A NEW SIZE OR A NEW COLOUR. --dz-meta is already the package's
 * one "quieter than body, still legible" step - MEASURED at 4.5:1+ on --dz-card in both the light
 * and dark spine (00-tokens.css's own comment on the token, and voyager.darkmode.css's restatement
 * of it for a dark ground) - so this hint is legible in both themes with no new hook at all. It is
 * invariant chrome (COBRANDING.md Layer A), the same as every other type-scale decision. */
.db-field__hint {
    display: block;
    margin-top: var(--dz-field-label-gap);
    color: var(--dz-meta);
    font-family: var(--dz-font-family);
    font-size: var(--dz-field-label-font-size);
    font-style: italic;
}

/* An OUTLINE CHIP: the accent on a 1px edge and on the text, over a wash of itself.
 *
 * This is the shape both mocks use for every status that is not the grid's dot-and-text - invoice
 * and transaction states, sub-status tags, the "current" marker on a revision.
 *
 * The ONLY input is --dz-badge-accent. A host sets that per class - which is what
 * voyager.badges.css does for Voyager's own status vocabulary - and gets the border, the text and
 * the wash from this rule. Nothing here knows what any of the colours mean.
 *
 * color-mix() is what lets a stylesheet tint a colour it never sees. A browser without it drops
 * only the background line and keeps a plain outline in the right colour: degraded, still legible,
 * still correct. That failure mode is the reason the wash is a separate declaration rather than
 * being folded into a gradient or a pseudo-element. */
.db-badge.db-badge--outline {
    display: inline-flex;
    align-items: center;
    gap: var(--dz-badge-gap);
    margin: 0;
    padding: var(--dz-badge-padding-y) var(--dz-badge-padding-x);
    color: var(--dz-badge-accent, var(--dz-badge-neutral-accent));
    background-color: color-mix(in srgb,
        var(--dz-badge-accent, var(--dz-badge-neutral-accent)) var(--dz-badge-outline-tint),
        transparent);
    border: var(--dz-badge-border-width) solid var(--dz-badge-accent, var(--dz-badge-neutral-accent));
    border-radius: var(--dz-badge-radius);
    font-family: var(--dz-font-family);
    font-size: var(--dz-badge-outline-font-size);
    font-weight: var(--dz-badge-font-weight);
    line-height: var(--dz-badge-line-height);
    vertical-align: middle;
    white-space: nowrap;
}

/* A GLYPH BADGE: the accent as a solid square carrying a mark, then the value as row text.
 *
 * The second of the two shapes a status can take. The Billing Center mock draws all five invoice
 * states this way - PD Paid, AP Applied, DR Draft, OP Open, VD Void - and sub-status with the mark
 * alone and the full name in the tooltip. A mark-only badge is simply one with no text node after
 * the mark, and the gap then has nothing to space.
 *
 * Like the outline chip, the ONLY colour input is --dz-badge-accent: a rule that cannot own a
 * colour cannot grow a copy of one.
 *
 * Unlike the outline chip, the accent is a FILL and the text beside it is Ink. On the outline chip
 * the accent IS the text, so a
 * colour has to be legible at 14px on white; here the accent is behind two 11px letters in white,
 * so it has to be legible the other way round. Every one of the guide's five roles clears both. */
.db-badge.db-badge--glyph {
    display: inline-flex;
    align-items: center;
    gap: var(--dz-badge-glyph-gap);
    margin: 0;
    padding: 0;
    color: var(--dz-ink);
    background: none;
    border: 0;
    border-radius: 0;
    font-family: var(--dz-font-family);
    font-size: var(--dz-grid-row-font-size);
    font-weight: 400;
    line-height: 1.4;
    text-align: left;
    vertical-align: middle;
    white-space: nowrap;
}

/* Two badges in one cell are two units, spaced the way two chips are.
 *
 * The outline chip could leave this to its own padding; a glyph badge has none, so the label's last
 * letter would sit flush against the next badge's square ("DR Draft" then an "RR" sub-status).
 *
 * The adjacent selector, not a trailing margin, because a status badge is usually the only thing in
 * its cell and a trailing margin there is invisible space that shifts nothing but is still real. */
.db-badge.db-badge--glyph + .db-badge {
    margin-left: var(--dz-badge-gap);
}

/* The mark. --dz-badge-glyph-foreground rather than a literal white because the accent behind it can
   be a TENANT'S colour, not just one of the guide's roles - a sub-status square is filled with
   whatever the customer configured, and "Approved" on this dev database is #ffdbdb, on which white
   letters measure 1.16:1 and are simply not there. The caller picks the foreground when it knows the
   fill; this default is the right answer for every role colour in the package. */
.db-badge.db-badge--glyph .db-badge__mark {
    display: inline-flex;
    flex: 0 0 auto;
    align-items: center;
    justify-content: center;
    width: var(--dz-badge-glyph-size);
    height: var(--dz-badge-glyph-size);
    border-radius: var(--dz-badge-glyph-radius);
    /* --dz-badge-mark-color: a mark's own ink, for a light carrier fill white cannot sit on. */
    color: var(--dz-badge-mark-color, var(--dz-badge-glyph-foreground));
    background-color: var(--dz-badge-mark-background, var(--dz-badge-accent, var(--dz-badge-neutral-accent)));
    font-size: var(--dz-badge-glyph-font-size);
    font-weight: var(--dz-badge-glyph-font-weight);
    line-height: 1;
}

/* NO DROP-DOWN MENU IS NARROWER THAN ITS ITEMS, whoever built it.
 *
 * dxDropDownButton copies the TRIGGER's width onto its popup when dropDownOptions.width is unset,
 * and a trigger is sized for its own label: the page toolbar's 40px export square would get a 40px
 * menu with "Export Selected" wanting 125px. Page-owned drop-down buttons declared in views
 * (Account Dashboard's Statement, via Html.DevExtreme().DropDownButton()) have the same problem, so
 * this is a rule here rather than a dropDownOptions.minWidth at one mount point.
 *
 * min-width alone is not enough: it can only RAISE the inline width DevExtreme copies off the
 * trigger, so a menu is still pinned to its button's width and its longest item clips. So the width
 * is released and the floor keeps its job. `auto` on an absolutely positioned overlay is
 * shrink-to-fit, so the popup is as wide as its widest item, raised to the floor when the items are
 * short.
 *
 * THE MAX IS A GUARD, NOT A SIZE. Shrink-to-fit on a long enough label would walk the menu off the
 * side of the viewport, because DevExtreme has already positioned it by then. The cap is what makes
 * a pathological item wrap instead of escaping; nothing in the product reaches it today.
 *
 * GATED on .db-shared-dropdownbutton-popups, which is this file's existing rule for anything aimed
 * at a drop-down popup and is pinned by datablaze.ui.delivery.harness.js. The popup is appended to
 * <body>, so it sits outside every scope the rest of this sheet uses - .db-grid, .db-page-toolbar -
 * and an ungated rule here would resize the menus of every application that merely references
 * SharedMvc, adopted or not. The class is written on <body> by the delivery helper for a host that
 * has opted in; Voyager is the only one today. */
/* !important: beats DevExtreme's inline width, copied off the trigger at open time. */
.db-shared-dropdownbutton-popups .dx-dropdownbutton-popup-wrapper .dx-overlay-content {
    width: auto !important;
    min-width: var(--dz-menu-min-width);
    max-width: var(--dz-menu-max-width);
}

/* RELEASING THE WIDTH IS NOT ENOUGH ON ITS OWN.
 *
 * The popup content holds a dxScrollView and `.dx-popup-content` carries `max-width: 100%`, so the
 * list's intrinsic width never reaches the overlay: sizing flows DOWN (popup -> content -> scroller
 * -> item) and a long item ellipsises at the floor instead of widening the menu.
 *
 * So the path is opened explicitly. `max-content` is what carries the widest item up through the
 * list, and dropping `.dx-popup-content`'s `max-width: 100%` is what stops the cap being circular -
 * 100% of a box that is itself being sized by its contents.
 *
 * `min-width: 100%` on the list rows is the other half and it is not decoration: without it each row
 * shrink-wraps its own label, so a four-item menu draws four hover highlights of four different
 * widths. The rows fill the menu; the menu is sized by the longest of them.
 *
 * `min-width: 100%` on the content box is the same rule one level up. When the floor raises the
 * card above the widest item, a max-content box stays at the item width inside it, and every row's
 * highlight stopped there with an unlit strip down the right of the card (MEASURED: Subscriber
 * detail's Actions, rows 114px in a 190px card).
 *
 * SCOPED TO THE SCROLLVIEW'S CONTENT AND THE LIST: the .dx-scrollable-container / -wrapper /
 * -content chain measured as making no difference, so it is left out. */
.db-shared-dropdownbutton-popups .dx-dropdownbutton-popup-wrapper .dx-popup-content {
    width: max-content;
    min-width: 100%;
    max-width: none;
}

.db-shared-dropdownbutton-popups .dx-dropdownbutton-popup-wrapper .dx-scrollview-content,
.db-shared-dropdownbutton-popups .dx-dropdownbutton-popup-wrapper .dx-list-items,
.db-shared-dropdownbutton-popups .dx-dropdownbutton-popup-wrapper .dx-list-item {
    width: max-content;
    min-width: 100%;
}

/* A NOTE CHIP: the glyph badge's square, at note size, carrying one character and no label.
 *
 * Nothing but four token overrides, and that is the whole argument for it existing as a modifier
 * rather than as a rule of its own. The square's geometry, centring, fill and foreground already
 * live in .db-badge__mark above; a second rule drawing a smaller version of the same square is a
 * second place to fix when the shape moves. So this sets the four numbers that differ and inherits
 * the rest.
 *
 * The overrides are declared on the OUTER element and inherited by the mark, so a host can
 * override them again on the badge itself.
 *
 * The mock draws a filled square with a letter in it, not a coloured icon glyph.
 * --dz-badge-accent is the only colour input, so each state is one accent declaration in
 * voyager.badges.css and no hex moves into a view.
 *
 * No `cursor` on purpose. A note chip is sometimes a button (Billing Center's MRO chips open a
 * detail popup) and sometimes inert (the stale-draft warning is a warning, not a control), and a
 * pointer on something that does not respond is worse than no pointer on something that does. */
.db-badge.db-badge--note {
    --dz-badge-glyph-size: var(--dz-badge-note-size);
    --dz-badge-glyph-radius: var(--dz-badge-note-radius);
    --dz-badge-glyph-font-size: var(--dz-badge-note-font-size);
    --dz-badge-glyph-font-weight: var(--dz-badge-note-font-weight);
}

/* A toolbar's actions collapsed behind one "Actions" menu.
 *
 * The original buttons are hidden, not removed, because the view bound their handlers and the menu
 * forwards to them - and because switching the preference off has to bring them straight back.
 *
 * Only a container the runtime marked is collapsed, and it only marks one holding more than one
 * action, so a toolbar with a single button renders unchanged.
 *
 * The `after` group is deliberately untouched anywhere here: the column chooser, the export button
 * and the search box are the grid's own controls, not the page's actions.
 */
.db-grid .dx-datagrid-header-panel .dx-toolbar .dx-toolbar-item[data-db-toolbar-action="collapsed"],
.db-grid .dx-treelist-header-panel .dx-toolbar .dx-toolbar-item[data-db-toolbar-action="collapsed"],
[data-db-action-bar][data-db-toolbar-actions="collapsed"] > [data-db-toolbar-action="collapsed"] {
    display: none;
}

/* Composes the disclosure role - toolbaractions.js stamps the trigger's className as
   `db-toolbar__actions-trigger db-button db-button--native db-button--disclosure`, so the PAINT
   (colour, background, border, every state) comes from `.db-button--native` reading the
   disclosure role's tokens. Nothing here sets a colour.
 *
 * What is left is geometry, reading the SAME --dz-button-* / --dz-control-* tokens
 * `.db-button--native` reads, so a geometry change still reaches this trigger. It is restated
 * rather than composed away because `.db-button--native`'s padding is asymmetric (`padding-y
 * padding-x calc(padding-y + 1px)`, for a native element's baseline) and this trigger's is
 * symmetric; removing this block would shift its bottom padding by 1px. */
.db-toolbar__actions-trigger {
    display: inline-flex;
    align-items: center;
    gap: var(--dz-button-content-gap);
    min-height: var(--dz-control-visual-height);
    padding: var(--dz-button-padding-y) var(--dz-button-padding-x);
    font-family: var(--dz-font-family);
    font-size: var(--dz-button-font-size);
    font-weight: var(--dz-button-font-weight);
    cursor: pointer;
}

/* The caret. A menu button has to say it opens a menu, and the shared button roles carry no
   affordance for that on their own. */
.db-toolbar__actions-trigger::after {
    font-family: FontAwesome;
    font-size: 11px;
    line-height: 1;
    content: "\f0d7";
}

/* Body-level, because a menu inside the toolbar is clipped by the grid's own overflow. */
.db-toolbar__actions-menu {
    position: absolute;
    z-index: 1600;
    min-width: 184px;
    padding: 4px;
    background: var(--dz-card);
    border: 1px solid var(--dz-border);
    border-radius: var(--dz-control-radius);
    box-shadow: var(--dz-toolbar-overflow-shadow);
    font-family: var(--dz-font-family);
    display: grid;
    gap: 4px;
}

/* A pointer-opened menu takes focus itself so Tab walks into the entries, and the popup already
   reads as open through its own border and shadow. A focus ring on the container would be a second
   box around the first. Entries keep theirs. */
.db-toolbar__actions-menu:focus {
    outline: 0;
}

.db-toolbar__actions-item-host {
    display: block;
}

.db-toolbar__actions-item {
    display: flex;
    justify-content: flex-start;
    width: 100%;
    text-align: left;
    border-radius: calc(var(--dz-control-radius) - 2px);
}

.db-toolbar__actions-item .dx-icon,
.db-toolbar__actions-item .fa {
    flex: 0 0 auto;
    width: 16px;
    text-align: center;
}

/* The hint that explains a disabled entry sits on the wrapping host as a title, because a disabled
   <button> carries no title of its own and a disabled control is not a reliable hover target in
   every engine. Letting pointer events pass through makes the host the element under the cursor
   everywhere.

   Fill, text colour and border style all differ from an enabled entry; a fade or grayscale alone
   does not read, because a neutral entry (#31302f on #fff in a #c4c0be border) has no saturation to
   remove. The dashed border is what reads at a glance, and it is the whole indicator by owner
   decision: no explanatory text in the row. */
.db-toolbar__actions-item[disabled] {
    pointer-events: none;
    opacity: 1;
    color: var(--dz-meta);
    background-color: var(--dz-surface-active);
    border-style: dashed;
}

.db-toolbar__actions-item[disabled] .dx-icon,
.db-toolbar__actions-item[disabled] .fa {
    opacity: 0.6;
}

/* Every cell in a data row is centred on the row, and so is every badge inside one.
 *
 * DevExtreme top-aligns cells, so each one lands wherever its own content box puts it: measured on
 * a 41.5px Subscribers row, the contents spread over 5.5px (text, carrier badge, status badge and
 * row-action trigger each at a different height), which reads as a ragged row. Centred, the spread
 * is 0.75px.
 *
 * The badges diverge because both are baseline-aligned boxes taller than the text line box, and an
 * inline-flex chip and an inline-block chip put their baselines in different places. Centring the
 * boxes fixes every content type at once.
 *
 * Rows are ~3px shorter than baseline alignment gives, because a chip no longer grows the line box:
 * the row is exactly its padding plus its tallest content.
 *
 * Scoped to grid rows on purpose. Elsewhere a badge sits inline in a sentence, where baseline is
 * right, so voyager.badges.css keeps that as the default. */
.db-grid .dx-datagrid-rowsview .dx-data-row > td,
.db-grid .dx-treelist-rowsview .dx-data-row > td,
.db-grid .dx-datagrid-rowsview .dx-data-row .db-badge,
.db-grid .dx-treelist-rowsview .dx-data-row .db-badge {
    vertical-align: middle;
}

/* A status in a GRID is plain text: no dot, no bump in weight.
 *
 * The two surfaces genuinely differ and both mockups are explicit about it. The Subscriber Detail
 * page draws a coloured dot beside ink text, because there is one status on the page and it is a
 * headline. A grid draws the State column as ordinary 400-weight ink like every other cell, because
 * fifty rows of coloured dots is a column of decoration - and the sample's own State column has
 * neither dot nor bold. (The sample also renames the column State, which the grid's own column
 * definition does.)
 *
 * TOKENS, not a restyle of the badge, for two reasons. The shared package deliberately does not own
 * the status badge's shape or paint - voyager.badges.css does, and datablaze.ui.badge.harness.js
 * fails the build if a shared rule takes that over - so the seam is the token. And a token inherits:
 * Voyager's badge bundle loads AFTER the shared one on purpose, so a rule here would have to
 * out-specify it, whereas a custom property set on the grid's own root simply reaches every badge
 * inside it.
 *
 * A 0 dot with a 0 gap and not `display: none`, because that pairing is the marker's documented
 * no-op state (see the ::before rule in voyager.badges.css): the dot is a flex item, so zeroing the
 * size alone would still spend the 8px gap. */
.db-grid {
    --dz-status-dot-size: 0;
    --dz-status-gap: 0;
    --dz-status-font-weight: 400;
}

/* ...and the opt-back-in, for a column whose status is a binary rather than a vocabulary.
 *
 * The rule above is right for a State column with eight possible words, where the word IS the
 * information and a dot only repeats it in colour. Endpoints is the other case: Online / Offline /
 * Partially Online is a health reading, and the dot is what a viewer scans rather than reads.
 *
 * Set on the badge ITSELF, which is why no .db-grid prefix is needed and why this cannot be lost to
 * source order. .db-grid declares these on the grid root, so they reach the badge by inheritance,
 * and a custom property declared on an element always beats one it inherited - specificity never
 * enters into it. Still tokens, not paint: the accent stays with the status class beside it, which
 * is voyager.badges.css's to own. */
.db-badge--status.db-badge--status-dot {
    --dz-status-dot-size: var(--dz-status-marker-size);
    --dz-status-gap: var(--dz-space-1);
}

.db-grid .dx-datagrid-pager,
.db-grid .dx-treelist-pager,
.db-grid .dx-datagrid-total-footer,
.db-grid .dx-treelist-total-footer {
    background: var(--dz-grid-pager-background);
    border-top: 1px solid var(--dz-grid-border-color);
    /* DevExtreme's own 9px, restated as a token so density reaches it. The pager is the
       second-tallest fixed block on a full-page grid - 58px MEASURED on Subscribers, of which 18 is
       this padding - so leaving it fixed makes Compact look like it did nothing here. */
    padding-top: var(--dz-grid-pager-padding-y);
    padding-bottom: var(--dz-grid-pager-padding-y);
}

.db-button--notification-unread {
    --dz-button-color: var(--dz-button-action-selected-color);
    --dz-button-background: var(--dz-button-action-selected-background);
    --dz-button-border-color: var(--dz-button-action-selected-border-color);
    /* WL-22 fix round 3: the hover/pressed STATE paints its own fill (-hover-background, accent-active)
       -- it now reads its OWN ink too (-hover-color), not the REST state's (-selected-color), since the
       two fills are different ramp steps and one ink cannot always be readable on both (see
       00-tokens.css's own comment on --dz-button-action-selected-hover-color). */
    --dz-button-hover-color: var(--dz-button-action-selected-hover-color);
    --dz-button-hover-background: var(--dz-button-action-selected-hover-background);
    --dz-button-hover-border-color: var(--dz-button-action-selected-border-color);
    --dz-button-active-background: var(--dz-button-action-selected-hover-background);
}

/* PRESSED: a toggle button that is on - aria-pressed="true", set by datablaze.ui.button.setPressed.
 * The Action role's selected band, the same one the unread state wears, because "this toggle is on"
 * and "this control is holding something for you" are the two states the role already draws as
 * selected. The first caller is Home/Index's Top Data Users expand toggle, whose on state was a
 * page-local gold glyph. */
.db-button--pressed {
    --dz-button-color: var(--dz-button-action-selected-color);
    --dz-button-background: var(--dz-button-action-selected-background);
    --dz-button-border-color: var(--dz-button-action-selected-border-color);
    /* WL-22 fix round 3: see .db-button--notification-unread's own comment above, same reasoning. */
    --dz-button-hover-color: var(--dz-button-action-selected-hover-color);
    --dz-button-hover-background: var(--dz-button-action-selected-hover-background);
    --dz-button-hover-border-color: var(--dz-button-action-selected-border-color);
    --dz-button-active-background: var(--dz-button-action-selected-hover-background);
}

/* BARE: an icon control drawn as its glyph alone - no ground, no border, in any state. For a
 * card header's table controls, which sit beside the title like its edit pencils rather than as a
 * row of buttons (owner direction, 2026-10-07; first caller Home/Index's Top Data Users strip).
 * The role still owns the size, the hit target and the focus ring; this repoints only its paint
 * hooks. Two classes, so it wins over every role's (0,1,0) band whatever the file order. */
.db-button.db-button--bare,
.db-icon-dropdown.db-button--bare {
    --dz-button-color: var(--dz-icon-control-glyph-color);
    --dz-button-background: transparent;
    --dz-button-border-color: transparent;
    --dz-button-hover-color: var(--dz-ink);
    --dz-button-hover-background: transparent;
    --dz-button-hover-border-color: transparent;
    --dz-button-active-background: transparent;
}

/* The icon dropdown's gear is a mask painted in its own colour rather than the button's ink, so it
 * takes the bare hover here to darken with the glyphs beside it. */
.db-icon-dropdown.db-button--bare.dx-state-hover .dx-dropdowneditor-icon::before {
    background-color: var(--dz-button-hover-color);
}

/* A bare toggle that is on has no ground to fill, so the glyph carries the state, in the selection
 * colour the checkbox and segmented control use. */
.db-button.db-button--bare.db-button--pressed {
    --dz-button-color: var(--dz-selection-color);
    --dz-button-hover-color: var(--dz-selection-color);
}

/* GEOMETRY for a DevExtreme widget button.
 *
 * The role rules below paint a dxButton; without this its size, corners and text size come from
 * dx.light.css (36px tall, 4px corners), which beside a .db-icon-button (40px, 6px, drawn by this
 * package) reads as a different control family in the same toolbar.
 *
 * Padding goes on .dx-button-content because that is where DevExtreme puts its own; setting it on
 * the button as well would add the two together.
 *
 * .dx-button-has-text is matched explicitly so a labelled button takes the stated height too, and
 * is never shorter than the icon button beside it. */
.dx-button.db-button,
.dx-button.db-button.dx-button-has-text,
.dx-dropdownbutton.db-button .dx-button,
.dx-dropdownbutton.db-button .dx-button.dx-button-has-text {
    min-height: var(--dz-control-visual-height);
    border-radius: var(--dz-control-radius);
    font-size: var(--dz-button-font-size);
    font-weight: var(--dz-button-font-weight);
}

/* The content box must FILL the button, or every label sits high in it.
 *
 * MEASURED without this: a 40px button's 34px content box sat 1px from the top and 5px from the
 * bottom; a 22px content box was 17px out. The box, not the text inside it, sits high.
 *
 * dx.light.css says
 * `.dx-button-content { height: 100% }`, and this role sizes the button with MIN-height, leaving its
 * `height` auto. A percentage height against an auto-height parent is indefinite, so it resolves to
 * auto - and because `height` is nonetheless a SPECIFIED value, it also disqualifies the item from
 * the flex stretch that would have filled the button. So the one declaration meant to make the
 * content fill the button is exactly what stops it.
 *
 * `height: auto` restores stretch eligibility and `align-self: stretch` states the intent; the
 * content's own `align-items: center` (dx.light.css) then centres the label. (A DropDownButton's
 * content sits in a wrapper whose height IS definite, so it never had the problem.)
 *
 * Fixed on the role rather than per button: the offset scales with how far the content is from the
 * button height, so it is worst on the icon-only controls. */
.dx-button.db-button > .dx-button-content,
.dx-dropdownbutton.db-button .dx-button > .dx-button-content {
    padding: var(--dz-button-padding-y) var(--dz-button-padding-x);
    height: auto;
    align-self: stretch;
}

/* An icon-only button is a square, so the horizontal padding that gives a label room would make it
   an oblong. It keeps the vertical padding and centres. */
.dx-button.db-button:not(.dx-button-has-text) > .dx-button-content {
    padding-right: 0;
    padding-left: 0;
}

/* ...and the square has to be STATED.
 *
 * `.dx-button.db-button` pins min-height to the control height; without a matching width, zeroing
 * the horizontal padding above leaves an icon-only button as wide as its glyph (MEASURED: 20x40 and
 * 18x40 beside 40px neighbours) wherever the surrounding layout does not size it.
 *
 * min-width rather than width, so a control that declares its own box still wins - the
 * edit-affordance pencil's 20px square (09-edit-affordance-action-menu.css) is declared later at the
 * same specificity and beats this: the package states the default target, a component overrides it
 * deliberately. 40px is also the WCAG 2.5.8 pointer target. */
.dx-button.db-button:not(.dx-button-has-text) {
    min-width: var(--dz-interactive-target-size);
}

/* ...and a button CARRYING a label is never narrower than that label needs.
 *
 * Views state button widths in pixels - .Width(100), .Width(120), .Width(160) - picked against
 * DevExtreme's own text-button metrics (10px padding each side). This role's --dz-button-padding-x
 * is 18px, so a .Width(100) button leaves 64px for the label instead of 80px, and DevExtreme's
 * `.dx-button-has-text .dx-button-content { overflow: hidden; text-overflow: ellipsis }` clips the
 * rest without complaint (MEASURED on the New Invoice popup's 100px Generate button). It is stated
 * once here rather than corrected per call site.
 *
 * min-width BEATS width, including an inline one, so a stated .Width(100) still renders at 100px
 * whenever the label fits and only grows when it would otherwise be cut. No !important, and no call
 * site changes.
 *
 * max-content and not fit-content: fit-content still collapses under a constrained parent, which is
 * exactly the flex toolbar case where the clipping shows up. And because every DevExtreme widget is
 * box-sizing: border-box, max-content already includes this role's padding and border rather than
 * measuring the bare text.
 *
 * Scoped to .dx-button-has-text deliberately. An icon-only button has no label to fit and its
 * max-content is the glyph alone - about 20px, under the 40px pointer target the rule above pins -
 * so applying this to both kinds would quietly undo that square. */
.dx-button.db-button.dx-button-has-text,
.dx-dropdownbutton.db-button .dx-button.dx-button-has-text {
    min-width: max-content;
}

/* ...without LOWERING a floor something else already stated, which the rule above does on its own.
 *
 * min-width beats width, which is the property that makes this work - but it also beats another
 * min-width of lower specificity, and DevExtreme states one: `.dx-popup-bottom .dx-button` carries
 * min-width: 100px, giving every popup-footer button a common width so a Cancel/Save pair matches.
 * That selector is (0,2,0) and the rule above is (0,3,0), so without this a footer button shrinks to
 * its label: a short one, such as Cancel, narrower than the 100px floor its pair shares.
 *
 * max(100px, max-content) is NOT valid CSS: min(), max() and clamp() take lengths and percentages,
 * not intrinsic keywords, so the whole declaration would be dropped.
 *
 * Restating DevExtreme's own floor is enough, because a popup-footer button has no stated width: it
 * is sized by the toolbar, so shrink-to-fit already grows it past 100px when the label needs more. */
.dx-popup-bottom .dx-button.db-button.dx-button-has-text {
    min-width: 100px;
}

/* Every shared dxButton is the containing block for anything positioned inside it.
 *
 * Without it an absolute child (the edit affordance's hit area) resolves against the nearest
 * positioned ancestor - on a Voyager page #nav-shell, position: fixed, the whole app shell. A
 * transparent pseudo sized that way is a hit target over the whole page (on Subscriber Detail, a click
 * anywhere downloaded the Recent Connections xlsx). Do not narrow this to .dx-button-has-text. */
.dx-button.db-button,
.dx-dropdownbutton.db-button .dx-button {
    position: relative;
}

/* COMPACT, for a DevExtreme button (the NATIVE rule below handles it through a :not() carve-out).
 *
 * .DbAction(compact: true) on an Html.DevExtreme().Button() renders a div.dx-button, so without
 * this the 40px min-height above stays in force. The callers are icon buttons in .rounded-header,
 * whose 38px is 22px of content box; a 40px button hangs out of the bar, and an escaping float
 * beside .dx-gridbase-container (a BFC root, which may not overlap a float) narrows the grid.
 *
 * SQUARE only when the button is icon-only. A compact button carrying a label needs its height
 * released and its width left to the label. */
.dx-button.db-button--compact {
    min-height: var(--dz-control-compact-height);
}

.dx-button.db-button--compact.dx-button-has-icon:not(.dx-button-has-text) {
    width: var(--dz-control-compact-height);
    height: var(--dz-control-compact-height);
    min-width: 0;
    padding: 0;
}

.dx-button.db-button--compact.dx-button-has-icon:not(.dx-button-has-text) > .dx-button-content {
    padding: 0;
}

.dx-button.db-button--notification-unread > .dx-button-content {
    position: relative;
}

.dx-button.db-button--notification-unread > .dx-button-content::after {
    position: absolute;
    top: 7px;
    right: 7px;
    width: 7px;
    height: 7px;
    content: "";
    background: currentColor;
    border-radius: 50%;
}

/* ATTENTION: the control has something worth a look before you use it - Home/Index's expand toggle
 * when the collapsed panel is cutting columns off. An accent dot on the button's corner, set by
 * datablaze.ui.button.setAttention; the button's hint says what it is. Its own hook (00-tokens.css),
 * so a tenant can move the dot without moving its accent. Outside the box at the
 * corner rather than inset like the unread dot, because a compact 24px button has no room inside it
 * that is not the glyph. */
.dx-button.db-button--attention {
    position: relative;
    overflow: visible;
}

.dx-button.db-button--attention::after {
    position: absolute;
    top: -3px;
    right: -3px;
    width: 8px;
    height: 8px;
    content: "";
    background: var(--dz-button-attention-color);
    border-radius: 50%;
    pointer-events: none;
}

/* The same paint, for a DevExtreme widget button.
 *
 * Without it a dxButton's colour comes entirely from the DevExtreme widget type the caller passes
 * (type 'success' becomes .dx-button-mode-contained.dx-button-success, #5cb85c), and the role tokens
 * resolve with nothing reading them.
 *
 * Two classes, and this sheet loads after dx.light.css, so it wins the contained-mode rules outright;
 * the state rules below add a third to clear DevExtreme's own two-class state selectors. The
 * dropdown button is the same control wrapped one level deeper, so it is matched alongside.
 *
 * Call sites should still stop passing that type - a dead option that still spells a colour is how
 * the wrong colour creeps back. This removes the consequence. */
.dx-button.db-button,
.dx-dropdownbutton.db-button .dx-button {
    color: var(--dz-button-color);
    background-color: var(--dz-button-background);
    border-color: var(--dz-button-border-color);
}

/* The label and the icon each carry their own DevExtreme colour, keyed off the same widget type as
   the background - .dx-button-mode-contained.dx-button-success .dx-icon is a flat #fff. Left alone,
   an icon stays white while the role paints the button white underneath it, and the glyph vanishes.
   Both inherit from the button instead, so whatever the role says about foreground is what they get,
   in every state, without this rule having to know any of the colours. */
.dx-button.db-button .dx-button-text,
.dx-button.db-button .dx-icon,
.dx-dropdownbutton.db-button .dx-button .dx-button-text,
.dx-dropdownbutton.db-button .dx-button .dx-icon {
    color: inherit;
}

/* THE DISCLOSURE CARET IS A STROKED CHEVRON, not DevExtreme's filled triangle.
 *
 * DevExtreme draws this from the DXIcons font as a solid triangle; every mock draws a 24x24, 2.5px
 * round-capped chevron, so no colour or size on a font character gets there - the glyph is
 * REPLACED (see the `%dx%-group-closed` rule for the grid's expand caret, same fix). The artwork is
 * `.db-icon--chevron-down`'s.
 *
 * `content: none` on the pseudo-element is not optional. With the character left in place it renders
 * INSIDE the mask and the caret comes out as a triangle clipped to a chevron, which reads as a
 * rendering fault rather than as either glyph.
 *
 * COLOUR COMES FROM THE ROLE, for free. `background-color: currentColor` under the mask means the
 * caret is whatever the rule above already gave the icon - white on Billing Center's filled primary
 * New, ink on the outlined Billing Settings beside it - so no role needs naming here.
 *
 * SCOPED TO `.db-button`, which is the package's own dropdown button and nothing else. The other two
 * families on that page keep what they have and should: `.db-icon-dropdown` is already masked to a
 * cog (it is the layout gear, not a disclosure), and a dxDateBox's trigger draws a CALENDAR from the
 * same font - a blanket `.dx-dropdowneditor-icon` rule would turn every date picker into a chevron. */
.dx-dropdownbutton.db-button .dx-button .dx-icon-spindown,
.dx-dropdownbutton.db-button .dx-button .dx-icon-spindown {
    width: var(--dz-dropdown-caret-size);
    height: var(--dz-dropdown-caret-size);
    margin-left: var(--dz-dropdown-caret-gap);
    background-color: currentColor;
    -webkit-mask-image: var(--db-icon-source);
    mask-image: var(--db-icon-source);
    -webkit-mask-repeat: no-repeat;
    mask-repeat: no-repeat;
    -webkit-mask-position: center;
    mask-position: center;
    -webkit-mask-size: contain;
    mask-size: contain;
    --db-icon-source: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'><polyline points='6 9 12 15 18 9'/></svg>");
}

    .dx-dropdownbutton.db-button .dx-button .dx-icon-spindown::before {
        content: none;
    }

.dx-button.db-button.dx-state-hover,
.dx-dropdownbutton.db-button .dx-button.dx-state-hover {
    color: var(--dz-button-hover-color);
    background-color: var(--dz-button-hover-background);
    border-color: var(--dz-button-hover-border-color);
}

.dx-button.db-button.dx-state-active,
.dx-dropdownbutton.db-button .dx-button.dx-state-active {
    /* WL-22 fix round 4 (PM ruling, ClickUp 86894yg24): PRESSED reads its OWN ink
       (--dz-button-active-color), not hover's -- see 00-tokens.css's own comment on
       --dz-button-primary-active-color for why one shared ink could fail both floors at once. */
    color: var(--dz-button-active-color);
    background-color: var(--dz-button-active-background);
    border-color: var(--dz-button-hover-border-color);
}

/* FOCUS RESTATES THE GROUND IT SITS ON, or buttons "flash green after clicking".
 *
 * A click focuses the button, so dx-state-focused lands and stays until focus moves elsewhere.
 * Without a background here, dx.light.css's per-TYPE focus rule paints it, and most of these
 * buttons are still declared `type: 'success'` in Razor: the focused button jumps to DevExtreme's
 * brighter 65,150,65 and holds it until focus moves.
 *
 * The specificity is a dead heat:
 * `.dx-button-mode-contained.dx-button-success.dx-state-focused` is (0,3,0) and
 * `.dx-button.db-button.dx-state-focused` is (0,3,0) too, so the shared sheet wins only on source
 * order - and only for the declarations it actually makes. An omitted property is not a win.
 *
 * The value is the REST background, not a focus-specific one, because focus is communicated by the
 * ring: a control that also changes colour on focus reads as pressed. Same reason the colour is
 * restated rather than the border-color left to do the work - the theme's rule is a background. */
.dx-button.db-button.dx-state-focused,
.dx-dropdownbutton.db-button .dx-button.dx-state-focused {
    color: var(--dz-button-color);
    background-color: var(--dz-button-background);
    border-color: var(--dz-button-hover-border-color);
    box-shadow: 0 0 0 var(--dz-control-focus-width) var(--dz-button-focus-ring);
}

/* Hover and active still win over focus, because a pointer on a focused button has to respond.
   These are the same declarations as the two rules above with one more class, which is the whole
   point: focus is a state a button KEEPS, hover and active are states it is in right now. */
.dx-button.db-button.dx-state-focused.dx-state-hover,
.dx-dropdownbutton.db-button .dx-button.dx-state-focused.dx-state-hover {
    color: var(--dz-button-hover-color);
    background-color: var(--dz-button-hover-background);
}

.dx-button.db-button.dx-state-focused.dx-state-active,
.dx-dropdownbutton.db-button .dx-button.dx-state-focused.dx-state-active {
    /* WL-22 fix round 4: same repoint as the plain .dx-state-active rule above. */
    color: var(--dz-button-active-color);
    background-color: var(--dz-button-active-background);
}

/* The same paint a THIRD time, for a button DevExtreme relocated into its own toolbar overflow menu.
 *
 * When a toolbar runs out of width, DevExtreme moves its overflowing items into a kebab menu, and
 * re-skins them there as flat menu rows: `.dx-toolbar-menu-section .dx-toolbar-hidden-button
 * .dx-button` strips the background, the border and the radius, then a per-TYPE rule paints the
 * label and the icon in that type's colour. So a button declared `type: 'success'` - which most of
 * these still are - reappears in the menu as green text on nothing, and a dxDropDownButton, which
 * DevExtreme drops in as a whole widget rather than a hidden button, keeps the full #5cb85c fill.
 *
 * The role rules above cannot reach any of it. They are two and three classes; DevExtreme's are four
 * and five, and Voyager's own Site.css re-asserts the green fill at four
 * (`.dx-overlay-wrapper .dx-toolbar-menu-custom .dx-button.dx-button-success`) - a pre-contract rule
 * that sets only the background.
 *
 * Hence the three-ancestor scope: five classes for the paint, six for the states and the icon, which
 * clears every one of those without !important and without editing Site.css - a sheet that still
 * serves the buttons on non-adopting pages. The two selectors cover both shapes at once: `.db-button`
 * IS the button for a hidden button, and is its ancestor for a dropdown button.
 *
 * A relocated action should look like the action it was, so this restores the role paint rather than
 * neutralising the green - the entry keeps the surface, border and radius it had in the toolbar. */
.dx-overlay-wrapper .dx-toolbar-menu-section .dx-toolbar-menu-custom .db-button.dx-button,
.dx-overlay-wrapper .dx-toolbar-menu-section .dx-toolbar-menu-custom .db-button .dx-button {
    color: var(--dz-button-color);
    background-color: var(--dz-button-background);
    border: 1px solid var(--dz-button-border-color);
    border-radius: var(--dz-control-radius);
}

/* The two shapes sit at different insets, which shows once the rows are bordered.

   DevExtreme zeroes the row padding for a relocated BUTTON (`.dx-toolbar-hidden-button
   .dx-list-item-content { padding: 0 }`) and leaves the list's own 7px/9px on a relocated WIDGET,
   which a dxDropDownButton is.

   Restoring the list's padding on the button rows squares them up. It deliberately does not test
   for a shared button - CSS cannot select an ancestor by its descendant, and zeroing the WIDGET row
   instead would reach non-shared rows the same way. This direction leaves an unadopted row aligned
   with the widget beside it, and moves nothing but whitespace. */
.dx-overlay-wrapper .dx-toolbar-menu-section .dx-toolbar-hidden-button > .dx-list-item-content {
    padding: 7px 9px;
}

/* DevExtreme pads a menu row to 10px, against the 36px control height the toolbar uses, so a
   bordered entry would sit 4px taller than the button it stands for. */
.dx-overlay-wrapper .dx-toolbar-menu-section .dx-toolbar-menu-custom .db-button.dx-button .dx-button-content,
.dx-overlay-wrapper .dx-toolbar-menu-section .dx-toolbar-menu-custom .db-button .dx-button .dx-button-content {
    padding: var(--dz-button-padding-y) var(--dz-button-padding-x);
}

.dx-overlay-wrapper .dx-toolbar-menu-section .dx-toolbar-menu-custom .db-button.dx-button .dx-button-text,
.dx-overlay-wrapper .dx-toolbar-menu-section .dx-toolbar-menu-custom .db-button.dx-button .dx-icon,
.dx-overlay-wrapper .dx-toolbar-menu-section .dx-toolbar-menu-custom .db-button .dx-button .dx-button-text,
.dx-overlay-wrapper .dx-toolbar-menu-section .dx-toolbar-menu-custom .db-button .dx-button .dx-icon {
    color: inherit;
}

.dx-overlay-wrapper .dx-toolbar-menu-section .dx-toolbar-menu-custom .db-button.dx-button.dx-state-hover,
.dx-overlay-wrapper .dx-toolbar-menu-section .dx-toolbar-menu-custom .db-button .dx-button.dx-state-hover {
    color: var(--dz-button-hover-color);
    background-color: var(--dz-button-hover-background);
    border-color: var(--dz-button-hover-border-color);
}

.dx-overlay-wrapper .dx-toolbar-menu-section .dx-toolbar-menu-custom .db-button.dx-button.dx-state-active,
.dx-overlay-wrapper .dx-toolbar-menu-section .dx-toolbar-menu-custom .db-button .dx-button.dx-state-active {
    /* WL-22 fix round 4: same repoint as the plain .dx-state-active rule above. */
    color: var(--dz-button-active-color);
    background-color: var(--dz-button-active-background);
    border-color: var(--dz-button-hover-border-color);
}

.dx-overlay-wrapper .dx-toolbar-menu-section .dx-toolbar-menu-custom .db-button.dx-button.dx-state-focused,
.dx-overlay-wrapper .dx-toolbar-menu-section .dx-toolbar-menu-custom .db-button .dx-button.dx-state-focused {
    border-color: var(--dz-button-hover-border-color);
    box-shadow: 0 0 0 var(--dz-control-focus-width) var(--dz-button-focus-ring);
}

/* The same disabled treatment the collapsed Actions menu uses, for the same reason: an opacity
   wash over an otherwise identical row does not read as disabled. Both sheets fade it that way -
   DevExtreme through .dx-state-disabled and Site.css through its own success rule - so the opacity is
   reset before the fill, muted text and dashed border take over. */
.dx-overlay-wrapper .dx-toolbar-menu-section .dx-toolbar-menu-custom .db-button.dx-button.dx-state-disabled,
.dx-overlay-wrapper .dx-toolbar-menu-section .dx-toolbar-menu-custom .db-button .dx-button.dx-state-disabled {
    opacity: 1;
    color: var(--dz-meta);
    background-color: var(--dz-surface-active);
    border-style: dashed;
}

.dx-overlay-wrapper .dx-toolbar-menu-section .dx-toolbar-menu-custom .db-button.dx-button.dx-state-disabled .dx-icon,
.dx-overlay-wrapper .dx-toolbar-menu-section .dx-toolbar-menu-custom .db-button .dx-button.dx-state-disabled .dx-icon {
    opacity: 0.6;
}
button.db-button--native,
input.db-button--native,
a.db-button--native {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    box-sizing: border-box;
    position: relative;
    color: var(--dz-button-color);
    background-color: var(--dz-button-background);
    border: 1px solid var(--dz-button-border-color);
    border-radius: var(--dz-control-radius);
    font-family: var(--dz-font-family);
    font-weight: var(--dz-button-font-weight);
    text-align: center;
    text-decoration: none;
    white-space: nowrap;
    cursor: pointer;
}

/* The box, held apart from the paint above so the compact density can hand it back. A native element
   always arrives with a class of the caller's own, and that class loses to this rule on specificity
   no matter what order the sheets load - so "let the caller size it" has to be spelled out here
   rather than left to the cascade. Compact is how a caller says the control is not a page button:
   a 24px control inside a grid cell keeps the role's colour, border and font, and keeps its own box. */
button.db-button--native:not(.db-button--compact),
input.db-button--native:not(.db-button--compact),
a.db-button--native:not(.db-button--compact) {
    column-gap: var(--dz-button-content-gap);
    min-width: var(--dz-control-visual-height);
    min-height: var(--dz-control-visual-height);
    height: var(--dz-control-visual-height);
    padding: var(--dz-button-padding-y) var(--dz-button-padding-x) calc(var(--dz-button-padding-y) + 1px);
    font-size: var(--dz-button-font-size);
    line-height: 1;
}

input.db-button--native {
    display: inline-block;
}

button.db-button--native:hover,
input.db-button--native:hover,
a.db-button--native:hover {
    color: var(--dz-button-hover-color);
    background-color: var(--dz-button-hover-background);
    border-color: var(--dz-button-hover-border-color);
    text-decoration: none;
}

button.db-button--native:active,
input.db-button--native:active,
a.db-button--native:active {
    /* WL-22 fix round 4: same repoint as the plain .dx-state-active rule above. */
    color: var(--dz-button-active-color);
    background-color: var(--dz-button-active-background);
    border-color: var(--dz-button-hover-border-color);
}

button.db-button--native:focus-visible,
input.db-button--native:focus-visible,
a.db-button--native:focus-visible {
    outline: 0;
    border-color: var(--dz-button-hover-border-color);
    box-shadow: 0 0 0 var(--dz-control-focus-width) var(--dz-button-focus-ring);
}

button.db-button--native:disabled,
input.db-button--native:disabled,
a.db-button--native.disabled,
a.db-button--native[aria-disabled="true"] {
    opacity: 0.65;
    cursor: not-allowed;
}

.db-button--pending {
    cursor: wait;
}

/* A DevExtreme 'success' button that never took a role is a SUBMIT.
 *
 * 155 call sites in 66 Voyager files still build a Save as `type: 'success'` / ButtonType.Success and
 * nothing else, so dx.light paints them #5cb85c with white letters: 2.48:1. Every one of them is a
 * save, which is the Submit role's meaning, so this gives them that role's paint rather than a darker
 * green. A button that already carries .db-button is left to its own role - the exclusion is a
 * :where() so it adds no weight, and each selector ties the vendor rule it beats and wins on this
 * sheet's later source order. The overflow-menu arm matches Site.css's four-class green re-assertion
 * for the same reason.
 *
 * Converting a call site to .DbSubmit() is still the right fix when one is touched; this removes the
 * failing pair for the ones that have not been. */
.dx-button-mode-contained.dx-button-success:not(:where(.db-button)),
.dx-button-mode-contained.dx-button-success.dx-state-disabled:not(:where(.db-button)),
.dx-overlay-wrapper .dx-toolbar-menu-custom .dx-button.dx-button-success:not(:where(.db-button)) {
    color: var(--dz-button-submit-color);
    background-color: var(--dz-button-submit-background);
    border-color: var(--dz-button-submit-border-color);
}

.dx-button-mode-contained.dx-button-success:not(:where(.db-button)) .dx-button-text,
.dx-button-mode-contained.dx-button-success:not(:where(.db-button)) .dx-icon {
    color: inherit;
}

.dx-button-mode-contained.dx-button-success.dx-state-hover:not(:where(.db-button)) {
    color: var(--dz-button-submit-hover-color);
    background-color: var(--dz-button-submit-hover-background);
    border-color: var(--dz-button-submit-hover-border-color);
}

.dx-button-mode-contained.dx-button-success.dx-state-active:not(:where(.db-button)) {
    /* WL-31 (ClickUp 86894yg24): reads the role's own pressed ink now, not hover's -- the same
       repoint the .db-button--submit role class gets (01-button-base.css), so a role-less
       success dxButton (Account Settings' "Save") shows the identical pressed state as a real
       .DbSubmit() button. Defaults to --dz-button-submit-hover-color (00-tokens.css), so this is
       a no-op for every tenant until WL-31's whitelabel hook actually diverges the two. */
    color: var(--dz-button-submit-active-color);
    background-color: var(--dz-button-submit-active-background);
    border-color: var(--dz-button-submit-hover-border-color);
}

/* Focus keeps the rest ground and shows the ring, the same rule the role buttons follow above. */
.dx-button-mode-contained.dx-button-success.dx-state-focused:not(:where(.db-button)) {
    color: var(--dz-button-submit-color);
    background-color: var(--dz-button-submit-background);
    border-color: var(--dz-button-submit-hover-border-color);
    box-shadow: 0 0 0 var(--dz-control-focus-width) var(--dz-button-submit-focus-ring);
}

.dx-button-mode-contained.dx-button-success.dx-state-focused.dx-state-hover:not(:where(.db-button)) {
    color: var(--dz-button-submit-hover-color);
    background-color: var(--dz-button-submit-hover-background);
}

/* WL-31 fix round 1 (ClickUp 86894yg24, adversary 9864bf045, LOW defect, pre-existing cascade this
   item's own comment above wrongly claimed was already fixed): a real mouse press on this button
   lands BOTH dx-state-focused AND dx-state-active at once, and .dx-state-active (above) and
   .dx-state-focused (above) are a DEAD HEAT at (0,3,0) -- focused, being LATER in source, repainted
   the RESTING fill over the pressed one, so this button's pressed state never showed (the neutral
   active fill for an untouched tenant, or Primary's pressed fill once WL-31's follow applies). One
   more class than either plain rule -- (0,4,0) -- outranks both regardless of source order, the same
   technique .dx-button.db-button.dx-state-focused.dx-state-active already uses above. Border and the
   focus ring are deliberately left to cascade from the plain .dx-state-focused rule (unchanged by
   this rule, same as that role-path pair) -- a pressed button still shows the ring, not a resized box.
   VISIBLE CHANGE for every tenant, Primary-following or not: this role-less Save's held-mouse pressed
   state now actually darkens, where it previously stayed at the resting fill. */
.dx-button-mode-contained.dx-button-success.dx-state-focused.dx-state-active:not(:where(.db-button)) {
    color: var(--dz-button-submit-active-color);
    background-color: var(--dz-button-submit-active-background);
}

/* !important: beats dx.light's outlined-editor hover and focus borders, which reach (0,3,0) against this (0,2,0). */
.db-dropdown.dx-texteditor {
    position: relative;
    min-height: var(--dz-control-visual-height);
    height: var(--dz-control-visual-height);
    color: var(--dz-dropdown-color);
    background-color: var(--dz-dropdown-background);
    border-color: var(--dz-dropdown-border-color) !important;
    border-radius: var(--dz-control-radius) !important;
}

.db-dropdown.dx-texteditor::before {
    position: absolute;
    inset-block: -2px;
    inset-inline: 0;
    content: "";
}

/* !important: beats DevExtreme's inline width. */
.db-dropdown--toolbar {
    width: var(--dz-dropdown-toolbar-width) !important;
}

/* FOCUS TAKES THE BRAND COLOUR, ON EVERY TEXT EDITOR.
 *
 * The design says so outright - Login.dc.html is the one sample that declares a focus state as a
 * RULE rather than drawing it: `#login input:focus { border-color: #E45325 !important }`. Everything
 * else in the exports draws states, so this is the clearest statement of intent in the set.
 *
 * Without it every text editor focuses to dx.light.css's stock blue, rgb(51,122,183).
 *
 * Border AND ring, where the sample shows only a border: a 1px colour change is a weak focus
 * affordance for a keyboard user, the ring is the shape every other focusable thing here already
 * uses (see the button roles' --dz-button-focus-ring), and the style guide lists the accent as valid
 * for a focus ring. --dz-accent rather than a literal, so a reseller's focus follows their brand.
 *
 * Scoped to .dx-texteditor and NOT to .db-grid,
 * because an editor is an editor wherever it renders: the grid filter row, a popup's search box and
 * a form field are all this control. More specific treatments still win on specificity - the
 * dropdown immediately below keeps its own border and ring. */
/* !important: beats dx.light's outlined and filled `.dx-state-focused` editor rules, which reach (0,3,0) against this (0,2,0). */
.dx-texteditor.dx-state-focused {
    border-color: var(--dz-control-focus-color) !important;
    box-shadow: 0 0 0 var(--dz-control-focus-width) var(--dz-control-focus-ring-color) !important;
}

/* ROOM FOR THAT RING WHEN A POPUP SCROLLS A FORM.
 *
 * The ring above is drawn OUTSIDE the editor, and DevExtreme wraps a popup's form - a grid's edit
 * popup is the common one - in a scroll view whose container is overflow: hidden and sits exactly on
 * the form's edges, so an edge editor's ring is cut square (e.g. every right-column editor in the
 * Dashboard's Global UDF Configuration popup).
 *
 * The gutter is the ring's width, out and back in. The negative margin and the taller height move
 * the clip edge outward into the popup's own padding, and the content padding brings the form back
 * to where it was - the form and the popup do not move. The height is not optional - the scroll
 * view is 100% of the popup content, so the margin alone leaves it 4px short and it scrolls.
 *
 * Only a scroll view that holds a form. A dropdown's list is a scroll view in a popup too, and it
 * has no ring to make room for. */
.dx-popup-content > .dx-scrollable:has(> .dx-scrollable-wrapper > .dx-scrollable-container > .dx-scrollable-content > .dx-form) {
    height: calc(100% + 2 * var(--dz-control-focus-width));
    margin: calc(-1 * var(--dz-control-focus-width));
}

.dx-popup-content > .dx-scrollable:has(> .dx-scrollable-wrapper > .dx-scrollable-container > .dx-scrollable-content > .dx-form) > .dx-scrollable-wrapper > .dx-scrollable-container > .dx-scrollable-content {
    padding: var(--dz-control-focus-width);
}

/* And the grid's focus ring, which is a SEPARATE ELEMENT and would otherwise stay blue on top of an
   orange editor.
 *
 * DevExtreme marks a focused grid cell by positioning .dx-datagrid-focus-overlay over it rather than
 * by styling the cell, so the editor rule above cannot reach it; left alone it paints 2px solid
 * rgb(92,149,197) over an accent-focused editor.
 *
 * Scoped to .db-grid, unlike the editor rule: the overlay is a grid construct and every other grid
 * rule in this sheet is scoped the same way. */
.db-grid .dx-datagrid-focus-overlay,
.db-grid .dx-treelist-focus-overlay {
    border-color: var(--dz-grid-focus-color);
}

/* NO CELL OVERLAY IN THE HEADER, which is where the filter row lives.
 *
 * The sample draws no focus treatment on a filter box beyond the box itself; the overlay would add a
 * rectangle around the whole CELL - a second focus indicator, a different shape, on one control.
 *
 * Scoped to the headers view and not removed outright. DevExtreme renders the overlay into whichever
 * view owns the focused cell, so a data cell's overlay is a sibling inside %dx%-rowsview and is
 * untouched here - that one is the ONLY focus affordance a keyboard user gets on a data cell, and
 * deleting it would take the affordance away rather than de-duplicate it. */
/* !important: beats DevExtreme's inline display on the overlay. */
.db-grid .dx-datagrid-headers .dx-datagrid-focus-overlay,
.db-grid .dx-treelist-headers .dx-treelist-focus-overlay {
    display: none !important;
}

/* !important: outranks this sheet's own `.dx-texteditor.dx-state-focused` !important border and ring. */
.db-dropdown.dx-state-focused.dx-texteditor {
    border-color: var(--dz-dropdown-border-color) !important;
    box-shadow: 0 0 0 var(--dz-control-focus-width) var(--dz-dropdown-focus-ring) !important;
}

.db-dropdown .dx-dropdowneditor-button {
    min-width: var(--dz-dropdown-trigger-width);
    width: var(--dz-dropdown-trigger-width);
    color: var(--dz-meta);
    background-color: transparent;
    border-left: 1px solid var(--dz-dropdown-border-color);
    border-radius: 0 var(--dz-control-radius) var(--dz-control-radius) 0;
}

.db-dropdown.dx-state-hover .dx-dropdowneditor-button,
.db-dropdown .dx-dropdowneditor-button.dx-state-hover {
    background-color: var(--dz-dropdown-hover-background);
}

.db-dropdown.dx-dropdowneditor-active .dx-dropdowneditor-button,
.db-dropdown.dx-state-active .dx-dropdowneditor-button,
.db-dropdown .dx-dropdowneditor-button.dx-state-active {
    background-color: transparent;
}

/* !important: beats dx.light `.dx-dropdowneditor.dx-state-hover:not(.dx-custom-button-hovered) .dx-dropdowneditor-icon` at (0,4,0) against this (0,2,0). */
.db-dropdown .dx-dropdowneditor-icon {
    color: currentColor;
    background-color: transparent !important;
    font-size: 12px;
}

.db-dropdown--flush-content .dx-popup-content {
    padding: var(--dz-dropdown-content-padding);
}

/* !important: beats dx.light `.dx-popup-wrapper .dx-state-focused.dx-overlay-content` and `.dx-dropdownlist-popup-wrapper.dx-popup-wrapper .dx-overlay-content` at (0,3,0) against this (0,2,0). */
.db-dropdown--flush-content > .dx-overlay-content {
    overflow: hidden;
    background: var(--dz-dropdown-background);
    border: 1px solid var(--dz-dropdown-border-color) !important;
    border-radius: var(--dz-control-radius) !important;
    box-shadow: var(--dz-dropdown-shadow) !important;
}

/* !important: beats DevExtreme's inline height on the rows view. */
.db-dropdown--flush-content .dx-datagrid-rowsview.dx-empty,
.db-dropdown--flush-content .dx-treelist-rowsview.dx-empty {
    min-height: 52px;
    height: 52px !important;
}

.db-dropdown--flush-content .dx-datagrid-nodata,
.db-dropdown--flush-content .dx-treelist-nodata {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: var(--dz-interactive-target-size);
    color: var(--dz-meta);
    font-family: var(--dz-font-family);
    font-size: var(--dz-button-font-size);
}

/* A PICKER GRID THAT IS A DROPDOWN'S CONTENT FILLS IT.
 *
 * The dropdown's overlay is already a framed surface, so the grid's card - hairline border, rounded
 * corners, low shadow (02-grid-shell.css) - would be a second frame inset by DevExtreme's 20px popup
 * padding. Here the padding goes, the grid keeps only the rule under its headers, and the overlay
 * clips the header strip to its own corners.
 *
 * :has() reads the answer from the DOM, so every dropdown picker gets it with no class for a host to
 * forget, and a picker inside a dialog keeps its card. Without :has() the grid stays an inset card. */
.dx-dropdowneditor-overlay > .dx-overlay-content:has(> .dx-popup-content > .db-grid--picker) {
    overflow: hidden;
}

.dx-dropdowneditor-overlay .dx-popup-content:has(> .db-grid--picker) {
    padding: 0;
}

.dx-dropdowneditor-overlay .db-grid--picker .dx-datagrid-headers,
.dx-dropdowneditor-overlay .db-grid--picker .dx-treelist-headers {
    border-width: 0 0 1px;
    border-radius: 0;
}

.dx-dropdowneditor-overlay .db-grid--picker .dx-datagrid-rowsview,
.dx-dropdowneditor-overlay .db-grid--picker .dx-treelist-rowsview {
    border: 0;
    border-radius: 0;
    box-shadow: none;
}

.db-shared-dropdownbutton-popups .dx-dropdownbutton-popup .dx-list-item,
.db-shared-dropdownbutton-popups .dx-dropdownbutton-popup .dx-list-item-content {
    min-height: var(--dz-interactive-target-size);
}

.db-shared-dropdownbutton-popups .dx-dropdownbutton-popup .dx-list-item-content {
    display: flex;
    align-items: center;
    color: var(--dz-ink);
    font-family: var(--dz-font-family);
    font-size: var(--dz-button-font-size);
}

.db-badge--carrier.badge-unknown { --dz-badge-accent: var(--dz-badge-unknown-background); }
.db-badge--carrier.badge-Verizon { --dz-badge-accent: var(--dz-carrier-verizon-badge-background); }
.db-badge--carrier.badge-VerizonVPS,
.db-badge--carrier.badge-VZWVPS,
.db-badge--carrier.badge-VerizonBroadband,
.db-badge--carrier.badge-VZWBroadband { --dz-badge-accent: var(--dz-carrier-verizon-vps-badge-background); }
.db-badge--carrier.badge-Helix { --dz-badge-accent: var(--dz-carrier-helix-badge-background); --dz-badge-mark-color: var(--dz-on-bright-fill); }
.db-badge--carrier.badge-Rogers { --dz-badge-accent: var(--dz-carrier-rogers-badge-background); --dz-badge-mark-background: var(--dz-badge-mark-rogers-background); }
.db-badge--carrier.badge-Telus { --dz-badge-accent: var(--dz-carrier-telus-badge-background); }
.db-badge--carrier.badge-TMobile,
.db-badge--carrier.badge-T-Mobile { --dz-badge-accent: var(--dz-carrier-tmobile-badge-background); }
/* AT&T is the second carrier that cannot carry white text, for the same reason the MW family
   below cannot: #00a8e0 measures 2.74:1 against white and 4.82:1 against dark. Site.css declares
   the matching legacy `color` - the two sets are held equal in both directions by
   datablaze.ui.badge.harness.js. */
.db-badge--carrier.badge-ATT {
    --dz-badge-accent: var(--dz-carrier-att-badge-background);
    --dz-badge-foreground: var(--dz-carrier-badge-dark-text);
}
.db-badge--carrier.badge-Vodafone { --dz-badge-accent: var(--dz-carrier-vodafone-badge-background); }
.db-badge--carrier.badge-Telefonica { --dz-badge-accent: var(--dz-carrier-telefonica-badge-background); --dz-badge-mark-color: var(--dz-on-bright-fill); }
.db-badge--carrier.badge-Datablaze,
.db-badge--carrier.badge-DB1 { --dz-badge-accent: var(--dz-carrier-datablaze-badge-background); --dz-badge-mark-background: var(--dz-badge-mark-datablaze-background); }
.db-badge--carrier.badge-Webbing { --dz-badge-accent: var(--dz-carrier-webbing-badge-background); --dz-badge-mark-color: var(--dz-on-bright-fill); }
.db-badge--carrier.badge-CSGSureConnect { --dz-badge-accent: var(--dz-carrier-csg-sure-connect-badge-background); }
.db-badge--carrier.badge-PrivateLTE,
.db-badge--carrier.badge-PLTE { --dz-badge-accent: var(--dz-carrier-private-lte-badge-background); --dz-badge-mark-color: var(--dz-on-bright-fill); }
.db-badge--carrier.badge-Nokia { --dz-badge-accent: var(--dz-carrier-nokia-badge-background); }
.db-badge--carrier.badge-MultiCarrier { --dz-badge-accent: var(--dz-carrier-multi-carrier-badge-background); --dz-badge-mark-background: var(--dz-badge-mark-neutral-background); }
.db-badge--carrier.badge-Celona { --dz-badge-accent: var(--dz-carrier-celona-badge-background); --dz-badge-mark-background: var(--dz-badge-mark-neutral-background); }
/* These two declare white explicitly rather than leaning on the fallback (same value), so that
   "legacy declared a colour for this carrier" and "the shared layer maps a foreground for this
   carrier" stay the same set, which the harness checks. */
.db-badge--carrier.badge-FloLive {
    --dz-badge-accent: var(--dz-carrier-flolive-badge-background);
    --dz-badge-foreground: var(--dz-carrier-badge-light-text);
    --dz-badge-mark-color: var(--dz-on-bright-fill);
}
.db-badge--carrier.badge-Starlink,
.db-badge--carrier.badge-starlink {
    --dz-badge-accent: var(--dz-carrier-starlink-badge-background);
    --dz-badge-foreground: var(--dz-carrier-badge-light-text);
}
.db-badge--carrier.badge-Telna { --dz-badge-accent: var(--dz-carrier-telna-badge-background); --dz-badge-mark-color: var(--dz-on-bright-fill); }
/* The MW family is the ONE carrier group that does not take the default white label.
   Every other carrier inherits white text, which is what legacy `.badge { color: #fff }` painted, so
   the consuming rule's #ffffff fallback reproduces them. The MW family is yellow (#ffd200) and needs
   dark text, declared HERE as a foreground token because the badge layer sets `color: ...
   !important` to beat a label's inline fill, which beats the legacy Site.css colour whatever its
   specificity. Without this line the MW badges render white on yellow at roughly 1.4:1.

   A carrier added later that needs anything other than white text must declare its foreground here
   for the same reason. Pinned by datablaze.ui.badge.harness.js. */
.db-badge--carrier.badge-MWV,
.db-badge--carrier.badge-MWW,
.db-badge--carrier.badge-MWF,
.db-badge--carrier.badge-MWVF,
.db-badge--carrier.badge-MWO,
.db-badge--carrier.badge-MWIoT,
.db-badge--carrier.badge-MWH,
.db-badge--carrier.badge-MWT {
    --dz-badge-accent: var(--dz-carrier-mw-badge-background);
    --dz-badge-foreground: var(--dz-carrier-badge-dark-text);
}

/* !important: beats the badge layer's own !important paint, and Site.css `.data-content.badge-ATTT-Mobile`, which ties at (0,2,0). */
.db-badge--carrier.badge-ATTT-Mobile,
.db-badge--carrier.badge-ATTVerizon,
.db-badge--carrier.badge-VerizonT-Mobile {
    background: linear-gradient(var(--dz-badge-background), var(--dz-badge-background)) padding-box,
        var(--dz-badge-border-background) border-box !important;
    border-color: transparent !important;
}

.db-badge--carrier.badge-ATTT-Mobile {
    --dz-badge-border-background: repeating-linear-gradient(90deg,
        var(--dz-carrier-att-badge-background) 0 45px,
        var(--dz-carrier-tmobile-badge-background) 45px 200px);
}

.db-badge--carrier.badge-ATTVerizon {
    --dz-badge-border-background: repeating-linear-gradient(90deg,
        var(--dz-carrier-att-badge-background) 0 45px,
        var(--dz-carrier-verizon-badge-background) 45px 200px);
}

.db-badge--carrier.badge-VerizonT-Mobile {
    --dz-badge-border-background: repeating-linear-gradient(90deg,
        var(--dz-carrier-verizon-badge-background) 0 55px,
        var(--dz-carrier-tmobile-badge-background) 55px 200px);
}

.dx-toolbar .dx-toolbar-item:has(.db-button, .db-dropdown),
.dx-toolbar .dx-toolbar-item-content:has(.db-button, .db-dropdown) {
    min-height: var(--dz-interactive-target-size);
    overflow: visible;
}

/* The target-size min-height makes the toolbar cell taller than the control inside it; DevExtreme's
   own widgets (the grid search box) centre in it, so ours must too or they sit a couple of pixels
   high. Derived from the box, not a fixed nudge, so it holds if either size changes. */
.dx-toolbar .dx-toolbar-item-content:has(.db-button, .db-dropdown) {
    display: flex;
    align-items: center;
}

/* The edit glyph beside an editable value - the design's inline edit affordance: a bare 13px glyph
 * with nothing around it, not a bordered chip.
 *
 * The chrome is removed through the ROLE TOKENS, not by out-specifying the role's rules. The Action
 * role's ground and border are var() reads, so redeclaring those variables here (pointed at the
 * --dz-button-edit-affordance-* band in 00-tokens.css, which carries the values and reasoning) lets
 * the base `.dx-button.db-button` rules paint transparent of their own accord - no specificity
 * fight, and a future change to how the role paints still flows through. The focus ring is
 * deliberately NOT cleared: this is an icon-only control, so the ring is the only thing a keyboard
 * user gets.
 *
 * The BOX is bigger than the 13px glyph, because the design's 13px hit target is too small for a
 * pointer and this control sits inches from Terminate. The glyph is identical; only the clickable
 * area is bigger. min-height is restated because the role pins it at 40px, which beats an inline
 * height. */
.db-edit-affordance {
    --dz-button-background: var(--dz-button-edit-affordance-background);
    --dz-button-border-color: var(--dz-button-edit-affordance-border-color);
    --dz-button-hover-background: var(--dz-button-edit-affordance-hover-background);
    --dz-button-hover-border-color: var(--dz-button-edit-affordance-hover-border-color);
    --dz-button-active-background: var(--dz-button-edit-affordance-active-background);
    --dz-button-color: var(--dz-button-edit-affordance-color);
    --dz-button-hover-color: var(--dz-button-edit-affordance-hover-color);
}

/* 20px, which is the height of a field cell's VALUE line.
 *
 * A ResponsiveBox row is as tall as its tallest cell, so any box taller than the value line makes
 * every cell in a row with an affordance taller than a row without one (28px measured 65px vs 74px
 * rows). The design draws a bare 13px glyph with no box at all.
 *
 * The POINTER TARGET is 24px, WCAG 2.5.8's minimum: the ::before below bleeds 2px past every edge.
 * Measured on Subscriber Detail: both shapes answer a click 1.9px outside the box on all four sides. */
a.db-edit-affordance,
.db-edit-affordance.dx-button.db-button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 20px;
    min-width: 20px;
    height: 20px;
    min-height: 20px;
    padding: 0;
    text-decoration: none;
    vertical-align: middle;
    cursor: pointer;
    position: relative;
    /* DevExtreme clips .dx-button (overflow: hidden), which would clip the hit area with it. */
    overflow: visible;
}

/* COLOUR, SPLIT OUT OF THE SHARED GEOMETRY BLOCK ABOVE (WL-21 round 2, ClickUp 86894yg24, D2).
 *
 * The <a> SHAPE carries no .dx-button/.db-button class (it never passes through the DevExtreme
 * widget), so nothing else ever resolves its ink -- it keeps the literal here, same as before.
 *
 * The dxButton SHAPE does carry .dx-button.db-button, so it already has a way to read its OWN role
 * ink without a literal: 07-button-roles.css's `.dx-button.db-button { color: var(--dz-button-color) }`
 * (0,2,0) resolves this role's --dz-button-color -> --dz-button-edit-affordance-color, the exact same
 * value this literal names. Restating it here at (0,3,0) was not wrong at rest -- it was redundant --
 * but it also beat 07's `.dx-button.db-button.dx-state-hover { color: var(--dz-button-hover-color) }`
 * (also (0,3,0)) on SOURCE ORDER (this file loads after 07), freezing the dxButton pencils at rest ink
 * in every state and reading as "this control's hover never does anything" -- exactly what the design
 * draws a step for on the <a> shape just below. Dropping the literal here lets the dxButton shape
 * resolve through the same --dz-button-color/-hover-color chain as any other shared button, which
 * already has somewhere to land: .db-edit-affordance (above) declares both. */
a.db-edit-affordance {
    color: var(--dz-body);
}

    a.db-edit-affordance::before,
    .db-edit-affordance.dx-button.db-button::before {
        position: absolute;
        inset: -2px;
        content: "";
    }

    /* 3px, not 2: the role's transparent 1px border is inside the 20px box, and an absolute child
       measures from inside the border. */
    .db-edit-affordance.dx-button.db-button::before {
        inset: -3px;
    }

    /* PHONES ONLY: the same ::before bleeds to a 44px target instead of 24 (the condition and its
       reason are in 02-grid-shell.css, TOUCH HIT AREAS). 12px past the <a>'s 20px box on every
       side is 44; 13px past the dxButton's 18px padding box (its 1px border is inside the 20) is
       the same 44. The glyph and the box are untouched, so nothing reflows. The bleed covers the end
       of the value the pencil sits beside, which is text and not a control. */
    @media (max-width: 824.98px) and (pointer: coarse) {
        a.db-edit-affordance::before {
            inset: -12px;
        }

        .db-edit-affordance.dx-button.db-button::before {
            inset: -13px;
        }
    }

    /* DevExtreme pads the content box of a button, which would push a centred 13px glyph off centre
       inside the square. */
    .db-edit-affordance.dx-button .dx-button-content {
        padding: 0;
    }

    .db-edit-affordance .db-icon {
        --db-icon-size: 13px;
    }

    a.db-edit-affordance:hover,
    a.db-edit-affordance:focus-visible {
        color: var(--dz-ink);
    }

/* Its own row above the panel it acts on, aligned to the panel's right edge. Not inside the title bar
   (the actions are not part of the panel's identity, and a 36px control in a 40px header forces the
   bar to grow around it) and not below the detail grid, where it would sit between this panel and
   the cards under it and read as belonging to neither. */
.db-action-menu {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: flex-end;
    gap: 8px;
    min-height: 44px;
    padding: 0 0 8px;
}

.db-action-menu > .db-action-menu__secondary {
    min-height: 36px;
    min-width: 120px;
}

/* The standalone controls that sit beside the menu rather than inside it - Network Refresh today.
   Its own flex row so several of them stay evenly spaced if another is ever pulled out, and the
   same 8px the bar uses between the group and the menu. */
.db-action-menu__standalone {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px;
}

/* Entries are TEXT in a menu, not a stack of buttons - owner decision. Bordered button entries
   crowd a 190px popup. The design draws plain rows: 400 14px/1.3 text at 12px padding, no ground,
   no border, with the whole row lighting up on hover.

   The hover is deliberately the same one the row-action popout uses - Sunken across the full width
   of the row. A button says "I am a control", a row that highlights says "I am one of a list".

   It is a span, not a <button>: the dxDropDownButton list owns the click, and a nested button inside
   a role="option" would fight it for the event. */
.db-action-menu__item {
    display: flex;
    align-items: center;
    /* 12px on all four sides, from the design. The row's own padding is what gives the hover its
       height, so there is no min-height here - a fixed one would leave the highlight standing off
       the text at the bottom of the popup. */
    /* The comfortable density's 12, written as a literal, so a menu row does not respond to the
       density gear. Deliberately not
       --dz-grid-cell-padding-x, which would follow the GRID's density - the wrong axis for a menu
       row. Leave it until a menu/control density band exists. */
    padding: 12px;
    color: var(--dz-ink);
    font-size: 14px;
    font-weight: 400;
    line-height: 1.3;
    white-space: nowrap;
    cursor: pointer;
}

/* A destructive entry is RED TEXT - owner decision. The filled danger role is rejected: solid red
   blocks in a five-row popup dominate a menu whose other entries are words. Red text is the same
   treatment as every other row, in the danger colour, which is what the design draws. The
   destructive meaning is carried twice: by hue here, and by the "(destructive action)" accessible
   label the action plan attaches.

   The accent, not the button-danger background: this is text on a light ground, so it needs the
   role's readable colour rather than its fill. */
.db-action-menu__item--danger {
    color: var(--dz-danger-text-color);
}

/* The card the entries sit on. DevExtreme's own (6px corners, no border, a rgba(0, 0, 0, .176)
   shadow) reads as a different grey from everything else on the page, so it takes the row-action
   popout's shape and the product's two popout menus are one component.
   (The design draws this one at 8px and the gear panel at 10px. Taking 10px for both is deliberate:
   an 8px literal would be the only radius in the sheet with no token, to express a 2px difference
   between two menus that never appear together.)

   The selector names the wrapper's own DevExtreme classes as well as ours, and that is what makes
   the border appear at all. dx.light.css deliberately detaches this popup from its trigger with
   `.dx-dropdownbutton-popup-wrapper.dx-popup-wrapper .dx-overlay-content { border-top-width: 0 }` -
   a LONGHAND at (0,3,0), against which a `border: 1px solid` shorthand at (0,2,0) loses for the top
   edge only, leaving a card with no top border. Adding their two classes to ours makes this
   (0,4,0). A partly-applied border is a specificity symptom: a longhand elsewhere beats your
   shorthand per-edge. */
.db-action-menu__popup.dx-dropdownbutton-popup-wrapper.dx-popup-wrapper .dx-overlay-content {
    background-color: var(--dz-card);
    border: 1px solid var(--dz-border);
    border-radius: var(--dz-grid-card-radius);
    box-shadow: var(--dz-surface-float-shadow);
}

.db-action-menu__popup .dx-list {
    /* 4px top and bottom only. The rows run to the card's edges so their hover does too - side
       padding here would leave an unlit gutter down both sides of the highlighted row. */
    padding: 4px 0;
    /* The card owns the corners, so the list must not paint its own square ground over them. */
    background-color: transparent;
    border: none;
}

/* DevExtreme pads its list item content 7px/9px; the entry carries its own 12px padding, so the
   wrapper contributing more is double spacing. Rows are flush: a gap would break the run of
   highlights into detached chips.

   The selector is long because DevExtreme's is: it ships
   `.dx-dropdownbutton-popup-wrapper .dx-list:not(.dx-list-select-decorator-enabled) .dx-list-item-content`,
   four classes deep. Matching that and adding the wrapper class outranks it on specificity alone, so
   this does not need !important and does not depend on stylesheet order. */
.db-action-menu__popup.dx-dropdownbutton-popup-wrapper .dx-list:not(.dx-list-select-decorator-enabled) .dx-list-item-content {
    padding: 0;
}

/* Hover paints the ROW: with no ground on the entry, the row is exactly the right box - full width,
   its height set by the entry's padding.

   The selector mirrors DevExtreme's own rather than being the short one this obviously wants to be.
   dx.light.css ships
   `.dx-list:not(.dx-list-select-decorator-enabled) .dx-list-item.dx-state-hover`, and the :not()
   argument counts toward the class column - so theirs is (0,4,0) and a bare
   `.db-action-menu__popup .dx-list-item.dx-state-hover` is (0,3,0) and loses silently. Matching
   their shape and adding the wrapper class makes this (0,5,0), which wins on specificity alone and
   does not depend on stylesheet order. The grid's selected-row rule has the same trap. */
.db-action-menu__popup .dx-list:not(.dx-list-select-decorator-enabled) .dx-list-item.dx-state-hover,
.db-action-menu__popup .dx-list:not(.dx-list-select-decorator-enabled) .dx-list-item.dx-state-focused,
.db-action-menu__popup .dx-list:not(.dx-list-select-decorator-enabled) .dx-list-item.dx-state-active {
    background-color: var(--dz-sunken);
}

/* A danger entry has no hover, focus or pressed fill of its own: it keeps its red text on the same
   Sunken row as every other entry, as the design draws - the row says "you are here", the colour
   says "this one destroys something". The one silent failure, the danger colour losing to the row
   hover, is pinned in the harness rather than guarded here. */

/* Two regions: a captioned list of saved items, then a divided footer that owns every action. The list
   and the actions are different kinds of thing - one is data you pick from, the other is what you can do -
   so a rule separates them instead of leaving one undifferentiated column of controls. */
.db-layout-panel {
    display: flex;
    flex-direction: column;
}

/* The caption treatment is declared once, further down with the rest of the settings menu, so the
   panel has one section-header style. */

/* No border of its own. The footer always follows a section caption, and
   .db-layout-panel__caption:not(:first-child) already draws the rule between sections; a border
   here would print a second line under the "Layout" caption. The design has exactly one 1px
   hairline per section break. */
.db-layout-panel__footer {
    margin-top: 8px;
    padding-top: 8px;
}

/* Saved items need a real hit area and a selected state, or they read as plain text. The active one
   takes an accent bar plus a tinted ground, which is how selection reads everywhere in the palette. */
.db-layout-panel .dx-datagrid .dx-row > td,
.db-layout-panel .dx-treelist .dx-row > td {
    padding: 8px 10px;
    cursor: pointer;
}

    .db-layout-panel .dx-datagrid .dx-row > td:first-child,
.db-layout-panel .dx-treelist .dx-row > td:first-child {
        padding-left: 12px;
    }

/* The overwrite/delete icons show only on row hover, so their column is collapsed to nothing and the
   icons are overlaid on the row's right edge; the name gets the full width at rest. table-layout is
   fixed. A fixed table shares column widths across all rows, so the give-back on hover is
   padding on the name cell, which IS per-row. */
/* !important: beats DevExtreme's inline width on the <col>. */
.db-layout-panel .dx-datagrid-rowsview col:last-child,
.db-layout-panel .dx-treelist-rowsview col:last-child {
    width: 0 !important;
}

.db-layout-panel .dx-datagrid-rowsview .dx-data-row,
.db-layout-panel .dx-treelist-rowsview .dx-data-row {
    position: relative;
}

.db-layout-panel .dx-datagrid-rowsview .dx-data-row > td.dx-command-edit,
.db-layout-panel .dx-treelist-rowsview .dx-data-row > td.dx-command-edit {
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    /* Only the cell's position is ours; the icons keep DevExtreme's own centring and link margins.
       Flex centres vertically now that the cell is out of the table's row box. */
    display: flex;
    align-items: center;
    justify-content: center;
    /* DevExtreme sizes .dx-command-edit from the theme (100px generic, 85px light, 70px
       light-compact). All three of width, min-width and max-width are pinned to our token from a
       selector that out-specifies .dx-command-edit, because the hover padding below gives back
       exactly this much: if the theme's min-width won, a transparent strip of the cell would hang
       over the name and swallow the clicks that select a layout. */
    width: var(--dz-layout-item-actions-width);
    min-width: var(--dz-layout-item-actions-width);
    max-width: var(--dz-layout-item-actions-width);
    padding: 0;
    background-color: transparent;
}

/* Overwrite and Delete sit a few pixels apart and mean "replace this layout" and "destroy it", so
   each icon gets its own 26px chip so the target is never in doubt before the click.

   The selector restates DevExtreme's six-class chain with the panel in front: dx.light.css pins
   these links to 18px from
   `.dx-datagrid .dx-datagrid-content .dx-datagrid-table .dx-row .dx-command-edit-with-icons .dx-link`,
   and anything shorter loses. Out-specifying is preferable to !important because the rule is scoped
   to this panel and nothing needs to beat it in turn.

   26px chips at 3px margins occupy 64px of the 68px strip (--dz-layout-item-actions-width), so the
   give-back padding covers them and nothing overhangs the name. */
.db-layout-panel .dx-datagrid .dx-datagrid-content .dx-datagrid-table .dx-row .dx-command-edit-with-icons .dx-link,
.db-layout-panel .dx-treelist .dx-treelist-content .dx-treelist-table .dx-row .dx-command-edit-with-icons .dx-link {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 26px;
    height: 26px;
    margin: 0 3px;
    border-radius: var(--dz-control-radius);
    /* An explicit 0: DevExtreme's .dx-link carries no border today, and a theme bump adding one
       would make the chip shift on hover. (A mask would clip any border to the glyph anyway.) */
    border: 0;
}

/* THE HIGHLIGHT IS THE GLYPH'S OWN COLOUR, because on a masked element nothing else paints.
 *
 * These icons are MASKS: `.db-icon` is a mask-image over `background-color: currentColor`, and
 * DevExtreme hangs the icon class on the link itself, so the 26px chip and the 15px glyph are one
 * element and the link's background-color IS the glyph's ink (#666261 at rest). A mask clips
 * everything the element paints - fill, border, shadow - to the glyph's silhouette. So a chip fill
 * cannot work: a light fill paints the ICON light and it disappears into the panel (the owner's
 * "highlighting white, which causes them to get lost in their own background").
 *
 * Changing the ink is the one thing a mask cannot swallow: accent against Body is an unmistakable
 * shift on a 15px glyph, and it needs no chip. */
.db-layout-panel .dx-datagrid .dx-datagrid-content .dx-datagrid-table .dx-row .dx-command-edit-with-icons .dx-link:hover,
.db-layout-panel .dx-treelist .dx-treelist-content .dx-treelist-table .dx-row .dx-command-edit-with-icons .dx-link:hover,
.db-layout-panel .dx-datagrid .dx-datagrid-content .dx-datagrid-table .dx-row .dx-command-edit-with-icons .dx-link:focus-visible,
.db-layout-panel .dx-treelist .dx-treelist-content .dx-treelist-table .dx-row .dx-command-edit-with-icons .dx-link:focus-visible {
    background-color: var(--dz-layout-action-hover-color);
}

/* The keyboard ring goes on the CELL, for the same reason: a box-shadow on the masked link is
 * clipped to the glyph and never visible. The td is not masked; :has() puts the state on the
 * parent of the focused link.
 *
 * INSET, so the ring stays inside the action strip rather than reaching over the layout name. The
 * cell holds both chips, so this says "focus is in the actions" and the accent ink says which one -
 * weaker than a ring round one chip, and the strongest a masked icon allows. Making it stronger
 * means moving the icon class off the link in every caller. */
.db-layout-panel .dx-datagrid .dx-row > td.dx-command-edit-with-icons:has(.dx-link:focus-visible),
.db-layout-panel .dx-treelist .dx-row > td.dx-command-edit-with-icons:has(.dx-link:focus-visible) {
    box-shadow: inset 0 0 0 var(--dz-control-focus-width) var(--dz-focus-ring);
    border-radius: var(--dz-control-radius);
}

/* The glyph inside the chip: the design's own artwork, at the design's own size, in Body.
 *
 * mask-size, because the chip and the glyph are the SAME element: .db-icon's `mask-size: contain`
 * would scale the artwork to fill the 26px box. Naming the size keeps the glyph at the 15px the
 * sample draws, centred by the base class's mask-position.
 *
 * colour, because dx.light.css pins `.dx-datagrid .dx-link` to its link blue at (0,2,0) and the
 * mask takes its colour from the element. This selector is (0,4,0), which also clears the
 * `.db-icon.dx-icon { color: inherit }` tie-breaker that would hand them the cell's ink.
 *
 * BOTH chips take one treatment; Delete is deliberately not singled out (owner decision). The hover
 * highlight is chrome and answers "which control", not "what will this do" - the icons and tooltips
 * carry the difference. */
.db-layout-panel .dx-command-edit .dx-link.db-icon {
    -webkit-mask-size: var(--db-icon-size, 15px);
    mask-size: var(--db-icon-size, 15px);
    color: var(--dz-body);
}

/* The name truncates only on the row being hovered: the padding shrinks its content box by exactly
   the strip the icons occupy, so the ellipsis appears where the icons begin. */
.db-layout-panel .dx-datagrid-rowsview .dx-data-row.dx-state-hover > td:first-child,
.db-layout-panel .dx-treelist-rowsview .dx-data-row.dx-state-hover > td:first-child {
    padding-right: var(--dz-layout-item-actions-width);
}

/* These selectors out-specify DevExtreme's
   .dx-datagrid-rowsview .dx-selection.dx-row:not(.dx-row-focused) > td, which otherwise paints the
   selected row its default grey - so the row is styled through .dx-datagrid-rowsview and the
   :not(.dx-row-focused) variant is matched explicitly. Outside the gear panel the band spans
   the whole row rather than a rounded box per cell, which would seam between name and icons, and is
   painted on the row as well as the cells because the overlaid action cell is transparent. In the gear
   panel the row paints none and its cells carry a rounded band (see the saved layouts block below). */
.db-layout-panel .dx-datagrid-rowsview .dx-selection.dx-row,
.db-layout-panel .dx-treelist-rowsview .dx-selection.dx-row,
.db-layout-panel .dx-datagrid-rowsview .dx-selection.dx-row > td,
.db-layout-panel .dx-treelist-rowsview .dx-selection.dx-row > td,
.db-layout-panel .dx-datagrid-rowsview .dx-selection.dx-row:not(.dx-row-focused) > td,
.db-layout-panel .dx-treelist-rowsview .dx-selection.dx-row:not(.dx-row-focused) > td,
.db-layout-panel .dx-datagrid-rowsview .dx-selection.dx-row:hover > td,
.db-layout-panel .dx-treelist-rowsview .dx-selection.dx-row:hover > td {
    background-color: var(--dz-selection-tint);
    color: var(--dz-ink);
    font-weight: 600;
}

    /* First cell only: on every cell it would draw a second stripe partway across the row. */
    .db-layout-panel .dx-datagrid-rowsview .dx-selection.dx-row > td:first-child,
.db-layout-panel .dx-treelist-rowsview .dx-selection.dx-row > td:first-child {
        box-shadow: inset 3px 0 0 0 var(--dz-selection-color);
    }

/* Outside the gear panel hover is deliberately left to DevExtreme. Its own translucent overlay
   out-specifies anything we set here short of an arms race, it already paints the overlaid action cell
   to match, and the row only needs to read as hoverable. The gear panel takes the arms race, because
   there the row must read as one of the menu's items (see the saved layouts block below). */

.db-layout-action {
    padding: var(--dz-layout-action-padding);
}

/* Inside the grid's settings menu the layout actions are menu ROWS, not buttons.
 *
 * Elsewhere this panel is a picker opened deliberately, and outlined buttons are the right weight.
 * Behind the gear it is a menu, between two radios and a list of saved names, and bordered boxes
 * read as a form embedded in a menu. The mock draws flat rows with a hover tint, as the saved-layout
 * list already does.
 *
 * Only the paint changes: they are the same shared buttons in the same role, so the focus ring, the
 * pending lock and the disabled treatment still apply.
 *
 * On a themed page the adapter's contained-button reset (22-devextreme-adapter.css) out-ranks these
 * rules, so that reset excludes these rows by location; without the exclusion every gear's rows
 * were boxed again on a themed page. */
.db-layout-panel--gear .db-layout-action > .dx-button.db-button {
    background: transparent;
    border-color: transparent;
    box-shadow: none;
    /* A menu item's height, not a button's (PP-D81). The button role pins min-height to the 40px
       control height and the row added 6px beneath, a 46px pitch against the saved layouts' 36; the
       item is --dz-layout-item-height and the rows touch, as the saved rows below do. */
    height: var(--dz-layout-item-height);
    min-height: var(--dz-layout-item-height);
}

/* The row's own padding has no bottom: the pitch is the item's height. The box is inset from the
   panel's content edge by --dz-layout-item-inset, the inset the saved rows take too. */
.db-layout-panel--gear .db-layout-action {
    padding: 0 var(--dz-layout-item-inset);
}

    .db-layout-panel--gear .db-layout-action > .dx-button.db-button .dx-button-content {
        justify-content: flex-start;
        /* The content box stretches to the 36px item, and the item's height is the whole of it. */
        padding-top: 0;
        padding-bottom: 0;
        padding-right: var(--dz-layout-item-pad);
        padding-left: var(--dz-layout-item-pad);
    }

    .db-layout-panel--gear .db-layout-action > .dx-button.db-button:hover {
        background: var(--dz-sunken);
        border-color: transparent;
    }

/* The row's own padding no longer separates the last action from the name form, so the form carries
   its own top gap. */
.db-layout-panel--gear .db-layout-name-editor {
    padding-top: var(--dz-layout-editor-gap);
}

/* THE SAVED LAYOUTS ARE ITEMS TOO (PP-D81). Behind the gear the list is a menu section, so each saved
 * layout is painted like the Layout actions above it: the same inset box, the same 36px height, the
 * same rounded Sunken highlight on hover, its name LEFT aligned one item padding inside the box (the
 * left edge of a Layout action's glyph and a Density radio).
 *
 * Before this the list was a bare grid in the middle of a menu: the name hugged the menu's edge at the
 * captions' own x, and the highlight was DevExtreme's translucent band running full width with square
 * corners.
 *
 * The box inset is the body's padding, so the grid is exactly the width of an action row. */
.db-layout-panel--gear .db-layout-panel__body {
    padding: 0 var(--dz-layout-item-inset);
}

/* Every section's first item sits directly under its caption. The Density and Saved layouts items
   already do; the Layout footer added 8px of margin and 8px of padding above its actions, a 16px gap
   the other two sections do not have (PP-D81). The name form, which is also in the footer, keeps its
   own gap. */
.db-layout-panel--gear .db-layout-panel__footer {
    margin-top: 0;
    padding-top: 0;
}

/* THE NAME IS CENTRED IN ITS ROW. dx.light.css pins every data cell to vertical-align: top at
   (0,3,1), and that used to be invisible because the cell's own 8px padding set the text's place.
   The row is now a fixed 36px with no vertical padding, so a top-aligned name sat at the row's top
   edge, about 6px above the centre the icons and the Layout labels share. The chain restates
   DevExtreme's own so this wins on weight, not on source order. */
.db-layout-panel--gear .dx-datagrid .dx-datagrid-content .dx-datagrid-table .dx-data-row > td,
.db-layout-panel--gear .dx-treelist .dx-treelist-content .dx-treelist-table .dx-data-row > td {
    vertical-align: middle;
}

/* A cell's radius does nothing in a collapsed table (the CSS table model drops it), and DevExtreme's
   table is border-collapse: collapse. The list draws no row, column or outer lines
   (voyager.components.toolbar.js), so there is no border for separate spacing to change. */
.db-layout-panel--gear .dx-datagrid-rowsview .dx-datagrid-table,
.db-layout-panel--gear .dx-treelist-rowsview .dx-treelist-table {
    border-collapse: separate;
    border-spacing: 0;
}

.db-layout-panel--gear .dx-datagrid .dx-data-row > td,
.db-layout-panel--gear .dx-treelist .dx-data-row > td {
    box-sizing: border-box;
    height: var(--dz-layout-item-height);
    padding-top: 0;
    padding-bottom: 0;
    border-radius: var(--dz-control-radius);
}

    /* The name starts where an action's glyph does (box inset, then item padding), left aligned, so the
       Density radios, the Layout icons and the saved names share one left edge. */
    .db-layout-panel--gear .dx-datagrid .dx-data-row > td:first-child,
.db-layout-panel--gear .dx-treelist .dx-data-row > td:first-child {
        padding-left: var(--dz-layout-item-pad);
    }

/* The overlaid actions cell is the row's right end: it takes the item's right corners only, the name
   cell under it takes the whole box. */
.db-layout-panel--gear .dx-datagrid-rowsview .dx-data-row > td.dx-command-edit,
.db-layout-panel--gear .dx-treelist-rowsview .dx-data-row > td.dx-command-edit {
    border-radius: 0 var(--dz-control-radius) var(--dz-control-radius) 0;
}

/* The selected layout's band is painted on its cells, so the row itself paints none: a row's
   background is a rectangle, and it would show square corners round the rounded cells. */
.db-layout-panel--gear .dx-datagrid-rowsview .dx-selection.dx-row,
.db-layout-panel--gear .dx-treelist-rowsview .dx-selection.dx-row {
    background-color: transparent;
}

/* Hover is the menu's own highlight, the Sunken the Layout actions and the Density rows take, on both
   cells of the row (the name and the overlaid actions). The selected row keeps its tint.
   !important: beats dx.light's hover rule, whose :not() chain makes it (0,9,1) against this (0,5,1). */
.db-layout-panel--gear .dx-datagrid-rowsview .dx-data-row.dx-state-hover:not(.dx-selection) > td,
.db-layout-panel--gear .dx-treelist-rowsview .dx-data-row.dx-state-hover:not(.dx-selection) > td {
    background-color: var(--dz-sunken) !important;
}

/* A destructive entry in a row menu (Terminate, the one irreversible item in the Subscribers row
 * menu). Both mocks colour it, in the guide's Terminated role. */
.db-menu-item--danger .dx-menu-item-text,
.db-menu-item--danger.dx-menu-item .dx-menu-item-text {
    color: var(--dz-danger-text-color);
}

/* !important: beats Site.css `.themed-context-menu .dx-icon` color, which is itself !important. */
.db-menu-item--danger .dx-icon {
    color: var(--dz-danger-text-color) !important;
}

.db-layout-action > .dx-button {
    width: 100%;
}

/* The name editor is a small form inside the layout panel and takes the panel's vertical rhythm:
   full-width name box, then one split row of labelled buttons. Deliberately the SAME horizontal
   padding as .db-layout-action, so the input lines up with the buttons stacked above and below it. */
.db-layout-name-editor {
    display: flex;
    flex-direction: column;
    gap: var(--dz-layout-editor-gap);
    padding: var(--dz-layout-action-padding);
}

/* The name box still carries the legacy .small-textbox class, which pins it to 24px - well under the
   shared control height - so it reads as a different control family from the buttons around it. */
.db-layout-name-input.dx-texteditor {
    width: 100%;
    min-width: 0;
    height: var(--dz-control-visual-height);
}

    /* FOUR CLASSES DEEP TO BEAT ONE RULE IN Site.css - do not flatten it.
     *
     * Site.css carries
     *   `.small-textbox > .dx-texteditor-container > .dx-texteditor-input-container > input`
     *   { min-height: 22px; padding: 1px 2px 2px }
     * at (0,3,1), and this input still carries .small-textbox. The obvious
     * `.db-layout-name-input.dx-texteditor .dx-texteditor-input` is (0,3,0) and loses silently (the
     * flex row stretches the input anyway; only the 2px side padding shows).
     *
     * Matching Site.css's descent reaches (0,5,0) without !important. Site.css sets padding as a
     * SHORTHAND, so a longhand here cannot win per-edge against it. */
    .db-layout-name-input.dx-texteditor > .dx-texteditor-container > .dx-texteditor-input-container > .dx-texteditor-input {
        min-height: calc(var(--dz-control-visual-height) - 2px);
        padding-top: 0;
        padding-bottom: 0;
        /* 8px, the standard inset for a control of this height; 2px reads as the text touching the
           border. */
        padding-left: var(--dz-space-1);
        padding-right: var(--dz-space-1);
    }

    /* THE PLACEHOLDER IS A SECOND ELEMENT AND NEEDS THE SAME INSET.
     *
     * DevExtreme draws the placeholder as `content` on `.dx-placeholder::before`, a separate
     * absolutely positioned node, so the input's padding does not reach it; without this the
     * placeholder and the typed value sit at different insets.
     *
     * Site.css pads it from the same .small-textbox block:
     *   `.small-textbox > … > .dx-placeholder:before { padding: 1px 2px 2px 2px }`
     * at (0,4,1), so this mirrors that descent to reach (0,5,1). Vertical stays 0 to match the
     * input: the box centres the text by its height, and vertical padding would nudge the
     * placeholder off the value's baseline. */
    .db-layout-name-input.dx-texteditor > .dx-texteditor-container > .dx-texteditor-input-container > .dx-placeholder::before {
        padding: 0 var(--dz-space-1);
    }

/* Cancel and Save split the row evenly and stretch to its ends, so the pair reads as one block the same
   width as the stacked buttons instead of two small controls floating at one edge. flex-basis 0 with
   equal grow keeps them matched regardless of label length. */
.db-layout-name-editor__actions {
    display: flex;
    gap: var(--dz-layout-editor-gap);
}

    .db-layout-name-editor__actions > .dx-button {
        flex: 1 1 0;
        min-width: 0;
    }

/* !important: beats DevExtreme's inline display and any class rule's display, e.g. `.db-layout-name-editor__actions` display:flex (0,1,0), whatever their specificity. */
.db-is-hidden {
    display: none !important;
}

/* ================================================================================================
   PAGE TOOLBAR
   ================================================================================================

   The row above a full-page grid: the page's own title, then pushed right, a search box and the
   controls that act on the table rather than on the records in it.

   It sits in the PAGE rather than in DevExtreme's header panel. Both mocks put the title row outside
   the grid's card and leave only record-level actions - bulk operations, the group-by hint - in the
   strip above the rows. DevExtreme has no such split: .dx-datagrid-header-panel is a child of
   .dx-datagrid alongside the headers and the rowsview, so a card drawn around the widget encloses
   the toolbar too.

   A host renders the markup itself and mounts its own controls into it. Nothing here reaches into a
   grid, and no grid knows it is below one.
   ================================================================================================ */
.db-page-toolbar {
    display: flex;
    align-items: center;
    gap: var(--dz-space-1half);
    flex-wrap: wrap;
    margin-bottom: var(--dz-page-toolbar-margin-bottom);
    /* For the narrow row below. Inline-size containment means the toolbar's width comes from its
       parent and never from its controls; every caller renders it as a block. */
    container-type: inline-size;
}

/* A NARROW ROW: the title on a line of its own, the controls on the next, the search box taking the
 * space they leave.
 *
 * Left to wrap on its own, the row put the layout gear on a third line on a phone: at 390px the
 * row is 348px and a 280px search, export and the gear need 384. Shrinking the box alone does not
 * fix it, because then a short title and the box fit together and the buttons wrap instead.
 *
 * 560px is about where the widest common row (title, a 280px box, two icon buttons) stops fitting
 * on one line, so above it nothing changes. A container query and not a media query: the row's own
 * width is the condition, whatever rail, drawer or dialog sits beside it. A custom property cannot
 * be read in a query condition, hence the literal. */
@container (max-width: 560px) {
    .db-page-toolbar__title {
        flex-basis: 100%;
    }

    .db-page-toolbar__spacer {
        display: none;
    }

    /* Under the parent, so it out-ranks the box's own 280px further down this file. */
    .db-page-toolbar .db-page-toolbar__search.dx-texteditor {
        flex: 1 1 0;
        width: auto;
        min-width: 0;
    }
}

.db-page-toolbar__title {
    margin: 0;
    color: var(--dz-ink);
    font-family: var(--dz-font-family);
    font-size: 26px;
    font-weight: 600;
    line-height: 1.2;
}

/* Takes the slack, so everything after it is right-aligned however long the title is. */
.db-page-toolbar__spacer {
    flex: 1 1 auto;
}

/* The package's typeface and ink reach the editor BOX, not just its inner input.
 *
 * dx.light.css gives the editor box "Helvetica Neue" and #333, which shows as soon as anything
 * renders text outside the input - a placeholder, a dropdown's selected value, a mask, an
 * invalid-badge tooltip.
 *
 * Scoped to the editors this package owns rather than a blanket .dx-texteditor: the product has
 * DevExtreme editors this package does not style. Adding a class to this list is how a new editor
 * opts in.
 *
 * #333 is not in the palette. --dz-ink (#31302f) is deliberately warmer, like the whole neutral
 * ramp. */
.db-page-toolbar__search.dx-texteditor,
.db-page-toolbar__search.dx-texteditor .dx-texteditor-input,
.db-dropdown.dx-texteditor,
.db-dropdown.dx-texteditor .dx-texteditor-input,
.db-layout-name-input.dx-texteditor,
.db-layout-name-input.dx-texteditor .dx-texteditor-input,
.db-icon-dropdown.dx-dropdowneditor .dx-texteditor-input,
.db-grid .dx-datagrid-search-panel,
.db-grid .dx-treelist-search-panel,
.db-grid .dx-datagrid-search-panel .dx-texteditor-input,
.db-grid .dx-treelist-search-panel .dx-texteditor-input,
.db-grid .dx-datagrid-filter-row .dx-texteditor,
.db-grid .dx-treelist-filter-row .dx-texteditor,
.db-grid .dx-datagrid-filter-row .dx-texteditor-input,
.db-grid .dx-treelist-filter-row .dx-texteditor-input {
    color: var(--dz-ink);
    font-family: var(--dz-font-family);
}

/* The placeholder is Meta - the same step the package gives a field's micro-label, because a
   placeholder names what goes in the box rather than being content itself. DevExtreme renders it
   as its own element rather than through ::placeholder, so it needs naming. */
.db-page-toolbar__search.dx-texteditor .dx-placeholder,
.db-dropdown.dx-texteditor .dx-placeholder,
.db-layout-name-input.dx-texteditor .dx-placeholder,
.db-grid .dx-datagrid-search-panel .dx-placeholder,
.db-grid .dx-treelist-search-panel .dx-placeholder,
.db-grid .dx-datagrid-filter-row .dx-placeholder,
.db-grid .dx-treelist-filter-row .dx-placeholder {
    color: var(--dz-meta);
    font-family: var(--dz-font-family);
}

.db-page-toolbar__search.dx-texteditor {
    width: 280px;
    max-width: 100%;
    height: var(--dz-interactive-target-size);
    border-color: var(--dz-border);
    border-radius: var(--dz-control-radius);
}

    /* No magnifier, and the padding it held comes back with it.
     *
     * The sample's toolbar search is a bare box - `padding: 0 14px`, no glyph. DevExtreme's search
     * mode injects a 34px .dx-icon-search and reserves 34px of left padding for it, so hiding the
     * glyph alone would leave the text indented by an icon that is not there.
     *
     * This is the control Subscribers renders. The grid's own .dx-datagrid-search-panel is a
     * separate element with the same treatment below - a page has one or the other, not both. */
    .db-page-toolbar__search.dx-texteditor .dx-icon-search {
        display: none;
    }

    .db-page-toolbar__search.dx-texteditor .dx-texteditor-input {
        min-height: calc(var(--dz-interactive-target-size) - 2px);
        /* From the sample, replacing the magnifier's 34px gutter. */
        padding: 0 var(--dz-search-input-padding-x);
        font-family: var(--dz-font-family);
        font-size: 14px;
    }

    /* ...and the PLACEHOLDER's gutter, a separate declaration. DevExtreme reserves the magnifier's
       34px in one rule covering two elements - `.dx-searchbox .dx-placeholder::before,
       .dx-searchbox .dx-texteditor-input` - so restating only the input leaves "Search..." 20px
       right of the input's text, jumping left the moment you type. */
    .db-page-toolbar__search.dx-texteditor .dx-placeholder::before {
        padding-inline-start: var(--dz-search-input-padding-x);
    }

/* The grid's OWN search box, which is a different element from .db-page-toolbar__search.
 *
 * A page that builds its own toolbar puts .db-page-toolbar__search in its markup; a grid that
 * answers `searchPanel` gets this one, injected by DevExtreme into the header panel with no hook for
 * a view to name. Twenty-two of the grids take this route.
 *
 * The values are the ones docs/style/devextreme.md states for inputs - 6px radius, Border #C4C0BE -
 * at the 40px the mocks draw every toolbar control.
 *
 * Width is deliberately absent. DevExtreme writes searchPanel.width into an inline style, so a CSS
 * width here would lose to it on every grid that sets one and win on every grid that does not -
 * the worst of both. The default measures 240px, over the mock's 230px minimum. */
.db-grid .dx-datagrid-search-panel,
.db-grid .dx-treelist-search-panel {
    height: var(--dz-interactive-target-size);
    border-color: var(--dz-border);
    border-radius: var(--dz-control-radius);
}

    .db-grid .dx-datagrid-search-panel .dx-texteditor-input,
.db-grid .dx-treelist-search-panel .dx-texteditor-input {
        min-height: calc(var(--dz-interactive-target-size) - 2px);
        padding: 0 var(--dz-search-input-padding-x);
        font-size: 14px;
    }

    /* The magnifier goes: the sample's toolbar input carries no glyph. The input's padding above
       reclaims the 34px DevExtreme reserves for it. */
    .db-grid .dx-datagrid-search-panel .dx-icon-search,
.db-grid .dx-treelist-search-panel .dx-icon-search {
        display: none;
    }

    /* The placeholder's half of the same gutter, as for the page toolbar's box above.

       ANCHORED ON THE CONTROL, NOT ON AN ANCESTOR. DevExtreme renders the search panel into
       .dx-datagrid-header-panel, never into .dx-datagrid-headers, so its own
       `.dx-datagrid-headers .dx-searchbox .dx-placeholder::before` never matches and the base
       `.dx-searchbox .dx-placeholder::before` keeps its 34px. .dx-searchbox sits on the search panel
       itself, so pairing it with the search-panel class reaches (0,4,1) without betting on where
       DevExtreme puts the element.

       Any rule that hides a magnifier owes both gutters it reserved - the input's and the
       placeholder's. */
    .db-grid .dx-datagrid-search-panel.dx-searchbox .dx-placeholder::before,
.db-grid .dx-treelist-search-panel.dx-searchbox .dx-placeholder::before {
        padding-inline-start: var(--dz-search-input-padding-x);
    }

/* A grid toolbar that renders controls needs air beneath it; an empty one must not add any.
 *
 * `.db-grid .dx-datagrid-header-panel` zeroes DevExtreme's padding (02-grid-shell.css), which is
 * right for the worklists: Subscribers and Endpoints lift search, export and the layout gear into
 * the page's title row, leaving a header panel with only the group panel. The embedded archetype
 * keeps its controls in the grid's own toolbar, where that zero butts the search box onto the
 * column headers (MEASURED at 0px on the Groups detail page).
 *
 * :has() rather than an archetype class, because the condition is "this toolbar renders a search
 * box", which a view answers per grid. Without :has() the gap is simply absent. */
.db-grid .dx-datagrid-header-panel:has(.dx-datagrid-search-panel),
.db-grid .dx-treelist-header-panel:has(.dx-treelist-search-panel) {
    padding-bottom: var(--dz-space-1);
}

/* A STRIP WHOSE EVERY ITEM IS HIDDEN TAKES NO HEIGHT AT ALL.
 *
 * DevExtreme renders .dx-datagrid-header-panel whenever the grid has any toolbar item, even when
 * every item is hidden. A grid that relocates search and export to the page is left with two: the
 * shared selection block (`hidden` until rows are ticked) and Clear filter (.dx-state-invisible
 * until a filter is applied). MEASURED on the Account Dashboard's Transactions grid: 46px of empty
 * white above the column headers.
 *
 * The two are hidden by DIFFERENT mechanisms - an attribute on the item's content, a class on the
 * item - and both are live, so the rule tests the content rather than a count and the strip comes
 * back on its own. A strip holding a group panel, a search box or anything else a view put there
 * never matches.
 *
 * ON A PHONE the selection block leaves its toolbar item for a row after the toolbar
 * (.db-selection-actions-row, 19-grid-selection.css), so the block no longer counts as item content.
 * A shown row is the same "something to act on" and keeps the panel, which is the row's parent. The
 * row exists only on a phone, so this changes nothing on a desktop.
 *
 * Without :has() the strip keeps its height. */
.db-grid .dx-datagrid-header-panel:not(:has(.dx-toolbar-item:not(.dx-state-invisible) .dx-item-content > :not([hidden]), .db-selection-actions-row:not([hidden]))),
.db-grid .dx-treelist-header-panel:not(:has(.dx-toolbar-item:not(.dx-state-invisible) .dx-item-content > :not([hidden]), .db-selection-actions-row:not([hidden]))) {
    display: none;
}

/* THE FILTER ROW takes the package's face, ink and box.
 *
 * The box is the Billing Center mock's filter-row component: a white 32px box with a 1px
 * --border-default edge, a 6px radius, 8px of side padding, a 13px value and a small glyph at its
 * left.
 *
 * WHAT WE DO NOT TAKE FROM THE MOCK: its magnifier. The mock's is one fixed glyph on every column;
 * DevExtreme's is the filter-OPERATION chooser, and its glyph changes with the operation in force
 * (contains, equals, starts with, between...). A fixed magnifier would paint over live state, so the
 * glyph stays DevExtreme's and only its size and colour come from the package.
 *
 * The box goes on .dx-editor-with-menu, the wrapper, not on .dx-texteditor inside it: the menu is
 * absolutely positioned at the wrapper's left edge, so a border on the editor would leave the glyph
 * outside its box. */
/* The row. Its own tint and its own top rule, so it reads as a band under the column names rather
 * than as more of the header.
 *
 * border-TOP on the filter cells rather than border-bottom on the header cells, because those carry
 * that transparent !important. The row's bottom edge is the header container's own rule. */
/* !important: background and border outrank this sheet's own !important header paint and transparent cell borders; padding beats Site.css `.themed-datagrid .dx-datagrid-headers .dx-datagrid-filter-row > td.dx-editor-cell` at (0,5,0) against this (0,4,0). */
.db-grid .dx-datagrid-headers .dx-datagrid-filter-row > td,
.db-grid .dx-treelist-headers .dx-treelist-filter-row > td {
    padding: var(--dz-grid-filter-cell-padding-y) var(--dz-grid-filter-cell-padding-x) !important;
    background-color: var(--dz-grid-filter-row-background) !important;
    border-top: 1px solid var(--dz-grid-header-rule-color) !important;
    vertical-align: middle;
}

/* A command column takes no box. The mock leaves its checkbox, actions and icon cells empty on this
   row, and DevExtreme renders no editor in a command cell - so this only undoes the side padding,
   which on a 20px indent cell is wider than the cell. */
/* !important: outranks the filter-row cell padding above, which is itself !important. */
.db-grid .dx-datagrid-headers .dx-datagrid-filter-row > td[class*="dx-command"],
.db-grid .dx-treelist-headers .dx-treelist-filter-row > td[class*="dx-command"] {
    padding-right: 0 !important;
    padding-left: 0 !important;
}

.db-grid .dx-datagrid-filter-row .dx-editor-cell > .dx-editor-with-menu,
.db-grid .dx-treelist-filter-row .dx-editor-cell > .dx-editor-with-menu,
.db-grid .dx-datagrid-filter-row .dx-editor-cell > .dx-texteditor,
.db-grid .dx-treelist-filter-row .dx-editor-cell > .dx-texteditor {
    box-sizing: border-box;
    height: var(--dz-grid-filter-editor-height);
    background: var(--dz-card);
    border: 1px solid var(--dz-border);
    border-radius: var(--dz-control-radius);
    overflow: hidden;
}

/* The editor fills its box, which takes the whole CHAIN.
 *
 * DevExtreme nests .dx-editor-container, .dx-texteditor-container and .dx-texteditor-input-container
 * between the widget and its input, and a percentage height resolves against the nearest ancestor
 * that HAS one. Skipping any link puts the stack back at content height (MEASURED: 20px at the top
 * of a 32px box).
 *
 * The buttons container is in the chain because the centring rule below stops flex children
 * stretching: without an explicit height the date filter's .dx-dropdowneditor-button collapses to
 * 34x4px (a grey sliver where the calendar icon should be). With it, 34x30 with a 32x28 icon. */
.db-grid .dx-datagrid-filter-row .dx-editor-cell .dx-editor-container,
.db-grid .dx-treelist-filter-row .dx-editor-cell .dx-editor-container,
.db-grid .dx-datagrid-filter-row .dx-editor-cell .dx-texteditor-container,
.db-grid .dx-treelist-filter-row .dx-editor-cell .dx-texteditor-container,
.db-grid .dx-datagrid-filter-row .dx-editor-cell .dx-texteditor-input-container,
.db-grid .dx-treelist-filter-row .dx-editor-cell .dx-texteditor-input-container,
.db-grid .dx-datagrid-filter-row .dx-editor-cell .dx-texteditor-buttons-container,
.db-grid .dx-treelist-filter-row .dx-editor-cell .dx-texteditor-buttons-container {
    height: 100%;
}

/* Centred, not on .dx-texteditor-input-container's baseline (DevExtreme's value), which is not the
   same once the container is taller than the text. */
.db-grid .dx-datagrid-filter-row .dx-editor-cell .dx-texteditor-container,
.db-grid .dx-treelist-filter-row .dx-editor-cell .dx-texteditor-container,
.db-grid .dx-datagrid-filter-row .dx-editor-cell .dx-texteditor-input-container,
.db-grid .dx-treelist-filter-row .dx-editor-cell .dx-texteditor-input-container {
    align-items: center;
}

/* The inner boxes paint NOTHING. The white box is .dx-editor-with-menu above; anything painted
 * inside draws a second, shorter rectangle that reads as a line through the control.
 *
 * Site.css `.darkened-headers .dx-datagrid-headers .dx-datagrid-table
 * .dx-row.dx-datagrid-filter-row input` and the same chain ending `.dx-texteditor-container` paint
 * Sunken at (0,5,1) and (0,6,0), against this rule's (0,4,1). It goes with the same port step as the
 * cell padding above. background-image as well as background-color, because those Site.css rules
 * set the `background` shorthand. */
/* !important: beats those Site.css rules at (0,5,1) and (0,6,0) against this (0,4,1). */
.db-grid .dx-datagrid-filter-row .dx-editor-cell .dx-texteditor,
.db-grid .dx-treelist-filter-row .dx-editor-cell .dx-texteditor,
.db-grid .dx-datagrid-filter-row .dx-editor-cell .dx-texteditor-container,
.db-grid .dx-treelist-filter-row .dx-editor-cell .dx-texteditor-container,
.db-grid .dx-datagrid-filter-row .dx-editor-cell input.dx-texteditor-input,
.db-grid .dx-treelist-filter-row .dx-editor-cell input.dx-texteditor-input {
    background-color: transparent !important;
    background-image: none !important;
}

.db-grid .dx-datagrid-filter-row .dx-editor-cell .dx-texteditor,
.db-grid .dx-treelist-filter-row .dx-editor-cell .dx-texteditor {
    height: 100%;
    min-height: 0;
    background: transparent;
    border: 0;
    border-radius: 0;
}

.db-grid .dx-datagrid-filter-row .dx-editor-cell .dx-texteditor-input,
.db-grid .dx-treelist-filter-row .dx-editor-cell .dx-texteditor-input {
    height: 100%;
    min-height: 0;
    padding-top: 0;
    padding-right: 8px;
    padding-bottom: 0;
    padding-left: var(--dz-grid-filter-operation-width);
    font-size: var(--dz-grid-filter-editor-font-size);
    line-height: 1.2;
}

/* The operation glyph: Meta, and sized to the mock's 13px. Its 30px hit target stays - it opens a
   real menu - so only the glyph inside it shrinks. */
.db-grid .dx-datagrid-filter-row .dx-editor-cell .dx-menu .dx-icon,
.db-grid .dx-treelist-filter-row .dx-editor-cell .dx-menu .dx-icon {
    color: var(--dz-meta);
    font-size: var(--dz-grid-filter-editor-font-size);
}

/* A date filter is a drop-down editor, so its calendar button sits at the RIGHT edge. Same box,
   and the input's right padding gives way to it instead of to the 8px a text column keeps. */
.db-grid .dx-datagrid-filter-row .dx-editor-cell .dx-dropdowneditor-input-wrapper .dx-texteditor-input,
.db-grid .dx-treelist-filter-row .dx-editor-cell .dx-dropdowneditor-input-wrapper .dx-texteditor-input {
    padding-right: 0;
}

.db-grid .dx-datagrid-filter-row .dx-editor-cell .dx-dropdowneditor-icon,
.db-grid .dx-treelist-filter-row .dx-editor-cell .dx-dropdowneditor-icon {
    color: var(--dz-meta);
    font-size: var(--dz-grid-filter-editor-font-size);
}

/* A HOVER TIP: the bubble a small control shows when there is no room to label it.
 *
 * ONE element, appended to <body> by datablaze.ui.tip.js and positioned in viewport coordinates.
 * Authored on the control as `data-db-tip="Recurring Invoice"`; that attribute is the whole API, and
 * nothing here matches it - the styles below dress the bubble, and the module does the rest.
 *
 * Not the browser's `title` tooltip: it takes about a second to appear, renders in the OS font at
 * the OS size, and cannot be styled - so on a row of 26px glyphs, where the tip IS the label, it
 * reads as a fallback rather than as part of the page.
 *
 * KEEP `title` TOO on anything a keyboard or a screen reader has to reach: a pointer tip is
 * mouse-only by construction. The module takes `title` off the control while its own bubble is up so
 * the two do not both fire, and puts it back on the way out. Where a call site has a real accessible
 * name elsewhere - a badge whose mark is announced, an <img alt> - the tip is decoration and `title`
 * can go.
 *
 * ***** Why this is not two pseudo-elements on the control *****
 *
 * Inside a DevExtreme grid that cannot work. The <td> clips (overflow: hidden, for the
 * ellipsis) and lifting that is not enough: the fixed-column overlay, .dx-datagrid-content-fixed, is
 * a second absolutely-positioned copy of the table at the same z-index as the scroller and LATER in
 * tree order, so it paints over anything the main table draws - and a bubble inside the main table
 * cannot out-z-index it, because its nearest stacking context is the layer that loses. The full
 * measurement is in the header of datablaze.ui.tip.js. A body-level element has no clipping ancestor
 * and no borrowed stacking context, so the same tip works in a grid cell, a toolbar and a form.
 *
 * WHITE, and this is a DEPARTURE FROM THE SAMPLE - see the tokens for the note and for where a light
 * bubble's border and shadow come from. */
.db-tip {
    position: fixed;
    top: 0;
    left: 0;
    z-index: var(--dz-tip-layer);
    box-sizing: border-box;
    max-width: 320px;
    padding: var(--dz-tip-padding-y) var(--dz-tip-padding-x);
    color: var(--dz-tip-color);
    background: var(--dz-tip-background);
    border: 1px solid var(--dz-border);
    border-radius: var(--dz-radius-sm);
    box-shadow: var(--dz-tip-shadow);
    font-family: var(--dz-font-family);
    font-size: var(--dz-tip-font-size);
    font-weight: 400;
    line-height: 1.2;
    /* The pointer must never reach it. A bubble that sits between the pointer and the control it
       labels takes the mouseout, which flickers it on and off; and one over a neighbouring row eats
       that row's clicks. Belt and braces with the module, which anchors clear of the control. */
    pointer-events: none;
    white-space: nowrap;
}

/* The arrow. --db-tip-arrow-x is written by the module and is the CONTROL's centre measured from the
   bubble's left edge, not the bubble's own centre: a bubble clamped away from a viewport edge still
   has to point at the thing it labels. */
.db-tip::after {
    content: "";
    position: absolute;
    top: 100%;
    left: var(--db-tip-arrow-x, 50%);
    /* The bubble's own fill, with no hairline of its own - the one thing a light tip gives up, since
       an outlined triangle wants two pseudo-elements and this is the only one. At 5px the missing
       1px does not read; the shadow is what separates the tip from what is under it. */
    border: 5px solid transparent;
    border-top-color: var(--dz-tip-background);
    transform: translateX(-50%);
}

.db-tip.db-tip--below::after {
    top: auto;
    bottom: 100%;
    border-top-color: transparent;
    border-bottom-color: var(--dz-tip-background);
}

/* A square control that carries a glyph and nothing else: export, settings.
 *
 * GEOMETRY AND GLYPH TONE only - box, radius, centring, and the glyph's colour. The glyph reads
 * --dz-icon-control-glyph-color, a shape token, because a control with no label reads one step
 * lighter than a labelled one. The surface (ground, border, hover, focus) reads the GENERIC
 * --dz-button-* properties, so it comes from whichever role class rides alongside:
 *   - `db-icon-button db-button--chrome` - a table-level control (export, column chooser, the
 *     layout/density gear): neutral, insulated from a themed Action.
 *   - `db-icon-button db-button--action` - a record-level icon action.
 * A caller that adds neither gets an unstyled square - deliberately, the same "plainly unfinished"
 * signal a bare `.db-button` gives. Every caller must bring a role. */
.db-icon-button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    box-sizing: border-box;
    width: var(--dz-interactive-target-size);
    height: var(--dz-interactive-target-size);
    padding: 0;
    color: var(--dz-icon-control-glyph-color);
    background: var(--dz-button-background);
    border: 1px solid var(--dz-button-border-color);
    border-radius: var(--dz-control-radius);
    cursor: pointer;
    font-size: 16px;
    line-height: 1;
}

    .db-icon-button:hover {
        background: var(--dz-button-hover-background);
        border-color: var(--dz-button-hover-border-color);
    }

    .db-icon-button:active {
        background: var(--dz-button-active-background);
    }

    .db-icon-button:focus-visible {
        outline: none;
        box-shadow: 0 0 0 3px var(--dz-button-focus-ring);
    }

/* The export control is a DevExtreme drop-down button, because the choice between exporting the
   selection and exporting everything is a real one and the icon has to open it. Everything below
   strips the widget back to the same square the plain .db-icon-button draws: DevExtreme renders a
   button with padding, a caret slot and its own min-width, none of which fit a 40px square.

   GEOMETRY ONLY, same as .db-icon-button above: colour reads the generic --dz-button-*
   properties, which a role class on the .db-icon-dropdown element supplies (`db-button--chrome`
   for the grid's layout/density gear and a page toolbar's Export). */
.db-icon-dropdown.dx-dropdownbutton .dx-button {
    width: var(--dz-interactive-target-size);
    height: var(--dz-interactive-target-size);
    min-width: 0;
    padding: 0;
    color: var(--dz-icon-control-glyph-color);
    background: var(--dz-button-background);
    border: 1px solid var(--dz-button-border-color);
    border-radius: var(--dz-control-radius);
    box-shadow: none;
}

    .db-icon-dropdown.dx-dropdownbutton .dx-button .dx-button-content {
        padding: 0;
    }

    .db-icon-dropdown.dx-dropdownbutton .dx-button:hover {
        background: var(--dz-button-hover-background);
        border-color: var(--dz-button-hover-border-color);
    }

/* THE ONE-CLICK EXPORT: a plain chrome dxButton carrying only a glyph - .DbExport() in Razor, and a
 * grid's own exportButton slot (SharedGridExtensions.Toolbar.cs, grid.js exportSlot). No menu to
 * open, so no dropdown, but the same control a viewer reads beside it: the glyph one step lighter
 * than a labelled button's ink, the same as .db-icon-dropdown above (without this, rgb(49, 48, 47)
 * beside the page toolbar export's rgb(102, 98, 97)). */
.dx-button.db-button--chrome:not(.dx-button-has-text) .dx-icon,
.dx-toolbar-text-auto-hide .dx-button.db-button--chrome .dx-icon {
    color: var(--dz-icon-control-glyph-color);
}

/* ...and every other glyph-only control in a grid's strip, whatever its role: Reset Columns, the
 * column chooser, a batch grid's Add / Save / Discard. Beside the export they drew the action role's
 * ink, rgb(49, 48, 47) against rgb(102, 98, 97), and read as a second set (owner rule 2026-10-07: one
 * glyph set per strip). A control showing its label keeps the role's ink, and a filled role or a
 * pressed toggle keeps the colour its own ground was picked against. */
.dx-datagrid-header-panel .dx-button:not(.dx-button-has-text):not(:is(.db-button--primary, .db-button--submit, .db-button--danger, .db-button--positive, .db-button--next, .db-button--answer, .db-button--pressed, .dx-button-default, .dx-button-danger)) .dx-icon,
.dx-treelist-header-panel .dx-button:not(.dx-button-has-text):not(:is(.db-button--primary, .db-button--submit, .db-button--danger, .db-button--positive, .db-button--next, .db-button--answer, .db-button--pressed, .dx-button-default, .dx-button-danger)) .dx-icon,
.dx-datagrid-header-panel .dx-toolbar-text-auto-hide .dx-button:not(:is(.db-button--primary, .db-button--submit, .db-button--danger, .db-button--positive, .db-button--next, .db-button--answer, .db-button--pressed, .dx-button-default, .dx-button-danger)) .dx-icon,
.dx-treelist-header-panel .dx-toolbar-text-auto-hide .dx-button:not(:is(.db-button--primary, .db-button--submit, .db-button--danger, .db-button--positive, .db-button--next, .db-button--answer, .db-button--pressed, .dx-button-default, .dx-button-danger)) .dx-icon {
    color: var(--dz-icon-control-glyph-color);
}

/* ...and square in a grid's toolbar. DevExtreme keeps the slot's label for the overflow menu and
   hides it on the strip (showText: inMenu), but the button still carries dx-button-has-text, so
   the role's label padding would widen it to 56x40 beside a 40px square. */
.dx-toolbar-text-auto-hide .dx-button.db-button--chrome {
    min-width: var(--dz-interactive-target-size);
    width: var(--dz-interactive-target-size);
}

    .dx-toolbar-text-auto-hide .dx-button.db-button--chrome > .dx-button-content {
        padding-right: 0;
        padding-left: 0;
    }

/* The named-layout picker mounted as a square icon rather than as a 180px named box.
 *
 * A dxDropDownBox always renders a text input, so the input is what has to be hidden - not the
 * widget. Collapsing it to zero width rather than display:none keeps it focusable, which is the
 * only thing that still opens the panel from the keyboard. */
/* !important: beats the inline width DevExtreme writes when a caller passes a `width` option. */
.db-icon-dropdown.dx-dropdowneditor {
    position: relative;
    box-sizing: border-box;
    width: var(--dz-interactive-target-size) !important;
    min-width: 0;
    height: var(--dz-interactive-target-size);
    background: var(--dz-button-background);
    border: 1px solid var(--dz-button-border-color);
    border-radius: var(--dz-control-radius);
}

    .db-icon-dropdown.dx-dropdowneditor:hover {
        background: var(--dz-button-hover-background);
        border-color: var(--dz-button-hover-border-color);
    }

    /* flex-basis, not just width. DevExtreme makes the input container a flex child that grows, so
       `width: 0` alone leaves it filling the row (36 of the 40 pixels, beside a 2px button).
       Zeroing the basis and the grow is what actually collapses it. */
    .db-icon-dropdown.dx-dropdowneditor .dx-texteditor-input-container {
        flex: 0 0 0;
        width: 0;
        overflow: hidden;
    }

    .db-icon-dropdown.dx-dropdowneditor .dx-texteditor-buttons-container {
        flex: 1 1 auto;
        width: auto;
        justify-content: center;
    }

    .db-icon-dropdown.dx-dropdowneditor .dx-dropdowneditor-button,
    .db-icon-dropdown.dx-dropdowneditor .dx-dropdowneditor-icon {
        width: 100%;
        min-width: 0;
        background: none;
        border: 0;
    }

    /* The gear below is a block, so it sat at the box's top-left: 3px off centre in the 20px box of
       the compact size (MEASURED, Top Data Users). The box centres it instead. */
    .db-icon-dropdown.dx-dropdowneditor .dx-dropdowneditor-icon {
        display: flex;
        align-items: center;
        justify-content: center;
    }

    /* The widget's own caret is replaced rather than hidden: leaving it beside a gear reads as two
       controls, and removing it without a substitute leaves nothing to click.

       The gear is the design's own 2px-stroke artwork, not Font Awesome's solid \f013: beside a
       stroked download glyph the solid one reads as the heavier of two controls that are meant to
       match. Sized 18px to the mockup, on a 40px button. */
    .db-icon-dropdown.dx-dropdowneditor .dx-dropdowneditor-icon::before {
        content: "";
        display: block;
        width: 18px;
        height: 18px;
        background-color: var(--dz-icon-control-glyph-color);
        -webkit-mask: var(--db-icon-source) center / contain no-repeat;
        mask: var(--db-icon-source) center / contain no-repeat;
        --db-icon-source: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><circle cx='12' cy='12' r='3'/><path d='M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.6h.09A1.65 1.65 0 0 0 10 3.09V3a2 2 0 0 1 4 0v.09A1.65 1.65 0 0 0 15 4.6a1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9v.09a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z'/></svg>");
    }

/* COMPACT, for an icon dropdown inside a fixed-height strip - Home/Index's saved-layouts gear in the
 * Top Data Users header, beside the compact Action icon buttons there. The same square at the compact
 * control size, so a 41px dashboard strip does not grow to hold a 40px box. Two modifiers on top of
 * the base pair, so the !important here beats the base's own.
 *
 * The gear keeps its 18px: the glyphs beside it are 18px in the same 24px box, and a 14px gear read
 * as a different, smaller control in the same row. */
.db-icon-dropdown.db-button--compact.dx-dropdowneditor {
    width: var(--dz-control-compact-height) !important;
    height: var(--dz-control-compact-height);
    min-height: 0;
}

/* ================================================================================================
   The detail grid: a record's fields laid out by a DevExtreme ResponsiveBox, one field per cell.
   ================================================================================================ */

/* ADO #143, fix plan 3.5: moved here from Subscriber/Detail's <style> block, where these rules and
 * their reasoning were written. The box is `.db-detail-grid`; each field cell is
 * `.db-detail-grid__cell`, holding a `.db-field__label` and its value. The cell's own padding and
 * flex layout are the host page's; this owns the grid's rules and its ground.
 *
 * THE GROUND IS CARD, not a grey of its own: the strip above is Sunken, so the strip reads as a
 * header and the hairlines inside the grid stay visible. (The page's #f5f5f5 sat a hair off the
 * Sunken strip above it, and on it a hairline was very nearly invisible.) */
.db-detail-grid {
    background-color: var(--dz-card);
}

/* ROW RULES live on the ResponsiveBox ROW (the horizontal flex that holds a row's cells), not on each
 * cell. A per-cell border doubled with the wrapper's own bottom border under the last row, and was
 * absent under empty trailing cells, so the bottom edge read thick-then-thin. Bordering the row gives
 * one full-width line per row; the wrapper draws no bottom border of its own, so the last row's line
 * is the bottom edge.
 *
 * HAIRLINE, the guide's "dividers, table rules" tone. The #c0c0c0 this used to be is off the palette
 * and darker than Border, so a 30-cell grid drawn in it read as a cage. */
.db-detail-grid .dx-box-item-content.dx-box-flex {
    border-bottom: 1px solid var(--dz-hairline);
}

/* ...except on a row with no cell in it, which draws no rule at all. A ResponsiveBox's .Rows() is a
 * fixed list declared before any item is configured, so a host declares it generously and the surplus
 * rows collapse to nothing; their only contribution was a second hairline 1px under the last real
 * row's, reading as a 2px rule. The cell class is the predicate because every template, blank
 * fillers included, renders one (a padded row is a real row and keeps its rule) - keying on
 * DevExtreme's own nesting would cost every row its line the next time that changed. */
.db-detail-grid .dx-box-item-content.dx-box-flex:not(:has(.db-detail-grid__cell)) {
    border-bottom: none;
}

/* COLUMN RULES: the design draws each field as a bordered cell, and the pair of rules is what makes
 * thirty label/value pairs read as a table. The selector is the cell one level inside the row: the
 * ResponsiveBox nests .dx-item.dx-box-item twice (row, then cell), and only the row's own
 * .dx-box-item-content between them tells a cell from a row - a bare `.dx-box-item` would rule the
 * whole panel's height. The rightmost cell drops its line, where it would double with the wrapper's
 * edge; an empty trailing cell keeps the line to its left, which belongs to the cell before it. */
.db-detail-grid .dx-box-item-content > .dx-item.dx-box-item {
    border-right: 1px solid var(--dz-hairline);
}

.db-detail-grid .dx-box-item-content > .dx-item.dx-box-item:last-child {
    border-right: none;
}

/* The cell: the predicate above, and the floor every cell stands on (its compact value is in
 * 20-density.css's band), so a blank cell is as tall as a filled one. */
.db-detail-grid__cell {
    min-height: var(--dz-field-cell-min-height);
}

/* ================================================================================================
   Detail-grid segments built from data: the subscriber's user-defined fields.
   ================================================================================================ */

/* More cells on a detail grid, for values that arrive after the page is rendered.
 *
 * The grid above these is a DevExtreme ResponsiveBox whose layout Razor computes while rendering -
 * a per-screen row and column count, advanced item by item. That cannot absorb data fetched later,
 * so these are a plain CSS grid instead, sized to the SAME column count and holding the same cell
 * markup. The result is one continuous grid; the seam is invisible because the last ResponsiveBox
 * row already draws its own bottom rule, which becomes the line above the first segment. */
.db-detail-segments {
    display: grid;
    /* md's four, as the default. The attribute below is written from the ResponsiveBox's own screen
       class, so this value only ever shows for the instant before the first sync. */
    grid-template-columns: repeat(4, minmax(0, 1fr));
}

.db-detail-segments[data-db-segments-screen="ss"] {
    grid-template-columns: repeat(1, minmax(0, 1fr));
}

.db-detail-segments[data-db-segments-screen="xs"] {
    grid-template-columns: repeat(2, minmax(0, 1fr));
}

.db-detail-segments[data-db-segments-screen="sm"] {
    grid-template-columns: repeat(3, minmax(0, 1fr));
}

.db-detail-segments[data-db-segments-screen="md"] {
    grid-template-columns: repeat(4, minmax(0, 1fr));
}

.db-detail-segments[data-db-segments-screen="lg"] {
    grid-template-columns: repeat(5, minmax(0, 1fr));
}

/* The hairlines are SHADOWS rather than borders, which is what lets a data-length grid line up with
   a declared one.
 *
 * A border would have to be removed from whichever cell ends a row, which changes with the column
 * count - an :nth-child rule per breakpoint. A shadow takes no space and is not clipped by the grid,
 * so the last cell in every row paints its right-hand line 1px outside the container, exactly on
 * the wrapper's own border. A partly-filled last row leaves no coloured blocks either, because
 * empty tracks are not cells and paint nothing.
 *
 * minmax(0, 1fr) above rather than 1fr is what lets a long value truncate: a bare 1fr track has an
 * auto minimum, so one unbroken ICCID-length string would push the column wider than its share. */
.db-detail-segments__cell {
    min-width: 0;
    /* The SAME floor the ResponsiveBox cells above stand on (.data-container sets this token; its
       compact value lives in 20-density.css's band). Without it a blank or add cell collapses to its
       content and the segment row stands shorter than the rows above. A cell holding a
       .data-container gets the min-height twice over, which is harmless. */
    min-height: var(--dz-field-cell-min-height);
    box-shadow:
        1px 0 0 0 var(--dz-hairline),
        0 1px 0 0 var(--dz-hairline);
}

/* A PLAIN SEGMENTS GRID: read-only fields in a card body (the legacy endpoint page's Summary), set
 * apart by spacing alone. Opt-in, so the user-defined-field grid above and Subscriber Detail keep
 * their hairlines.
 *
 * No hairlines: the cell's two shadows (the rules between cells, and the one on the grid's right
 * edge that read as a stray divider beside the signal icon) are dropped, and so is the cell's
 * 68px floor, which is a data cell's height and only added air to a bare .db-field.
 *
 * The air is the field list's own: rows are --dz-field-list-cell-padding apart (12px, 6px at
 * compact, the step Group View's Summary stands on) and columns one lattice step (--dz-space-2).
 * It is a gap and not cell padding, so the card body's own padding is the only inset.
 *
 * The columns answer the grid's own width and not the screen's, because the grid sits beside a
 * 190px picture until the page stacks: as many 150px-or-wider tracks as fit, never more than three.
 * 100% less two gaps, thirded, is the width of one of three columns, so the max() is 150px until the
 * grid is wide enough for three such thirds. 150px holds a 20-digit ICCID (155px measured at 14px)
 * once the tracks share out the spare width; the side-by-side grid is 496px, three 160px tracks with
 * one-step gaps, and a 16px gap would push it to two columns and clip the last row. A SIM number or a date therefore never breaks
 * mid-value in a track narrower than itself. The row gap is one lattice step (--dz-space-1) so
 * three rows of fields stand well inside a 300px card body (see .db-card__body--fit-300). */
.db-detail-segments--plain {
    --db-detail-segments-gap: var(--dz-space-1);
    grid-template-columns: repeat(auto-fill, minmax(max(150px, calc((100% - 2 * var(--db-detail-segments-gap)) / 3)), 1fr));
    gap: var(--dz-space-1) var(--db-detail-segments-gap);
}

.db-detail-segments--plain > .db-detail-segments__cell {
    min-height: 0;
    box-shadow: none;
}

/* --wide is a cell that takes the whole row (a description, a list of labels), where the grid's
 * columns would wrap it into a narrow track beside a short value. */
.db-detail-segments__cell--wide {
    grid-column: 1 / -1;
}

/* A CARD BODY OF FIXED HEIGHT that lets go on a phone. The height itself is the view's inline layout
 * (a card stood beside a 300px tile view); this is the opt-in release, because below this width the
 * neighbour stacks underneath and the fields no longer fit in 300px. */
@media (max-width: 640px) {
    .db-card__body--fit-300 {
        height: auto !important;
    }
}

/* The cell that adds another custom field; add, change and remove all live in the dialog it opens.
 * It is a cell, not a button above the grid, because the design ends the row of custom fields with
 * the means of adding one. */
.db-detail-segments__cell--add {
    display: flex;
}

.db-detail-segments__add {
    display: flex;
    align-items: center;
    gap: 8px;
    width: 100%;
    padding: var(--dz-detail-segments-add-padding);
    color: var(--dz-link);
    font-size: 14px;
    font-weight: 600;
    line-height: 1.4;
    text-decoration: none;
    cursor: pointer;
}

    .db-detail-segments__add .db-icon {
        --db-icon-size: 15px;
    }

    .db-detail-segments__add:hover,
    .db-detail-segments__add:focus-visible {
        background-color: var(--dz-sunken);
        color: var(--dz-link-hover);
        text-decoration: none;
    }

/* ================================================================================================
   A detail card: one bordered, rounded surface holding a header strip and a field grid.
   ================================================================================================ */

/* The header strip. A flex row, so the title, its pencil, the labels and the cycle sit on one line
   and wrap together on a narrow screen, in the design's order.
 *
 * min-height is what makes the card headers agree with each other. Without it a strip is as tall as
 * whatever control is in it (MEASURED: 53, 53, 47 and 63px across four cards on one page). It also
 * decouples the strip from its contents, so the 20px pencil does not shrink the header.
 *
 * 49px: 8px padding, a 32px control, 8px padding, plus the 1px border-bottom, which counts because
 * everything here is border-box. 48px leaves control-bearing strips 1px taller than title-only ones.
 * The vertical air is 8px rather than the 12px side inset because a title-only strip holds 17px of
 * text: at 12px it stood 57px and read as oversized beside the dashboard's 41px strips.
 *
 * The strip paints its OWN Sunken ground and ink text rather than relying on Site.css's legacy
 * .rounded-header, so a page adopting the component alone gets the full strip. Sunken-with-ink is
 * the grid column headers' pair, so a card header and a grid header read as the same rank.
 *
 * .db-card__header is the same strip under a second name; one rule serves both. */
.db-card-header,
.db-card__header {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--dz-card-header-gap);
    min-height: var(--dz-card-header-min-height);
    padding: var(--dz-card-header-padding);
    /* WL-1 (ClickUp 86894yg24): repointed from var(--dz-ink)/var(--dz-sunken) to
       --dz-header-ink/--dz-header-background (00-tokens.css) -- same resolved value today, and
       it is what lets the whitelabel palette's headerBackground/headerInk keys move this strip in
       step with grid headers and .rounded-header rather than the whole neutral spine. */
    color: var(--dz-header-ink);
    background-color: var(--dz-header-background);
    border-bottom: 1px solid var(--dz-grid-header-rule-color);
    font-family: var(--dz-font-family);
    font-size: 14px;
    font-weight: 600;
    line-height: 1.4;
}

/* A DASHBOARD-DENSITY STRIP, on a card carrying .db-card--dense: the floor and air the dashboards'
 * own page rules gave their legacy .rounded-header strips (41px, 37px compact), with a 4px row gap
 * so a wrapped control group does not touch the line above it.
 *
 * The control height is redeclared for this strip's descendants because the 32px a card header
 * gives its controls plus 5px either side would push the strip past its own 41px floor. 31px does
 * not move with density: the compact band trims only the padding around it.
 *
 * The strip's own 1px bottom rule counts, because the box is border-box, so it comes out of the
 * bottom padding: 5 + 31 + 4 + 1 = 41, and compact 3 + 31 + 2 + 1 = 37. */
.db-card--dense > .db-card-header,
.db-card--dense > .db-card__header {
    --dz-card-header-control-height: var(--dz-dashboard-header-control-height);
    box-sizing: border-box;
    row-gap: 4px;
    min-height: var(--dz-dashboard-header-min-height);
    padding: var(--dz-dashboard-header-padding-y) var(--dz-dashboard-header-padding-x) calc(var(--dz-dashboard-header-padding-y) - 1px);
}

/* A GROUP OF CONTROLS IN A CARD HEADER: everything in the strip that is a control rather than a
 * label. Controls in a card header are 32px - taller and they set the strip's height, shorter and
 * they float in it - so the height is stated ONCE here, and DevExtreme's own wrappers are named
 * below because each sizes itself differently.
 *
 * TWO NAMES, ONE GROUP; THE ONLY DIFFERENCE IS WHICH END OF THE STRIP IT TAKES. `__actions` pulls
 * itself to the right with `margin-left: auto`; `__controls` takes whatever position the strip's
 * layout gives it. A strip needing both ends (the Account Dashboard's Transactions band: date range
 * left, search and export right) cannot use two `__actions`, which would split the free space.
 * Reach for `__actions` by default; `__controls` is for the strip with no title to hold the left.
 *
 * ORDER MATTERS IN THIS SELECTOR, for a reason outside the cascade: the contract harness finds a
 * rule by its selector text, first match wins, so leading with __controls would make a lookup of
 * `.db-card-header__actions` land on this shared rule instead of on its own one below. */
.db-card-header__actions,
.db-card-header__controls {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--dz-space-1);
}

.db-card-header__actions {
    margin-left: auto;
}

    .db-card-header__controls .dx-button,
    .db-card-header__controls .dx-buttongroup,
    .db-card-header__controls .dx-texteditor,
    .db-card-header__controls .dx-datebox,
    .db-card-header__actions .dx-button,
    .db-card-header__actions .dx-buttongroup,
    .db-card-header__actions .dx-texteditor,
    .db-card-header__actions .dx-datebox {
        box-sizing: border-box;
        min-height: var(--dz-card-header-control-height, 32px);
        height: var(--dz-card-header-control-height, 32px);
    }

    /* A LABELLED button is pinned again: the button role's .dx-button.db-button.dx-button-has-text
       (07-button-roles.css) out-ranks the rule above and restores its 40px min-height, which made a
       strip 65px. Same specificity as that rule and later in the bundle, so this one wins. */
    .db-card-header__controls .dx-button.dx-button-has-text,
    .db-card-header__actions .dx-button.dx-button-has-text {
        min-height: var(--dz-card-header-control-height, 32px);
    }

    /* The GROUP is sized above, and its options fill it. Sizing the options to 32px instead puts
       the group's 1px border outside them, and the group measures 34px. */
    .db-card-header__controls .dx-buttongroup .dx-buttongroup-wrapper,
    .db-card-header__controls .dx-buttongroup .dx-button,
    .db-card-header__actions .dx-buttongroup .dx-buttongroup-wrapper,
    .db-card-header__actions .dx-buttongroup .dx-button {
        min-height: 0;
        height: 100%;
    }

    /* AN ICON-ONLY DROPDOWN IS THE ONE CASE THAT `height: 100%` DOES NOT REACH.
     *
     * A dxDropDownButton is a dxButtonGroup wrapped in another widget:
     * .dx-dropdownbutton > .dx-buttongroup > .dx-buttongroup-wrapper > .dx-button. MEASURED on the
     * Account Dashboard's export control: outer box and group 32px with a definite height, wrapper
     * and button 20px, leaving twelve dead pixels under the glyph.
     *
     * A LENGTH rather than a percentage, because 100% resolves to content here despite every box in
     * the chain having a definite height - something outside this rule answers `height` on the
     * wrapper. Same token as the group above, so the control still follows the strip and density.
     *
     * (0,4,0), which beats both the 100% rule above and .db-icon-dropdown's own
     * --dz-interactive-target-size height, without depending on source order. */
    .db-card-header__controls .db-icon-dropdown.dx-dropdownbutton .dx-buttongroup-wrapper,
    .db-card-header__controls .db-icon-dropdown.dx-dropdownbutton .dx-button,
    .db-card-header__actions .db-icon-dropdown.dx-dropdownbutton .dx-buttongroup-wrapper,
    .db-card-header__actions .db-icon-dropdown.dx-dropdownbutton .dx-button {
        min-height: 0;
        height: var(--dz-card-header-control-height, 32px);
    }

    /* An icon-only control is a SQUARE. Width and height are set together; otherwise the role's
       min-height with a glyph-fitted width reads as stretched. */
    .db-card-header__controls .dx-button:not(.dx-button-has-text),
    .db-card-header__actions .dx-button:not(.dx-button-has-text) {
        width: var(--dz-card-header-control-height, 32px);
        min-width: var(--dz-card-header-control-height, 32px);
        padding: 0;
    }

    /* A checkbox is text beside a box, not a control with a chrome box of its own, so it takes the
       row's alignment and none of the sizing above. */
    .db-card-header__controls .dx-checkbox,
    .db-card-header__actions .dx-checkbox {
        min-height: 0;
        height: auto;
    }

        /* Not white: a white label measures 1.16:1 against the Sunken strip. WL-24 (ClickUp
           86894yg24), canary G19 (PM ruling): not --dz-body either, for the same reason the card
           header's __meta text moved off it (16-card-usage-udf.css) -- this checkbox label sits ON
           the header strip (.db-card-header/.db-card__header, reads --dz-header-background), and
           --dz-body is a fixed neutral that does not move with a tenant's headerBackground. Reads
           --dz-header-ink directly, matching the strip's own text and every other control label in
           it. Same resolved value today (--dz-header-ink's own default IS --dz-ink, close to --dz-body
           but not identical - MEASURED, untouched Sunken strip: 11.38:1, was 7.54:1 with --dz-body;
           both clear 4.5:1, a visible but minor darkening, disclosed not hidden). */
        .db-card-header__controls .dx-checkbox .dx-checkbox-text,
        .db-card-header__actions .dx-checkbox .dx-checkbox-text {
            color: var(--dz-header-ink);
        }

/* A date picker in a card header. The design draws a 32px field with a 6px radius, a Border outline
   and a white ground.

   THE GROUND IS PAINTED ONCE, ON THE EDITOR ROOT. The inner container keeps the radius (it clips the
   input to the rounded field) but no fill of its own: when it carried one too, any theme that
   repaints the root (the dark adapter's inset editors) left a second, different fill inside the
   border, and its 6px corners inside a 1px border showed the root's colour at each corner. */
.db-card-header__controls .dx-datebox.dx-texteditor,
.db-card-header__actions .dx-datebox.dx-texteditor {
    border-radius: var(--dz-control-radius);
    background-color: var(--dz-card);
}

.db-card-header__controls .dx-datebox .dx-texteditor-container,
.db-card-header__actions .dx-datebox .dx-texteditor-container {
    border-radius: var(--dz-control-radius);
}

.db-card-header__controls .dx-datebox .dx-texteditor-input,
.db-card-header__actions .dx-datebox .dx-texteditor-input {
    height: var(--dz-card-header-control-height, 32px);
    min-height: 0;
    padding: 0 10px;
    font-size: 13px;
    line-height: var(--dz-card-header-control-height, 32px);
}

/* THE CARRIER PALETTE, FOR AN ADOPTING HOST ONLY.
 *
 * Same reasoning as the two rules below: Site.css reaches Atlas and Olympus, and Atlas renders
 * carrier badges at runtime (datablaze.utils.js concatenates 'badge-' + the carrier a PLMN lookup
 * returns). Restating the hues there would repaint two applications that have not adopted it.
 *
 * Site.css keeps, and Atlas renders, Verizon #000000, T-Mobile #ff63b6, AT&T #35afe3. A host taking
 * this bundle gets the real brand hue the Subscribers mock draws: on a 22px square carrying two
 * letters, the hue IS the identification, and a black square or a washed-out tint reads as "some
 * colour" rather than as the carrier.
 *
 * Declared as token overrides rather than as repeated .badge-* rules so the gradients composed from
 * them - ATT+T-Mobile, ATT+Verizon, Verizon+T-Mobile - follow without being restated.
 *
 * WHICH OF THE TWO DECLARATIONS WINS IS DECIDED BY STYLESHEET ORDER, NOTHING ELSE. Both files
 * declare all three tokens at :root, so the last one the host loads is the one that renders:
 * Voyager loads Site.css and then this package, so these values win; m2mNet loads only this
 * package; Atlas and Olympus load only Site.css. That is why BundleConfig's order is commented as
 * deliberate - reordering it silently repaints every carrier badge in Voyager.
 *
 * DO NOT "CONSOLIDATE" THE TWO SETS. Deleting the Site.css declarations leaves Atlas and Olympus
 * with unresolved variables and unpainted badges, because Site.css is their only source for them
 * and neither loads this package. The duplication is the mechanism, not an oversight. */
:root {
    --dz-carrier-verizon-badge-background: #ee0000;
    --dz-carrier-tmobile-badge-background: #e20074;
    --dz-carrier-att-badge-background: #00a8e0;
}

/* AT&T is the one brand hue that cannot carry white. #00a8e0 measures 2.74:1 against white on 10px
 * text inside a 22px square - below the guide's 4.5:1 floor - and 4.82:1 against dark. The mock
 * draws white; contrast is a floor rather than a product-surface value the mockup gets to set, so
 * this is the one place the carrier square deviates from what is drawn.
 *
 * Gated with the hue it belongs to: Atlas keeps the legacy #35afe3 with white text, and this file
 * does not reach it. */
.badge-ATT,
.data-content.badge-ATT {
    color: var(--dz-carrier-badge-dark-text);
}

/* TWO RULES THAT BELONG IN THIS BUNDLE, NOT IN Site.css, SO THEY REACH AN ADOPTING HOST ONLY.
 *
 * Site.css is linked directly by Atlas and Olympus (both csprojs Content-Include it from
 * SharedMvc; neither has a local copy), and neither app registers the shared UI bundle. THIS file
 * ships only in that bundle, so a rule here reaches an adopting host and nobody else.
 *
 * Both rules are right for Voyager and wrong as cross-host defaults:
 *
 * 1. `body` font-family, so text the package does not explicitly name renders in Poppins rather
 *    than bootstrap's stack. Atlas and Olympus declare no --dz-* spine and never load the Poppins
 *    face, so in Site.css it would resolve the literal fallback (Segoe UI) there. Here, the spine is
 *    guaranteed present, so no fallback is needed.
 *
 * 2. `.rounded-header`. The legacy card-header strip, used by Voyager views and by two views in
 *    each of Atlas and Olympus. Voyager takes the Sunken-and-ink treatment; the other two keep the
 *    dark slate Site.css states.
 *
 * These win over Site.css on SOURCE ORDER at equal specificity: _LayoutHeadStyles renders
 * ~/Content/bundle (Site.css), then DevExtremeBundle, then SharedUiDelivery.RenderStyles() - which
 * returns empty for a host that did not register the bundle. Do not raise the specificity here to
 * "make sure"; the order is asserted by datablaze.ui.delivery.harness.js, and a heavier selector
 * would start winning over callers that legitimately override this strip. */
body {
    font-family: var(--dz-font-family);
}

/* Sunken ground, ink text and the 10px card radius - the same pair the grid's column headers take,
 * which is what makes a card header and a grid header on one page read as the same rank.
 *
 * AND IT CONTAINS ITS FLOATS. Every caller fills this strip with floated wrappers -
 * .header-button-wrapper left, .frequency-button-wrapper and .range-selector-wrapper right - and a
 * block box does not grow for a float, so anything taller than the strip's text hangs out of it -
 * and the grid below narrows by the float's width, because .dx-gridbase-container is a flex
 * container and therefore a block formatting context root, which may not overlap a float.
 *
 * flow-root and NOT overflow: hidden, which is the older way to the same containment: this bar sits
 * under dropdowns and tooltips that are allowed to leave it, and hidden would clip them. */
.rounded-header {
    display: flow-root;
    border-radius: var(--dz-grid-card-radius) var(--dz-grid-card-radius) 0 0;
    /* WL-1 (ClickUp 86894yg24): repointed from var(--dz-sunken)/var(--dz-ink) to
       --dz-header-background/--dz-header-ink (00-tokens.css) -- same resolved value today. See
       .db-card-header's own WL-1 comment (13-detail-segments-card-header.css); this is the same
       repoint on the legacy panel-header class. */
    background-color: var(--dz-header-background);
    color: var(--dz-header-ink);
}

    /* White was legible on the slate strip and is invisible on the Sunken one. */
    .rounded-header a.icon:not(:hover) {
        color: var(--dz-body);
    }

/* A card header that is ALSO a legacy .rounded-header is still a flex row.
 *
 * Both classes are one class, so they tie at (0,1,0) and source order decides - and .rounded-header
 * is declared after .db-card-header, so its `display: flow-root` would win on every card header in
 * the product, all of which carry both names. Outside a flex parent .db-card-header__labels (itself
 * `display: flex`) is block-level and takes the full width, and .db-card-header__actions'
 * `margin-left: auto` resolves to 0, so title, cycle and actions each take a line of their own.
 *
 * Flex costs nothing flow-root is there for: a flex container establishes a formatting context too,
 * and floats do not apply to flex items.
 *
 * Two classes rather than moving the block: .rounded-header's own comment forbids raising ITS
 * specificity, because it deliberately beats Site.css on source order at equal weight and a heavier
 * selector would start winning over callers that legitimately override the strip. This selector
 * raises the specificity of the OVERLAP only, so both of those properties survive. */
.db-card-header.rounded-header,
.db-card__header.rounded-header {
    display: flex;
}

/* A segmented control: one row of options where exactly one is chosen.
 *
 * Built on a dxButtonGroup because that is what the page already renders.
 *
 * THE CHOSEN OPTION IS FILLED WITH THE TENANT'S ACCENT AND THE OTHERS ARE WHITE (owner direction),
 * so a cobrand's toggles carry its brand. The fill is one step down the accent ramp, because white on
 * the accent itself measures 3.78:1 against a 4.5:1 floor; the values and their measurement are with
 * --dz-segmented-selected in 00-tokens.css.
 *
 * THE BORDER IS LOAD-BEARING: on a card body the white track and the ground behind it are the same
 * tone, and only the 1px hairline draws the control's edge. Treat the border as part of the control,
 * not as trim.
 *
 * Neither reading is welded in: the three --dz-segmented-* tokens are what this rule resolves, and a
 * tenant that wants the neutral reading points the selected pair at --dz-card and --dz-ink.
 *
 * DevExtreme's own contained styling paints the chosen option #d4d4d4, a grey that says "disabled"
 * more than "chosen". */
.db-segmented.dx-buttongroup {
    border: 1px solid var(--dz-segmented-border);
    border-radius: var(--dz-control-radius);
    overflow: hidden;
    background-color: var(--dz-segmented-track);
    /* THE CONTROL IS AN INLINE-BLOCK, SO IT SITS ON ITS PARENT'S TEXT BASELINE and reserves room
       under itself for the descenders of text that is not there (5px: a 31px control makes a 36px
       wrapper). Aligning to the middle takes the box back to the control's own height.

       On the COMPONENT rather than the wrappers, because the gap follows the control into any
       non-flex parent. A flex parent such as .db-card-header__actions has no gap. */
    vertical-align: middle;
}

    /* DevExtreme nests the options one level down, inside a wrapper that paints its own white pill
       with a 4px radius. Left alone that pill peeks out of the 6px outer radius at each corner. */
    /* EQUAL-WIDTH SEGMENTS, and a GRID rather than a flex row is what it takes.
     *
     * The sample states it - "Equal-width segments in a 6px-cornered container" - and draws it with
     * `flex: 1` on each option. That works in the sample because its container has a width to
     * divide; ours is a shrink-to-fit control in a card header, and `flex: 1` sets flex-basis to
     * ZERO, so the options stop asking for their content and the container collapses to
     * min-content (Home/Index's Top Usage rendered "25" as "2...").
     *
     * `grid-auto-columns: 1fr` in an auto-width grid sizes every track to the WIDEST option's
     * max-content instead, which is the same picture the sample draws and the one the words
     * describe. The row is still one line because the flow is column. */
    .db-segmented .dx-buttongroup-wrapper {
        display: grid;
        grid-auto-flow: column;
        grid-auto-columns: 1fr;
        border-radius: 0;
        background-color: transparent;
        box-shadow: none;
    }

    .db-segmented .dx-buttongroup-item.dx-button {
        border: 0;
        border-radius: 0;
        background-color: transparent;
        /* SUBHEAD, NOT INK, and the pair is the point: the sample gives the inactive label #4c4948
           and the active one #31302f, so the two differ in tone as well as in weight. Both are
           tokens this spine already declares - --dz-subhead IS #4c4948. */
        color: var(--dz-subhead);
        box-shadow: none;
        font-size: 13px;
        font-weight: 400;
    }

        /* 14px is the design's own option padding.

           THE VERTICAL 8px IS WHAT GIVES THE CONTROL ITS HEIGHT: with no vertical padding the
           option collapses to its line box. The component owns its size; callers should not set a
           widget .Height().

           8px + 13px + 8px = 29px per option, 31px with the container's border. That is the
           mockup's arithmetic exactly (`padding:8px 14px;font:500 13px/1`), which is why the
           numbers are stated rather than rounded to a token step. */
        .db-segmented .dx-buttongroup-item.dx-button .dx-button-content {
            padding: 8px 14px;
        }

        /* THE LINE BOX IS THE TEXT SPAN, NOT THE CONTENT DIV, and that is the one non-obvious step
           in the arithmetic above. dx.light.css sets a line-height on BOTH `.dx-button-content`
           (17.643px) and `.dx-button-text` (16.715px), and the span is display:block - so it forms
           the line box itself and the parent's value never reaches it (the option would be 3.7px
           over, which looks like wrong padding). Naming the span lands it on 13px. */
        .db-segmented .dx-buttongroup-item.dx-button .dx-button-text {
            line-height: 1;
        }

        /* THE DIVIDER is the subtle hairline, per the guide: "Gray 100 inactive, white SemiBold
           active, 1px hairlines between", and its sample draws #e2dfdd between every pair.
           --dz-hairline IS #e2dfdd. */
        .db-segmented .dx-buttongroup-item.dx-button + .dx-buttongroup-item.dx-button {
            border-left: 1px solid var(--dz-hairline);
        }

        /* Hover states the CONTROL, not the container it happens to sit in.
         *
         * The colour is stated as well as the background, and the glyph is pinned to follow the
         * text, because this control gets dropped into strips that paint their own hover: e.g.
         * group-detail.css's `.section-strip .dx-button.dx-state-hover` (and its `.dx-icon`
         * partner) set color #fff for a DARK strip, which would turn a hovered option white on
         * the near-white fill.
         *
         * Naming both halves here is the fix rather than narrowing those two rules: a segmented
         * control that cannot say what its own hover looks like will keep losing this argument to
         * the next container it is placed in. */
        .db-segmented .dx-buttongroup-item.dx-button.dx-state-hover {
            background-color: var(--dz-segmented-hover);
            color: var(--dz-subhead);
        }

        /* (0,5,0) on the hover arm, which is what it takes to beat a container's own
           `.strip .dx-button.dx-state-hover .dx-icon` at (0,4,0). */
        .db-segmented .dx-buttongroup-item.dx-button .dx-icon,
        .db-segmented .dx-buttongroup-item.dx-button.dx-state-hover .dx-icon,
        .db-segmented .dx-buttongroup-item.dx-button.dx-item-selected .dx-icon,
        .db-segmented .dx-buttongroup-item.dx-button.dx-item-selected.dx-state-hover .dx-icon {
            color: inherit;
        }

        /* The chosen option. dx-item-selected is DevExtreme's own class, and the specificity here
           has to beat its contained-mode paint, which is why the element and both classes appear. */
        .db-segmented .dx-buttongroup-item.dx-button.dx-item-selected,
        .db-segmented .dx-buttongroup-item.dx-button.dx-item-selected.dx-state-hover {
            background-color: var(--dz-segmented-selected);
            color: var(--dz-segmented-selected-ink);
            font-weight: 600;
        }

        /* EVERY OPTION RESERVES ITS SEMIBOLD WIDTH, so choosing one never resizes the control.
           The tracks are the widest option's max-content (above), and SemiBold is wider than
           Regular: MEASURED on Account Edit's Payment Type, "Wire Transfer" is 85.2px Regular and
           88.3px SemiBold, so choosing it widened all five segments 114.2 -> 117.3px and the control
           573 -> 588px, while the shorter labels stayed under 85.2px and changed nothing.

           An invisible, zero-height SemiBold copy of the label sits under the content and holds
           the width. DevExtreme writes the option's text into aria-label, so attr() reads the
           label without a caller attribute. Text options only: an icon-only option's aria-label is
           the icon name. The 14px is the option padding above.

           The option is a flex ROW in dx.light.css, which would put the copy BESIDE the label and
           add its width (every segment measured 187px). A column stacks it under instead; the
           content still stretches to the full width and centres its own text. */
        .db-segmented .dx-buttongroup-item.dx-button.dx-button-has-text {
            flex-direction: column;
        }

        .db-segmented .dx-buttongroup-item.dx-button.dx-button-has-text::after {
            content: attr(aria-label);
            display: block;
            height: 0;
            padding: 0 14px;
            overflow: hidden;
            visibility: hidden;
            font-weight: 600;
            white-space: nowrap;
        }

/* A table INSIDE a card USED TO get a lighter header strip than a table that IS the page: a
 * standalone grid (Subscribers) drew its column headers on Sunken #f0eeec, and a table inside a card
 * drew them on Page #f8f7f5, on the reasoning that inside a card the Sunken tone was already spoken
 * for by the card's own header strip, and two Sunken bands stacked would read as one heavy block.
 *
 * WL-24 (ClickUp 86894yg24), Brad A100 OVERRULES this (his walkthrough: the Subscriber Details Logs
 * card's column-header row stayed near-white while the rest of the card themed) -- every grid's
 * header row, embedded in a card or not, now reads the SAME --dz-grid-header-background chain
 * 00-tokens.css declares (still Sunken by default, so an untouched tenant sees no change here: the
 * Logs-style cards move from #f8f7f5 to #f0eeec, the Recent Connections card the same). See that
 * token's own comment for the archetype side of this (02-grid-shell.css's `.db-grid--card`).
 *
 * The type is the design's: 600 14px/1.4 in ink, a hairline rule underneath. The padding is the
 * grid's own header tokens, 12px at comfortable, so a compact card grid's header steps down with its
 * rows (it held 43.6px at both densities while it was a literal). */
.db-card .dx-datagrid-headers,
.db-card .dx-treelist-headers,
.db-detail-card .dx-datagrid-headers,
.db-detail-card .dx-treelist-headers {
    border: 0;
    border-radius: 0;
    background-color: var(--dz-grid-header-background);
}

.db-card .dx-datagrid-headers .dx-header-row > td,
.db-card .dx-treelist-headers .dx-header-row > td,
.db-detail-card .dx-datagrid-headers .dx-header-row > td,
.db-detail-card .dx-treelist-headers .dx-header-row > td {
    padding: var(--dz-grid-header-padding-y) var(--dz-grid-cell-padding-x);
    background-color: transparent;
    border-bottom: 1px solid var(--dz-hairline);
    color: var(--dz-grid-header-color);
    font-size: 14px;
    font-weight: 600;
    line-height: 1.4;
}

/* A card's own grid has no rounded corners or shadow of its own - the card supplies both, and a
   second rounded box inside a rounded box reads as disconnected. */
.db-card .dx-datagrid-rowsview,
.db-card .dx-treelist-rowsview,
.db-detail-card .dx-datagrid-rowsview,
.db-detail-card .dx-treelist-rowsview {
    border: 0;
    border-radius: 0;
    box-shadow: none;
}

/* 14px/600, which is the design's card title: a strong label, not a heading. The page's own <h1> is
   the 26px heading; a card is not.

   WL-20a (ClickUp 86894yg24), item 4 / WL-19 audit G19: `color: inherit` rather than
   var(--dz-ink), so a title sitting in a .db-card-header/.db-card__header strip (every current
   caller: `db-card-header rounded-header` wraps every .db-card-title in Voyager) follows that
   strip's own --dz-header-ink instead of overriding it back to the neutral spine's Ink. Same
   resolved value today, since --dz-header-ink defaults to var(--dz-ink). */
.db-card-title {
    margin: 0;
    color: inherit;
    font-size: 14px;
    font-weight: 600;
    line-height: 1.2;
}

/* The labels sit immediately after the title and its pencil, at the LEFT, and the cycle is pushed
   to the far end by a spacer rather than floated - a float would leave the labels' own wrap
   ungoverned. */
.db-card-header__labels {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 6px;
}

/* The strip's gap is the only spacing between chips, across and between wrapped lines. A chip's own
   right and bottom margin (voyager.badges.css) is the gap for chips flowing in a grid cell; added
   to this gap it doubled the space between chips, inset the last chip from the strip's end, and
   lifted every chip off the strip's centre (MEASURED on Account/Dashboard's Summary strip: the
   chip 3px above centre, 16px from the end against the strip's 10px padding). */
.db-card-header__labels > .db-badge.db-badge--label {
    margin: 0;
}

.db-card-header__spacer {
    flex: 1 1 auto;
}

/* WL-24 (ClickUp 86894yg24), canary G19 (PM ruling): this sits ON a header strip
   (.db-card-header/.db-card__header, 13-detail-segments-card-header.css), which follows a tenant's
   headerBackground, but --dz-body is a fixed neutral grey that does not move with it -- on a tenant
   header (#1a7a3c bg, #ffe600 ink) --dz-body measured ~1.1:1. --dz-body is also the WRONG family: it
   is the spine's own grey, independent of the strip it sits on.

   Fixed by reading --dz-header-ink directly, matching the title beside it (.db-card-title, this same
   file, below) and the strip itself (13-detail-segments-card-header.css's .db-card-header), rather
   than opacity: a composited alpha shifts hue as well as lightness, and MEASURED against this exact
   tenant pair it fails WORSE than the bold figure beside it (opacity 0.72 composites to ~2.9:1, under
   even the pre-fix figure's own ~4.3:1) -- the one thing opacity is supposed to avoid. Visually
   secondary is carried by WEIGHT alone (400 here, 600 on the figure), the "or weight" half of the PM
   ruling, which costs no contrast at all: at full --dz-header-ink the meta text is exactly as legible
   as the bold figure it sits beside, in every mode and every tenant pair the header pair itself
   already has to clear.

   MEASURED, untouched (Sunken #f0eeec header, Ink #31302f): 11.38:1 (was var(--dz-body) #666261,
   ~7.5:1) -- both comfortably clear 4.5:1; a visible but minor darkening, disclosed, not hidden.
   The assigned canary pair (#1a7a3c / #ffe600) measures 4.26:1 at full header-ink -- under 4.5:1, but
   that is the PRE-EXISTING contrast of every headerInk/headerBackground consumer today (the title
   text itself, any .rounded-header caption), not a regression this rule introduces; a floor on a
   tenant-set headerInk/headerBackground PAIR is the compiler's/editor's job (the contrast chips
   DESIGN.md describes), not this rule's. */
.db-card-header__meta {
    color: var(--dz-header-ink);
    font-size: 14px;
    font-weight: 400;
    line-height: 1.3;
}

    .db-card-header__meta b {
        color: inherit;
        font-weight: 600;
    }

/* A stand-in chip for "this subscriber has no labels yet".
 *
 * Without it the header is a name, then a pencil, and nothing - and that pencil reads as though it
 * edits the NAME, which the pencil beside the title already does. The dashed outline is what says
 * empty-and-editable rather than a label literally called "Labels": a solid chip in grey would read
 * as a real label. Same geometry and type as a real label chip so the row does not change height
 * when the first label is added. */
.db-label-placeholder {
    display: inline-flex;
    align-items: center;
    padding: 3px 8px;
    border: 1px dashed var(--dz-border);
    border-radius: var(--dz-radius-sm);
    background-color: transparent;
    color: var(--dz-meta);
    font-size: 12px;
    font-weight: 500;
    line-height: 1.3;
}

/* The card header strip - its padding, its gap and the control height every control inside it sizes
   to - is --dz-card-header-padding, --dz-card-header-gap and --dz-card-header-control-height in
   00-tokens.css, read by .db-card-header (13-detail-segments-card-header.css). The padding and gap
   tighten in 20-density.css's compact band; the control height does not, since no control changes
   size with density. */

/* ================================================================================================
   The usage meter, and the usage status beside it.
   ================================================================================================ */

/* One line: how much has been used, the bar, then the allowance.
 *
 * The mockup draws it inside a single field cell that spans two columns, which is why the parts are
 * inline rather than stacked - a caption over a full-width bar reads as two or three separate rows
 * inside one cell once the label above it is also on its own line.
 *
 * flex-basis 100% is what makes it fill the line the label's wrap started. Without a definite
 * width the track's `flex: 1` has nothing to divide, so the bar collapses to nothing and the two
 * numbers sit next to each other. */
.db-usage {
    display: flex;
    flex: 1 1 100%;
    align-items: center;
    gap: var(--dz-usage-gap);
    min-width: 0;
}

.db-usage__used {
    color: var(--dz-ink);
    font-size: 14px;
    font-weight: 600;
    line-height: 1.4;
    white-space: nowrap;
}

/* max-width caps the bar so a wide screen does not draw a 900px rule through the card; min-width
   keeps it a bar rather than a dot when the numbers are long. Sunken is the design's own track. */
.db-usage__track {
    flex: 1 1 auto;
    min-width: 40px;
    max-width: 420px;
    height: 6px;
    border-radius: 3px;
    background-color: var(--dz-sunken);
    overflow: hidden;
}

.db-usage__fill {
    display: block;
    height: 100%;
    border-radius: 3px;
}

/* The three states: normal is a fraction of a known allowance, overage is the one state that is a
   warning, and unlimited is a state rather than a judgement, so it takes info rather than success. */
.db-usage__fill--normal {
    background-color: var(--dz-usage-normal-color);
}

.db-usage__fill--overage {
    background-color: var(--dz-status-danger-accent);
}

.db-usage__fill--unlimited {
    background-color: var(--dz-status-info-accent);
}

/* A coloured dot and the text in ink - the same shape as the SIM status badge three cells earlier,
   so two status cells on one row agree, and a sentence ("2.5 GB Remaining") is not the coloured
   element. */
.db-usage-status {
    display: inline-flex;
    align-items: center;
    gap: var(--dz-space-1);
    font-weight: 600;
}

/* NOT --dz-status-dot-size, and that is deliberate rather than an oversight. That token is the
   BADGE's marker and `.db-grid` sets it to 0; a usage dot that read it would vanish the day one of
   these lands in a grid cell, which is a failure mode nothing on screen would explain. It reads the
   house marker size instead, so the two still move together from one edit. */
.db-usage-status__dot {
    width: var(--dz-status-marker-size);
    height: var(--dz-status-marker-size);
    border-radius: var(--dz-status-marker-radius);
    /* flex: none, or the dot is the flex item that gives up its width when the text is long */
    flex: none;
}

.db-usage-status__dot--ok {
    background-color: var(--dz-status-active-accent);
}

.db-usage-status__dot--unknown {
    background-color: var(--dz-meta);
}

.db-usage-status__dot--alert {
    background-color: var(--dz-status-danger-accent);
}

.db-udf-editor {
    display: flex;
    flex-direction: column;
    gap: var(--dz-space-2);
}

.db-udf-editor__field {
    display: flex;
    flex-direction: column;
    gap: var(--dz-space-half);
}

.db-udf-editor__label {
    font-size: 12px;
    font-weight: 600;
    line-height: 1.3;
    color: var(--dz-body);
}

/* Empty until something is wrong. Its paint applies only once it has text; while it is empty it takes
   no room at all, through the :empty rule below, because a reserved line would put a permanent gap
   above the buttons. role="alert" is on the element in the script, so a message appearing here is
   announced. */
.db-udf-editor__error:not(:empty) {
    color: var(--dz-danger-text-color);
    font-size: 13px;
    line-height: 1.4;
}

/* The empty node has no height but is still a flex item, so it took the column's 16px gap and doubled
   the gap above the buttons to 32 (PP-D72, audit row sub-18). The negative margin cancels that gap
   while the role="alert" node stays rendered, which display: none would not. */
.db-udf-editor__error:empty {
    margin-top: calc(-1 * var(--dz-space-2));
}

.db-udf-editor__spacer {
    flex: 1 1 auto;
}

/* ================================================================================================
   The row-action popout: what the three dots on a row open.
   ================================================================================================ */

/* EVERY CONTEXT MENU'S OUTER BOX FOLLOWS ITS SURFACE'S CORNERS, themed or not.
 *
 * A dxContextMenu is two layers: the overlay (div.dx-overlay-content.dx-context-menu), which DevExtreme
 * positions and sizes, and the .dx-submenu inside it, which is the visible card. The row-action
 * rule below rounds only the card. The overlay kept the square ground the adapter gives every
 * overlay (22-devextreme-adapter.css), so its four corners showed outside the card's curve. Menus
 * that never asked for .themed-context-menu (the e-mail templates row menu, a grid's column and
 * header menus) had a square card as well.
 *
 * So the overlay gives up its ground - the card is the one surface - and takes the card radius, so
 * its own box matches the card's curve whatever else is painted on it. The card radius is the same
 * token the row-action popout and the drop-down button popup use, so every fly-out has one corner.
 *
 * NO overflow: hidden, anywhere in this rule. dx.light.css sets `.dx-context-menu.dx-overlay-content
 * { overflow: inherit }` on purpose: the card's shadow lives outside its box and a hover-expanding
 * submenu is a .dx-submenu nested inside its parent row, so a clip on the overlay, the card or the
 * list would cut both. The first and last rows take the card's corners instead (below), which is what
 * keeps their hover wash inside the curve.
 *
 * The drop-down button popup, an editor's drop-down list and a lookup need no such rule: DevExtreme
 * leaves `overflow: clip` on their overlay, which clips the rows to the overlay's own radius. Only
 * the context menu opts out of that, so only it needs this. */
html[data-db-theme] .dx-overlay-content.dx-context-menu {
    background-color: transparent;
    border-radius: var(--dz-grid-card-radius);
}

html[data-db-theme] .dx-context-menu .dx-submenu {
    border-radius: var(--dz-grid-card-radius);
}

/* The first and last row of a card DevExtreme drew itself. Its hover wash is the row's whole box, so
 * a square-cornered row would poke past the curve at the card's corners.
 *
 * Inside the 1px card border and the 1px DevExtreme pads the list with, hence the 2px.
 *
 * :where() keeps this at the lowest specificity: the row-action popout below sets a radius on every
 * row with its own padding around the list, and has to win. Only a menu that has nothing of its own
 * reaches this. */
:where(html[data-db-theme] .dx-context-menu .dx-submenu > .dx-menu-items-container > .dx-menu-item-wrapper:first-child) > .dx-menu-item {
    border-top-left-radius: calc(var(--dz-grid-card-radius) - 2px);
    border-top-right-radius: calc(var(--dz-grid-card-radius) - 2px);
}

:where(html[data-db-theme] .dx-context-menu .dx-submenu > .dx-menu-items-container > .dx-menu-item-wrapper:last-child) > .dx-menu-item {
    border-bottom-left-radius: calc(var(--dz-grid-card-radius) - 2px);
    border-bottom-right-radius: calc(var(--dz-grid-card-radius) - 2px);
}

/* Scoped to .themed-context-menu, which five surfaces ask for by name - Subscribers, Accounts, the
 * Account dashboard, Invoices and Plans. The class is the existing shared name for this control, so
 * all five row menus move together. Values are measured against the mockup's .rowmenu-pop.
 *
 * The overlay is appended to <body>, not inside .db-grid, which is why this cannot hang off the
 * grid's own scope the way the rest of the strip does. */
.themed-context-menu .dx-submenu {
    padding: 6px;
    background-color: var(--dz-card);
    border: 1px solid var(--dz-border);
    /* 10px - the card radius, not the 6px control radius. The design rounds a floating surface
       more than a control, and this is a surface. */
    border-radius: var(--dz-grid-card-radius);
    box-shadow: var(--dz-tip-shadow);
}

    .themed-context-menu .dx-submenu .dx-menu-items-container {
        padding: 0;
    }

    .themed-context-menu .dx-submenu .dx-menu-item {
        border-radius: var(--dz-control-radius);
    }

        .themed-context-menu .dx-submenu .dx-menu-item .dx-menu-item-content {
            padding: 10px 12px;
            /* The line-height has to be restated on the CONTENT, not just on the text span inside
               it: DevExtreme gives this box its own, which adds 3px over the mockup's 36.8px item. */
            line-height: 1.2;
        }

        .themed-context-menu .dx-submenu .dx-menu-item .dx-menu-item-text {
            /* DevExtreme pads the text element itself - 3px 25px 5px 23px - and the 23px on the
             * left is the gutter it reserves for an icon. Hiding the icon leaves the gutter, so the
             * text would sit 41px in from the card's edge (6 card + 12 content + 23 gutter) where
             * the mockup puts it at 18px, and the card would be 47px wider than the design's 190px.
             *
             * Padding belongs to .dx-menu-item-content alone, the box the design specifies. */
            padding: 0;
            color: var(--dz-ink);
            font-family: var(--dz-font-family);
            font-size: 14px;
            font-weight: 400;
            line-height: 1.2;
            white-space: nowrap;
        }

        /* Terminate keeps its red. .db-menu-item--danger .dx-menu-item-text is (0,2,0), declared
         * elsewhere in the stylesheet; the scoped rule above is (0,4,0) and would repaint it. A
         * general rule written for a surface can out-rank a modifier written for a state. */
        .themed-context-menu .dx-submenu .dx-menu-item.db-menu-item--danger .dx-menu-item-text {
            color: var(--dz-danger-text-color);
        }

    /* Sunken on hover, which is the same wash the mockup uses and the same one the grid gives a
       hovered row - so a menu line and a row line highlight identically. */
    .themed-context-menu .dx-submenu .dx-menu-item.dx-state-hover,
    .themed-context-menu .dx-submenu .dx-menu-item.dx-state-focused {
        background-color: var(--dz-sunken);
    }

/* A SECTION inside the popout, which is what the mock uses instead of a submenu.
 *
 * The mock's own rule, transcribed - .rowmenu-pop span.sec:
 *
 *     padding: 8px 12px 4px; margin-top: 4px; border-top: 1px solid var(--border-subtle);
 *     font: 600 11px/1.2 Poppins; letter-spacing: .12em; text-transform: uppercase;
 *     color: var(--db-orange); cursor: default;
 *
 * which is the design system's eyebrow - "ALL-CAPS SemiBold with wide tracking (0.12em) in orange"
 * - applied to a menu. The border is the line break the section reads as; there is no separate
 * divider element.
 *
 * Not a HOVER-EXPANDING SUBMENU: a second popout off the side of the first means two surfaces, a
 * hover target to keep, and a menu reaching further than its row. The mock puts all of it in one
 * popout and labels the group, which is also the only shape that works on a touch pointer.
 *
 * NOT INTERACTIVE, and pointer-events is how rather than `disabled`. DevExtreme's disabled state
 * greys an item to 50% opacity, which reads as "this action is unavailable right now" - the exact
 * wrong message for a heading. pointer-events: none removes the hover wash and the click together
 * and leaves the paint alone; the menu also cannot close on a click that never lands.
 *
 * The padding is on the ITEM here, not on .dx-menu-item-content, because it replaces that box's
 * 10px 12px rather than adding to it - see the note on .dx-menu-item-text for why this menu has
 * three nested padding boxes to keep straight. */
.themed-context-menu .dx-submenu .dx-menu-item.db-menu-item--section {
    margin-top: var(--dz-space-half);
    border-top: 1px solid var(--dz-hairline);
    border-radius: 0;
    pointer-events: none;
}

.themed-context-menu .dx-submenu .dx-menu-item.db-menu-item--section .dx-menu-item-content {
    /* The eyebrow's own padding, and the 12 in it matches .dx-menu-item-content's own 12 two rules
       up so the heading lines up with the items under it. Deliberately NOT --dz-space-* or
       --dz-grid-cell-padding-x: this menu's three nested padding boxes are tuned against each
       other, and the grid one is redeclared under compact density. The eyebrow band is the seam -
       one number, two surfaces, and it only moves when the eyebrow moves. */
    padding: var(--dz-eyebrow-padding);
}

.themed-context-menu .dx-submenu .dx-menu-item.db-menu-item--section .dx-menu-item-text {
    color: var(--dz-eyebrow-color);
    /* The design system's eyebrow: 11px SemiBold, 0.12em tracking, upper-case, accent. */
    font-size: var(--dz-eyebrow-font-size);
    font-weight: var(--dz-eyebrow-font-weight);
    letter-spacing: var(--dz-eyebrow-letter-spacing);
    text-transform: uppercase;
}

/* The row menu's line before Delete (`beginGroup`). DevExtreme draws a group break as a bare
 * .dx-menu-separator in its own colour; repainted from the hairline token so a tenant theme can
 * repoint it with every other rule line. */
.themed-context-menu .dx-submenu .dx-menu-separator {
    height: 1px;
    margin: var(--dz-space-half) 0;
    background-color: var(--dz-hairline);
}

/* No glyphs on the lines.
 *
 * The mockup draws this menu as text only, and it is right to: these are six unrelated verbs, and
 * six unrelated icons beside them are decoration that has to be read past rather than a way to
 * find the row you want. Callers still declare `icon: 'fa fa-...'`, so hiding them here reaches all
 * five surfaces without editing the view scripts.
 *
 * The danger colour stays. That is not decoration: Terminate cannot be undone, and it is the one
 * line the mockup colours. */
.themed-context-menu .dx-submenu .dx-menu-item .dx-icon {
    display: none;
}

/* Poppins reaches every DevExtreme WIDGET.
 *
 * dx.light.css declares the family on `.dx-widget`. Inheritance flows from the nearest ancestor that
 * declares a value, so `body { font-family }` cannot reach anything inside a widget.
 *
 * This is safe for the icon fonts (a rule elsewhere warns against this selector, for fear of a
 * family turning a glyph into a letter): an icon font declares its family ON the element that draws
 * the glyph (.dx-icon is DXIcons, .fa is Font Awesome), and a declaration on the element always
 * beats an inherited value. So .dx-widget reaches labels, cells and inputs, and stops at every
 * glyph. Verified in the browser across .dx-icon, .fa and the CSS-drawn row-action dots.
 *
 * A glyph that does NOT declare its own family would break here, which is the one thing to check
 * when adding an icon: if it renders as a letter, it is missing its font-family, not fighting this. */
.dx-widget {
    font-family: var(--dz-font-family);
}

/* dx.light.css's OWN `.dx-widget input, .dx-widget textarea` selector is as specific as the rule
 * above and, wherever it loads after this bundle, keeps typed text in "Helvetica Neue" regardless
 * of theme (WL-19 audit, item 2 / G13). `html` in front is not decorative -- it is what reliably
 * beats dx.light's pair without depending on load order. `inherit` rather than restating
 * var(--dz-font-family) so this still tracks whatever the rule above resolves to. WL-20a (ClickUp
 * 86894yg24). */
html .dx-widget input,
html .dx-widget textarea {
    font-family: inherit;
}

/* ================================================================================================
   The icon set, drawn from the design's own artwork.
   ================================================================================================ */

/* Glyphs taken from the design's artwork (mostly Lucide, copied out of the mockup export), delivered
 * as MASKS.
 *
 * A mask because of colour. The mockup draws these with stroke="currentColor", so the glyph follows
 * the text or button role it sits in - a link's teal, a toolbar button's Body grey, a danger item's
 * red. A background-image bakes its colour in; an inline <svg> needs every call site to emit markup,
 * and DevExtreme's `icon:` option takes a CLASS. The mask keeps one artwork and one call-site shape.
 *
 * The stroke colour inside the data URI is meaningless - a mask reads alpha only - and is left as
 * plain black so nobody edits it expecting a colour change.
 *
 * These replace Font Awesome only at the call sites the design specifies, not generally. Adding a
 * glyph here means the design names it somewhere.
 *
 * Sizes are per-glyph because the design draws them at different sizes for different jobs - 15px
 * beside a selection action's text, 18px alone inside a 40px toolbar button, 13px beside a field
 * label. --db-icon-size carries that, and a call site can override it. */
.db-icon {
    display: inline-block;
    flex: none;
    width: var(--db-icon-size, 15px);
    height: var(--db-icon-size, 15px);
    background-color: currentColor;
    -webkit-mask-image: var(--db-icon-source);
    mask-image: var(--db-icon-source);
    -webkit-mask-repeat: no-repeat;
    mask-repeat: no-repeat;
    -webkit-mask-position: center;
    mask-position: center;
    -webkit-mask-size: contain;
    mask-size: contain;
}

/* A Font Awesome class on the same element would otherwise still contribute its glyph through
   ::before, and the two would stack - the mask plus a letter beside it. */
.db-icon::before {
    content: none;
}

/* Two classes, to tie with DevExtreme and win on source order.
 *
 * Where the glyph comes from a widget's own icon option, DevExtreme also marks the element .dx-icon,
 * and dx.light.css pins ".dx-button-mode-outlined .dx-icon" to #333 at (0,2,0), which beats a bare
 * .db-icon. .db-icon.dx-icon is the same specificity and this sheet is linked after dx.light.css,
 * so no !important is needed.
 *
 * The SIZE for the same reason: dx.light.css ships `.dx-button .dx-icon { width: 18px; height: 18px }`
 * and `.dx-button-has-icon .dx-icon` likewise, both (0,2,0), so inside a dxButton every masked
 * glyph would render at 18px whatever --db-icon-size says. This is a property of how DevExtreme
 * marks up an icon, so it is fixed here once rather than per button. */
.db-icon.dx-icon {
    color: inherit;
    width: var(--db-icon-size, 15px);
    height: var(--db-icon-size, 15px);
}

.db-icon--tag {
    --db-icon-source: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><path d='M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z'/><circle cx='7.5' cy='7.5' r='.5' fill='black' stroke='none'/></svg>");
    --db-icon-size: 15px;
}

.db-icon--transfer {
    --db-icon-source: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><path d='m16 3 4 4-4 4'/><path d='M20 7H4'/><path d='m8 21-4-4 4-4'/><path d='M4 17h16'/></svg>");
    --db-icon-size: 15px;
}

/* Drawn for the Endpoints worklist's Share action, in the same Lucide-derived family as the rest of
   the set - 24x24 viewBox, 2px round-capped stroke - so it sits beside the tag without reading as a
   guest from another icon library. The three-node graph rather than an arrow-out-of-a-box: this
   action grants another account visibility of a row, which is a relationship, not an export. */
.db-icon--share {
    --db-icon-source: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><circle cx='18' cy='5' r='3'/><circle cx='6' cy='12' r='3'/><circle cx='18' cy='19' r='3'/><line x1='8.59' y1='13.51' x2='15.42' y2='17.49'/><line x1='15.41' y1='6.51' x2='8.59' y2='10.49'/></svg>");
    --db-icon-size: 15px;
}

/* Billing Center's Send Email selection action: a paper plane, in the same family as the rest. */
.db-icon--send {
    --db-icon-source: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><path d='m22 2-7 20-4-9-9-4Z'/><path d='M22 2 11 13'/></svg>");
    --db-icon-size: 15px;
}

.db-icon--settings {
    --db-icon-source: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><circle cx='12' cy='12' r='3'/><path d='M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.6h.09A1.65 1.65 0 0 0 10 3.09V3a2 2 0 0 1 4 0v.09A1.65 1.65 0 0 0 15 4.6a1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9v.09a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z'/></svg>");
    --db-icon-size: 18px;
}

.db-icon--download {
    --db-icon-source: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><path d='M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4'/><polyline points='7 10 12 15 17 10'/><line x1='12' y1='15' x2='12' y2='3'/></svg>");
    --db-icon-size: 18px;
}

.db-icon--plus {
    --db-icon-source: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><path d='M12 5v14'/><path d='M5 12h14'/></svg>");
    --db-icon-size: 15px;
}

.db-icon--save {
    --db-icon-source: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><path d='M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z'/><path d='M17 21v-8H7v8'/><path d='M7 3v5h8'/></svg>");
    --db-icon-size: 15px;
}

.db-icon--reset {
    --db-icon-source: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><path d='M3 7v6h6'/><path d='M21 17a9 9 0 0 0-15-6.7L3 13'/></svg>");
    --db-icon-size: 15px;
}

/* A table's filter and its widen toggle, for the controls that sit beside a card title with the
 * layout gear (Home/Index's Top Data Users). Same stroke family and 18px box as the gear, so the
 * three read as one set; they were Font Awesome solid glyphs beside this outline gear, at three
 * sizes. The funnel is filter-clear's without the cross. */
.db-icon--filter {
    --db-icon-source: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><polygon points='22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3'/></svg>");
    --db-icon-size: 18px;
}

/* The widen toggle is a pair: arrows out (Lucide maximize-2) while the panel can grow, arrows in
 * (minimize-2) once it has. The glyph carries the state, so the toggle needs no pressed tint (owner
 * call 2026-10-07; the one double arrow plus a tint did not read as either state). */
.db-icon--expand {
    --db-icon-source: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><polyline points='15 3 21 3 21 9'/><polyline points='9 21 3 21 3 15'/><line x1='21' x2='14' y1='3' y2='10'/><line x1='3' x2='10' y1='21' y2='14'/></svg>");
    --db-icon-size: 18px;
}

.db-icon--collapse {
    --db-icon-source: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><polyline points='4 14 10 14 10 20'/><polyline points='20 10 14 10 14 4'/><line x1='14' x2='21' y1='10' y2='3'/><line x1='3' x2='10' y1='21' y2='14'/></svg>");
    --db-icon-size: 18px;
}

/* Choosing which columns a table shows (Lucide columns-3): the grid's column chooser. Not the
 * settings gear, which is density and layout. 18px, a table control alone in its button. */
.db-icon--columns {
    --db-icon-source: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><rect width='18' height='18' x='3' y='3' rx='2'/><path d='M9 3v18'/><path d='M15 3v18'/></svg>");
    --db-icon-size: 18px;
}

/* Choosing which fields a card shows (Lucide list-checks): Group View's card-field picker. A list
 * with ticks rather than columns-3, because a card's fields are a list, not table columns (owner,
 * 2026-10-07). 18px, alone in its button like the gear beside it in Table mode. */
.db-icon--list-checks {
    --db-icon-source: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><path d='m3 17 2 2 4-4'/><path d='m3 7 2 2 4-4'/><path d='M13 6h8'/><path d='M13 12h8'/><path d='M13 18h8'/></svg>");
    --db-icon-size: 18px;
}

/* Discarding unsaved edits (Lucide undo-2), a batch-edit grid's Discard changes. Not reset's arc:
 * reset puts a layout back to its default, and this takes back what the viewer just typed. */
.db-icon--undo {
    --db-icon-source: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><path d='M9 14 4 9l5-5'/><path d='M4 9h10.5a5.5 5.5 0 0 1 5.5 5.5 5.5 5.5 0 0 1-5.5 5.5H11'/></svg>");
    --db-icon-size: 18px;
}

/* Outbound webhooks (Lucide webhook), Watchdogs' link to the webhook destinations it notifies. It
 * drew a Font Awesome cog, which reads as the settings gear: density and layout, not a place to
 * go. 18px, the box of the Add glyph beside it in the grid strip. */
.db-icon--webhook {
    --db-icon-source: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><path d='M18 16.98h-5.99c-1.1 0-1.95.94-2.48 1.9A4 4 0 0 1 2 17c.01-.7.2-1.4.57-2'/><path d='m6 17 3.13-5.78c.53-.97.1-2.18-.5-3.1a4 4 0 1 1 6.89-4.06'/><path d='m12 6 3.13 5.73C15.66 12.7 16.9 13 18 13a4 4 0 0 1 0 8'/></svg>");
    --db-icon-size: 18px;
}

/* Clearing a filter: the same funnel the column headers draw, with a cross beside it.
 *
 * The funnel and not a generic undo arrow, because this button is the one control that acts on what
 * those header funnels did - a viewer scanning the toolbar should be able to pair the two without
 * reading either label. The outline weight matches the header's unfiltered funnel (stroke 2 on the
 * same 24-unit box), so the two read as one family at different jobs rather than two icon sets.
 *
 * The funnel's stem is cut short on the right to leave the cross its corner; at full size the cross
 * runs through the funnel's edge and smudges at 15px. */
.db-icon--filter-clear {
    --db-icon-source: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><path d='M13.5 3H2l8 9.46V19l4 2v-8.54l1.2-1.42'/><path d='m17 4 5 5'/><path d='m22 4-5 5'/></svg>");
    --db-icon-size: 15px;
}

/* DELETE: Font Awesome's solid trash (fa-trash, U+F1F8), FILLED (owner decision: edit and delete are
 * filled glyphs, not thin outlines). The outline is taken from the Font Awesome Pro 6.1.2 solid face
 * Voyager already ships and licenses (Voyager/Content/font-awesome/webfonts/fa-solid-900.ttf),
 * delivered as a mask filled in currentColor. A row delete in a grid draws this; the gear menu's saved
 * layout row draws the light glyph below instead. */
.db-icon--trash {
    --db-icon-source: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 448 512'><path d='M135 18Q144 1 164 0H284Q304 1 313 18L320 32H416Q430 32 439 41Q448 50 448 64Q448 78 439 87Q430 96 416 96H32Q18 96 9 87Q0 78 0 64Q0 50 9 41Q18 32 32 32H128L135 18ZM395 466Q393 486 380 499Q366 512 347 512H101Q82 512 68 499Q55 486 53 466L31 128H416L395 466Z'/></svg>");
    --db-icon-size: 15px;
}

/* DELETE, LIGHT (PP-D81): the design's 2px-stroke trash (Lucide trash-2), the stroke family every other
 * glyph in the gear menu is drawn in. The saved layout row's Overwrite is .db-icon--save, a stroke
 * glyph that fills about 11px of its 15px box (a 24-unit artwork with a 3-unit margin); the filled
 * .db-icon--trash above runs edge to edge in its box, so the two sat in one row as a small light glyph
 * and a larger solid one. The cause was the glyph, so the row names this one instead of sizing the
 * solid one down: same artwork family, same 15px box, same ink. */
.db-icon--trash-light {
    --db-icon-source: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><path d='M3 6h18'/><path d='M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6'/><path d='M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2'/><line x1='10' x2='10' y1='11' y2='17'/><line x1='14' x2='14' y1='11' y2='17'/></svg>");
    --db-icon-size: 15px;
}

/* INFO: a column or field that explains itself on hover - Home/Index's Top Data Users headers carry
 * one beside "% of Pool", "Total pool %" and "Trend". The design's 2px-stroke circle-i, drawn the
 * same way as the filter glyphs, at 12px so it sits inside a header's text line rather than standing
 * beside it. The explanation is the caller's data-tip / title on the glyph. */
.db-icon--info {
    --db-icon-source: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><circle cx='12' cy='12' r='10'/><path d='M12 16v-4'/><path d='M12 8h.01'/></svg>");
    --db-icon-size: 12px;
}

.db-icon--check {
    --db-icon-source: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'><polyline points='20 6 9 17 4 12'/></svg>");
    --db-icon-size: 16px;
}

/* EDIT: Font Awesome's solid pen-to-square (fa-edit / fa-pen-to-square, U+F044), FILLED (owner
 * decision: every edit in the product is this glyph - no pencils - and it is filled, not an outline).
 * Taken from the same licensed Font Awesome Pro 6.1.2 solid face as the trash above. Every edit
 * draws it - a row's edit button through the row-action vocabulary (manifest rowAction.actions.edit)
 * and the inline affordance beside an editable value - so redrawing an edit is this one rule.
 *
 * 13px is the inline affordance's size; a row action sets its own
 * (--dz-grid-row-action-icon-size, 02-grid-shell.css). */
.db-icon--edit {
    --db-icon-source: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'><path d='M490 40Q507 58 507 80Q507 102 490 120L460 150L362 52L392 22Q410 5 432 5Q454 5 472 22L490 40ZM172 242 340 74 438 172 270 340Q261 349 248 353L160 383Q145 387 135 376Q125 367 129 352L159 264Q163 251 172 242ZM192 63Q206 64 215 73Q224 82 224 95Q224 109 215 118Q206 127 192 127H96Q82 128 73 137Q64 146 64 159V416Q64 430 73 439Q82 448 96 448H352Q366 448 375 439Q384 430 384 416V319Q384 306 393 297Q402 288 416 287Q430 288 439 297Q448 306 448 319V416Q447 457 420 484Q393 511 352 512H96Q55 511 28 484Q1 457 0 416V159Q1 118 28 91Q55 64 96 63H192Z'/></svg>");
    --db-icon-size: 13px;
}

/* THE TRANSACTION TYPE GLYPHS - recurring invoice, one-time invoice, credit memo, payment.
 *
 * The Billing Center export names all four, as assets/ico-recurring.svg, ico-one-time.svg,
 * ico-credit-memo.svg and ico-payment.svg. The path data below IS those files, with their C2PA
 * metadata blocks stripped.
 *
 * These are LIGHT (hollow) glyphs: a document outline with a folded corner, the detail drawn as line
 * work inside it. A filled card reads as a dark blob in a grid cell.
 *
 * They use a 640 viewBox against everyone else's 24 because they come from a different family than
 * the Lucide-derived line set. The difference is accepted: nothing else in a grid ROW is one of the
 * line icons.
 *
 * As masks they take currentColor, so they are the row's own ink rather than a hard-coded slate.
 *
 * 26px is the mock's, and it does not fit a narrow command cell (12px padding either side plus 26px
 * is 50 in a 30px cell, clipping the mark). The glyph belongs beside its words in the Type cell -
 * see .dz-type-cell below; nothing should put it back into a narrow command cell. */

/* THE TYPE CELL: a glyph and its words on one line. It lives with the glyphs so a host adopting the
 * bundle gets the box that holds them too.
 *
 * inline-flex rather than the icon simply preceding the text, because the glyph is 26px against
 * 14px text: on a shared baseline the icon sits low and the row looks uneven. The 8px gap is
 * --dz-space-1, the same step the badge uses between its mark and its label, which is the closest
 * thing in the grid to this pairing. */
.dz-type-cell {
    display: inline-flex;
    align-items: center;
    gap: var(--dz-space-1);
    min-width: 0;
    /* Not cosmetic: an inline-flex box sits on the text baseline, so the 26px glyph reserves a
       descender's worth of space under it and the whole grid's rows grow. MEASURED: 56px a row with
       the default alignment, 51px with this. */
    vertical-align: middle;
}

.dz-type-cell__label {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* A TYPE GLYPH THAT FETCHES ITS DOCUMENT - Billing Center's invoice and credit-memo glyphs download
 * the PDF (owner decision). The caller wraps the glyph in a real <a href download>, so a
 * middle-click, a keyboard Enter and "Save link as" all work; this rule only keeps it looking like the
 * glyph it is.
 *
 * `color: inherit` because the glyph paints with currentColor (.db-icon above): inside a bare anchor
 * it would turn link-blue at rest, which reads as every row carrying a hyperlink. The hover takes the
 * link colour instead - the one moment it should read as one - and the focus ring is the product's
 * own, offset so it clears the 26px glyph rather than clipping it. */
.dz-type-cell__document {
    display: inline-flex;
    border-radius: var(--dz-radius-xs);
    text-decoration: none;
}

/* The grid-scoped selectors are not decoration. 02-grid-shell.css colours every link in a data row
   (`.db-grid %dx%-rowsview .dx-data-row a:not(.dx-link)`, specificity 0,4,1), and a bare class loses
   to it. These match that specificity and sit later in the cascade (sources.json order), so they win
   without !important. */
.dz-type-cell__document,
.dz-type-cell__document,
.db-grid .dx-datagrid-rowsview .dx-data-row a.dz-type-cell__document,
.db-grid .dx-treelist-rowsview .dx-data-row a.dz-type-cell__document {
    color: inherit;
}

.dz-type-cell__document:hover,
.db-grid .dx-datagrid-rowsview .dx-data-row a.dz-type-cell__document:hover,
.db-grid .dx-treelist-rowsview .dx-data-row a.dz-type-cell__document:hover {
    color: var(--dz-link-hover);
}

.dz-type-cell__document:focus-visible {
    outline: var(--dz-control-focus-width) solid var(--dz-focus-color);
    outline-offset: 2px;
}

/* THE SUB-ACCOUNT MARK: a muted turn-down glyph before a value that hangs off another row.
 *
 * A TreeList's 14px-per-level indent is otherwise the only thing saying a row belongs to anyone,
 * and it stops holding once a viewer sorts by another column, searches, or collapses a parent. On
 * the Accounts worklist 58 of 80 accounts are sub-accounts, up to four levels deep.
 *
 * META, not Ink, and deliberately. The glyph qualifies the name beside it rather than competing with
 * it, and a column of full-strength arrows down the left of every name would read as a second
 * control. It is also why this is a glyph and not a word: a word on 58 rows is a column, and the
 * column that carries the same fact in full - which parent - is Parent Account.
 *
 * The caller supplies its own title, because the useful tooltip names the parent and this rule has
 * no idea who that is. */
.dz-subaccount-mark {
    margin-right: var(--dz-space-half);
    color: var(--dz-meta);
    font-size: 12px;
}

/* A recurring invoice: the document, with the cycle arrows through it. */
.db-icon--invoice-recurring {
    --db-icon-source: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 640 640' fill='black'><path d='M320,96h-128c-17.7,0-32,14.3-32,32v384c0,17.7,14.3,32,32,32h256c17.7,0,32-14.3,32-32v-256h-96c-35.3,0-64-28.7-64-64v-96ZM466.7,224l-114.7-114.7v82.7c0,17.7,14.3,32,32,32h82.7ZM128,128c0-35.3,28.7-64,64-64h133.5c17,0,33.3,6.7,45.3,18.7l122.5,122.6c12,12,18.7,28.3,18.7,45.3v261.4c0,35.3-28.7,64-64,64H192c-35.3,0-64-28.7-64-64V128ZM208,160h64c8.8,0,16,7.2,16,16s-7.2,16-16,16h-64c-8.8,0-16-7.2-16-16s7.2-16,16-16ZM208,224h64c8.8,0,16,7.2,16,16s-7.2,16-16,16h-64c-8.8,0-16-7.2-16-16s7.2-16,16-16ZM413.2,346.7c4-4,4-10.6,0-14.6l-37.8-37.9c-4-4-10.6-4-14.6,0-4,4-4,10.6,0,14.6l20.2,20.2h-88.6c-45.5,0-82.5,37-82.5,82.5s4.6,10.3,10.3,10.3,10.3-4.6,10.3-10.3c0-34.2,27.7-61.9,61.9-61.9h88.6l-20.2,20.2c-4,4-4,10.6,0,14.6,4,4,10.6,4,14.6,0l37.8-37.8h0ZM226.8,469.5c-1.9,1.9-3,4.6-3,7.3s1.1,5.4,3,7.3l37.8,37.8c4,4,10.6,4,14.6,0,4-4,4-10.6,0-14.6l-20.2-20.2h88.6c45.5,0,82.5-37,82.5-82.5s-4.6-10.3-10.3-10.3-10.3,4.6-10.3,10.3c0,34.2-27.7,61.9-61.9,61.9h-88.6l20.2-20.2c4-4,4-10.6,0-14.6-4-4-10.6-4-14.6,0l-37.8,37.7Z'/></svg>");
    --db-icon-size: 26px;
}

/* A one-time invoice: the same document, with a single charge in it. */
.db-icon--invoice-one-time {
    --db-icon-source: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 640 640' fill='black'><path d='M320,96h-128c-17.7,0-32,14.3-32,32v384c0,17.7,14.3,32,32,32h256c17.7,0,32-14.3,32-32v-256h-96c-35.3,0-64-28.7-64-64v-96ZM466.7,224l-114.7-114.7v82.7c0,17.7,14.3,32,32,32h82.7ZM128,128c0-35.3,28.7-64,64-64h133.5c17,0,33.3,6.7,45.3,18.7l122.5,122.6c12,12,18.7,28.3,18.7,45.3v261.4c0,35.3-28.7,64-64,64H192c-35.3,0-64-28.7-64-64V128ZM224,360v80c0,4.4,3.6,8,8,8h176c4.4,0,8-3.6,8-8v-80c0-4.4-3.6-8-8-8h-176c-4.4,0-8,3.6-8,8ZM192,360c0-22.1,17.9-40,40-40h176c22.1,0,40,17.9,40,40v80c0,22.1-17.9,40-40,40h-176c-22.1,0-40-17.9-40-40v-80ZM208,160h64c8.8,0,16,7.2,16,16s-7.2,16-16,16h-64c-8.8,0-16-7.2-16-16s7.2-16,16-16ZM208,224h64c8.8,0,16,7.2,16,16s-7.2,16-16,16h-64c-8.8,0-16-7.2-16-16s7.2-16,16-16ZM335.1,387.4v37.8c0,6.3-5.1,11.3-11.3,11.3s-11.3-5.1-11.3-11.3v-22.7c-4.7,0-9.2-3-10.8-7.8-2-6,1.2-12.4,7.2-14.4l11.3-3.8c3.5-1.1,7.3-.6,10.2,1.6s4.7,5.6,4.7,9.2Z'/></svg>");
    --db-icon-size: 26px;
}

/* A credit memo, and also a plain credit: the document with the amount going back out. */
.db-icon--invoice-credit-memo {
    --db-icon-source: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 640 640' fill='black'><path d='M320,96h-128c-17.7,0-32,14.3-32,32v384c0,17.7,14.3,32,32,32h256c17.7,0,32-14.3,32-32v-256h-96c-35.3,0-64-28.7-64-64v-96ZM466.7,224l-114.7-114.7v82.7c0,17.7,14.3,32,32,32h82.7ZM128,128c0-35.3,28.7-64,64-64h133.5c17,0,33.3,6.7,45.3,18.7l122.5,122.6c12,12,18.7,28.3,18.7,45.3v261.4c0,35.3-28.7,64-64,64H192c-35.3,0-64-28.7-64-64V128ZM208,160h64c8.8,0,16,7.2,16,16s-7.2,16-16,16h-64c-8.8,0-16-7.2-16-16s7.2-16,16-16ZM208,224h64c8.8,0,16,7.2,16,16s-7.2,16-16,16h-64c-8.8,0-16-7.2-16-16s7.2-16,16-16ZM210.6,363.2c-4.5-5.5-4.2-13.7,1-18.8l49.2-49.2c5.5-5.5,14.4-5.5,19.9,0,5.5,5.5,5.5,14.4,0,19.9l-25.2,25.2h92.7c45.2,0,82,35.5,84.3,80.1v4.4c0,46.6-37.7,84.4-84.3,84.4h-99.9c-7.1-.8-12.6-6.8-12.6-14.1s5.5-13.3,12.6-14h1.5c0,0,98.5,0,98.5,0,31.1,0,56.3-25.2,56.3-56.3v-2.9c-1.6-29.7-26.2-53.4-56.3-53.4h-92.7l25.2,25.2,1,1.1c4.5,5.5,4.2,13.7-1,18.8-5.1,5.1-13.3,5.5-18.8,1l-1.1-1-49.2-49.2-1-1.1Z'/></svg>");
    --db-icon-size: 26px;
}

/* A payment: a cheque rather than a document, because it is money arriving, not a bill. */
.db-icon--invoice-payment {
    --db-icon-source: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 640 640' fill='black'><path d='M263.2 65.7C258.4 63.3 252.8 63.5 248.1 66.1L200 93.6L151.9 66.1C147 63.3 140.9 63.3 136 66.2C131.1 69.1 128 74.3 128 80L128 560C128 565.7 131 571 136 573.8C141 576.6 147 576.7 152 573.9L200.1 546.4L248.2 573.9C252.8 576.6 258.5 576.7 263.3 574.3L320.1 545.9L376.9 574.3C381.7 576.7 387.3 576.5 392 573.9L440.1 546.4L488.2 573.9C493.2 576.7 499.2 576.7 504.2 573.8C509.2 570.9 512.2 565.7 512.2 560L512.2 80C512.2 74.3 509.2 69 504.2 66.2C499.2 63.4 493.2 63.3 488.2 66.1L440 93.6L391.9 66.1C387.3 63.4 381.6 63.3 376.8 65.7L320 94.1L263.2 65.7zM207.9 125.9L256.5 98.1L312.8 126.3C317.3 128.6 322.6 128.6 327.1 126.3L383.4 98.1L432 125.9C436.9 128.7 443 128.7 447.9 125.9L480 107.6L480 532.5L447.9 514.2C443 511.4 436.9 511.4 432 514.2L383.4 542L327.1 513.8C322.6 511.5 317.3 511.5 312.8 513.8L256.5 542L207.9 514.2C203 511.4 196.9 511.4 192 514.2L160 532.4L160 107.6L192.1 125.9C197 128.7 203.1 128.7 208 125.9zM224 208C215.2 208 208 215.2 208 224C208 232.8 215.2 240 224 240L416 240C424.8 240 432 232.8 432 224C432 215.2 424.8 208 416 208L224 208zM208 416C208 424.8 215.2 432 224 432L416 432C424.8 432 432 424.8 432 416C432 407.2 424.8 400 416 400L224 400C215.2 400 208 407.2 208 416zM224 304C215.2 304 208 311.2 208 320C208 328.8 215.2 336 224 336L416 336C424.8 336 432 328.8 432 320C432 311.2 424.8 304 416 304L224 304z'/></svg>");
    --db-icon-size: 26px;
}

/* The disclosure chevron, at the mock's 2.5px stroke - the same weight the check glyph above uses,
   and heavier than the set's 2px default because both of those are drawn small enough that a 2px
   stroke reads as grey rather than as a shape. Declared here so the grid's caret rule and any other
   call site share one artwork. */
.db-icon--chevron-down {
    --db-icon-source: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'><polyline points='6 9 12 15 18 9'/></svg>");
    --db-icon-size: 14px;
}

/* DEVEXTREME'S OWN GRID-TOOLBAR GLYPHS, REDRAWN FROM THIS SET.
 *
 * A grid's built-in toolbar items (column chooser, add / save / discard row, export) and the
 * toolbar actions views declare with DevExtreme icon names (revert, plus) draw from DevExtreme's
 * icon font. In a strip beside this set's download glyph and settings gear they read as a second
 * family (owner rule 2026-10-07: one glyph set per strip). Masking the font's element here reaches
 * every grid at once, legacy grids included, without a toolbarPreparing hook in each runtime.
 *
 * Scoped to the header panel, so the same icon names elsewhere (a form's revert, a popup's plus)
 * keep DevExtreme's drawing. Each --db-icon-source below repeats its glyph's artwork from the
 * .db-icon--<name> rule above; stylesheet-icons-menus.harness.js holds the two copies identical.
 * The element is DevExtreme's 18px icon box, the size this set draws a lone toolbar glyph. */
.dx-datagrid-header-panel .dx-button .dx-icon-column-chooser,
.dx-treelist-header-panel .dx-button .dx-icon-column-chooser,
.dx-datagrid-header-panel .dx-button .dx-icon-revert,
.dx-treelist-header-panel .dx-button .dx-icon-revert,
.dx-datagrid-header-panel .dx-button .dx-icon-plus,
.dx-treelist-header-panel .dx-button .dx-icon-plus,
.dx-datagrid-header-panel .dx-button .dx-icon-edit-button-addrow,
.dx-treelist-header-panel .dx-button .dx-icon-edit-button-addrow,
.dx-datagrid-header-panel .dx-button .dx-icon-edit-button-save,
.dx-treelist-header-panel .dx-button .dx-icon-edit-button-save,
.dx-datagrid-header-panel .dx-button .dx-icon-edit-button-cancel,
.dx-treelist-header-panel .dx-button .dx-icon-edit-button-cancel,
.dx-datagrid-header-panel .dx-button .dx-icon-xlsxfile,
.dx-treelist-header-panel .dx-button .dx-icon-xlsxfile,
.dx-datagrid-header-panel .dx-button .dx-icon-export,
.dx-treelist-header-panel .dx-button .dx-icon-export {
    background-color: currentColor;
    -webkit-mask-image: var(--db-icon-source);
    mask-image: var(--db-icon-source);
    -webkit-mask-repeat: no-repeat;
    mask-repeat: no-repeat;
    -webkit-mask-position: center;
    mask-position: center;
    -webkit-mask-size: contain;
    mask-size: contain;
}

    .dx-datagrid-header-panel .dx-button .dx-icon-column-chooser::before,
.dx-treelist-header-panel .dx-button .dx-icon-column-chooser::before,
    .dx-datagrid-header-panel .dx-button .dx-icon-revert::before,
.dx-treelist-header-panel .dx-button .dx-icon-revert::before,
    .dx-datagrid-header-panel .dx-button .dx-icon-plus::before,
.dx-treelist-header-panel .dx-button .dx-icon-plus::before,
    .dx-datagrid-header-panel .dx-button .dx-icon-edit-button-addrow::before,
.dx-treelist-header-panel .dx-button .dx-icon-edit-button-addrow::before,
    .dx-datagrid-header-panel .dx-button .dx-icon-edit-button-save::before,
.dx-treelist-header-panel .dx-button .dx-icon-edit-button-save::before,
    .dx-datagrid-header-panel .dx-button .dx-icon-edit-button-cancel::before,
.dx-treelist-header-panel .dx-button .dx-icon-edit-button-cancel::before,
    .dx-datagrid-header-panel .dx-button .dx-icon-xlsxfile::before,
.dx-treelist-header-panel .dx-button .dx-icon-xlsxfile::before,
    .dx-datagrid-header-panel .dx-button .dx-icon-export::before,
.dx-treelist-header-panel .dx-button .dx-icon-export::before {
        content: none;
    }

/* columns */
.dx-datagrid-header-panel .dx-button .dx-icon-column-chooser,
.dx-treelist-header-panel .dx-button .dx-icon-column-chooser {
    --db-icon-source: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><rect width='18' height='18' x='3' y='3' rx='2'/><path d='M9 3v18'/><path d='M15 3v18'/></svg>");
}

/* reset: a view's Reset Columns action */
.dx-datagrid-header-panel .dx-button .dx-icon-revert,
.dx-treelist-header-panel .dx-button .dx-icon-revert {
    --db-icon-source: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><path d='M3 7v6h6'/><path d='M21 17a9 9 0 0 0-15-6.7L3 13'/></svg>");
}

/* plus: a view's Add action and the built-in Add a row */
.dx-datagrid-header-panel .dx-button .dx-icon-plus,
.dx-treelist-header-panel .dx-button .dx-icon-plus,
.dx-datagrid-header-panel .dx-button .dx-icon-edit-button-addrow,
.dx-treelist-header-panel .dx-button .dx-icon-edit-button-addrow {
    --db-icon-source: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><path d='M12 5v14'/><path d='M5 12h14'/></svg>");
}

/* save */
.dx-datagrid-header-panel .dx-button .dx-icon-edit-button-save,
.dx-treelist-header-panel .dx-button .dx-icon-edit-button-save {
    --db-icon-source: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><path d='M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z'/><path d='M17 21v-8H7v8'/><path d='M7 3v5h8'/></svg>");
}

/* undo: the built-in Discard changes */
.dx-datagrid-header-panel .dx-button .dx-icon-edit-button-cancel,
.dx-treelist-header-panel .dx-button .dx-icon-edit-button-cancel {
    --db-icon-source: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><path d='M9 14 4 9l5-5'/><path d='M4 9h10.5a5.5 5.5 0 0 1 5.5 5.5 5.5 5.5 0 0 1-5.5 5.5H11'/></svg>");
}

/* download: the built-in export on a grid that is not on the shared shell */
.dx-datagrid-header-panel .dx-button .dx-icon-export,
.dx-treelist-header-panel .dx-button .dx-icon-export,
.dx-datagrid-header-panel .dx-button .dx-icon-xlsxfile,
.dx-treelist-header-panel .dx-button .dx-icon-xlsxfile {
    --db-icon-source: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><path d='M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4'/><polyline points='7 10 12 15 17 10'/><line x1='12' y1='15' x2='12' y2='3'/></svg>");
}

/* ================================================================================================
   The grid's own strip: record-level actions and the group-by hint, directly above the card.
   ================================================================================================ */

/* 12px between toolbar items, which is what both mockup toolbar rows set as their flex gap.
 *
 * DevExtreme uses no gap here - it puts `padding-right: 5px` on each .dx-toolbar-item - so
 * restating the padding is what reaches it; a gap on the container would add to the 5px rather
 * than replace it.
 *
 * :last-child keeps the trailing item flush, so the row still ends at the card's edge rather than
 * 12px short of it.
 *
 * The `after` group needs two more zeroes: DevExtreme also gives its items
 * `padding-inline-start: 5px` and its search panel a 15px left margin (MEASURED: 17px between
 * controls and 32px before the search box, against the page toolbar's 12px). */
.db-grid .dx-datagrid-header-panel .dx-toolbar .dx-toolbar-item,
.db-grid .dx-treelist-header-panel .dx-toolbar .dx-toolbar-item {
    padding-right: 12px;
    padding-inline-start: 0;
}

    .db-grid .dx-datagrid-header-panel .dx-toolbar .dx-toolbar-item:last-child,
.db-grid .dx-treelist-header-panel .dx-toolbar .dx-toolbar-item:last-child {
        padding-right: 0;
    }

.db-grid .dx-datagrid-header-panel .dx-toolbar .dx-datagrid-search-panel,
.db-grid .dx-treelist-header-panel .dx-toolbar .dx-treelist-search-panel {
    margin-left: 0;
}

/* The toolbar's leading group is a TABLE, which sizes to its content and overflows its container,
   letting the last item hang off the right edge where the header panel clips it. Capping it at the
   container width is what makes the group panel's min-width (below) the thing that gives. */
.db-grid .dx-datagrid-header-panel .dx-toolbar .dx-toolbar-before,
.db-grid .dx-treelist-header-panel .dx-toolbar .dx-toolbar-before {
    max-width: 100%;
}

/* ...and when even the compressed group panel is not enough, the selection block takes a second line
   rather than being cut off: a count and its actions read fine stacked, where a half-visible
   "Assign Lab" reads as breakage. DevExtreme fixes the toolbar at 36px, so without these two
   heights the second line renders ON TOP of the column headers instead of pushing them down.

   The floor is the controls' own height (--dz-control-visual-height, 40), not DevExtreme's 36;
   at 36 the controls hang 4px below the box and eat the gap to the column headers
   (02-grid-shell.css) - all of it in compact. */
.db-grid .dx-datagrid-header-panel .dx-toolbar,
.db-grid .dx-treelist-header-panel .dx-toolbar,
.db-grid .dx-datagrid-header-panel .dx-toolbar .dx-toolbar-items-container,
.db-grid .dx-treelist-header-panel .dx-toolbar .dx-toolbar-items-container {
    height: auto;
    min-height: var(--dz-control-visual-height);
}

/* PHONES ONLY (the condition and its reason are in 02-grid-shell.css, TOUCH HIT AREAS): the strip's
   search box stops at DevExtreme's own default width, 160px.

   DevExtreme pins the strip's `after` group to the right edge and gives the `before` group whatever
   is left; an `after` group wider than the strip runs off its LEFT edge and over the `before` items,
   and no overflow menu can take the search box. A view's searchPanel width is chosen for a desktop
   (MEASURED at 360px: TOS Documents' 260 put the box at x=-8; Invite Links' 232 beside its Create
   button put that button at x=-237). At 160 the box and the gear fit any phone strip, and a page's
   own buttons go to the strip's overflow menu (settleToolbar, grid.toolbar.js). DevExtreme measures
   the box after this rule applies, so its arithmetic sees 160. */
@media (max-width: 824.98px) and (pointer: coarse) {
    .db-grid .dx-datagrid-header-panel .dx-toolbar .dx-datagrid-search-panel,
.db-grid .dx-treelist-header-panel .dx-toolbar .dx-treelist-search-panel {
        max-width: 160px;
    }
}

/* The master-detail expander.
 *
 * The column is --dz-grid-expand-cell-width and the glyph inside it is --dz-grid-expand-caret-size,
 * centred in a target big enough to press; both carry their measurements where they are declared.
 * The caret is Body rather than Ink because it is chrome next to the row's own content, and it
 * turns rather than swapping glyphs, so open and closed are obviously one control in two states. */
/* .dx-datagrid-expand is doing real work in this selector. On a GROUPED grid DevExtreme emits one
   indent cell per group level, each also classed dx-command-expand; only the one carrying
   dx-datagrid-expand holds a caret. Without the extra class this rule would reach the spacers. */
.db-grid .dx-datagrid-content .dx-datagrid-table .dx-row > td.dx-command-expand.dx-datagrid-expand,
.db-grid .dx-treelist-content .dx-treelist-table .dx-row > td.dx-command-expand.dx-treelist-expand {
    width: var(--dz-grid-expand-cell-width);
    min-width: var(--dz-grid-expand-cell-width);
    max-width: var(--dz-grid-expand-cell-width);
    /* Both of the next two undo a DevExtreme declaration.
     *
     * `padding-top: 0` beats `.dx-datagrid-table .dx-row .dx-command-expand.dx-datagrid-group-space
     * { padding-top: 7px }` in dx.light.css - (0,3,0) against this rule's (0,4,2). That 7px suits a
     * one-line GROUP row; on a 51px data row carrying a 26px badge it floats the caret near the top.
     *
     * `vertical-align: middle` is the other half: the cell inherits `top` from the rowsview, so
     * with the padding gone alone the caret would sit against the cell's top edge instead. */
    padding-top: 0;
    padding-right: 0;
    text-align: center;
    vertical-align: middle;
}

/* The grouping indents. They repeat the expander's width rather than asking for a spacer's, because
   DevExtreme sizes every expand column to the max across them (see --dz-grid-expand-cell-width) and
   this rule is what keeps DevExtreme's own 30px default from being that max. Anything SMALLER here
   is silently ignored; anything larger drags the caret column out with it. */
.db-grid .dx-datagrid-content .dx-datagrid-table .dx-row > td.dx-command-expand.dx-datagrid-group-space:not(.dx-datagrid-expand),
.db-grid .dx-treelist-content .dx-treelist-table .dx-row > td.dx-command-expand.dx-treelist-group-space:not(.dx-treelist-expand) {
    width: var(--dz-grid-expand-cell-width);
    min-width: var(--dz-grid-expand-cell-width);
    max-width: var(--dz-grid-expand-cell-width);
    padding-right: 0;
}

/* THE CARET SITS RIGHT OF THE ROW MENU, by being painted out of order - DevExtreme will not declare
 * it out of order.
 *
 * The mockup draws one cell holding the three-dot menu, then the chevron, then the info badge.
 * DevExtreme builds a row's command cells structurally - selection, one expand cell per group level,
 * the master-detail expander, then everything else - so the chevron lands BEFORE the actions column.
 * MEASURED: visibleIndex on the `buttons` columns or on the expand column changes nothing.
 *
 * `position: relative` moves the two painted boxes past each other without touching the table: the
 * colgroup, column widths and every other cell stay put. The offsets are each other's width through
 * the same tokens that set those widths, so the pair cannot drift apart.
 *
 * Hit testing follows the box (MEASURED: elementFromPoint over the caret's new position returns the
 * caret, and clicking expands the row). DevExtreme binds the toggle to the 14px glyph, not the cell,
 * so a click on the cell's padding does nothing either way.
 *
 * DATA ROWS ONLY. A group row's caret is the same class in the same column and must not move: it has
 * no actions cell beside it to swap with, and it is the control that collapses a whole band.
 *
 * AND ONLY WHEN A ROW MENU FOLLOWS. A master-detail grid with no actions column has nothing to swap
 * with, so an unconditional shift slid the caret over the first data cell (MEASURED on Group View's
 * history popup: the date read "/11/2026"). A browser without :has() leaves the caret before the
 * menu, which is the order DevExtreme builds and still legible. */
.db-grid .dx-datagrid-rowsview .dx-data-row > td.dx-command-expand.dx-datagrid-expand:has(+ td.dx-command-edit),
.db-grid .dx-treelist-rowsview .dx-data-row > td.dx-command-expand.dx-treelist-expand:has(+ td.dx-command-edit) {
    position: relative;
    left: var(--dz-grid-action-cell-width);
}

.db-grid .dx-datagrid-rowsview .dx-data-row > td.dx-command-expand.dx-datagrid-expand + td.dx-command-edit,
.db-grid .dx-treelist-rowsview .dx-data-row > td.dx-command-expand.dx-treelist-expand + td.dx-command-edit {
    position: relative;
    left: calc(-1 * var(--dz-grid-expand-cell-width));
}

/* Colour, size and rotation ONLY - deliberately no `display`.
 *
 * A host decides PER ROW whether a row has anything to expand by setting `display: none` on the
 * caret (Billing Center's .no-payments-row > div, (0,1,1)). Any display here would be (0,3,0) and
 * beat it, giving every row a caret. Centring is the cell's job (text-align above).
 *
 * That is also why the artwork is a MASK declared inline rather than .db-icon on the element:
 * .db-icon sets `display: inline-block`.
 *
 * WHY THE GLYPH IS REPLACED RATHER THAN RECOLOURED. DevExtreme draws a FILLED TRIANGLE from
 * DXIcons; every mock draws a stroked chevron (24x24 viewBox, 2.5px round-capped stroke, the icon
 * set's family). `content: none` on the pseudo-element stops the two stacking - otherwise the
 * triangle renders clipped to the chevron mask.
 *
 * 140ms and this easing curve are the mock's. */
.db-grid .dx-datagrid-rowsview .dx-datagrid-group-closed,
.db-grid .dx-treelist-rowsview .dx-treelist-group-closed,
.db-grid .dx-datagrid-rowsview .dx-datagrid-group-opened,
.db-grid .dx-treelist-rowsview .dx-treelist-group-opened {
    color: var(--dz-body);
    background-color: currentColor;
    -webkit-mask-image: var(--db-icon-source);
    mask-image: var(--db-icon-source);
    -webkit-mask-repeat: no-repeat;
    mask-repeat: no-repeat;
    -webkit-mask-position: center;
    mask-position: center;
    -webkit-mask-size: var(--dz-grid-expand-caret-size);
    mask-size: var(--dz-grid-expand-caret-size);
    --db-icon-source: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'><polyline points='6 9 12 15 18 9'/></svg>");
    height: var(--dz-grid-expand-caret-size);
    line-height: 1;
    transition: transform 140ms cubic-bezier(0.2, 0, 0.1, 1), color 140ms cubic-bezier(0.2, 0, 0.1, 1);
}

    .db-grid .dx-datagrid-rowsview .dx-datagrid-group-closed::before,
.db-grid .dx-treelist-rowsview .dx-treelist-group-closed::before,
    .db-grid .dx-datagrid-rowsview .dx-datagrid-group-opened::before,
.db-grid .dx-treelist-rowsview .dx-treelist-group-opened::before {
        content: none;
    }

    .db-grid .dx-datagrid-rowsview .dx-datagrid-group-closed:hover,
.db-grid .dx-treelist-rowsview .dx-treelist-group-closed:hover,
    .db-grid .dx-datagrid-rowsview .dx-datagrid-group-opened:hover,
.db-grid .dx-treelist-rowsview .dx-treelist-group-opened:hover {
        color: var(--dz-ink);
    }

    /* One glyph, two states. The artwork is the OPEN state - a chevron pointing down at the panel
       it has revealed - and closed is that turned a quarter anticlockwise to point at the row. */
    .db-grid .dx-datagrid-rowsview .dx-datagrid-group-closed,
.db-grid .dx-treelist-rowsview .dx-treelist-group-closed {
        transform: rotate(-90deg);
    }

    /* Size only, and only inside a group row; everything else comes from the rule above. (0,4,0)
       against that one's (0,3,0), so it wins on specificity, and it declares no `display` for the
       reason stated there. */
    .db-grid .dx-datagrid-rowsview .dx-group-row .dx-datagrid-group-closed,
.db-grid .dx-treelist-rowsview .dx-group-row .dx-treelist-group-closed,
    .db-grid .dx-datagrid-rowsview .dx-group-row .dx-datagrid-group-opened,
.db-grid .dx-treelist-rowsview .dx-group-row .dx-treelist-group-opened {
        -webkit-mask-size: var(--dz-grid-group-caret-size);
        mask-size: var(--dz-grid-group-caret-size);
        height: var(--dz-grid-group-caret-size);
    }

/* The group panel is an empty drop target until a column is dragged onto it, so it has to look like
   one. DevExtreme renders it as bare text, which reads as a stray sentence in the toolbar. */
.db-grid .dx-datagrid-header-panel .dx-datagrid-group-panel,
.db-grid .dx-treelist-header-panel .dx-treelist-group-panel {
    display: inline-flex;
    align-items: center;
    box-sizing: border-box;
    /* 160px: this panel is the only item on the row that can give width back. dx.light.css gives it
     * width:100%, so it still takes the whole gap when there is one, and .dx-group-panel-message
     * carries text-overflow: ellipsis. 160px keeps it a credible drop target. (MEASURED at a 1150px
     * viewport, a 300px floor pushed the `before` group to 1064px in a 991px toolbar and clipped the
     * selection block.)
     *
     * No `flex` here, deliberately. DevExtreme renders .dx-toolbar-before as display:table with
     * table-cell items, so a flex-basis is read by nothing. The pair that works is this floor plus
     * the max-width on the table above. */
    min-width: 160px;
    height: var(--dz-interactive-target-size);
    padding: 0 20px;
    /* A109 (ClickUp 86894yg24, WL-32): named so a customized Surface can move it -- see the token's
       own comment (00-tokens.css). Same resolved default as before this item (var(--dz-page)). */
    background: var(--dz-grid-group-panel-background);
    border: 1px dashed var(--dz-border);
    border-radius: var(--dz-control-radius);
}

    .db-grid .dx-datagrid-header-panel .dx-datagrid-group-panel .dx-group-panel-message,
.db-grid .dx-treelist-header-panel .dx-treelist-group-panel .dx-group-panel-message {
        padding: 0;
        color: var(--dz-body);
        font-family: var(--dz-font-family);
        font-size: 14px;
        opacity: 1;
    }

/* What is selected, and what can be done with it. Hidden with nothing selected, because an action
   that needs a selection has nothing to say until there is one - and a permanently visible disabled
   button is a worse answer than no button: it spends toolbar room advertising something the viewer
   cannot do and cannot see how to enable. */
.db-selection-actions {
    display: inline-flex;
    align-items: center;
    /* Wraps rather than clips. Every part of this block is white-space: nowrap, so with the row out
       of width it has no give at all and the tail of it disappears under the card's edge. On a wide
       screen this changes nothing - the block is one line and there is room. */
    flex-wrap: wrap;
    /* 22px, from the mockup (see the token), so the count and actions read as separate affordances
       rather than one run of text. */
    gap: var(--dz-selection-actions-gap);
    /* ...but only horizontally. Between wrapped LINES 22px is a gap, not a rhythm. */
    row-gap: var(--dz-space-half);
}

.db-selection-actions[hidden] {
    display: none;
}

/* PHONES ONLY (the condition and its reason are in 02-grid-shell.css, TOUCH HIT AREAS): the block
   leaves the toolbar strip for a row of its own after it (selectionactions.js places it). The strip
   is a fixed 40px that DevExtreme measures once, while the block is empty, so a block that needs a
   second line wraps down over the grid. In a row of normal flow the block takes the width of the
   phone and wraps whole items - the count and each action are nowrap - onto as many lines as it
   needs, and the header and rows below move down with it. Hidden is no height at all, so a grid
   with nothing selected has no gap. The bottom margin is the strip's own gap to the column headers,
   which this row now sits between. */
@media (max-width: 824.98px) and (pointer: coarse) {
    .db-selection-actions-row {
        display: flex;
        flex-wrap: wrap;
        gap: var(--dz-selection-actions-gap);
        row-gap: var(--dz-space-half);
        margin-bottom: var(--dz-grid-toolbar-margin-bottom);
    }

    .db-selection-actions-row[hidden] {
        display: none;
    }
}

.db-selection-actions__count {
    color: var(--dz-ink);
    font-family: var(--dz-font-family);
    font-size: 14px;
    /* 1.2, from the mockup (16.8px); the inherited line-height renders the run 19px tall and loose
       beside the 40px group panel. */
    line-height: 1.2;
    font-weight: 600;
    white-space: nowrap;
}

/* Link teal rather than a button, because these act on the rows the viewer just picked - they
   belong to the selection, not to the page. Buttons here would put three more bordered boxes in a
   strip that already has one. */
/* 7px, and deliberately not --dz-space-1's 8px.
 *
 * The mockup measures the gap between a selection action's glyph and its text at 7px, one pixel off
 * the spacing scale. This is a glyph against its own label inside one control, and the design chose
 * the tighter value on purpose. Overriding --dz-space-1 to 7px to avoid a literal here would move
 * every other 8px in the package. */
.db-selection-actions__action {
    display: inline-flex;
    align-items: center;
    /* 1.2, the same step as the count beside it - a button does not inherit the page's line-height,
       so without this the two halves of one run sit at different heights. */
    line-height: 1.2;
    gap: 7px;
    padding: 0;
    color: var(--dz-link);
    background: none;
    border: 0;
    cursor: pointer;
    font-family: var(--dz-font-family);
    font-size: 14px;
    /* 700, from the mockup - heavier than the 600 of the count beside it, which is the one place
       in the package where an action outweighs its own label. A selection action is the only thing
       on the row that does something. */
    font-weight: var(--dz-selection-actions-font-weight);
    white-space: nowrap;
}

    .db-selection-actions__action:hover {
        color: var(--dz-link-hover);
        text-decoration: underline;
    }

    /* :focus, not only :focus-visible. A click focuses the button without :focus-visible, and
       Bootstrap's `button:focus { outline: 5px auto }` then wins; Chrome's auto outline traces the
       glyph and the text separately, so it hugs the label in a curved outline. A pointer gets no
       ring, the same as every other package control; the keyboard ring is the box below. */
    .db-selection-actions__action:focus {
        outline: none;
    }

    .db-selection-actions__action:focus-visible {
        border-radius: var(--dz-radius-xs);
        box-shadow: 0 0 0 3px var(--dz-focus-ring);
    }

/* A TREELIST'S CHECKBOX IN A COLUMN OF ITS OWN (grid.selectcolumn.js, `.DbSelectColumn()`).
 *
 * The column draws its own checkbox and select-all; these rules take away the built-in pair
 * DevExtreme still draws inside the first data cell and header, and the room it reserved for them.
 * Hidden rather than switched off because a TreeList has no option to keep selection and drop its
 * box - selection has to stay on for the widget to track rows at all. Scoped to the expandable
 * cell and the select-all header so no other checkbox in the grid is caught.
 *
 * The GUTTER, closed: DevExtreme pads the icon container for the checkbox it positions there, and
 * indents the first header 44px (the rule in 02-grid-shell.css) to clear its select-all. With both
 * gone the header only has to clear the one 14px indent box every top-level row keeps, so it
 * lines up with the rows' first character. */
.db-grid--select-column .dx-treelist-rowsview .dx-treelist-cell-expandable .dx-select-checkbox,
.db-grid--select-column .dx-treelist-headers .dx-treelist-select-all .dx-select-checkbox {
    display: none;
}

.db-grid--select-column .dx-treelist-rowsview .dx-data-row .dx-treelist-icon-container {
    padding-right: 0;
}

.db-grid.db-grid--select-column .dx-treelist-headers .dx-header-row > td.dx-treelist-select-all {
    padding-left: calc(var(--dz-grid-cell-padding-x) + 14px);
}

/* A TREELIST WITHOUT A SELECT COLUMN (PP-D72, audit row A-C). Every row's first cell keeps the 14px
   indent box DevExtreme draws before the text (an icon container, or an empty space on a leaf), so the
   header text sat 14px left of the rows' first text (MEASURED on Admin's APN list: header text at 347,
   row text at 361, both cells 12px padded). The header's first cell takes the same 14px, the number
   kept as DevExtreme's like the rule above. The cell holding a select-all (02-grid-shell.css) and a
   command column's cell are not this cell, and a grid with a select column is the rule above. */
.db-grid:not(.db-grid--select-column) .dx-treelist-headers .dx-header-row > td:first-child:not(.dx-treelist-select-all, [class*="dx-command-"]) {
    padding-left: calc(var(--dz-grid-cell-padding-x) + 14px);
}

/* The row cell's font goes to zero for the reason the shared -with-icons rule gives: DevExtreme
   writes a whitespace text node after the template's span (~4px at 14px), which puts the centred box
   2px left of the header's. A template button does not earn the -with-icons class, so that rule
   never reaches this cell. */
.db-grid--select-column .dx-data-row > td.dx-command-edit:has(.db-select-column__cell) {
    font-size: 0;
}

/* The column's own boxes sit on the cell's middle line, header and rows alike, so the select-all
   and every row's box share one x and one centre. */
.db-select-column__cell,
.db-select-column__header {
    display: inline-flex;
    vertical-align: middle;
}

/* PHONES ONLY: this column's own checkboxes reach 44px, as the built-in selection checkbox does (the
   condition, the reason and the cell-clipping rules are in 02-grid-shell.css, TOUCH HIT AREAS). The
   boxes are 14px in a 22px action cell, so 15px past every side is 44 and the bleed into each
   neighbour is 11px, inside its 12px padding. A body cell already lets its overflow out through that
   file's `.dx-command-edit` rule; the header cell is named here for the same reason. */
@media (max-width: 824.98px) and (pointer: coarse) {
    .db-grid--select-column .dx-datagrid-headers .dx-header-row > td.dx-command-edit:has(.db-select-column__header):not(:last-child),
.db-grid--select-column .dx-treelist-headers .dx-header-row > td.dx-command-edit:has(.db-select-column__header):not(:last-child) {
        overflow-x: visible;
        overflow-y: clip;
    }

    .db-select-column__cell,
    .db-select-column__header {
        position: relative;
    }

    .db-select-column__cell::after,
    .db-select-column__header::after {
        content: "";
        position: absolute;
        inset: -15px;
    }
}

/* A TREELIST'S EXPAND CARET IN A COLUMN OF ITS OWN (grid.treecaret.js, `.DbTreeCaretColumn()`).
 *
 * The built-in caret keeps its BOX and loses its glyph, unlike the built-in checkbox above. That box
 * is also the last of the row's `level + 1` indent boxes, so `display: none` on it would shorten the
 * indent on expandable rows only and put a parent's name 14px left of its leaf sibling's. The glyph
 * is the inner <span> - dx.light.css draws it from `.dx-treelist-collapsed span::before` - so that is
 * what goes. */
.db-grid--tree-caret .dx-treelist-rowsview .dx-data-row .dx-treelist-collapsed > span,
.db-grid--tree-caret .dx-treelist-rowsview .dx-data-row .dx-treelist-expanded > span {
    display: none;
}

/* Font to zero for the whitespace reason the select column gives above. */
.db-grid--tree-caret .dx-data-row > td.dx-command-edit:has(.db-tree-caret) {
    font-size: 0;
}

/* The caret: the expander's artwork, size, colour and turn (the group caret rule above), so a
 * TreeList row and a DataGrid row read as one control.
 *
 * The BOX is 18px, the row-action anchor's, so a caret beside a row menu shares its target and its
 * middle line; the cell centres it (the command-cell rule in 02-grid-shell.css). The GLYPH is a mask
 * on ::before rather than on the box, because a mask clips the box's own shadow and the focus ring is
 * a shadow. */
.db-tree-caret {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 18px;
    height: 18px;
    vertical-align: middle;
    color: var(--dz-body);
    border-radius: var(--dz-radius-xs);
    cursor: pointer;
    transition: color 140ms cubic-bezier(0.2, 0, 0.1, 1);
}

    .db-tree-caret::before {
        content: "";
        width: var(--dz-grid-expand-caret-size);
        height: var(--dz-grid-expand-caret-size);
        background-color: currentColor;
        -webkit-mask-image: var(--db-icon-source);
        mask-image: var(--db-icon-source);
        -webkit-mask-repeat: no-repeat;
        mask-repeat: no-repeat;
        -webkit-mask-position: center;
        mask-position: center;
        -webkit-mask-size: var(--dz-grid-expand-caret-size);
        mask-size: var(--dz-grid-expand-caret-size);
        --db-icon-source: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'><polyline points='6 9 12 15 18 9'/></svg>");
        transition: transform 140ms cubic-bezier(0.2, 0, 0.1, 1);
    }

    .db-tree-caret:hover {
        color: var(--dz-ink);
    }

    /* A pointer gets no ring, the same as every other package control; the keyboard gets the box. */
    .db-tree-caret:focus {
        outline: none;
    }

    .db-tree-caret:focus-visible {
        box-shadow: 0 0 0 3px var(--dz-focus-ring);
    }

/* One glyph, two states, as on the expander: the artwork is OPEN, and closed is that turned a
   quarter anticlockwise to point at the row. */
.db-tree-caret--collapsed::before {
    transform: rotate(-90deg);
}

/* COMPACT DENSITY for a grid.
 *
 * Comfortable is the default; compact is an explicit opt-in the viewer chooses, stored with their
 * grid appearance preferences and published as an attribute on the grid shell. It is CSS only - no
 * widget option changes - so the settings menu flips it live by rewriting the attribute.
 *
 * The rows and the spacing around them - the pager band, the gap under the toolbar - take the same
 * values as the body band, so a grid compact on a page that is not (the invoice editor's tab grids
 * declare it) spaces like one. No control size is here: the toolbar's search box and buttons are
 * the same at every density (20-density.css). */
.db-grid[data-db-density="compact"] {
    /* Rows are padded 4px, below the mock's 6px (owner decision: "still not compact-y enough"); the
       header row keeps 6px. The row height must move too, since it is the larger of the two: the
       tallest thing a compact row carries is the carrier mark's 22px square, and 22 + 4 + 4 is 30. */
    --dz-grid-row-padding-y: 4px;
    --dz-grid-header-padding-y: 6px;
    --dz-grid-cell-padding-x: 6px;
    --dz-grid-row-height: 30px;
    --dz-grid-toolbar-margin-bottom: var(--dz-space-half);
    --dz-grid-pager-padding-y: var(--dz-space-half);
    /* A chip's padding steps down with the row's: 1px makes a label chip the 20px the status and
       carrier badges beside it already are, where the default 3px made it 24 and set the height of
       every compact row that carried one. Padding, not type - the chip's text is unchanged. */
    --dz-badge-padding-y: 1px;
}

/* A LABEL CHIP IN A ROW THAT CANNOT WRAP CARRIES NO ROW GAP. Its bottom margin
 * (voyager.badges.css) is the gap between two wrapped lines of chips; in a single-line cell there is
 * no second line, and the margin only makes the row taller (MEASURED on compact Subscribers: 42-43px
 * rows against 34-35px). With word wrap on the chips can wrap, so the gap comes back.
 * Middle-aligned like the badges beside it. */
.db-grid:not([data-db-grid-word-wrap="true"]) .db-badge.db-badge--label {
    margin-bottom: 0;
    vertical-align: middle;
}

/* ...AND COMPACT TIGHTENS THE SPACING AROUND THE ROWS - never the size of a control.
 *
 * The grid's own compact block (19-grid-selection.css) is the whole of what compact means for a row.
 * This is the same step for the rest of the page's SPACING: a card's body, a detail field, a form's
 * rhythm, a metric tile, the pager band, the padding inside a card or dashboard header strip, and
 * the gaps under the page toolbar and a grid's toolbar.
 *
 * WHAT IT NEVER TOUCHES IS A CONTROL: search box, gear, export and download, buttons, a header's
 * segmented switch are the same size at every density (owner decision, from the mocks: density
 * changes the content and the space around it, not the controls). It also keeps a page consistent
 * when a grid is compact and the page is not: a saved Compact stamps every grid shell, but the body
 * only takes it on a page with a density gear. A header strip still shrinks - its padding halves
 * around controls that do not (also an owner decision).
 *
 * THE PAGE PADDING IS LEFT ALONE TOO. #mainbox is Voyager's wrapper, not a construct of this
 * package, and its padding is written in 44 separate views in four different shapes - `20px`,
 * `10px`, `20px 20px 0 20px` and a handful of one-offs. A blanket compact override flattens the ten
 * that deliberately carry no bottom padding.
 *
 * WHY IT IS A TOKEN LIST AND NOT A LIST OF RULES. Every value here is declared at :root and read at
 * its use site, so this block is the one place the compact numbers live, and a surface that starts
 * reading one of these tokens gets the density for free - which is why a control-size token must
 * not be added here.
 *
 * NOTHING HERE HIDES DATA, which is the rule this band is measured against. Every value is a
 * padding, a margin or a floor. No type size changes, no column is dropped, no row is truncated,
 * and a compact page shows strictly more of the same content than a default one. */
body[data-db-density="compact"] {
    --dz-page-toolbar-margin-bottom: var(--dz-space-1);
    /* .db-card-header / .db-card__header (13-detail-segments-card-header.css): the strip tightens
       around its controls, which keep their size. 6 + a 32px control + 6 is 44, so that is the
       floor - higher would hold the strip open, lower does nothing. */
    --dz-card-header-min-height: 44px;
    --dz-card-header-padding: 6px 12px;
    --dz-card-header-gap: var(--dz-space-1);
    --dz-card-body-padding: var(--dz-space-1);
    /* .db-detail-segments__add (13-detail-segments-card-header.css), matching .data-container's
       compact padding so the add cell stands the same height as a data cell. */
    --dz-detail-segments-add-padding: 6px;
    /* .db-detail-segments__cell's floor (13-detail-segments-card-header.css). */
    --dz-field-cell-min-height: 54px;
    /* .db-field-list's rows (05-field-badge.css): half the padding, as the detail grid's cells. */
    --dz-field-list-cell-padding: 6px;
    /* .db-usage's gap (16-card-usage-udf.css). */
    --dz-usage-gap: var(--dz-space-1);
    /* The two dashboards' strips, on this list rather than in a rule of their own, and the harness
       enforces it: a SECOND `body[data-db-density="compact"]` block would be shadowed by this one
       for a lookup that finds a rule by selector text, first match wins. Padding only: 5 -> 3
       around the strip's 31px controls, so 37 is the floor they produce. */
    --dz-dashboard-header-min-height: 37px;
    --dz-dashboard-header-padding-y: 3px;
    /* A hand-laid form's rhythm (29-form.css): one lattice step down each, 16 -> 12 between fields
       and 24 -> 16 above a section. A dxForm's own item spacing is DevExtreme's layout and does not
       move here. */
    --dz-form-field-gap: var(--dz-space-1half);
    --dz-form-section-gap: var(--dz-space-2);
    --dz-grid-toolbar-margin-bottom: var(--dz-space-half);
    --dz-grid-pager-padding-y: var(--dz-space-half);
    /* The metric tile's floor. At compact 150px holds four mostly-empty tiles open across the top of
       a dashboard, where a viewer would see the density has not arrived. 120px still clears a 36px
       value over a 16px label with the tile's own padding either side. */
    --dz-metric-min-height: 120px;
    /* A view's panel box: 6px is the shared grid's own compact cell padding, so the two densities
       step by the same amount. */
    --dz-panel-padding: 6px;
}

/* ================================================================================================
   The grid's settings menu: density, layout actions, saved layouts.
   ================================================================================================ */

/* Section headers inside a panel: the eyebrow (see the --dz-eyebrow-* tokens for why the accent is
   valid on this 11px micro-label). */
.db-layout-panel__caption {
    padding: var(--dz-eyebrow-padding);
    color: var(--dz-eyebrow-color);
    font-family: var(--dz-font-family);
    font-size: var(--dz-eyebrow-font-size);
    font-weight: var(--dz-eyebrow-font-weight);
    letter-spacing: var(--dz-eyebrow-letter-spacing);
    line-height: var(--dz-eyebrow-line-height);
    text-transform: uppercase;
}

/* A rule above every section but the first, so the regions read as separate without a heavier
   divider. :first-child rather than a modifier class: the density section is conditional, so which
   caption comes first depends on how the panel was mounted. */
.db-layout-panel__caption:not(:first-child) {
    margin-top: 4px;
    border-top: 1px solid var(--dz-hairline);
}

/* The layout panel's own rows, at the sample's 400 14px/1.2 Poppins with a 15px glyph. A dxButton's
 * label is set on .dx-button-text, which the page family does not reach through inheritance, so
 * dx.light.css's "Helvetica Neue" wins without this. */
.db-layout-action .dx-button .dx-button-text {
    font-family: var(--dz-font-family);
    font-size: 14px;
    font-weight: 400;
    line-height: 1.2;
}

.db-layout-action .dx-button .db-icon {
    --db-icon-size: 15px;
}

/* A gear panel holding nothing but density has no caption, so the panel's own padding is the whole
   frame; the divider and top padding only make sense under a caption. */
.db-layout-panel--density-only .db-layout-density {
    padding: 0;
    border-top: none;
}

/* The rows touch, as the Layout actions and the saved layouts do: every item in the gear menu is one
   36px row (--dz-layout-item-height), so the pitch is 36 throughout. The 2px gap here made 38, and
   the label's Bootstrap margin (see the choice below) made it 46. */
.db-layout-density {
    display: flex;
    flex-direction: column;
    gap: 0;
    /* The rows' box takes the item inset and the radio the item padding inside it, so the radios, the
       Layout actions' glyphs and the saved layouts' names share one left edge (PP-D81). */
    padding: 0 var(--dz-layout-item-inset) 4px;
}

.db-layout-density__choice {
    display: flex;
    align-items: center;
    gap: var(--dz-space-1);
    box-sizing: border-box;
    min-height: var(--dz-layout-item-height);
    /* The choice is a <label>, and Bootstrap's reset gives every label margin-bottom: .5rem; the row
       was 36px tall with 8px of margin under it, which is what made the pitch 46 (PP-D81). */
    margin: 0;
    padding: 8px var(--dz-layout-item-pad);
    border-radius: var(--dz-control-radius);
    cursor: pointer;
    font-family: var(--dz-font-family);
    font-size: 14px;
    /* A whole-pixel line box. At the font's own line-height the row is 37px, which puts the centred
       14px circle on a half pixel and blurs its edge at 1x. 20px makes the row 36 and the circle's
       offset 11. */
    line-height: 20px;
    color: var(--dz-ink);
}

    .db-layout-density__choice:hover {
        background: var(--dz-sunken);
    }

    /* THE RADIO IS PAINTED, NOT TINTED, the same way the grid's selection checkbox is: a neutral
     * ring at rest, and when chosen a disc of --dz-selection-color carrying a dot of
     * --dz-selection-ink.
     *
     * With native `accent-color` the BROWSER chooses the ring between circle and dot - Chrome picks a
     * dark grey for a light accent (MEASURED #403D3A inside SouthLight's #FF8000), a dark fringe on
     * every light cobrand. Painting it hands that choice to --dz-selection-ink, picked against the
     * accent for contrast (see --dz-on-accent). The input stays a real radio, so keyboard, form and
     * screen-reader behaviour are unchanged. */
    .db-layout-density__choice input {
        appearance: none;
        -webkit-appearance: none;
        flex: none;
        box-sizing: border-box;
        width: 14px;
        height: 14px;
        margin: 0;
        border: 1px solid var(--dz-border);
        border-radius: 50%;
        background-color: var(--dz-card);
        cursor: pointer;
    }

        /* The dot is the BACKGROUND showing through a 3px inset ring of accent - 14px less the 1px
           border is 12, less 3px each side leaves a 6px dot. Not a ::before: a pseudo-element on an
           <input> renders in Chrome only. */
        .db-layout-density__choice input:checked {
            border-color: var(--dz-selection-color);
            background-color: var(--dz-selection-ink);
            box-shadow: inset 0 0 0 3px var(--dz-selection-color);
        }
    /* The focus ring, in the design's own style.
     *
     * DevExtreme moves focus into the popup when it opens, and Chrome paints
     * `outline: rgb(16,16,16) auto 1px` on a focused radio, so a mouse click on the gear would show a
     * black browser ring. A keyboard user still needs a ring (WCAG 2.4.7).
     *
     * :focus-visible only, so it never shows for a pointer, and the accent at 2px reads as ours
     * rather than as the OS. The JS side stops the open from parking focus here at all - see
     * voyager.components.toolbar.js - and this is what a real Tab still gets. */
    .db-layout-density__choice input:focus-visible {
        outline: 2px solid var(--dz-focus-color);
        outline-offset: 2px;
    }

/* --------------------------------------------------------------------------------------------
   THE HEADER FILTER FUNNEL.

   DevExtreme's default is a filled triangle, mirrored to the LEFT of the caption on right-aligned
   columns, and turned white once a filter is applied.

   1. ALWAYS ON THE RIGHT. DevExtreme mirrors the icon with the column's alignment (on Billing
      Center, exactly the four right-aligned columns put it left). A funnel is chrome attached to the
      header, not part of the value, so it sits in the same place on every column.

   2. OUTLINE WHEN THERE IS NO FILTER, FILLED WHEN THERE IS. The state a viewer needs to read at a
      glance is "is this column filtered". Drawn like the expander caret - a mask over currentColor -
      so the two states are one artwork with a different fill and one colour declaration. dx.light's
      ::before character is switched off as in the caret rule; left in place it renders the triangle
      inside the mask. */
/* DevExtreme decides the funnel's side by DOM ORDER: a left-aligned column emits
   `.dx-datagrid-text-content >> .dx-column-indicators` and a right-aligned one the reverse. So the
   wrapper is what floats, and the caption flows beside it whichever order they were written in. */
/* :first-child IS the test for "this column is right-aligned": indicators in first position means
   exactly the mirrored case. Floating only that case leaves the already-correct left-aligned headers
   untouched. Two alternatives that do not work:
     - float: right on ALL indicators drops the funnel to a second line on any column narrow enough
       to truncate its caption, because a float can only sit beside a line box and the caption block
       is the full width of the cell;
     - display: flex on the td orders them but destroys the table layout (MEASURED: an 827px header
       row), because a flex td stops participating in the table's column sizing. */
.db-grid .dx-datagrid-headers .dx-header-row > td > .dx-column-indicators:first-child,
.db-grid .dx-treelist-headers .dx-header-row > td > .dx-column-indicators:first-child {
    float: right;
    margin-left: var(--dz-space-half);
}

/* !important: beats 02-grid-shell.css `%dx%-headers .dx-header-row .dx-header-filter` color, which is itself !important. */
.db-grid .dx-datagrid-headers .dx-header-row > td .dx-header-filter,
.db-grid .dx-treelist-headers .dx-header-row > td .dx-header-filter {
    float: none;
    margin-left: var(--dz-space-half);
    margin-right: 0;
    color: var(--dz-body) !important;
    background-color: currentColor;
    -webkit-mask-image: var(--db-funnel-source);
    mask-image: var(--db-funnel-source);
    -webkit-mask-repeat: no-repeat;
    mask-repeat: no-repeat;
    -webkit-mask-position: center;
    mask-position: center;
    -webkit-mask-size: var(--dz-grid-funnel-size);
    mask-size: var(--dz-grid-funnel-size);
    width: var(--dz-grid-funnel-size);
    height: var(--dz-grid-funnel-size);
    --db-funnel-source: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><polygon points='22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3'/></svg>");
}

    .db-grid .dx-datagrid-headers .dx-header-row > td .dx-header-filter::before,
.db-grid .dx-treelist-headers .dx-header-row > td .dx-header-filter::before {
        content: none;
    }

    /* Filtered. The accent, and the SOLID artwork - same outline, filled rather than stroked - so
       the difference between a filtered and an unfiltered column is a fill and a colour rather than
       a shape. DevExtreme marks the unfiltered state with .dx-header-filter-empty, so this is the
       absence of that class and not a second class of its own. */
    /* !important: beats 02-grid-shell.css `%dx%-headers .dx-header-row .dx-header-filter` color, which is itself !important. */
    .db-grid .dx-datagrid-headers .dx-header-row > td .dx-header-filter:not(.dx-header-filter-empty),
.db-grid .dx-treelist-headers .dx-header-row > td .dx-header-filter:not(.dx-header-filter-empty) {
        color: var(--dz-grid-funnel-active-color) !important;
        --db-funnel-source: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='black'><polygon points='22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3'/></svg>");
    }

    /* !important: beats 02-grid-shell.css `%dx%-headers .dx-header-row .dx-header-filter` color, which is itself !important. */
    .db-grid .dx-datagrid-headers .dx-header-row > td .dx-header-filter:hover,
.db-grid .dx-treelist-headers .dx-header-row > td .dx-header-filter:hover {
        color: var(--dz-ink) !important;
    }

    /* PHONES ONLY: the funnel's hit area (the condition and its reason are in 02-grid-shell.css,
       TOUCH HIT AREAS). dx.light.css already gives the 14px funnel an empty ::after, at 7px past each
       edge, which is 28px and `.dx-datagrid .dx-column-indicators .dx-header-filter.dx-header-filter`
       (0,4,1). This is the same box at 15px, 44px, one class heavier. The header cell clips it
       (`overflow: hidden`), so the target ends at the cell's own edge: about the cell's height, and
       the funnel's right padding. Nothing is painted and nothing moves. */
    @media (max-width: 824.98px) and (pointer: coarse) {
        .db-grid .dx-datagrid-headers .dx-header-row > td .dx-column-indicators .dx-header-filter::after,
.db-grid .dx-treelist-headers .dx-header-row > td .dx-column-indicators .dx-header-filter::after {
            margin: -15px;
        }
    }

/* THE FUNNEL'S POPUP, WHICH NO .db-grid RULE CAN REACH.
 *
 * DevExtreme renders this list into an overlay appended to <body>, OUTSIDE the grid element, so
 * every `.db-grid ...` rule misses it. Unstyled, it shows DevExtreme's stock #337AB7 on checked boxes
 * and the search focus ring, "Helvetica Neue" in the input, and OK and Cancel as identical untyped
 * buttons.
 *
 * `.dx-header-filter-menu` is on the overlay WRAPPER and belongs to this popup alone, which is what
 * makes a body-level selector safe here - the same shape as the toolbar overflow menu in
 * 07-button-roles.css, styled from `.dx-overlay-wrapper`.
 *
 * The surface repeats .db-action-menu__popup's exactly: a filter list and a row menu are both menus
 * over a grid, and two shadows for one idea is how a palette drifts. */
.dx-header-filter-menu.dx-popup-wrapper .dx-overlay-content {
    /* WIDTH IS A RANGE around DevExtreme's own measurement: 270px floor (the sample's width), the
       shared menu maximum as ceiling.
     *
     * DevExtreme writes a computed width inline, and bounding it is the only thing that works.
     * MEASURED, both intrinsic keywords fail: `auto` is shrink-to-fit on an absolutely positioned
     * overlay and collapses onto the floor once the items wrap, and `max-content` resolves to the
     * simulated scrollview's content element, which DevExtreme sizes to 1,000,000px, so every column
     * clamps to the ceiling. The widget's own number stands (252px on ICCID, too narrow for its
     * values) and the bounds keep it sane.
     *
     * Anything still too long for the ceiling wraps rather than truncating - see the list item
     * rule - so no value is ever cut off whatever width this lands on. */
    min-width: 270px;
    max-width: var(--dz-menu-max-width);
    background-color: var(--dz-card);
    border: 1px solid var(--dz-border);
    border-radius: var(--dz-grid-card-radius);
    box-shadow: var(--dz-surface-float-shadow);
}

/* dx.light.css sets the family on its own widget roots, which the page family does not reach by
   inheritance. */
.dx-header-filter-menu .dx-list,
.dx-header-filter-menu .dx-list-item,
.dx-header-filter-menu .dx-texteditor-input,
.dx-header-filter-menu .dx-placeholder,
.dx-header-filter-menu .dx-button .dx-button-text {
    font-family: var(--dz-font-family);
}

/* The same checkbox the grid's own selection column draws. Declared against the same tokens rather
   than copied values, so a reseller's accent moves both together. */
.dx-header-filter-menu .dx-checkbox .dx-checkbox-icon {
    width: var(--dz-grid-checkbox-size);
    height: var(--dz-grid-checkbox-size);
    border-color: var(--dz-border);
    border-radius: var(--dz-radius-xs);
    font-size: calc(var(--dz-grid-checkbox-size) - 4px);
}

.dx-header-filter-menu .dx-checkbox.dx-checkbox-checked .dx-checkbox-icon,
.dx-header-filter-menu .dx-checkbox.dx-checkbox-indeterminate .dx-checkbox-icon {
    color: var(--dz-selection-ink);
    background-color: var(--dz-selection-color);
    border-color: var(--dz-selection-color);
}

.dx-header-filter-menu .dx-checkbox.dx-checkbox-indeterminate .dx-checkbox-icon::before {
    background-color: var(--dz-on-accent);
}

.dx-header-filter-menu .dx-list-search.dx-texteditor {
    border-radius: var(--dz-control-radius);
}

/* WL-24 (ClickUp 86894yg24), canary G25: the search box's own magnifier glyph was left at
   dx.light.css's vendor literal (`.dx-icon-search { color: #999 }`), MEASURED live at exactly that
   value in BOTH themes -- nothing in this file or the adapter had ever named it. In the light popup
   #999 is a muted but legible 2.85:1 against the card (acceptable for a non-text glyph, 3:1 floor
   aside, since it sits beside placeholder text at a similar weight); in the DARK popup (#201f1e card)
   the same literal measures under 3:1 and reads as barely-there. --dz-meta is the package's own
   "quiet chrome glyph" tone (--dz-icon-control-glyph-color's own family) and is already ink in dark
   mode (voyager.darkmode.css: no grey text in dark), so this one rule fixes both. */
.dx-header-filter-menu .dx-icon-search {
    color: var(--dz-meta);
}

/* THE OPTION LIST IS ITS OWN BOXED REGION, as the sample draws it. Measured off Subscribers.dc.html's
   filter popover:
     list box    1px solid var(--border-subtle), radius 6px, max-height 210px, scrolls
     Select All  its own row with a bottom rule, separating it from the values it governs
     values      no rules between them - the ONLY divider in the list is under Select All
     rows        9px 10px, hover #F0EEEC
   Each value maps to a token here (--dz-hairline is the sample's border-subtle to the byte,
   --dz-sunken its #F0EEEC).

   The border goes on the scrollable WRAPPER rather than on .dx-list: the search box is a child of
   .dx-list too, and a border there would box the search and the options together as one region,
   which is the thing the sample deliberately does not do. */
.dx-header-filter-menu .dx-list .dx-scrollable-wrapper {
    border: 1px solid var(--dz-hairline);
    border-radius: var(--dz-radius-sm);
    overflow: hidden;
}

/* THE DEAD BAND BETWEEN THE LIST AND THE BUTTONS.
 *
 * dx.light.css pads .dx-popup-content and .dx-popup-bottom by 20px each, stacking to 38px of empty
 * popup between the option box and Cancel/OK; the sample pads the whole popover by 10 and separates
 * its three regions by 8. Halving both gives the space back to the list, which stretches to whatever
 * height DevExtreme leaves it - so the option box gets TALLER rather than the buttons moving up. */
.dx-header-filter-menu .dx-popup-content {
    padding: 10px;
}

.dx-header-filter-menu .dx-popup-bottom {
    padding: 0 10px 10px;
}

.dx-header-filter-menu .dx-list-select-all {
    /* flex with the sample's 10px gap: the label's own padding is zeroed just below, so without a
       gap here "Select All" sits flush against its checkbox while every value row below - which
       DevExtreme spaces itself - does not. */
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 9px 10px;
    border-bottom: 1px solid var(--dz-hairline);
}

.dx-header-filter-menu .dx-list-item {
    padding: 9px 10px;
}

    /* WRAP RATHER THAN TRUNCATE. dx.light.css sets nowrap + ellipsis on the item content, which on a
       list of ICCIDs and account names hides exactly the end of the string that distinguishes one
       row from the next. The popup widens first (above); wrapping catches anything still longer
       than the ceiling.

       overflow-wrap: anywhere rather than break-word: an ICCID or an APN is one unbroken token with
       no space to break at, and break-word leaves those overflowing. */
    .dx-header-filter-menu .dx-list-item .dx-list-item-content {
        white-space: normal;
        overflow: visible;
        text-overflow: clip;
        overflow-wrap: anywhere;
    }

    /* DevExtreme pads the row AND its content wrapper (9+10+19+10+9 = 57px against the sample's ~35).
       The row owns the padding here, so the inner wrapper gives its up. */
    .dx-header-filter-menu .dx-list-item .dx-list-item-content,
    .dx-header-filter-menu .dx-list-select-all .dx-list-select-all-label {
        padding: 0;
    }

    .dx-header-filter-menu .dx-list-item.dx-state-hover,
    .dx-header-filter-menu .dx-list-select-all:hover {
        background-color: var(--dz-sunken);
    }


    /* No focus rule here: the shared .dx-texteditor.dx-state-focused rule already gives every text
       editor the accent border and ring, this search box included. */

/* CANCEL THEN OK, WHICH IS THE ORDER THE DESIGN DRAWS AND THE OPPOSITE OF THE ONE DEVEXTREME EMITS.
   The DOM is OK first and is not ours to reorder - these two buttons are built inside the widget -
   so the row is reversed visually instead. That makes the FIRST toolbar item the confirming one,
   which is what the role rule below keys on; read the two together before changing either. */
.dx-header-filter-menu .dx-popup-bottom .dx-toolbar-after {
    display: flex;
    flex-direction: row-reverse;
    gap: var(--dz-space-1);
}

/* Both buttons resolve their paint from the same --dz-button-* variables the shared roles publish,
   so the colours have one home even though these two never pass through a Db* constructor. */
.dx-header-filter-menu .dx-popup-bottom .dx-button {
    color: var(--dz-button-color);
    background-color: var(--dz-button-background);
    border-color: var(--dz-button-border-color);
    border-radius: var(--dz-control-radius);
}

    .dx-header-filter-menu .dx-popup-bottom .dx-button .dx-button-text {
        color: inherit;
    }

    /* Cancel: the cancel role, as every other dismissing action takes.
       --dz-button-hover-border-color is set here too (WL-21 round 2, D3), not only inside the
       .dx-state-hover rule below: the focused rule at the bottom of this block reads it regardless
       of hover, mirroring 07-button-roles.css's own `.db-button` role blocks, which always declare
       the full eight-slot band rather than leaving the hover slot to appear only under .dx-state-hover.
       Without it here, tabbing to Cancel without also hovering it resolved an undefined custom
       property for border-color. */
    .dx-header-filter-menu .dx-popup-bottom .dx-toolbar-item .dx-button {
        --dz-button-color: var(--dz-button-cancel-color);
        --dz-button-background: var(--dz-button-cancel-background);
        --dz-button-border-color: var(--dz-button-cancel-border-color);
        --dz-button-hover-border-color: var(--dz-button-cancel-hover-border-color);
        --dz-button-focus-ring: var(--dz-button-cancel-focus-ring);
    }

    /* OK: the primary role. First in the DOM, last in the reversed row - see the comment above. */
    .dx-header-filter-menu .dx-popup-bottom .dx-toolbar-item:first-child .dx-button {
        --dz-button-color: var(--dz-button-primary-color);
        --dz-button-background: var(--dz-button-primary-background);
        --dz-button-border-color: var(--dz-button-primary-border-color);
        --dz-button-hover-border-color: var(--dz-button-primary-hover-border-color);
        --dz-button-focus-ring: var(--dz-button-primary-focus-ring);
    }

        .dx-header-filter-menu .dx-popup-bottom .dx-toolbar-item:first-child .dx-button.dx-state-hover {
            --dz-button-background: var(--dz-button-primary-hover-background);
            --dz-button-border-color: var(--dz-button-primary-hover-border-color);
        }

    /* Cancel's own hover (WL-21, ClickUp 86894yg24). Until now the global contained-button reset in
       22-devextreme-adapter.css matched this button too (it carried no role class the reset's
       exclusion list could key on) and its hover sibling was, incidentally, the only rule painting a
       hover state for Cancel at all - a side effect of a bug, not a declared hover. Excluding both
       buttons there by DOM location (so the reset stops overwriting their real roles with literal
       Standard colours) removed that accidental hover along with the bug, so Cancel needs its own
       hover here, mirroring OK's rule above. Background and border only, same as OK: the cancel
       ink does not change between rest and hover (--dz-button-cancel-hover-color equals
       --dz-button-cancel-color), so restating color here would be a no-op. */
    .dx-header-filter-menu .dx-popup-bottom .dx-toolbar-item:not(:first-child) .dx-button.dx-state-hover {
        --dz-button-background: var(--dz-button-cancel-hover-background);
        --dz-button-border-color: var(--dz-button-cancel-hover-border-color);
    }

    /* FOCUS, FOR BOTH BUTTONS (WL-21 round 2, ClickUp 86894yg24, D3 -- PM ruling). Pre-existing: a
       keyboard user tabbing to OK or Cancel got no visible indicator at all (WCAG 2.4.7), because
       neither carries .db-button, so 07-button-roles.css's own focused-state rule
       (`.dx-button.db-button.dx-state-focused`) never matches here. Mirrors that rule's shape exactly
       - same ring width token, same border-color fallback to the hover border - reading
       --dz-button-focus-ring, which the two role blocks above now set (--dz-button-cancel-focus-ring /
       --dz-button-primary-focus-ring) the same way they already set colour/background/border. No
       pressed/active state: neither role token set below declares an active-background counterpart
       for OK/Cancel specifically (unlike a .db-button, which always carries one), and DevExtreme does
       not mark these as pressed on click here, so there is nothing to read. */
    .dx-header-filter-menu .dx-popup-bottom .dx-button.dx-state-focused {
        border-color: var(--dz-button-hover-border-color);
        box-shadow: 0 0 0 var(--dz-control-focus-width) var(--dz-button-focus-ring);
    }

/* THE MULTI-SORT ORDINAL, WHICH A GROUPED GRID MAKES WRONG.
 *
 * DevExtreme numbers sorted columns and prints the ordinal beside the arrow. A GROUPED column is
 * sorted too and takes its place in that numbering, but grouping moves it out of the header. On
 * Billing Center MonthBilled is groupIndex 0 with sortIndex 0, so the only visible sorted column is
 * labelled "2" with no "1" on screen - and as a declared default it survives Reset Layout, reading as
 * a filter that will not clear.
 *
 * Hidden only while the grid is actually grouped; an ungrouped multi-column sort keeps its 1, 2, 3.
 * :has() lets the header row ask about the rows below it; .db-grid is their nearest common ancestor.
 *
 * .dx-sort-index-icon ONLY - the INNER span, which is the number. The outer span also carries the
 * .dx-sort-down that draws the ARROW in its ::before, so hiding it would take the sort direction with
 * it. The markup is
 * <span class="dx-sort dx-sort-down dx-sort-index"><span class="dx-sort-index-icon">2</span></span>. */
.db-grid:has(.dx-datagrid-rowsview .dx-group-row) .dx-datagrid-headers .dx-sort-index-icon,
.db-grid:has(.dx-treelist-rowsview .dx-group-row) .dx-treelist-headers .dx-sort-index-icon {
    display: none;
}

/* The DevExtreme surface adapter.
 *
 * dx.light.css paints its own surfaces in `background:` shorthands the --dz-* spine never reaches, so
 * each one is named here -- once, for every theme. Naming a DevExtreme class is this file's job and
 * no other file's; what a token resolves TO for a given theme is the theme's band (today, the dark
 * spine in voyager.darkmode.css). So a THEME is only a token band.
 *
 * WHY THE GATE IS html[data-db-theme] (ANY value) AND NOT [data-db-theme="dark"]. In the light theme
 * (no attribute at all) dx.light's own defaults differ subtly from the --dz-* light values -- its body
 * text is #333 where --dz-ink is #31302f -- so applying this file unconditionally would change light
 * mode. Gating on the attribute's PRESENCE leaves light mode to dx's own defaults, and a future theme
 * gets every rule below just by declaring its token band under its own attribute value.
 *
 * UNGATED RULES ARE MARKED. Light mode is no longer frozen (owner ruling, 2026-09-30), so a rule
 * whose light pair was measured failing drops the gate and says so in its comment. The rest stay gated
 * until each is measured in light.
 *
 * Every rule below reads a --dz-* token and names zero literal colours.
 */

/* `.dx-widget` GETS INK AND NOTHING ELSE, because of the navbar.
 *
 * It is the marker class dx puts on every widget root (111 elements on a list page), not a surface.
 * A host's navigation box can itself be a widget covering the navbar, so a fill here paints over
 * the <nav> while `getComputedStyle(nav)` still reports the nav's own colour.
 *
 * Every surface a widget sits on is painted by what actually owns it -- the host's content region
 * for the page, the grid rules below, the overlay rules for popups -- so a transparent widget
 * resolves to the right colour, and inside the navbar to the navbar.
 * MEASURED: 0 widgets show a light ancestor through.
 *
 * SHARED BUTTONS, ICON DROPDOWNS AND ROLE-LESS SUCCESS BUTTONS ARE EXCLUDED: they are widgets that
 * ARE surfaces. A button role paints its own fill (07-button-roles.css, and
 * `.db-icon-dropdown.dx-dropdowneditor` in 12-tip-icon-button.css), and this rule out-ranked it, so
 * every chrome and action button drew as a bare outline and the download control as a glyph with no
 * body. WL-20a fix round 2 (ClickUp 86894yg24), D5: `.dx-button-success` joins them for the same
 * reason -- 07-button-roles.css's own "role-less success dxButton is a SUBMIT" rule
 * (`.dx-button-mode-contained.dx-button-success:not(:where(.db-button))`) paints Account Settings'
 * two Save buttons, and this reset applies in every theme now that _Layout renders data-db-theme in
 * light too (D1), not only dark, so every tenant's Save button went transparent in BOTH themes.
 * Excluding the class here means the reset never MATCHES a role-less success button at all, rather
 * than trying to out-rank its fill on specificity: the two rules are (0,2,1) and (0,2,0), and
 * specificity alone can never make (0,2,1) lose, so comparing them is the wrong test for whether
 * this is fixed -- :not(:where()) exclusion is. Verified live (Account Settings Save = #dcd8d4 in
 * light and dark; see the item report for the before/after measurement).
 *
 * `.db-segmented` is excluded for the same reason: its outer dxButtonGroup is a widget that IS a
 * surface, the track 15-segmented.css paints from --dz-segmented-track at (0,2,0). This reset's
 * (0,2,1) beat it, so the track never drew and the unchosen options showed whatever ground was
 * behind the control. Only the outer element carries the class; its wrapper and options still take
 * the reset, which is the transparency the track needs to show through.
 *
 * THE EXCLUSIONS HERE AND ON THE EDITOR AND BUTTON RULES BELOW ARE :where(), NOT :is(). :not(:is())
 * takes its argument's weight, which lifted this reset to (0,3,1) and let it beat
 * `.dx-dropdownbutton.db-button .dx-button` (0,3,0): Billing Center's primary New lost its fill.
 * :where() excludes without adding weight, so each rule keeps the specificity it was written at. */
html[data-db-theme] .dx-theme-generic-typography {
    background-color: var(--dz-page);
    color: var(--dz-ink);
}

html[data-db-theme] .dx-widget:not(:where(.db-icon-dropdown, .db-button, .dx-button-success, .db-segmented)) {
    background-color: transparent;
    color: var(--dz-ink);
}

/* The grid, its rows, and the two chrome strips above and below it. */
html[data-db-theme] .dx-datagrid,
html[data-db-theme] .dx-treelist,
html[data-db-theme] .dx-datagrid-rowsview,
html[data-db-theme] .dx-treelist-rowsview,
html[data-db-theme] .dx-datagrid .dx-row > td:not(.dx-pointer-events-none),
html[data-db-theme] .dx-treelist .dx-row > td:not(.dx-pointer-events-none),
html[data-db-theme] .dx-datagrid-content:not(.dx-datagrid-content-fixed),
html[data-db-theme] .dx-datagrid-table:not(.dx-datagrid-table-fixed) {
    background-color: var(--dz-card);
    color: var(--dz-ink);
}

/* DATA ROWS TAKE THE GRID'S OWN ROW INK, not --dz-ink. The rule above paints every cell, header
 * cells included; this one narrows to the rows view and reads --dz-grid-row-color, the hook the
 * shared grid (02-grid-shell.css) already reads, so a theme can set a dense table's ink apart from
 * its headings. Without it the rule above out-ranks the grid's own and the hook does nothing in any
 * theme. Its default is var(--dz-ink), so a theme that does not set it sees no change. */
html[data-db-theme] .dx-datagrid-rowsview .dx-row > td:not(.dx-pointer-events-none),
html[data-db-theme] .dx-treelist-rowsview .dx-row > td:not(.dx-pointer-events-none) {
    color: var(--dz-grid-row-color);
}

/* A GRID WITH A FIXED COLUMN RENDERS ITS ROWS TWICE, and the second copy is an absolutely
 * positioned overlay spanning the FULL grid width -- not just the fixed columns. Only the cells it
 * owns carry paint; the rest are `.dx-pointer-events-none` and transparent, letting the scrolling
 * copy underneath show through.
 *
 * A background on `.dx-datagrid-content` lands on the overlay too, turns it opaque, and hides every
 * scrolling column behind a blank card (the text is still in the DOM, so it reads as "wrong text
 * colour" rather than "something on top").
 *
 * THE OVERLAY CAN PICK UP A FILL IN THREE PLACES: the content div, the table inside it, and the
 * placeholder cells. The :not()s above cover the first two; `.dx-pointer-events-none` the third.
 *
 * Those cells are transparent to HIT-TESTING but not to PAINTING, so `document.elementFromPoint`
 * on a hidden cell returns the row underneath and reports values for an element the eye cannot
 * see. Trust the screenshot over the computed style here.
 */
html[data-db-theme] .dx-datagrid-content-fixed,
html[data-db-theme] .dx-treelist-content-fixed,
html[data-db-theme] .dx-datagrid-table-fixed,
html[data-db-theme] .dx-treelist-table-fixed {
    background-color: transparent;
}

/* THE FIXED-COLUMN DIVIDER ON A GRID THE SHELL DOES NOT OWN. The placeholder cells above also carry
 * the divider where the pinned columns end, which DevExtreme paints 2px #ddd: a bright bar down a
 * dark page. 02-grid-shell.css recolours it only under `.db-grid`, so an unported grid (Products'
 * tree list) keeps the bar. This gives it the house border instead.
 * !important: dx.light's rule is (0,4,1), as the note at 02-grid-shell.css explains, and dx.light
 * does not use !important, so any !important beats it. The :where() holds this at (0,1,1), under the
 * shell's (0,2,1), so a ported grid keeps the grid border colour rather than this brighter one. */
:where(html[data-db-theme] .dx-datagrid) td.dx-pointer-events-none,
:where(html[data-db-theme] .dx-treelist) td.dx-pointer-events-none {
    border-left-color: var(--dz-border) !important;
    border-right-color: var(--dz-border) !important;
}

/* THE OVERLAY CANNOT FOLLOW A VERTICAL SCROLL INSIDE AN AUTO-HEIGHT GRID, so on iOS there must be none.
 * UNGATED: this is behaviour, not paint, and it fails the same way in every theme.
 *
 * dx.light gives iOS native scrolling `.dx-scrollable-native-ios .dx-scrollable-content
 * { min-height: 101% }` to force the rubber-band bounce. A grid with no height of its own then has
 * rows 1% taller than their container, so a vertical swipe scrolls the rows view (25px on a 50-row
 * Subscribers page). DevExtreme syncs the overlay by scrolling its wrapper to the same offset, but
 * that wrapper is exactly as tall as its table, so it cannot scroll: the checkbox and row-menu
 * columns stayed put while the rows moved under them. At 100% there is nothing to scroll, the page
 * scrolls instead, and only the bounce inside the grid is lost. Android and desktop never take the
 * 101% rule. Measured live with an iPhone user agent: drift 25px before, 0 after. One class above the
 * vendor's (0,3,0), so it wins on specificity rather than load order. */
.dx-datagrid-rowsview.dx-scrollable-native.dx-scrollable-native-ios .dx-scrollable-content,
.dx-treelist-rowsview.dx-scrollable-native.dx-scrollable-native-ios .dx-scrollable-content {
    min-height: 100%;
}

html[data-db-theme] .dx-datagrid-content-fixed > table > tbody > tr > td:not(.dx-pointer-events-none),
html[data-db-theme] .dx-treelist-content-fixed > table > tbody > tr > td:not(.dx-pointer-events-none) {
    background-color: var(--dz-card);
}

html[data-db-theme] .dx-datagrid-content-fixed > table > tbody > tr.dx-row-alt > td:not(.dx-pointer-events-none),
html[data-db-theme] .dx-treelist-content-fixed > table > tbody > tr.dx-row-alt > td:not(.dx-pointer-events-none) {
    background-color: var(--dz-page);
}

/* The fixed column's own fill is painted on a `<col>`, not on the cells -- a table-column element,
 * which is why a sweep over visible boxes reports a white rectangle with no obvious owner.
 *
 * The selector is this long on purpose. dx.light.css declares it as
 * `.dx-datagrid .dx-datagrid-content-fixed .dx-datagrid-table .dx-col-fixed`, four classes, and
 * Site.css restates it a level deeper under `.themed-datagrid`. A shorter override loses silently to
 * both, so the rule matches their depth rather than reaching for !important.
 */
html[data-db-theme] .dx-datagrid .dx-datagrid-content-fixed .dx-datagrid-table .dx-col-fixed,
html[data-db-theme] .dx-treelist-container .dx-treelist-content-fixed .dx-treelist-table .dx-col-fixed,
html[data-db-theme] .themed-datagrid .dx-datagrid .dx-datagrid-content-fixed .dx-datagrid-table .dx-col-fixed,
html[data-db-theme] .themed-treelist .dx-treelist .dx-treelist-content-fixed .dx-treelist-table .dx-col-fixed {
    background-color: var(--dz-card);
}

/* THE TOOLBAR STRIP TAKES THE PAGE, because it is not part of the card.
 *
 * `.dx-datagrid-header-panel` carries Bulk Actions, the group box and the grid's own buttons. The
 * design puts it ABOVE the card; DevExtreme renders it INSIDE `.dx-datagrid`, which carries the card's
 * ground, so the strip would read as a band a step off the page with the page-ground group panel as a
 * contrasting box inside it. Naming the page canvas makes the card start at the header row.
 *
 * --dz-page-canvas, not --dz-page: #app-content paints the canvas (white by default, a tenant's own
 * page background when set), while --dz-page is the grid-chrome grey, so reading it here drew the
 * strip as a grey band across a white page.
 *
 * Not `transparent`: that resolves to the nearest painted ancestor, `.dx-datagrid` -- the card.
 *
 * Not --dz-sunken: in the dark theme that equals the header row beneath and would fuse the two into
 * one band; a theme that wants them fused can still choose to. */
html[data-db-theme] .dx-datagrid-header-panel,
html[data-db-theme] .dx-treelist-header-panel {
    background-color: var(--dz-page-canvas);
}

html[data-db-theme] .dx-datagrid-group-panel,
html[data-db-theme] .dx-treelist-group-panel,
html[data-db-theme] .dx-datagrid-filter-row,
html[data-db-theme] .dx-treelist-filter-row {
    background-color: var(--dz-sunken);
    color: var(--dz-ink);
}

/* WL-24 FIX ROUND 1 (ClickUp 86894yg24), PM ruling D1: split out of the group above and repointed
 * onto the NAMED chain (--dz-grid-header-background/-color, 00-tokens.css) rather than restating
 * --dz-sunken/--dz-ink as a second literal. Same resolved value today for every tenant -- that
 * chain's own default IS --dz-sunken/--dz-ink -- but one declared source of truth instead of two
 * closes the gap the adversary measured: this rule out-specified (0,2,1) a `.db-grid`-scoped pager/
 * footer token rule (0,2,0), so under a tenant whose grid header ground diverged from this literal
 * (true before this round's fix repointed the header ground back to --dz-sunken too), the header row
 * moved while the pager/footer stayed behind on this hardcoded value. Reading the same token now
 * means the two can never diverge again, including from whatever WL-26 later makes --dz-sunken
 * itself resolve to for a tenant's Surface. Deliberately NOT the header panel above (kept on
 * --dz-page, for the fuse-into-one-band reason that rule's own comment gives) and NOT group-panel/
 * filter-row above (their own, separately-reasoned tokens, untouched by this item). The pager and
 * total footer have since left this group for the rule below, which reads the same pager hook as
 * the `.db-grid` rule, so the two still cannot diverge. */
html[data-db-theme] .dx-datagrid-headers,
html[data-db-theme] .dx-treelist-headers,
html[data-db-theme] .dx-datagrid-summary-item {
    background-color: var(--dz-grid-header-background);
    color: var(--dz-grid-header-color);
}

/* The pager and total footer read the grid's own pager hook, the one 06-toolbar-actions.css reads
   under `.db-grid`. This rule's html prefix out-ranks that one, so naming --dz-sunken here overrode
   the hook on every grid and turned light's pager a step darker than it was. */
html[data-db-theme] .dx-datagrid-pager,
html[data-db-theme] .dx-treelist-pager,
html[data-db-theme] .dx-pager,
html[data-db-theme] .dx-datagrid-total-footer,
html[data-db-theme] .dx-treelist-total-footer {
    background-color: var(--dz-grid-pager-background);
    color: var(--dz-ink);
}

/* The pager's CURRENT page and page-size chips. dx.light paints them as a light grey pill with dark
   ink, from `.dx-pager .dx-pages .dx-selection`, which nothing else in the stack names, so painting
   `.dx-pager` itself leaves two bright chips sitting on it. Their own hook, not the selected row's
   --dz-surface-active: in light that equals the sunken tone, and on a sunken bar the current page
   stopped standing out at all. */
html[data-db-theme] .dx-pager .dx-pages .dx-selection,
html[data-db-theme] .dx-pager .dx-page-sizes .dx-selection {
    background-color: var(--dz-grid-pager-selected-background);
    color: var(--dz-ink);
}

/* Rules between rows and around the grid. dx paints these as borders rather than backgrounds, so
   they survive the surface rules above and stay at dx's own colour unless named. */
html[data-db-theme] .dx-datagrid .dx-row-lines > td,
html[data-db-theme] .dx-treelist .dx-row-lines > td,
html[data-db-theme] .dx-datagrid-headers .dx-datagrid-table .dx-row > td,
html[data-db-theme] .dx-datagrid-borders > .dx-datagrid-headers,
html[data-db-theme] .dx-datagrid-borders > .dx-datagrid-rowsview,
html[data-db-theme] .dx-datagrid-borders > .dx-datagrid-total-footer,
html[data-db-theme] .dx-datagrid-headers,
html[data-db-theme] .dx-datagrid-rowsview.dx-datagrid-nowrap,
html[data-db-theme] .dx-datagrid-pager {
    border-color: var(--dz-hairline);
}

/* Site.css's grid frame (`.themed-datagrid` / `.themed-treelist`), drawn as #ddd rules on the rows
 * view and its content. In the dark theme they read as a white box round every legacy grid, and as a
 * white rule under the header and over the pager of every EMPTY grid, the shared one included,
 * because Site.css's `.dx-empty` arm is not limited to legacy grids.
 *
 * Each selector matches the depth of the Site.css rule it answers, (0,4,0) and (0,5,0), plus the
 * html prefix, so it wins on specificity and not on load order. */
html[data-db-theme] .themed-datagrid .dx-datagrid .dx-datagrid-rowsview,
html[data-db-theme] .themed-treelist .dx-treelist .dx-treelist-rowsview,
html[data-db-theme] .themed-datagrid .dx-datagrid .dx-datagrid-rowsview.dx-empty,
html[data-db-theme] .themed-treelist .dx-treelist .dx-treelist-rowsview.dx-empty,
html[data-db-theme] .themed-datagrid .dx-datagrid .dx-datagrid-rowsview .dx-datagrid-content,
html[data-db-theme] .themed-treelist .dx-treelist .dx-treelist-rowsview .dx-treelist-content {
    border-color: var(--dz-hairline);
}

/* The group box's hint, "Drag a column header here". dx.light: #959595, 3.18:1 on the themed box.
 * D4 (ClickUp 86894yg24), extending A105: this paints on the group panel's --dz-sunken ground (a
 * legacy, non-.db-grid grid; 19-grid-selection.css repaints the ported one as body on page already),
 * where meta measures under 4.5:1 -- the same shortfall A105 found for the switch label. Body clears
 * it, same fix. */
html[data-db-theme] .dx-datagrid-group-panel .dx-group-panel-message,
html[data-db-theme] .dx-treelist-group-panel .dx-group-panel-message {
    color: var(--dz-body);
}

/* Alternate rows. The :not() matches the cell rule at the top of this file, (0,4,2), so this one wins
   on source order; without it every stripe lost to the card. It also leaves the fixed-column
   overlay's placeholder cells transparent, as that rule does. */
html[data-db-theme] .dx-datagrid .dx-row-alt > td:not(.dx-pointer-events-none),
html[data-db-theme] .dx-treelist .dx-row-alt > td:not(.dx-pointer-events-none) {
    background-color: var(--dz-page);
}

/* A HOVERED ROW'S INK, AND THE SPECIFICITY IT TAKES TO OWN IT.
 *
 * The naive `.dx-datagrid .dx-data-row.dx-state-hover > td` (0,4,2) loses its COLOUR to dx.light.css's
 * hover chain, leaving DevExtreme's hard-coded light-mode ink on a themed card:
 *   .dx-datagrid-table .dx-data-row.dx-state-hover:not(.dx-selection):not(.dx-row-inserted)
 *   :not(.dx-row-removed):not(.dx-edit-row):not(.dx-row-focused) > td:not(.dx-focused)
 * Every :not() argument counts, so that is (0,9,1). Mirroring the chain inside our html scope makes
 * this (0,10,2), which wins outright rather than on load order. Verbose on purpose, so a reader can
 * diff it against the vendor rule.
 *
 * ONLY the unselected case needs the long form: :not(.dx-selection) is in the vendor chain, so a
 * selected-and-hovered row is covered by the short rule below.
 *
 * THE BACKGROUND IS NOT HERE FOR A .db-grid: the shared package sets --dz-grid-row-hover-background
 * with !important there (02-grid-shell.css), so a background on THIS rule would be dead for that
 * archetype. For a grid NOT under a .db-grid host, nothing else ever painted a hover background at
 * all -- DevExtreme's own rgba(0,0,0,.04) wash was the only thing visible, which reads as a fixed
 * grey in both themes and does not move with a tenant's Surface (A109, ClickUp 86894yg24, WL-32,
 * Brad's walkthrough item #3: "row hover is not styled"). See the SEPARATE legacy-only rule below
 * this one, which paints it -- kept apart from this ink rule (rather than adding background-color
 * here) so a .db-grid grid with hover intentionally turned off (no data-db-grid-hover="true") never
 * gets an unconditional background it never asked for. */
html[data-db-theme] .dx-datagrid .dx-data-row.dx-state-hover:not(.dx-selection):not(.dx-row-inserted):not(.dx-row-removed):not(.dx-edit-row):not(.dx-row-focused) > td:not(.dx-focused),
html[data-db-theme] .dx-treelist .dx-data-row.dx-state-hover:not(.dx-selection):not(.dx-row-inserted):not(.dx-row-removed):not(.dx-edit-row):not(.dx-row-focused) > td:not(.dx-focused) {
    color: var(--dz-grid-row-color);
}

/* The cases the vendor chain excludes - a hovered row that is also selected, inserted, being edited
 * or focused - kept on the same ink by the short selector. */
html[data-db-theme] .dx-datagrid .dx-data-row.dx-state-hover > td,
html[data-db-theme] .dx-treelist .dx-data-row.dx-state-hover > td {
    color: var(--dz-grid-row-color);
}

/* A109 (ClickUp 86894yg24, WL-32, Brad): the LEGACY (not under a .db-grid) row-hover BACKGROUND --
 * the identical selector chain as the ink rule above (same specificity reasoning: mirrors
 * DevExtreme's own (0,9,1) hover chain, PLUS the exclusion below, so this wins on specificity
 * rather than source order), with `:not(.db-grid .dx-data-row)` added so a .db-grid grid is left
 * entirely to its own package rule (02-grid-shell.css, data-db-grid-hover-gated, !important) -- this
 * rule would be harmless even without the exclusion (importance always beats it), but the exclusion
 * keeps a `.db-grid` grid with hover turned off from getting an unconditional background this rule
 * has no opt-out flag for.
 *
 * `:not(.db-grid .dx-data-row)` ON THE ROW ITSELF, NOT `.dx-datagrid:not(.db-grid)` ON THE
 * ANCESTOR -- `.db-grid` sits on the SAME element as `.dx-datagrid`/`.dx-treelist` (the ElementAttr
 * host DevExtreme's own class lands on too), so `.dx-datagrid:not(.db-grid)` can never be true for
 * ANY grid and the exclusion would be a no-op (MEASURED: Site.css's own
 * `.themed-datagrid:not(.db-grid) > .dx-datagrid > .dx-datagrid-rowsview` proves the shape -- `.db-grid`
 * is a SIBLING class on the host, not an ancestor selector away). The house pattern for excluding a
 * `.db-grid` host from an element further down its OWN tree is the `:not()` argument naming the
 * descendant chain explicitly -- see `.grid-icon:not(.db-grid .grid-icon)` (Account/Index.cshtml and
 * three siblings) -- which is what the two selectors below do, on `.dx-data-row` itself.
 *
 * var(--dz-grid-row-hover-background) is the SAME token the ported archetype reads -- untouched
 * tenants land on --dz-page (today's platform default for that token), a VISIBLE change from
 * DevExtreme's own translucent black wash (accepted -- the gap this item exists to close; Site.css's
 * own A83/A105 precedent already repaints legacy-grid default behaviour with disclosure). A
 * customized Surface moves it the same way the ported archetype's does (WhitelabelCompiler
 * .EmitDerivedGridMidtoneLight / the dark --dz-segmented-track reuse).
 *
 * NO SHORT "catch-all" SIBLING RULE HERE -- unlike the ink pair above, which deliberately ALSO paints
 * the vendor chain's excluded cases (a hovered row that is also selected/inserted/being edited/
 * focused) because the ink stays the same regardless. A first cut of this rule shipped that same
 * second rule for BACKGROUND too (`:not(.db-grid .dx-data-row) > td` with none of the vendor-chain
 * exclusions), which is wrong for a background: at (0,6,2) it out-specified this file's own
 * `.dx-selection > td` rule (0,3,2) below AND DevExtreme's focused-row ground, so hovering a SELECTED
 * or FOCUSED row in any legacy grid repainted it with the plain hover ground and the selection/focus
 * ground disappeared under the pointer, for every tenant (adversary, ClickUp 86894yg24, WL-32 round
 * 1). Selected/inserted/edit-row/removed/focused rows already have their OWN ground (selection
 * background below, DevExtreme's own insert/edit/remove/focus treatments) that hover must never
 * overwrite, so the single long-form rule below -- which EXCLUDES all of them, same as the ink rule's
 * own long form -- is the complete, correct behaviour: an unselected, unfocused hovered row takes
 * --dz-grid-row-hover-background, and every other state keeps its own ground under the pointer. */
html[data-db-theme] .dx-datagrid .dx-data-row.dx-state-hover:not(.db-grid .dx-data-row):not(.dx-selection):not(.dx-row-inserted):not(.dx-row-removed):not(.dx-edit-row):not(.dx-row-focused) > td:not(.dx-focused),
html[data-db-theme] .dx-treelist .dx-data-row.dx-state-hover:not(.db-grid .dx-data-row):not(.dx-selection):not(.dx-row-inserted):not(.dx-row-removed):not(.dx-edit-row):not(.dx-row-focused) > td:not(.dx-focused) {
    background-color: var(--dz-grid-row-hover-background);
}

html[data-db-theme] .dx-datagrid .dx-selection > td,
html[data-db-theme] .dx-treelist .dx-selection > td,
html[data-db-theme] .dx-datagrid .dx-selection.dx-row:not(.dx-row-focused) > td {
    background-color: var(--dz-surface-active);
    color: var(--dz-grid-row-color);
}

/* Editors: the search box, the filter-row inputs, every dropdown and date picker.

   NOT A SHARED BUTTON OR ICON DROPDOWN, on itself or inside it. An icon dropdown (`db-icon-dropdown
   db-button--chrome`, which carries a role but not .db-button itself;
   Subscriber detail's download) is a dxDropDownBox, so it carries the editor classes; without the
   exclusion it took an input's fill and read as a field rather than a button. Its role paints it.

   ONE FILL, ON THE ROOT. The inner parts (container, input, the dropdown's input wrapper, the number
   box's spinners) are transparent and let the root's fill show through. When each part took the fill
   itself, any construct that also painted one of them (the card-header date box's container) put a
   second colour inside the border, and the parts' own radii (6px, 4px, 0) showed it at the corners. */
html[data-db-theme] .dx-texteditor.dx-editor-outlined:not(:where(.db-button, .db-button *, .db-icon-dropdown, .db-icon-dropdown *)),
html[data-db-theme] .dx-texteditor.dx-editor-filled:not(:where(.db-button, .db-button *, .db-icon-dropdown, .db-icon-dropdown *)) {
    background-color: var(--dz-control-background);
    color: var(--dz-ink);
}

html[data-db-theme] .dx-texteditor-input:not(:where(.db-button, .db-button *, .db-icon-dropdown, .db-icon-dropdown *)),
html[data-db-theme] .dx-texteditor-container:not(:where(.db-button, .db-button *, .db-icon-dropdown, .db-icon-dropdown *)),
html[data-db-theme] .dx-dropdowneditor-input-wrapper:not(:where(.db-button, .db-button *, .db-icon-dropdown, .db-icon-dropdown *)),
html[data-db-theme] .dx-numberbox-spin-container:not(:where(.db-button, .db-button *, .db-icon-dropdown, .db-icon-dropdown *)) {
    background-color: transparent;
    color: var(--dz-ink);
}

/* AN EDITOR INSIDE A GRID CELL (a filter row, a cell being edited: Subscriber Journals' filter row).
   dx.light fills both the editor and its input white with `.dx-editor-cell .dx-texteditor
   .dx-texteditor-input { background: #fff }` (0,3,0), which outranks the transparent input above,
   so every filter box drew a white slab with the ink on it. Named at one class more; the root takes
   the same inset fill as any other editor. */
html[data-db-theme] .dx-editor-cell .dx-texteditor {
    background-color: var(--dz-control-background);
}

html[data-db-theme] .dx-editor-cell .dx-texteditor .dx-texteditor-input {
    background-color: transparent;
}

/* TEXT ENTRY IS 16PX ON A PHONE. UNGATED: it names no colour and reads no theme, so it applies in
 * light, dark and a host with no data-db-theme alike.
 *
 * iOS Safari zooms the whole page when it focuses a field whose font-size is under 16px, and never
 * zooms back out by itself. The package's inputs are 13 to 14px (the toolbar and grid search at 14,
 * the filter row at --dz-grid-filter-editor-font-size, the card-header date box at 13) and plain
 * DevExtreme editors are 14, so every focus on a phone jumped the layout. The condition and its reason
 * are in 02-grid-shell.css, TOUCH HIT AREAS: from 825px up, or with a mouse, none of this matches.
 * Only the size changes: a 16px line is a pixel or two taller than a 14px one, so an editor sized by
 * its content grows by that much on a phone, and one with a fixed height does not.
 *
 * THE SPECIFICITY IS THE POINT. The package's own sizes are (0,3,0) for the toolbar search and the
 * date box and (0,4,0) for the filter row (11-page-toolbar.css, 13-detail-segments-card-header.css),
 * so the full DevExtreme chain below, from `html`, is (0,4,2) and beats all of them without
 * !important. Every editor the widget draws has this chain (the layout-name rule in 10-layout-panel.css
 * documents it), and each descendant combinator is a space so a tag box's extra tag container
 * does not break it.
 *
 * The native controls come second: this is not DevExtreme, but it is the same defect and the same
 * 16px. Buttons, tick boxes and the other inputs that take no typed text are left out. The native rule
 * is weaker than the DevExtreme one, so a host stylesheet that sets a size on a native field with a
 * class selector wins and has to give it up itself. */
@media (max-width: 824.98px) and (pointer: coarse) {
    html body .dx-texteditor .dx-texteditor-container .dx-texteditor-input-container .dx-texteditor-input {
        font-size: 16px;
    }

    html body input:not([type="checkbox"], [type="radio"], [type="button"], [type="submit"], [type="reset"], [type="range"], [type="file"], [type="image"], [type="color"], [type="hidden"]),
    html body select,
    html body textarea {
        font-size: 16px;
    }
}

/* The dropdown caret and the date box's calendar glyph. dx.light paints them #333, which is 1.4:1 on
   a themed editor. */
html[data-db-theme] .dx-dropdowneditor-icon {
    color: var(--dz-ink);
}

/* A lookup's caret is its own glyph, not the dropdown icon above: dx.light paints `.dx-lookup-arrow`
   #333 at rest and `.dx-dropdowneditor-active .dx-lookup-arrow` #333 open (0,2,0), which all but
   vanished on the dark editor (Inventory's Add Item). */
html[data-db-theme] .dx-lookup-arrow {
    color: var(--dz-ink);
}

/* ...and its hover and pressed tiles. dx.light fills the glyph's box #e6e6e6 on hover and when the
   dropdown is open, at (0,4,0), so the ink glyph above sat white on near-white. Mirrored at the
   vendor's depth, on the spine's own hover and pressed steps. */
html[data-db-theme] .dx-dropdowneditor.dx-state-hover:not(.dx-custom-button-hovered) .dx-dropdowneditor-icon {
    background-color: var(--dz-surface-hover);
}

/* THE CALENDAR a date box opens. dx.light paints every day #333, the weekday row #333, hover and press
 * in translucent #337ab7 and the chosen day solid #337ab7; nothing else in the stack names them, so under
 * a theme the days were near-invisible on the dark popup.
 *
 * Days read the ink. A day from the neighbouring month keeps a dim tone, the placeholder's, because it
 * must still read as "not this month"; it is the one dim text the dark theme keeps besides placeholders.
 * The chosen day and today's ring take the selection hook, so a tenant's selection colour reaches them.
 *
 * Each rule sits one class or element above the vendor arm it answers: (0,2,1) for the day states,
 * (0,3,1) for the other-month and empty days. */
html[data-db-theme] .dx-calendar {
    background-color: var(--dz-card);
}

html[data-db-theme] .dx-calendar-body thead th,
html[data-db-theme] .dx-calendar-cell,
html[data-db-theme] .dx-calendar-navigator .dx-button,
html[data-db-theme] .dx-calendar-navigator .dx-button .dx-icon {
    color: var(--dz-ink);
}

html[data-db-theme] .dx-calendar-cell.dx-calendar-other-view,
html[data-db-theme] .dx-calendar-cell.dx-calendar-other-view span,
html[data-db-theme] .dx-calendar-cell.dx-calendar-empty-cell,
html[data-db-theme] .dx-calendar-cell.dx-calendar-empty-cell span {
    color: var(--dz-placeholder-color);
}

html[data-db-theme] .dx-calendar-cell.dx-state-hover span {
    background-color: var(--dz-surface-hover);
    color: var(--dz-ink);
}

html[data-db-theme] .dx-calendar-cell.dx-state-active span {
    background-color: var(--dz-surface-active);
}

html[data-db-theme] .dx-calendar-cell.dx-calendar-today span {
    border-color: var(--dz-selection-color);
}

html[data-db-theme] .dx-calendar-cell.dx-calendar-selected-date span,
html[data-db-theme] .dx-calendar-cell.dx-calendar-selected-date.dx-state-hover span {
    background-color: var(--dz-selection-color);
    color: var(--dz-selection-ink);
}

/* Pressed and open: dx.light's #b3b3b3 tile also re-sets the glyph to #333, so the ink is restated. */
html[data-db-theme] .dx-dropdowneditor-button.dx-state-active .dx-dropdowneditor-icon,
html[data-db-theme] .dx-dropdowneditor.dx-dropdowneditor-active .dx-dropdowneditor-icon {
    background-color: var(--dz-surface-active);
    color: var(--dz-ink);
}

html[data-db-theme] .dx-texteditor.dx-editor-outlined:not(:where(.db-button, .db-button *, .db-icon-dropdown, .db-icon-dropdown *)) {
    border-color: var(--dz-border);
}

/* THE FILE UPLOADER's text. dx.light paints the "or Drop a file here" label and a file's name #333 and
   its size and status #999, all of it dim or unreadable on the dark card (Inventory's serial-number
   upload). All of it reads the ink; the invalid status keeps its red, restated one class deeper
   because the vendor's `.dx-fileuploader-invalid` arm (0,2,0) falls below the ink rule. The drag-over
   frame's #ddd dash takes the control edge. */
html[data-db-theme] .dx-fileuploader-input-label,
html[data-db-theme] .dx-fileuploader-file-name,
html[data-db-theme] .dx-fileuploader-file-size,
html[data-db-theme] .dx-fileuploader-file-status-message {
    color: var(--dz-ink);
}

html[data-db-theme] .dx-fileuploader-invalid .dx-fileuploader-file-status-message {
    color: var(--dz-form-invalid-color);
}

html[data-db-theme] .dx-fileuploader-dragover .dx-fileuploader-input-label {
    border-color: var(--dz-border);
}

/* AN EDITOR'S OWN LABEL (labelMode Outside or Floating: Send SMS, Starlink Swap). dx.light paints it
 * #999 at rest, #333 outside the field (0,3,0), #337ab7 focused (0,3,0) and #d9534f invalid (0,4,0 deepest),
 * so an Outside label read 1.43:1 on the dark dialog. The label reads the ink at rest, the package's
 * focus paint focused (the --dz-control-focus-color the editor's own edge and label outline take, so
 * a focused editor shows one focus colour, not its ring and a dx.light blue label) and the form's red
 * invalid, one class deeper than rest.
 *
 * DevExtreme puts a Floating label's editor in .dx-texteditor-with-floating-label and a Static or
 * Outside one in .dx-texteditor-with-label, never both, so every rule below names both, as do the
 * label-outline rules after it: a rule on one class leaves the other editor's label on dx.light's
 * paint (#999 at rest, blue on focus). The focused and invalid
 * rules weigh the same (0,5,1), so the order is the precedence: invalid after focused. */
html[data-db-theme] .dx-texteditor.dx-texteditor-with-label .dx-texteditor-label,
html[data-db-theme] .dx-texteditor.dx-texteditor-with-floating-label .dx-texteditor-label {
    color: var(--dz-ink);
}

html[data-db-theme] .dx-texteditor.dx-texteditor-with-label.dx-state-focused .dx-texteditor-label,
html[data-db-theme] .dx-texteditor.dx-texteditor-with-floating-label.dx-state-focused .dx-texteditor-label {
    color: var(--dz-control-focus-color);
}

html[data-db-theme] .dx-texteditor.dx-texteditor-with-label.dx-invalid .dx-texteditor-label,
html[data-db-theme] .dx-texteditor.dx-texteditor-with-floating-label.dx-invalid .dx-texteditor-label {
    color: var(--dz-form-invalid-color);
}

html[data-db-theme] .dx-editor-outlined .dx-label-before,
html[data-db-theme] .dx-editor-outlined .dx-label,
html[data-db-theme] .dx-editor-outlined .dx-label-after {
    border-color: var(--dz-border);
}

/* THE LABEL OUTLINE'S THREE PIECES (.dx-label-before, .dx-label, .dx-label-after) draw the editor's top
 * edge around its label in the Floating and Static label modes: Order Workspace floats, and the card
 * entry's Make payment is Static, where focus in Payment Amount showed a blue top edge beside the
 * editor's own ring. dx.light edges them #ddd at rest (0,2,0), rgba(51,122,183,.4) on hover and
 * #337ab7 focused (0,4,0), and #d9534f invalid, at 40% at rest and in full hovered or focused (0,5,0).
 * They take the paint of the editor's own edge in each state, so the label never reads as a second
 * control:
 *
 * - at rest and focused, on every outlined editor, like the editor's edge (08-dropdown-editor.css
 *   takes focus for every text editor);
 * - hover and invalid only inside .db-form, because 29-form.css repaints the editor's hover and
 *   invalid edge only there. Outside it the editor keeps dx.light's, and so do these pieces.
 *
 * The rules are (0,5,1), one element above the vendor's deepest (0,5,0), and the invalid-and-focused
 * one a class deeper. `:where(.db-form)` adds no weight, so the order here is the precedence: focus
 * after invalid after hover, as the editor's own edge ranks them.
 *
 * Each rule names both label classes (.dx-texteditor-with-label for Static and Outside,
 * .dx-texteditor-with-floating-label for Floating): dx.light paints the pieces for both at the same
 * weight, and DevExtreme gives an editor one or the other. */
html[data-db-theme] :where(.db-form) .dx-editor-outlined.dx-texteditor-with-label.dx-state-hover .dx-label-before,
html[data-db-theme] :where(.db-form) .dx-editor-outlined.dx-texteditor-with-label.dx-state-hover .dx-label,
html[data-db-theme] :where(.db-form) .dx-editor-outlined.dx-texteditor-with-label.dx-state-hover .dx-label-after,
html[data-db-theme] :where(.db-form) .dx-editor-outlined.dx-texteditor-with-floating-label.dx-state-hover .dx-label-before,
html[data-db-theme] :where(.db-form) .dx-editor-outlined.dx-texteditor-with-floating-label.dx-state-hover .dx-label,
html[data-db-theme] :where(.db-form) .dx-editor-outlined.dx-texteditor-with-floating-label.dx-state-hover .dx-label-after {
    border-color: var(--dz-meta);
}

html[data-db-theme] :where(.db-form) .dx-editor-outlined.dx-texteditor-with-label.dx-invalid .dx-label-before,
html[data-db-theme] :where(.db-form) .dx-editor-outlined.dx-texteditor-with-label.dx-invalid .dx-label,
html[data-db-theme] :where(.db-form) .dx-editor-outlined.dx-texteditor-with-label.dx-invalid .dx-label-after,
html[data-db-theme] :where(.db-form) .dx-editor-outlined.dx-texteditor-with-floating-label.dx-invalid .dx-label-before,
html[data-db-theme] :where(.db-form) .dx-editor-outlined.dx-texteditor-with-floating-label.dx-invalid .dx-label,
html[data-db-theme] :where(.db-form) .dx-editor-outlined.dx-texteditor-with-floating-label.dx-invalid .dx-label-after {
    border-color: var(--dz-form-invalid-color);
}

html[data-db-theme] .dx-editor-outlined.dx-texteditor-with-label.dx-state-focused .dx-label-before,
html[data-db-theme] .dx-editor-outlined.dx-texteditor-with-label.dx-state-focused .dx-label,
html[data-db-theme] .dx-editor-outlined.dx-texteditor-with-label.dx-state-focused .dx-label-after,
html[data-db-theme] .dx-editor-outlined.dx-texteditor-with-floating-label.dx-state-focused .dx-label-before,
html[data-db-theme] .dx-editor-outlined.dx-texteditor-with-floating-label.dx-state-focused .dx-label,
html[data-db-theme] .dx-editor-outlined.dx-texteditor-with-floating-label.dx-state-focused .dx-label-after {
    border-color: var(--dz-control-focus-color);
}

html[data-db-theme] :where(.db-form) .dx-editor-outlined.dx-texteditor-with-label.dx-invalid.dx-state-focused .dx-label-before,
html[data-db-theme] :where(.db-form) .dx-editor-outlined.dx-texteditor-with-label.dx-invalid.dx-state-focused .dx-label,
html[data-db-theme] :where(.db-form) .dx-editor-outlined.dx-texteditor-with-label.dx-invalid.dx-state-focused .dx-label-after,
html[data-db-theme] :where(.db-form) .dx-editor-outlined.dx-texteditor-with-floating-label.dx-invalid.dx-state-focused .dx-label-before,
html[data-db-theme] :where(.db-form) .dx-editor-outlined.dx-texteditor-with-floating-label.dx-invalid.dx-state-focused .dx-label,
html[data-db-theme] :where(.db-form) .dx-editor-outlined.dx-texteditor-with-floating-label.dx-invalid.dx-state-focused .dx-label-after {
    border-color: var(--dz-form-invalid-color);
}

html[data-db-theme] .dx-placeholder,
html[data-db-theme] .dx-placeholder::before {
    color: var(--dz-placeholder-color);
}

/* A progress bar's TRACK. dx.light paints it a fixed light grey (border and ground both) in every
   theme, which on a dark card is a near-white slab behind the fill. The track is the hairline
   step of the neutral spine: on the platform light card it is within a few levels of dx.light's
   own grey, and in dark it follows the tenant's Surface like every other hairline. Deliberately
   unprefixed and one class deep, the same weight as the vendor rule, so it wins on order alone and
   the dialog run bar below (three classes, transparent track) and any widget a page restyles
   keep winning over it. The fill is a data colour and is left to the page. */
.dx-progressbar-container {
    border-color: var(--dz-hairline);
    background-color: var(--dz-hairline);
}
/* Anything dx floats above the page: popups, dropdown lists, tooltips, the column chooser, the
   filter builder, the load panel. One family, one surface. */
html[data-db-theme] .dx-overlay-content,
html[data-db-theme] .dx-popup-content,
html[data-db-theme] .dx-popup-title,
html[data-db-theme] .dx-popup-bottom,
html[data-db-theme] .dx-dropdowneditor-overlay .dx-overlay-content,
html[data-db-theme] .dx-list,
html[data-db-theme] .dx-list-item,
html[data-db-theme] .dx-datagrid-column-chooser .dx-overlay-content,
html[data-db-theme] .dx-treelist-column-chooser .dx-overlay-content,
html[data-db-theme] .dx-datagrid-filter-panel,
html[data-db-theme] .dx-treelist-filter-panel,
html[data-db-theme] .dx-loadpanel-content,
html[data-db-theme] .dx-context-menu .dx-submenu,
html[data-db-theme] .dx-menu-item-popout-container,
html[data-db-theme] .dx-scrollview-content {
    background-color: var(--dz-card);
    color: var(--dz-ink);
}

html[data-db-theme] .dx-overlay-content,
html[data-db-theme] .dx-popup-title,
html[data-db-theme] .dx-popup-bottom,
html[data-db-theme] .dx-list-item {
    border-color: var(--dz-hairline);
}

/* A popup focuses itself when it opens, so dx.light's focused frame,
   `.dx-popup-wrapper .dx-state-focused.dx-overlay-content { border: 1px solid #337ab7 }` (0,3,0), drew
   a Bootstrap-blue outline round every open dialog, which marks nothing the reader did. It keeps the
   hairline instead; one class above the vendor's arm. */
html[data-db-theme] .dx-popup-wrapper .dx-overlay-content.dx-state-focused {
    border-color: var(--dz-hairline);
}

/* The pointer a popover (a lookup's list, a tooltip) draws toward its target. dx.light fills it #fff
   with a #ddd edge, deepest at (0,3,1) (`.dx-tooltip-wrapper.dx-popover-wrapper` and
   `.dx-position-bottom`), so it stood as a white notch on the dark list. Same fill and edge as the
   overlay it belongs to; one element above the vendor. */
html[data-db-theme] .dx-popover-wrapper .dx-popover-arrow::after {
    background: var(--dz-card);
    border-color: var(--dz-hairline);
}

/* The backdrop behind a popup. dx.light washes the page white at 80%, which over the dark theme
   turns the page a flat pale grey behind the dialog instead of dimming it. */
html[data-db-theme] .dx-overlay-shader {
    background-color: var(--dz-overlay-scrim);
}

html[data-db-theme] .dx-list-item.dx-state-hover,
html[data-db-theme] .dx-context-menu .dx-menu-item.dx-state-hover {
    background-color: var(--dz-surface-hover);
}

/* KEYBOARD FOCUS AND SELECTION IN LISTS, MENUS AND TREES (PP-D87). dx.light fills a focused list
   item, menu item and plain tree item #337ab7 with white text (a focused selected list item
   rgba(51,122,183,.7)), blind to the tenant accent. A focused row takes the neutral active wash the
   package menus use; a selected row, focused or hovered or both, takes the selection tint, so the
   current choice stays distinct from the focused row while arrowing. Text, icons and the chevron
   take ink. Specificity decides, not file order: the gate's attribute and element put each short
   arm one step over dx.light's (whose list arms count `.dx-list:not(.dx-list-select-decorator-
   enabled)` as two classes) and leave it under the scoped package rules that keep their own focus
   wash, the action menu's in 09-edit-affordance-action-menu.css and the themed context menu's in
   17-context-menu.css. Where a focused and a selected arm tie, the selected arm is later and wins.
   The grid filter row's operation menu keeps DevExtreme's transparent focus: its dx rule is one
   class under the menu arm, so the arm leaves filter-row menus out, through :where() so the
   exclusion adds no specificity. Checkbox trees keep their checkbox focus paint further down. */
html[data-db-theme] .dx-list .dx-list-item.dx-state-focused,
html[data-db-theme] .dx-menu-item.dx-state-focused:not(:where(.dx-datagrid-filter-row .dx-menu-item, .dx-treelist-filter-row .dx-menu-item)),
html[data-db-theme] .dx-treeview-item-without-checkbox.dx-state-focused > .dx-treeview-item {
    background-color: var(--dz-surface-active);
    color: var(--dz-ink);
}

html[data-db-theme] .dx-list .dx-list-item.dx-state-focused .dx-list-item-icon-container .dx-icon,
html[data-db-theme] .dx-menu-item.dx-state-focused:not(:where(.dx-datagrid-filter-row .dx-menu-item, .dx-treelist-filter-row .dx-menu-item)) a {
    color: var(--dz-ink);
}

html[data-db-theme] .dx-list .dx-list-item.dx-state-focused .dx-list-item-chevron {
    border-color: var(--dz-ink);
}

/* dx.light gives the hovered-selected list item its own (0,6,0) arm and the focused-selected one
   (0,5,0), and the context menu's hover wash above is a step over a plain selected menu item, so
   each takes its own arm here. */
html[data-db-theme] .dx-list .dx-list-item.dx-list-item-selected,
html[data-db-theme] .dx-list .dx-list-item.dx-list-item-selected.dx-state-focused,
html[data-db-theme] .dx-list .dx-list-item.dx-list-item-selected.dx-state-hover:not(.dx-state-focused),
html[data-db-theme] .dx-menu-item.dx-menu-item-selected,
html[data-db-theme] .dx-context-menu .dx-menu-item.dx-menu-item-selected.dx-state-hover,
html[data-db-theme] .dx-treeview-item-without-checkbox.dx-state-selected > .dx-treeview-item {
    background-color: var(--dz-selection-tint);
    color: var(--dz-ink);
}

html[data-db-theme] .dx-list .dx-list-item.dx-list-item-selected .dx-list-item-icon-container .dx-icon,
html[data-db-theme] .dx-menu-item.dx-menu-item-selected a {
    color: var(--dz-ink);
}

/* A LIST THAT KEEPS THE NEUTRAL SELECTED ROW (db-list--neutral-selection, ADO #143, Cole 2026-10-16).
   Marketplace Settings' section list read as the neutral active wash before PP-D87 gave every selected
   row the selection tint, so its selected row takes --dz-surface-active with --dz-ink again, at rest,
   focused, hovered, and focused and hovered. The modifier on the list is the only opt-in; every other
   list keeps the tint above. The four arms split the selected row by state, so no two match the same
   row, and each is (0,7,1): over the PP-D87 selected arm of its state, the rest one (0,4,1), the
   focused ones (0,5,1) and the hovered one (0,6,1), and over dx.light's own selected arms, which carry
   no element. It wins on specificity, not file order, and none of the four stands in for another. The
   selected-icon arm above already reads --dz-ink, the same ink, so the icon needs no arm of its own. */
html[data-db-theme] .dx-list.db-list--neutral-selection .dx-list-item.dx-list-item-selected:not(.dx-state-focused):not(.dx-state-hover),
html[data-db-theme] .dx-list.db-list--neutral-selection .dx-list-item.dx-list-item-selected.dx-state-focused:not(.dx-state-hover),
html[data-db-theme] .dx-list.db-list--neutral-selection .dx-list-item.dx-list-item-selected.dx-state-hover:not(.dx-state-focused),
html[data-db-theme] .dx-list.db-list--neutral-selection .dx-list-item.dx-list-item-selected.dx-state-focused.dx-state-hover {
    background-color: var(--dz-surface-active);
    color: var(--dz-ink);
}

/* dx draws its own scrollbars inside a widget rather than using the browser's, so `color-scheme`
   does not reach them -- a grid wider than its container keeps dx's own thumb colour on a
   themed trough.

   WL-24 FIX ROUND 1 (ClickUp 86894yg24), PM ruling D4: reads --dz-scrollbar-thumb (35-scrollbar.css),
   not --dz-border directly any more, so this thumb and the native one stay the SAME fixed colour by
   construction -- --dz-border alone measured 1.81:1 on the card / 1.69:1 on the page, under the 3:1
   non-text floor a styled scrollbar, as an author-painted control, owes (WCAG 1.4.11); fixing it once
   at the token means this rule never drifts from that fix. */
html[data-db-theme] .dx-scrollable-scroll-content {
    background-color: var(--dz-scrollbar-thumb);
}

html[data-db-theme] .dx-scrollable-scrollbar.dx-state-invisible .dx-scrollable-scroll-content {
    background-color: transparent;
}

/* Buttons dx draws itself (the normal-mode toolbar buttons). Contained/success/danger modes keep
   their own fills, which are colour rather than neutral and read correctly against any ground.

   NOT A .db-button: a shared button role paints its own fill and ink (07-button-roles.css), and at
   this weight the rule here cleared it, so a chrome-role icon button (Subscriber detail's download)
   lost its --dz-button-chrome-background and read as a bare glyph. */
html[data-db-theme] .dx-button-mode-outlined:not(:where(.db-button)),
html[data-db-theme] .dx-button-mode-text:not(:where(.db-button)) {
    background-color: transparent;
    color: var(--dz-ink);
}

/* A contained button that carries a real colour keeps it. DevExtreme's `type` classes say so for
   its own buttons; a button painted by the shared contract carries no `type` but a ROLE, on itself
   or on a dropdown wrapper around it. Without the role exclusions Billing Center's `primary` "New"
   dropdown turns a dark neutral in dark mode.

   ONLY THE ROLES THAT CARRY A COLOUR, deliberately not every .db-button. Action, Cancel, Disclosure
   and Chrome read the Standard buttons group (--dz-button-standard-*) directly rather than a colour
   of their own (WL-20b round 4, A90, ClickUp 86894yg24): until A90 this reset repainted them with
   --dz-sunken/--dz-ink/--dz-border instead. _Layout renders html[data-db-theme] unconditionally
   (WL-20a, D1), so this rule matched on every page, and because it reaches (0,6,1) on specificity --
   six classes/attrs/:not()s plus the html element -- it beat the role's own background
   (`.dx-button.db-button`, (0,2,0), reading --dz-button-background -> --dz-button-action-background
   -> --dz-button-standard-background) outright regardless of source order. The tenant's Standard
   buttons setting therefore never reached Bulk Actions or any other .db-button--action (D11,
   measured live by the round-3 adversary at 5834c966f: /voyager/subscribers, Standard background
   #fafafa saved, Bulk Actions still painted #f0eeec, the --dz-sunken literal). A90: those buttons
   read the Standard tokens directly instead, so an untouched tenant now sees the Standard defaults
   (light: #fff/--dz-border; dark: the dark band's own --dz-card/--dz-ink/--dz-border aliases) rather
   than --dz-sunken -- a deliberate, accepted appearance change for every tenant who has not
   customised Standard buttons.

   NEXT AND POSITIVE JOIN THE EXCLUSION HERE (WL-20b round 5, A91, ClickUp 86894yg24). Unlike
   Action/Cancel/Disclosure/Chrome, --dz-button-next-background and --dz-button-positive-background
   carry their own literal/brand colour, not the Standard alias -- but DbNext/DbPositive do not set
   an outlined or text styling mode, so they render `.dx-button-mode-contained` the same as Action
   does and this rule used to match them: a themed Next or Positive button (Invoice's wizard "Next",
   Bulk's) was already losing its own colour to this reset before A90 (to --dz-sunken) and kept
   losing it after A90 (to Standard) -- a real gap, reported rather than fixed in round 4. Per A91,
   Next and Positive keep their role colour in BOTH modes (same treatment as Primary/Submit/Danger),
   so they are excluded the same way, by class rather than by DevExtreme type (neither carries one).

   WL-21 (ClickUp 86894yg24) CLOSES THESE: a `.db-button--action` also toggled `.db-button--selected`
   (Order Index's "Show Closed Orders" -- measured live before this fix: selected rendered Standard's
   hover grey, #f8f7f5 on a #c4c0be border in light, #2a2827 on #76716e in dark, instead of the
   accent-based selected tokens) and `.db-button--pressed`, the same shape on a toggle rather than a
   persistent state (Home/Index's Top Data Users expand icon -- same defect measured live, same
   grey). Both are excluded BARE, not as `.db-button--action.db-button--selected`: a compound selector
   would lift this rule from (0,6,1) to (0,7,1) on the :is() argument's own specificity (the comment
   above already warns :not(:is()) takes its argument's weight), and nothing else in the product
   defines a --selected or --pressed treatment outside these two roles, so the bare class is both
   sufficient and the narrower change. `.db-edit-affordance` (Subscriber Detail's inline edit pencils,
   e.g. "Edit default plan") is excluded the same way: measured live before this fix, the pencil
   rendered a visible fill and border (#fff on #c4c0be light, #201f1e on #76716e dark) instead of the
   ghost role's transparent ground -- a bordered chip, not the bare glyph the design draws.

   `.db-button--notification-unread` is NOT in this list. Its one live caller (the navbar bell,
   `_LayoutVoyagerNavbar.cshtml` / `_LayoutNavbar.cshtml`) always pairs the class with
   `Type(ButtonType.Danger)`, already excluded above by `:not(.dx-button-danger)`, so this reset never
   actually matches it today. The token mechanism is identical to `.db-button--pressed`'s, so a future
   caller that applies the class without the Danger type would hit the same gap -- worth watching, not
   yet a live one to fix.

   THE GRID HEADER-FILTER POPUP'S OK/CANCEL BUTTONS (21-grid-header-filter.css) carry no role class at
   all -- DevExtreme builds that popup's own toolbar, so there is no class this list could name.
   Measured live before this fix: OK and Cancel rendered identically, both Standard grey at rest and
   on hover, in both themes. The separate `:not(:where(...))` clause below excludes them by DOM
   location instead: `:where()` carries no specificity of its own, so a descendant selector can be
   excluded here without lifting the rule the way adding one inside `:is()` would. The popup's own
   stylesheet still gives OK and Cancel their colour by redeclaring the `--dz-button-*` custom
   properties its base `.dx-button` rule reads; excluding them here only stops THIS reset from
   overwriting that with literal Standard colours afterward. (An alternative considered and rejected:
   carrying the role through a `.db-button--primary`/`.db-button--cancel` class instead, the way every
   other excluded role is reached. DevExtreme builds this popup's toolbar itself with no option that
   passes a caller class through, so doing it that way would mean mutating the widget's own DOM from
   script after it renders -- imperative paint, the thing the style guide names as the easiest rule to
   break without anything catching it. The scoped structural exclusion is the narrower fix and adds no
   script.) 21-grid-header-filter.css gets a companion Cancel-hover rule alongside this: Cancel had no
   hover declaration of its own there because this reset's now-excluded hover sibling was, until now,
   the only thing painting its hover state at all.

   The `*` half of the :is() reaches a dropdown, where the role sits on the .dx-dropdownbutton wrapper
   and the inner .dx-button has no role class of its own.

   .db-segmented is excluded as a whole control: its chosen option (DevExtreme's .dx-item-selected,
   not a role class) paints from --dz-segmented-*, which a tenant may point at the accent, and an
   unselected option's transparent fill and --dz-ink label are the contract's too and correct on any
   themed ground.

   .db-button--bare is excluded because it has no ground or edge to reset: its hooks are transparent
   (07-button-roles.css), and this rule's standard fill would put the button box back.

   A gear's layout panel (.db-layout-panel--gear) is excluded by location, in a :where() of its
   own so the header-filter clause above stays as written: its action rows are flat menu rows
   (10-layout-panel.css), and this reset out-ranks the gear's flat paint. */
html[data-db-theme] .dx-button-mode-contained:not(.dx-button-success):not(.dx-button-danger):not(.dx-button-default):not(:is(.db-button--primary, .db-button--submit, .db-button--danger, .db-button--next, .db-button--positive, .db-button--answer, .db-button--primary *, .db-button--submit *, .db-button--danger *, .db-button--next *, .db-button--positive *, .db-button--answer *, .db-segmented, .db-segmented *, .db-button--selected, .db-button--selected *, .db-button--pressed, .db-button--pressed *, .db-edit-affordance, .db-edit-affordance *, .db-button--bare, .db-button--bare *)):not(:where(.dx-header-filter-menu .dx-popup-bottom .dx-button)):not(:where(.db-layout-panel--gear .db-layout-action > .dx-button)) {
    background-color: var(--dz-button-standard-background);
    color: var(--dz-button-standard-color);
    border-color: var(--dz-button-standard-border-color);
}

/* HOVER AND PRESSED, REPEATED AT THE SAME WEIGHT ON PURPOSE -- NEITHER EXISTED BEFORE A90, and this
   is beyond the letter of A90 (which named only the resting background/ink/border), added because
   leaving it out would leave A90 half done. The resting rule above has no state qualifier, so it
   keeps matching (and keeps winning on specificity, (0,6,1) against the role's own
   `.dx-state-hover`/`.dx-state-active` rules at (0,3,0)/(0,4,0) in 07-button-roles.css) through every
   state an affected button passes through. Without this pair, A90's resting fix would still freeze
   hover and active at the resting Standard colours: specificity does not care that the element also
   carries .dx-state-hover, only that this selector matches it too -- the SAME freeze that, pre-A90,
   pinned hover to the resting --dz-sunken literal (an existing defect this also happens to close,
   not one A90 or D11 named). Mirrors 07-button-roles.css's own .dx-state-active rule in reusing
   hover-color/hover-border-color: pressed only deepens the ground, the label and edge stay where
   hover left them. Next and Positive are excluded here too (WL-20b round 5, A91) -- the resting
   exclusion alone would stop the reset's own background/colour/border, but leave this sibling pair
   still matching and still overwriting their hover/active state, which is the same specificity-freeze
   shape A90 already closed for Standard; excluding by the same class list keeps all three rules
   agreeing on which buttons they ever touch. */
html[data-db-theme] .dx-button-mode-contained:not(.dx-button-success):not(.dx-button-danger):not(.dx-button-default):not(:is(.db-button--primary, .db-button--submit, .db-button--danger, .db-button--next, .db-button--positive, .db-button--answer, .db-button--primary *, .db-button--submit *, .db-button--danger *, .db-button--next *, .db-button--positive *, .db-button--answer *, .db-segmented, .db-segmented *, .db-button--selected, .db-button--selected *, .db-button--pressed, .db-button--pressed *, .db-edit-affordance, .db-edit-affordance *, .db-button--bare, .db-button--bare *)):not(:where(.dx-header-filter-menu .dx-popup-bottom .dx-button)):not(:where(.db-layout-panel--gear .db-layout-action > .dx-button)).dx-state-hover {
    background-color: var(--dz-button-standard-hover-background);
    color: var(--dz-button-standard-hover-color);
    border-color: var(--dz-button-standard-hover-border-color);
}

html[data-db-theme] .dx-button-mode-contained:not(.dx-button-success):not(.dx-button-danger):not(.dx-button-default):not(:is(.db-button--primary, .db-button--submit, .db-button--danger, .db-button--next, .db-button--positive, .db-button--answer, .db-button--primary *, .db-button--submit *, .db-button--danger *, .db-button--next *, .db-button--positive *, .db-button--answer *, .db-segmented, .db-segmented *, .db-button--selected, .db-button--selected *, .db-button--pressed, .db-button--pressed *, .db-edit-affordance, .db-edit-affordance *, .db-button--bare, .db-button--bare *)):not(:where(.dx-header-filter-menu .dx-popup-bottom .dx-button)):not(:where(.db-layout-panel--gear .db-layout-action > .dx-button)).dx-state-active {
    background-color: var(--dz-button-standard-active-background);
    color: var(--dz-button-standard-hover-color);
    border-color: var(--dz-button-standard-hover-border-color);
}

/* WL-21 round 2 (ClickUp 86894yg24), D1. BEFORE: this rule named every dxButton's icon with no
 * exclusion, (0,3,1) -- the html element plus two classes. 07-button-roles.css's own
 * `.dx-button.db-button .dx-icon { color: inherit }` is (0,3,0), one class lighter because it has
 * no html prefix, so it always lost here regardless of load order: a SHARED button's glyph never
 * inherited the role's own ink, it was pinned to --dz-ink no matter what the button's `color`
 * resolved to.
 *
 * MEASURED LIVE (wl-lane-a, light): the two round-1 on-state toggles (.db-button--selected,
 * .db-button--pressed) paint an accent ground with the glyph still #31302f on top -- 2.68:1 at rest
 * and focus, 2.03:1 on hover, under the WCAG 1.4.11 non-text 3:1 floor, because round 1 fixed the
 * BUTTON's colour but this rule still pinned the ICON underneath it. The same shape silently affects
 * every icon-bearing Primary/Danger/Next/Positive button too (not reported in round 1 because none of
 * those were in scope there): a dark glyph on a filled brand colour in light mode, 2.55-2.75:1.
 *
 * FIX: exclude a `.db-button`'s own icon, by ancestor, the same way the dxButton's own fill/border
 * rules already exclude it (`.dx-button-mode-contained:not(:is(.db-button--primary, ...))` above,
 * `.dx-widget:not(:where(.db-icon-dropdown, .db-button, ...))` near the top of this file). A SHARED
 * button's glyph then falls through to 07's `color: inherit`, taking whatever the button's role
 * already resolved -- --dz-on-accent (white) on a filled role, the role's own ink on an outlined one
 * -- in every state, because 07's rule has no state qualifier either.
 *
 * SPECIFICITY IS UNCHANGED FOR A ROLELESS BUTTON: :where() contributes zero weight whether or not it
 * matches, so this is still (0,3,1) -- every dxButton that never adopted the package (DevExtreme's own
 * toolbar icons, the calendar navigator, etc.) keeps exactly the ink it had before this round. */
html[data-db-theme] .dx-button:not(:where(.db-button)) .dx-icon {
    color: var(--dz-ink);
}

/* Checkboxes: the unticked box is a light fill in dx.light and disappears against a dark row. The fill
   is a token so each theme picks it: the card in light, the sunken tone in dark. */
html[data-db-theme] .dx-checkbox-icon {
    background-color: var(--dz-checkbox-fill);
    border-color: var(--dz-border);
}

/* A ticked box, through the hooks in 00-tokens.css: the selection colour fills the box and border and
   the selection ink draws the tick, light and dark alike (PP-D82, one cobrand checkbox everywhere; the
   grid's and the header filter's boxes already read the same colour). Only checked and indeterminate
   change: unchecked, hover, focus, disabled and readonly stay dx.light's with the neutral fill above. */
html[data-db-theme] .dx-checkbox-checked .dx-checkbox-icon,
html[data-db-theme] .dx-checkbox-indeterminate .dx-checkbox-icon {
    background-color: var(--dz-checkbox-checked-fill);
    border-color: var(--dz-checkbox-checked-border);
}

html[data-db-theme] .dx-checkbox-checked .dx-checkbox-icon {
    color: var(--dz-checkbox-tick-color);
}

html[data-db-theme] .dx-checkbox-indeterminate .dx-checkbox-icon::before {
    background-color: var(--dz-checkbox-tick-color);
}

/* A FOCUSED UNCHECKED BOX TAKES THE PACKAGE'S FOCUS EDGE (PP-D68), not dx.light's Bootstrap blue.
   dx.light draws `.dx-checkbox.dx-state-focused .dx-checkbox-icon { border: 1px solid #337ab7 }` at
   (0,3,0), and the package never answered it, so the Promote to reseller dialog's checkbox showed a
   blue edge beside the package's brand-coloured editors. The edge is --dz-control-focus-color, the
   colour of a focused text editor's (08-dropdown-editor.css).
   The rule is (0,5,1) against the vendor's (0,3,0): the prefix and the :not() each add one class.
   The :not() keeps every other arm where it was: a checked or indeterminate box keeps the selection
   fill and border above (it would otherwise lose them to this rule), an INVALID box keeps dx.light's red
   focused edge (`.dx-invalid.dx-state-focused .dx-checkbox-container .dx-checkbox-icon`, (0,4,0), which
   this rule would out-rank), and a readonly box keeps its grey. Hover and the rest edge are not changed
   here; a box that is both hovered and focused takes the focus edge, as an editor does.
   A list item's box and a tree item's box are the same vendor rule one nesting deeper, both at (0,5,0), and
   there the vendor out-ranks the package's checked border too, so those arms also restate
   the checked and indeterminate border. A list's radio decorator is left to dx.light on purpose
   (WL-33 round 3, below). */
html[data-db-theme] .dx-checkbox.dx-state-focused:not(.dx-invalid, .dx-state-readonly, .dx-checkbox-checked, .dx-checkbox-indeterminate) .dx-checkbox-icon,
html[data-db-theme] .dx-list.dx-list-select-decorator-enabled :is(.dx-list-item, .dx-list-select-all).dx-state-focused .dx-checkbox:not(.dx-invalid, .dx-state-readonly, .dx-checkbox-checked, .dx-checkbox-indeterminate) .dx-checkbox-icon,
html[data-db-theme] .dx-treeview-item-with-checkbox.dx-state-focused > .dx-treeview-item .dx-checkbox:not(.dx-invalid, .dx-state-readonly, .dx-checkbox-checked, .dx-checkbox-indeterminate) .dx-checkbox-icon {
    border-color: var(--dz-control-focus-color);
}

html[data-db-theme] .dx-list.dx-list-select-decorator-enabled :is(.dx-list-item, .dx-list-select-all).dx-state-focused .dx-checkbox:is(.dx-checkbox-checked, .dx-checkbox-indeterminate) .dx-checkbox-icon,
html[data-db-theme] .dx-treeview-item-with-checkbox.dx-state-focused > .dx-treeview-item .dx-checkbox:is(.dx-checkbox-checked, .dx-checkbox-indeterminate) .dx-checkbox-icon {
    border-color: var(--dz-checkbox-checked-border);
}

/* NATIVE CHECKBOXES AND RADIOS (PP-D82, PP-D83): a plain <input type="checkbox"> or <input type="radio">
   has no DevExtreme icon for the rules above and below to repaint (the Invite Link lists and Link Type
   radios, script-built lists, the island's toggles), so the browser's own control takes the selection
   colour through accent-color, and picks its own tick or dot ink against it. The selection colour is
   read directly, not through --dz-native-control-accent, which is the gate's (a Primary customization
   moves --dz-accent to the same colour). (0,2,2), over a page rule such as the Invite Link page's
   (0,2,1); the choice list's marker and the choice cards' own accent (25-choice-cards.css) are more
   specific and keep their colour. */
html[data-db-theme] input[type="checkbox"],
html[data-db-theme] input[type="radio"] {
    accent-color: var(--dz-selection-color);
}

/* RADIO BUTTONS (A111, ClickUp 86894yg24, WL-33; PP-D83, Cole: "THESE SHOULD ALL BE COLORED THE SAME").
   dx.light's structure is an outer ring (.dx-radiobutton-icon, which gains `-checked` when selected)
   around an inner dot (.dx-radiobutton-icon-dot, which gains a background-color and a 5px border when
   checked). A checked radio is painted like a checked checkbox (PP-D82): the RING is filled with the
   selection colour (--dz-radio-checked-fill, -border), the DOT is the selection ink
   (--dz-radio-checked-dot-color) and the dot's own 5px border is the ring's fill, so the dot reads as an
   ink disc inside the filled ring. The tokens default to the selection pair at :root (00-tokens.css), so
   the default tenant's radios are the brand's, and WhitelabelCompiler.EmitCheckboxAndRadioFollowPrimary
   still repoints them to Primary's fill, border and ink when Primary's background is customized.

   History this replaces: WL-33 round 2 left the three tokens undeclared at :root so an untouched tenant
   resolved every declaration to dx.light's own literal (an inline fallback for each), and painted the ring
   only where dx.light had no cue of its own. The default is now the brand, so the fallbacks are gone (a
   token :root declares takes none) and the states are answered arm by arm below, at the weights stated.

   THE ARMS, against dx.light's (all `html[data-db-theme]` prefixed, so one element above the vendor's):
   - CHECKED RING FILL, every state: the fill, including pressed (dx.light's grey wash is the unchecked cue).
   - CHECKED RING BORDER: the checked border at rest, hover and pressed, in a plain group and in a list's
     radio decorator (the list's own hover/focus arms are (0,5,0), so that arm is written one nesting deeper).
     An INVALID or READONLY radio keeps dx.light's red or grey ring, as the checkbox does.
   - FOCUS, checked or not: the ring's edge is --dz-control-focus-color (a text editor's focus edge), not
     dx.light's #337ab7, at (0,6,1) against the vendor's (0,3,0) and, inside a list, (0,7,1) against (0,5,0).
     An invalid focused radio keeps its red edge and a readonly one its grey. A focused CHECKED radio also
     draws the package focus ring (--dz-radio-checked-focus-ring), because its edge now matches its fill.
   - The list's radio decorator (a single-select dxList) was left to dx.light by WL-33 round 3: its
     focused and hovered item cue was dx.light's blue edge. It now reads the same tokens: a focused item's
     radio has the focus edge, a hovered unchecked one keeps dx.light's hover edge, a checked one keeps its fill.
   - HOVER of an UNCHECKED radio is left to dx.light's translucent blue edge, as an unchecked checkbox's is. */
html[data-db-theme] .dx-radiobutton .dx-radiobutton-icon-checked {
    background-color: var(--dz-radio-checked-fill);
}

html[data-db-theme] .dx-radiobutton .dx-radiobutton-icon-checked:not(.dx-invalid *, .dx-state-readonly *) {
    border-color: var(--dz-radio-checked-border);
}

html[data-db-theme] .dx-list.dx-list-select-decorator-enabled .dx-list-item .dx-radiobutton .dx-radiobutton-icon-checked:not(.dx-invalid *, .dx-state-readonly *) {
    border-color: var(--dz-radio-checked-border);
}

html[data-db-theme] .dx-radiobutton.dx-state-focused:not(.dx-state-readonly) .dx-radiobutton-icon:not(.dx-invalid *),
html[data-db-theme] .dx-list.dx-list-select-decorator-enabled .dx-list-item.dx-state-focused .dx-radiobutton:not(.dx-state-readonly) .dx-radiobutton-icon:not(.dx-invalid *) {
    border-color: var(--dz-control-focus-color);
}

html[data-db-theme] .dx-radiobutton.dx-state-focused .dx-radiobutton-icon-checked,
html[data-db-theme] .dx-list-item.dx-state-focused .dx-radiobutton-icon-checked {
    box-shadow: var(--dz-radio-checked-focus-ring);
}

html[data-db-theme] .dx-radiobutton .dx-radiobutton-icon-checked .dx-radiobutton-icon-dot {
    background-color: var(--dz-radio-checked-dot-color);
    border-color: var(--dz-radio-checked-fill);
}

/* The switch track. dx.light: `.dx-switch-container { border: 1px solid #ddd; background: #fff }`, so
   an off switch is a white box on a dark card. The handle keeps dx's own paint: it is a mark on the
   track, not a surface, and reads on either ground. Hover, focus and pressed out-rank this rule and
   keep their own track, which is transparent or a translucent wash and correct on any ground. */
html[data-db-theme] .dx-switch-container {
    background-color: var(--dz-sunken);
    border-color: var(--dz-border);
}

/* A FOCUSED OR PRESSED SWITCH takes the package's focus edge too (PP-D68). dx.light paints the track's
   border #337ab7 when focused (`.dx-switch.dx-state-focused .dx-switch-container`, (0,3,0)) and
   pressed (`.dx-switch.dx-state-active .dx-switch-container`, (0,3,0)), and the handle's mark #337ab7
   when focused (`.dx-switch.dx-state-focused .dx-switch-handle::before`, (0,3,1)). The Invite Link and
   Marketplace Settings pages draw switches. These are (0,4,1), (0,4,1) and (0,4,2). The pressed arm's
   translucent track wash is dx.light's and stays. They come before the readonly rule below, so a
   readonly switch that is focused keeps its hairline (equal weight, later wins). */
html[data-db-theme] .dx-switch.dx-state-focused .dx-switch-container,
html[data-db-theme] .dx-switch.dx-state-active .dx-switch-container {
    border-color: var(--dz-control-focus-color);
}

html[data-db-theme] .dx-switch.dx-state-focused .dx-switch-handle::before {
    background-color: var(--dz-control-focus-color);
}

html[data-db-theme] .dx-switch.dx-state-readonly .dx-switch-container {
    background-color: var(--dz-sunken);
    border-color: var(--dz-hairline);
}

/* The labels inside the track. dx.light: OFF #999, ON #333, the second 1.16:1 on the themed track. */
/* UNGATED, A105 (ClickUp 86894yg24): OFF reads --dz-body now, not --dz-meta. Meta is under 4.5:1
 * against the track this label actually sits on (.dx-switch-container's --dz-sunken ground, below);
 * the "4.56:1" this comment used to claim for meta was measured against the card, a lighter ground
 * than the track, never against the track itself. Body clears 4.5:1 on the track in light, and in
 * dark -- where body and meta both resolve to the theme's own ink (voyager.darkmode.css) -- it
 * passes the same way meta always did there. */
.dx-switch-off {
    color: var(--dz-body);
}

html[data-db-theme] .dx-switch-on {
    color: var(--dz-ink);
}

/* A plain dxForm - one not wearing .db-form, which paints these itself in 29-form.css with the same
   tokens. dx.light: label #333 (1.43:1 on the dark page), required mark #c9302c. */
html[data-db-theme] .dx-field-item-label-text {
    color: var(--dz-ink);
}

html[data-db-theme] .dx-field-item-required-mark {
    color: var(--dz-form-required-color);
}

/* A plain dxForm's section rule, under each group caption. dx.light:
   `.dx-form-group-with-caption > .dx-form-group-content { border-top: 1px solid #ddd }`, a bright
   line in the dark theme. The same token .db-form sets (29-form.css). */
html[data-db-theme] .dx-form-group-with-caption > .dx-form-group-content {
    border-top-color: var(--dz-hairline);
}

/* The grid's empty message. dx.light: `.dx-datagrid-nodata { color: #333; opacity: .7 }`, which is
   1.3:1 on the dark card. It is meta text, so it takes the meta token at full strength: meta at .7
   would fall below its own floor. */
html[data-db-theme] .dx-datagrid-nodata,
html[data-db-theme] .dx-treelist-nodata {
    color: var(--dz-meta);
    opacity: 1;
}

/* A column dragged into the group box. dx.light names it at (0,2,0):
   `.dx-datagrid-group-panel .dx-group-panel-item { color: #757575; border: 1px solid #ddd }`.
   UNGATED: #757575 is 4.3:1 on the light page, so Body paints it in both themes. */
.dx-datagrid-group-panel .dx-group-panel-item,
.dx-treelist-group-panel .dx-group-panel-item {
    color: var(--dz-body);
    border-color: var(--dz-border);
}

/* A PLAIN TAB STRIP, one that is not .db-tabs.
 *
 * dx.light draws a folder tab: a #f7f7f7 strip, a white open tab, #333 labels and #ddd rules, all of
 * it light in the dark theme. The same strip is kept here on the spine: the strip and the resting
 * tabs on --dz-sunken, the open or hovered tab on --dz-card, pressed on --dz-surface-active.
 *
 * .db-tabs IS EXCLUDED, on itself and on anything inside it. It is the shared underline strip
 * (04-tabs.css) and paints every one of these from its own tokens; these rules out-rank its selectors,
 * so without the exclusion they would repaint it as a folder tab.
 *
 * SPECIFICITY. The vendor's deepest arms are `.dx-tab.dx-tab-selected .dx-tab-text` (0,3,0) and the
 * focus ring `.dx-tabs.dx-state-focused .dx-tab.dx-state-focused::after` (0,4,1). The prefix plus
 * the exclusion puts every rule below above them, so the focus ring is restated last, on a longer
 * selector, rather than lost to the resting rule. */
html[data-db-theme] .dx-tabs:not(:is(.db-tabs, .db-tabs *)) .dx-tabs-wrapper,
html[data-db-theme] .dx-tabs:not(:is(.db-tabs, .db-tabs *)) .dx-tab {
    background-color: var(--dz-sunken);
    color: var(--dz-ink);
}

html[data-db-theme] .dx-tabs:not(:is(.db-tabs, .db-tabs *)) .dx-tab.dx-state-hover,
html[data-db-theme] .dx-tabs:not(:is(.db-tabs, .db-tabs *)) .dx-tab.dx-tab-selected {
    background-color: var(--dz-card);
}

html[data-db-theme] .dx-tabs:not(:is(.db-tabs, .db-tabs *)) .dx-tab.dx-state-active {
    background-color: var(--dz-surface-active);
}

html[data-db-theme] .dx-tabs:not(:is(.db-tabs, .db-tabs *)) .dx-tab .dx-tab-text,
html[data-db-theme] .dx-tabs:not(:is(.db-tabs, .db-tabs *)) .dx-tab .dx-icon {
    color: var(--dz-ink);
}

/* The wrapper takes both: dx draws the strip's rule on ::after, and a tab panel adds a real
   border-block on the wrapper itself. */
html[data-db-theme] .dx-tabs:not(:is(.db-tabs, .db-tabs *)) .dx-tabs-wrapper,
html[data-db-theme] .dx-tabs:not(:is(.db-tabs, .db-tabs *)) .dx-tabs-wrapper::after,
html[data-db-theme] .dx-tabs:not(:is(.db-tabs, .db-tabs *)) .dx-tab::after {
    border-color: var(--dz-hairline);
}

html[data-db-theme] .dx-tabs:not(:is(.db-tabs, .db-tabs *)).dx-state-focused .dx-tab.dx-state-focused::after {
    border-color: var(--dz-control-focus-color);
}

/* The frame round a tab panel's body. dx.light: `.dx-tabpanel .dx-multiview-wrapper { border: 1px
   solid #ddd }`, a white outline in the dark theme. .db-tabs suppresses this frame itself. The
   focused frame (0,4,0 in the vendor) keeps its own rule here for the same reason as the tabs. */
html[data-db-theme] .dx-tabpanel:not(.db-tabs) .dx-multiview-wrapper {
    border-color: var(--dz-hairline);
}

html[data-db-theme] .dx-tabpanel:not(.db-tabs).dx-state-focused .dx-multiview-wrapper.dx-state-focused {
    border-color: var(--dz-control-focus-color);
}

/* A TILE VIEW's tiles (API Connections). dx.light: `.dx-tile { color: #333; background-color: #fff;
   border: 1px solid rgba(221,221,221,.6) }`, fifteen white cards on the dark page. They are cards and
   take the card. Hover and focus step up a surface; the base rule outranks dx's (0,2,0) hover fill, so
   the step is restated here rather than left to the vendor's #fff. */
html[data-db-theme] .dx-tile {
    background-color: var(--dz-card);
    color: var(--dz-ink);
    border-color: var(--dz-hairline);
}

html[data-db-theme] .dx-tile.dx-state-hover,
html[data-db-theme] .dx-tile.dx-state-focused {
    background-color: var(--dz-surface-hover);
    border-color: var(--dz-border);
}

/* THE GANTT's chart side (Subscriber Journals' timeline). dx.light: `.dx-gantt .dx-gantt-view
   { background-color: #fff }`, a white slab beside the task list, which the grid rules above already
   paint. */
html[data-db-theme] .dx-gantt .dx-gantt-view {
    background-color: var(--dz-card);
}

/* A GRID's bottom load panel, the strip an infinite-scroll grid keeps under its last row (Endpoint
   Dashboard). dx.light: `.dx-datagrid-bottom-load-panel { border-top: 1px solid #ddd;
   background-color: #fff }`, a white band under the dark rows. It continues the rows, so it takes
   their card. */
html[data-db-theme] .dx-datagrid-bottom-load-panel,
html[data-db-theme] .dx-treelist-bottom-load-panel {
    background-color: var(--dz-card);
    border-color: var(--dz-hairline);
}

/* A GRID'S LOAD ERROR, UNGATED. dx.light fills the error row and the edit popup's message #e89895
   under white, 2.24:1 in either theme. Both take the grid-error pair. */
.dx-datagrid-rowsview .dx-error-row .dx-error-message,
.dx-treelist-rowsview .dx-error-row .dx-error-message,
.dx-datagrid-edit-popup .dx-error-message,
.dx-treelist-edit-popup .dx-error-message {
    background-color: var(--dz-grid-error-background);
    color: var(--dz-grid-error-color);
}

/* THE CONTRAST CHIP (ClickUp 86894yg24, WL-10a). A LEGIBILITY VERDICT, not a status and not a
 * label - it answers one question only: "does this colour, on the surface it actually sits on,
 * clear its contrast floor?" First caller: the whitelabel branding editor, which computes a
 * tenant's chosen colour against the surface it will paint (a card, a nav bar, a button fill) and
 * renders this beside the swatch before the tenant can save it.
 *
 * WHY NOT the badge contract's status kind (`db-badge--status`). A status badge reports a record's LIFECYCLE STATE from a closed,
 * named set the badge contract owns (active, suspend, hibernate, ...). This reports a MEASUREMENT
 * the CALLER made against a floor the caller also knows (4.5:1 text, 3.0:1 non-text - the same
 * pair 14-carrier-badge.css's own header cites). The package does not compute the ratio and does
 * not know which floor applies; it only renders whatever verdict the caller already decided. That
 * is also why this ships with no JavaScript: there is nothing here for the package to compute.
 *
 * MARKUP:
 *   <span class="db-contrast-chip db-contrast-chip--ok">
 *     <span class="db-contrast-chip__ratio">4.9:1</span>
 *     <span class="db-contrast-chip__verdict">AA</span>
 *   </span>
 * `--ok` is the default photograph if neither modifier is applied; `--fail` is the other state.
 * `__verdict` is caller text ("AA", "Fails", "Below floor") - the chip never invents wording, the
 * same rule status badges already follow for their own label text.
 *
 * READING IS NEVER COLOUR-ONLY. The ratio, the verdict word and the marker dot together carry the
 * meaning (WCAG 1.4.1) - a caller must never render this with only the modifier class and no
 * `__verdict` text.
 *
 * TEXT NEVER TAKES THE MARKER COLOUR. --dz-ink is what BOTH the ratio and the verdict word read,
 * because --dz-ink is one of the tokens Voyager's dark-mode sheet redeclares for a dark ground
 * (voyager.darkmode.css) - so the words stay legible in both themes with no work here. The marker
 * dot is the ONLY place the verdict colour appears, on the same principle the five status roles
 * already state at their own declaration (00-tokens.css, "the dot is the only place a status
 * colour appears").
 *
 * THE TWO HOOKS DEFAULT TO THE EXISTING STATUS ACCENTS RATHER THAN READING THEM DIRECTLY, so a
 * tenant can repoint this chip's verdict colour without moving every status dot in the product -
 * COBRANDING.md's rule that a construct names its OWN token even when the default is an existing
 * one.
 *
 * DARK-MODE CONTRAST IS A KNOWN, PRE-EXISTING GAP INHERITED HERE ON PURPOSE, NOT INTRODUCED BY IT.
 * Neither --dz-status-active-accent nor --dz-status-danger-accent is restated for a dark ground
 * anywhere in the package or in voyager.darkmode.css - MEASURED here rather than assumed: against
 * --dz-card's dark value (#201f1e), --dz-status-active-accent (#34803a) computes to roughly
 * 3.36:1 and --dz-status-danger-accent (#c0301f) to roughly 2.89:1, both against the 3.0:1 floor a
 * meaningful non-text marker needs, and the red case falls just short of it. That is the same
 * shortfall every existing SIM-status dot using these two tokens already carries in dark mode
 * today - this chip reuses those tokens rather than inventing a second, better-tuned pair, so it is
 * exactly as legible as the status dot it sits beside, not worse. Retuning --dz-status-*-accent
 * for a dark ground is its own pass across every caller of those two tokens, not a WL-10a fix. */
.db-contrast-chip {
    --dz-contrast-chip-marker: var(--dz-contrast-chip-ok-marker);
    display: inline-flex;
    align-items: center;
    gap: var(--dz-status-gap);
    padding: var(--dz-badge-padding-y) var(--dz-badge-padding-x);
    border-radius: var(--dz-radius-pill);
    background: color-mix(in srgb, var(--dz-contrast-chip-marker) var(--dz-badge-outline-tint), var(--dz-card));
    color: var(--dz-ink);
    font-family: var(--dz-font-family);
    font-size: var(--dz-badge-font-size);
    font-weight: var(--dz-badge-font-weight);
    line-height: var(--dz-badge-line-height);
    white-space: nowrap;
}

.db-contrast-chip::before {
    content: "";
    flex: none;
    width: var(--dz-status-marker-size);
    height: var(--dz-status-marker-size);
    border-radius: var(--dz-status-marker-radius);
    background: var(--dz-contrast-chip-marker);
}

.db-contrast-chip--fail {
    --dz-contrast-chip-marker: var(--dz-contrast-chip-fail-marker);
}

.db-contrast-chip__ratio {
    font-family: var(--dz-font-family-data);
}

/* THE CALLOUT (ClickUp 86894yg24, WL-10a). An inline block message, info or warning, that sits
 * inside a page's own content rather than floating above it - the whitelabel branding editor's
 * "Your brand red is close to the destructive colour" and "Imported from your previous custom.css"
 * messages are the first callers.
 *
 * NEITHER OF THE INVENTORY'S TWO ADJACENT ROWS HAD AN AGREED SHAPE FOR THIS YET.
 * `WebApps/Voyager/docs/ui-component-modernization-inventory.md`'s "Forms and field editors" row
 * names a "validation-message contract" as its next move, and its "Notifications" building-block
 * row separately says the current work "did not inventory page alerts, toasts, inline errors,
 * empty states, or retries" - so this is THIS item stating a shape, not inheriting one the owner
 * already settled. A later validation-message or inline-alert pass may fold this in rather than
 * building a second one; say so rather than treating this as the last word.
 *
 * MARKUP:
 *   <div class="db-callout db-callout--warning">
 *     <span class="db-callout__kind">Warning</span>
 *     <b>Your brand red is close to the destructive colour.</b> Delete and Suspend buttons would
 *     read the same as your primary actions.
 *     <div class="db-callout__note">Optional second line, muted and smaller.</div>
 *   </div>
 * `--info` is the default appearance if neither modifier is applied; `--warning` is the other
 * state. The leading sentence in bold is caller markup (a plain `<b>`), not a construct part - the
 * callout supplies the block's paint, not its copy.
 *
 * `__kind` IS REQUIRED, NOT DECORATIVE - THE CALLOUT IS NEVER COLOUR-ONLY (WCAG 1.4.1). Fix round 1
 * (WL-10a adversary pin #4): the marker hue was the ONLY thing distinguishing --warning from --info,
 * so a reader who cannot tell the two apart - or is looking at dark mode, where the info marker's own
 * wash contrast is already thin - had no way to know which one they were reading. Every caller MUST
 * render `__kind` with the word ("Info"/"Warning"), the same obligation 23-contrast-chip.css already
 * states for its own `__verdict` part - a callout with a marker colour and no `__kind` text is an
 * incomplete declaration, not a valid shorter one.
 *
 * BODY TEXT NEVER TAKES THE MARKER COLOUR. It reads --dz-body, which Voyager's dark-mode sheet
 * redeclares for a dark ground, so the message stays legible in both themes with no work here -
 * the same reason 23-contrast-chip.css keeps its own text on --dz-ink rather than on either verdict
 * colour. The marker colour appears in exactly two places: the left rule and a low wash behind the
 * whole block, both of which are NON-TEXT shapes measured against the 3.0:1 floor rather than the
 * 4.5:1 text floor.
 *
 * THE WASH IS COMPUTED, NOT A SECOND LITERAL. color-mix() against --dz-card is the same technique
 * 23-contrast-chip.css uses and the same reason: --dz-card flips for a dark ground in
 * voyager.darkmode.css, so the wash's OWN lightness follows the theme with no dark block declared
 * here - there is nothing in this file that only makes sense in light mode. */
.db-callout {
    --dz-callout-marker: var(--dz-callout-info-marker);
    display: flex;
    gap: var(--dz-space-1);
    padding: var(--dz-space-1half) var(--dz-space-2);
    border-radius: var(--dz-radius-md);
    border-left: 3px solid var(--dz-callout-marker);
    background: color-mix(in srgb, var(--dz-callout-marker) var(--dz-badge-outline-tint), var(--dz-card));
    color: var(--dz-body);
    font-family: var(--dz-font-family);
    font-size: 13px;
    line-height: 1.5;
}

.db-callout b,
.db-callout strong {
    color: var(--dz-ink);
}

.db-callout__kind {
    flex: none;
    font-weight: 700;
    font-size: 11px;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--dz-ink);
}

/* THE NOTE. An optional muted second line under the message, for a caller's secondary detail
 * (the Change Plan disclaimer's Note). It is smaller and sits a half step below the message.
 * It is NOT --dz-meta, the package's usual muted ink: that measures about 4.0:1 on this callout's
 * own washes, under the 4.5:1 text floor at 12px. It reads --dz-callout-note-ink (00-tokens.css),
 * which resolves to --dz-body (about 5.3:1 on the wash). Dark mode needs no override here:
 * voyager.darkmode.css already sets --dz-body to its ink. */
.db-callout__note {
    display: block;
    margin-top: var(--dz-space-half);
    font-size: 12px;
    color: var(--dz-callout-note-ink);
}

.db-callout--warning {
    --dz-callout-marker: var(--dz-callout-warning-marker);
}

/* CHOICE CARDS (ClickUp 86894yg24, WL-10a). A radio group rendered as selectable cards - a title
 * and a description per option, rather than a row of labelled dots. The whitelabel branding
 * editor's "Where the brand appears" choice ("Brand on primary actions" vs "Brand on identity
 * only") is the first caller.
 *
 * MARKUP:
 *   <div class="db-choice-cards">
 *     <label class="db-choice-card">
 *       <input type="radio" name="brandUse" value="brand">
 *       <span class="db-choice-card__body">
 *         <span class="db-choice-card__title">Brand on primary actions</span>
 *         <span class="db-choice-card__description">Primary buttons use your accent...</span>
 *       </span>
 *     </label>
 *     ...
 *   </div>
 * The radio stays a REAL, VISIBLE native input rather than a hidden one styled by its label's
 * border - copying `.db-layout-density__choice`'s pattern (20-density.css) rather than the mockup's
 * `label.on{border-color:var(--ink)}`, because a hidden input with only a card-edge cue drops the
 * one signal a screen-reader-off, low-vision user relies on fastest: the native control's own
 * shape. `accent-color` is what lets that native control still read as ours.
 *
 * THE SELECTED STATE READS --dz-selection-color / --dz-selection-tint, NOT A NEW HOOK. Those two
 * already mean "this one is chosen" everywhere the grid and the layout panel use them
 * (00-tokens.css's own comment: "Anything that says 'this one is chosen'"), and a card choosing
 * itself from a set is the same meaning one surface over - adding a second pair here would give a
 * tenant two knobs for one decision, which is the drift COBRANDING.md warns a construct into
 * avoiding. Repointing --dz-selection-color already moves this control along with the grid's
 * checkboxes and the density picker. The `--list` variant reads them through its component hooks
 * (--dz-choice-list-selected-*), which resolve to these two, so that repoint still moves the list.
 *
 * `:has()` DEGRADES TO "NO SELECTED STYLE", NOT TO BROKEN. The same tradeoff 02-grid-shell.css's
 * own `:has()` rules already document: a browser with no support renders an unselected-looking
 * card that is still fully operable, never a broken one. */
.db-choice-cards {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    gap: var(--dz-space-1);
}

.db-choice-card {
    display: flex;
    align-items: flex-start;
    gap: var(--dz-space-1);
    padding: var(--dz-space-1half);
    border: 1px solid var(--dz-border);
    border-radius: var(--dz-radius-md);
    background: var(--dz-card);
    cursor: pointer;
}

    .db-choice-card:hover {
        background: var(--dz-surface-hover);
    }

    .db-choice-card:has(input:checked) {
        border-color: var(--dz-selection-color);
        background: var(--dz-selection-tint);
    }

    .db-choice-card input[type="radio"] {
        flex: none;
        width: 14px;
        height: 14px;
        margin: 3px 0 0;
        accent-color: var(--dz-selection-color);
    }

        /* :focus-visible only, matching 20-density.css's own choice control: a keyboard user needs
           the ring (WCAG 2.4.7) and a mouse click on the card must not paint a browser default ring
           around the input it happened to land on. */
        .db-choice-card input[type="radio"]:focus-visible {
            outline: 2px solid var(--dz-focus-color);
            outline-offset: 2px;
        }

.db-choice-card__body {
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.db-choice-card__title {
    font-family: var(--dz-font-family);
    font-size: 14px;
    font-weight: 600;
    color: var(--dz-ink);
}

.db-choice-card__description {
    font-family: var(--dz-font-family);
    font-size: 12px;
    color: var(--dz-body);
}

/* THE LIST VARIANT (ADO #143): a dense radio or checkbox list with every detail of an option on its
 * row, first drawn for Voyager's call-plan picker. datablaze.ui.choice (src/choice.js) builds it:
 *
 *   <div class="db-choice-cards db-choice-cards--list" role="radiogroup" aria-label="Plan">
 *     <div class="db-choice-cards__heading" role="presentation">Pooled plans</div>
 *     <label class="db-choice-card">
 *       <input class="db-choice-card__input" type="radio" name="plan" value="12">
 *       <span class="db-choice-card__body">
 *         <span class="db-choice-card__title">IoT 1 GB<span class="db-choice-card__tags">
 *           <span class="db-choice-card__tag db-choice-card__tag--info">Current</span></span></span>
 *         <span class="db-choice-card__description">...</span>
 *         <span class="db-choice-card__facts"><span class="db-choice-card__fact">...</span></span>
 *       </span>
 *       <span class="db-choice-card__aside">$4.00<span class="db-choice-card__aside-caption">per month</span></span>
 *     </label>
 *   </div>
 *
 * One bordered list rather than a grid of cards: rows are separated by the hairline, and columns run
 * input / body / aside. Every rule is scoped under .db-choice-cards--list, so the card variant above
 * renders unchanged. The native input stays visible for the reason the card variant gives. The
 * checked row takes the --dz-choice-list-selected-* hooks: a tint and a 3px inset bar, drawn as a
 * shadow so the row's content never moves. */
.db-choice-cards.db-choice-cards--list {
    display: block;
    border: 1px solid var(--dz-border);
    border-radius: var(--dz-radius-md);
    background: var(--dz-card);
    overflow: hidden;
}

.db-choice-cards--list .db-choice-card {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) auto;
    align-items: start;
    column-gap: var(--dz-space-1half);
    padding: var(--dz-space-1half);
    border: 0;
    border-top: 1px solid var(--dz-hairline);
    border-radius: 0;
    /* The row is a <label>: a host's label margin (Bootstrap's 8px bottom in Voyager) would sit
       under every row as an untinted strip, visible on the checked one. */
    margin: 0;
    background: var(--dz-card);
}

.db-choice-cards--list .db-choice-card:first-child {
    border-top: 0;
}

.db-choice-cards--list .db-choice-card:hover {
    background: var(--dz-surface-hover);
}

.db-choice-cards--list .db-choice-card:has(.db-choice-card__input:checked) {
    border-color: var(--dz-hairline);
    background: var(--dz-choice-list-selected-background);
    box-shadow: inset 3px 0 0 var(--dz-choice-list-selected-marker);
}

.db-choice-cards--list .db-choice-card:has(.db-choice-card__input:disabled) {
    cursor: default;
}

.db-choice-cards--list .db-choice-card .db-choice-card__input {
    width: 14px;
    height: 14px;
    margin: 3px 0 0;
    accent-color: var(--dz-choice-list-selected-marker);
}

.db-choice-cards--list .db-choice-card .db-choice-card__input:focus-visible {
    outline: 2px solid var(--dz-focus-color);
    outline-offset: 2px;
}

/* The title stands apart from the detail under it: a 4px gap and medium (not bold) fact values, so
   the plan name is the one thing on the row that reads first. */
.db-choice-cards--list .db-choice-card__body {
    gap: var(--dz-space-half);
    min-width: 0;
}

.db-choice-cards--list .db-choice-card__tags {
    display: inline-flex;
    flex-wrap: wrap;
    gap: var(--dz-space-half);
    margin-left: var(--dz-space-1);
    vertical-align: 1px;
}

.db-choice-cards--list .db-choice-card__tag {
    padding: 0 6px;
    border: 1px solid currentColor;
    border-radius: var(--dz-radius-xs);
    font-size: 11px;
    font-weight: 500;
    white-space: nowrap;
}

.db-choice-cards--list .db-choice-card__tag--info {
    color: var(--dz-choice-list-tag-info-color);
}

.db-choice-cards--list .db-choice-card__tag--warning {
    color: var(--dz-choice-list-tag-warning-color);
}

.db-choice-cards--list .db-choice-card__facts {
    display: flex;
    flex-wrap: wrap;
    column-gap: var(--dz-space-1half);
    row-gap: 2px;
    font-family: var(--dz-font-family);
    font-size: 12px;
}

.db-choice-cards--list .db-choice-card__fact {
    white-space: nowrap;
}

.db-choice-cards--list .db-choice-card__fact-label {
    color: var(--dz-meta);
}

.db-choice-cards--list .db-choice-card__fact-value {
    font-weight: 500;
    color: var(--dz-ink);
}

.db-choice-cards--list .db-choice-card__fact-value--none {
    font-weight: 400;
    color: var(--dz-meta);
}

/* Meta text on a checked row reads its own hook: --dz-meta fails the floor on the selection tint. */
.db-choice-cards--list .db-choice-card:has(.db-choice-card__input:checked) .db-choice-card__fact-label,
.db-choice-cards--list .db-choice-card:has(.db-choice-card__input:checked) .db-choice-card__fact-value--none,
.db-choice-cards--list .db-choice-card:has(.db-choice-card__input:checked) .db-choice-card__aside-caption {
    color: var(--dz-choice-list-selected-meta);
}

.db-choice-cards--list .db-choice-card__fact-extra {
    color: var(--dz-body);
}

.db-choice-cards--list .db-choice-card__aside {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    font-family: var(--dz-font-family);
    font-size: 14px;
    font-weight: 600;
    color: var(--dz-ink);
    white-space: nowrap;
}

.db-choice-cards--list .db-choice-card__aside-caption {
    font-size: 11px;
    font-weight: 400;
    color: var(--dz-meta);
}

.db-choice-cards--list .db-choice-cards__heading {
    padding: var(--dz-space-1) var(--dz-space-1half) var(--dz-space-half);
    border-top: 1px solid var(--dz-hairline);
    background: var(--dz-surface-hover);
    font-family: var(--dz-font-family);
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--dz-body);
}

.db-choice-cards--list .db-choice-cards__heading:first-child {
    border-top: 0;
}

/* THE EMPTY STATE: the one line a choice list's host shows in place of the list while it loads,
 * when it fails to load, or when it has no options ("Loading plans...", "No plans available.").
 * datablaze.ui.choice.empty writes it. It sits in the host, not in a list, so it is not scoped under
 * the list modifier. Body ink, the description's tone, so it reads as a note rather than a value. */
.db-choice-cards__empty {
    padding: var(--dz-space-1) 0;
    font-family: var(--dz-font-family);
    font-size: 13px;
    color: var(--dz-body);
}

/* THE SECTION NAV LIST (ClickUp 86894yg24, WL-10a). A vertical, grouped navigation list for a
 * settings page: group headings, items, an optional count badge, and a selected state - the
 * whitelabel branding editor's own section list (Look / Identity / Advanced, each holding several
 * sections) is the first caller.
 *
 * MARKUP:
 *   <nav class="db-section-nav">
 *     <div class="db-section-nav__group">Look</div>
 *     <a class="db-section-nav__item db-section-nav__item--selected" href="#">
 *       Colors <span class="db-section-nav__count">2</span>
 *     </a>
 *     <a class="db-section-nav__item" href="#">Logos and icons</a>
 *     ...
 *   </nav>
 * `__count` is optional per item - a section with nothing to flag renders no span at all, exactly
 * as `.db-layout-panel__caption:not(:first-child)` only draws a rule between panel sections that
 * both exist.
 *
 * THE GROUP HEADING IS THE EXISTING EYEBROW BAND, NOT A NEW ONE. It reads the same
 * --dz-eyebrow-* tokens 20-density.css's `.db-layout-panel__caption` already declares - an
 * all-caps, tracked, accent-coloured micro-label is one meaning ("this labels the region below it"),
 * and this is the second place in the product that means it, not a second decision about what a
 * section heading looks like. A new selector is declared here rather than reusing that class
 * directly because `.db-layout-panel__caption` is scoped to the layout gear panel's own structure;
 * the TOKENS are what is shared, not the rule.
 *
 * THE SELECTED ITEM IS THE SAME MEANING THE LAYOUT PANEL ALREADY GIVES --dz-selection-tint: "the
 * ground under a selected row... and the bar at its leading edge" (00-tokens.css's own comment on
 * that token). A section nav's selected item is a selected row one surface over, so it reads the
 * same tint and the same leading-edge idea rather than inventing a second "selected" pair - exactly
 * the choice 25-choice-cards.css makes for the same reason.
 *
 * THE COUNT BADGE IS NOT A STATUS OR A LABEL. It is --dz-meta at 11px, chrome describing "how many
 * changes are pending in this section" rather than a state of the record - a status colour on it
 * would spend meaning the count does not have.
 *
 * FIX ROUND 1 (WL-10a adversary pin #2): `:focus-visible` was missing entirely, so a keyboard user
 * got only the user agent's default ring - not repointable by a tenant and not the ring every other
 * interactive construct in the package draws. Same pattern as 20-density.css's own choice control and
 * 25-choice-cards.css's radio input: `:focus-visible` only (never on a bare pointer click), painting
 * --dz-focus-color/-ring rather than the browser default. */
.db-section-nav {
    display: flex;
    flex-direction: column;
}

.db-section-nav__group {
    padding: var(--dz-eyebrow-padding);
    color: var(--dz-eyebrow-color);
    font-family: var(--dz-font-family);
    font-size: var(--dz-eyebrow-font-size);
    font-weight: var(--dz-eyebrow-font-weight);
    letter-spacing: var(--dz-eyebrow-letter-spacing);
    line-height: var(--dz-eyebrow-line-height);
    text-transform: uppercase;
}

    .db-section-nav__group:not(:first-child) {
        margin-top: 4px;
        border-top: 1px solid var(--dz-hairline);
    }

.db-section-nav__item {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--dz-space-1);
    padding: var(--dz-space-1) var(--dz-space-1half);
    border-left: 3px solid transparent;
    color: var(--dz-body);
    font-family: var(--dz-font-family);
    font-size: 14px;
    text-decoration: none;
}

    .db-section-nav__item:hover {
        background: var(--dz-surface-hover);
    }

    .db-section-nav__item:focus-visible {
        outline: 2px solid var(--dz-focus-color);
        outline-offset: -2px;
    }

.db-section-nav__item--selected {
    background: var(--dz-selection-tint);
    border-left-color: var(--dz-selection-color);
    color: var(--dz-ink);
    font-weight: 500;
}

.db-section-nav__count {
    font-size: 11px;
    color: var(--dz-meta);
}

/* THE UNSAVED-CHANGES INDICATOR (ClickUp 86894yg24, WL-10a). "N unsaved changes" - PENDING EDITS
 * in the current session, not a record's lifecycle state. The whitelabel branding editor's page
 * header is the first caller, showing it beside Save/Discard while a profile has draft edits.
 *
 * WHY NOT the badge contract's status kind (`db-badge--status`). A status badge answers "what is this row" from the badge contract's
 * own closed set (active, suspend, hibernate, ...), keyed to a record that exists independently of
 * who is looking at it. This answers "does the CURRENT VIEWER have unsaved work open" - a fact
 * about the session, not the record - and it counts rather than classifies, which a status badge's
 * contract has no slot for at all.
 *
 * WHY NOT 23-contrast-chip.css's chip, even though the shape rhymes. That one is a verdict against
 * a floor the caller measured; this is a plain count with no pass/fail axis - there is no "too many
 * unsaved changes" state for this construct to render, so borrowing the contrast chip's two
 * modifiers would invent one.
 *
 * WARNING-ISH, NOT A STATUS COLOUR. --dz-unsaved-chip-marker defaults to the same warning accent
 * 24-callout.css's warning variant uses, because "you have work you have not saved" is the same
 * register as "read this before you continue" - but it is its OWN hook, per COBRANDING.md, so a
 * tenant can move this chip's colour without moving every warning callout in the product.
 *
 * TEXT STAYS ON --dz-ink, MARKER CARRIES THE COLOUR - the same split 23-contrast-chip.css and
 * 24-callout.css both use, for the same reason: --dz-ink is guaranteed legible in both themes and
 * the marker is a small enough shape that the 3.0:1 non-text floor, not 4.5:1, applies. */
.db-unsaved-chip {
    display: inline-flex;
    align-items: center;
    gap: var(--dz-status-gap);
    padding: var(--dz-badge-padding-y) var(--dz-badge-padding-x);
    border-radius: var(--dz-radius-pill);
    background: color-mix(in srgb, var(--dz-unsaved-chip-marker) var(--dz-badge-outline-tint), var(--dz-card));
    color: var(--dz-ink);
    font-family: var(--dz-font-family);
    font-size: var(--dz-badge-font-size);
    font-weight: var(--dz-badge-font-weight);
    line-height: var(--dz-badge-line-height);
    white-space: nowrap;
}

.db-unsaved-chip::before {
    content: "";
    flex: none;
    width: var(--dz-status-marker-size);
    height: var(--dz-status-marker-size);
    border-radius: var(--dz-status-marker-radius);
    background: var(--dz-unsaved-chip-marker);
}

/* THE FLOATING ACTION BAR SURFACE (ClickUp 86894yg24, WL-10a). A fixed, bottom-centered pill
 * hosting text plus actions. The whitelabel branding editor's full-site preview mode ("Previewing
 * unsaved branding for Covenant Telecom" plus Light/Dark and Back to editor / Save) is the first
 * caller; the buttons inside it are ordinary `.db-button` roles, not this construct's own
 * vocabulary - the bar is the surface they sit on, and it owns no button paint.
 *
 * MARKUP:
 *   <div class="db-preview-bar">
 *     <span class="db-preview-bar__text">Previewing unsaved branding for Covenant Telecom</span>
 *     <div class="db-preview-bar__actions"> ...db-button roles, or a db-segmented control... </div>
 *   </div>
 *
 * DELIBERATE DEVIATION FROM THE MOCKUP, FLAGGED RATHER THAN DECIDED SILENTLY: the mockup draws
 * this bar as a dark pill (`#1f1f1f` on every page, light or dark). This construct instead reads
 * --dz-card / --dz-border / --dz-ink - the SAME floating-surface vocabulary 12-tip-icon-button.css's
 * hover tip already uses, and for the reason that file's own header gives for its own white-over-
 * mockup choice: "an owner decision, made after seeing the dark bubble in place" once a floating
 * dark surface was actually tried on this ground. This bar makes the analogous call rather than
 * repeating the mockup's dark literal, but it is a NEW call for a NEW surface, not a re-application
 * of a decision already made for this one - confirm with the owner before this ships looking
 * different from the mockup on every tenant.
 *
 * ITS OWN Z-LAYER, NOT A REUSE OF THE TIP'S OR THE ACTIONS MENU'S. --dz-preview-bar-layer sits at
 * 1650, strictly between the body-level actions menu (1600, 06-toolbar-actions.css) and the hover
 * tip (1700, 00-tokens.css) - declared and reasoned about beside those two numbers, in
 * 00-tokens.css, rather than here, because it has to be read against both of them to make sense.
 * Above the menu because a fixed pill has to sit over ordinary page chrome and any body-level menu
 * the page also shows; below the tip because a hover tip explaining one of the pill's OWN buttons
 * must still draw over it.
 *
 * FIX ROUND 1 (WL-10a adversary pin #A): `left: 50%; transform: translateX(-50%)` on a nowrap,
 * unconstrained-width row put the pill off BOTH edges of a 390px viewport with no way to reach the
 * clipped buttons - being `position: fixed` means the one way out of preview mode (Back to editor /
 * Save) was unreachable, not merely ugly. `max-width` bounds the box to the viewport minus a gutter
 * on each side regardless of content; `flex-wrap` on the bar and on `__actions` lets the text and the
 * action row each drop to their own line rather than being clipped - centering keeps every row
 * looking deliberate rather than ragged. None of this changes anything at a width wide enough to hold
 * one line, which is every width this was actually designed and screenshotted at.
 *
 * RADIUS DROPPED FROM --dz-radius-pill TO --dz-radius-xl FOR THE SAME FIX: border-radius clamps to
 * half the shorter side regardless of what caused the box to grow, so a "pill" radius on a bar that
 * has wrapped to two or three rows renders as a near-circular blob, not a rounded rectangle - MEASURED
 * on the 390px screenshot this fix produces. --dz-radius-xl (24px) still reads as heavily rounded on
 * the bar's normal single-row height (~56px, so a true pill radius would be ~28px - close enough that
 * the unwrapped case looks the same as before) and stays a sane rounded rectangle at every wrapped
 * height instead of degrading into a shape with no relationship to its content. */
.db-preview-bar {
    position: fixed;
    left: 50%;
    bottom: var(--dz-space-3);
    transform: translateX(-50%);
    z-index: var(--dz-preview-bar-layer);
    box-sizing: border-box;
    max-width: calc(100vw - var(--dz-space-3) * 2);
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    gap: var(--dz-space-1) var(--dz-space-2);
    padding: var(--dz-space-1) var(--dz-space-1) var(--dz-space-1) var(--dz-space-2);
    border: 1px solid var(--dz-border);
    border-radius: var(--dz-radius-xl);
    background: var(--dz-card);
    box-shadow: var(--dz-surface-float-shadow);
    color: var(--dz-ink);
    font-family: var(--dz-font-family);
    font-size: 13px;
}

.db-preview-bar__text {
    white-space: normal;
    overflow-wrap: break-word;
    text-align: center;
}

.db-preview-bar__actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    gap: var(--dz-space-1);
}

/* THE FORM.
 *
 * A form field is a detail field whose value is an editor. Its label is the detail field's label
 * (05-field-badge.css), read from the same --dz-field-label-* tokens, so a settings page and a detail
 * page name a field the same way; everything else here is what an editor adds - help text, a
 * required mark, a validation message, and three states.
 *
 * ONE PAINT, TWO DOORS, and the caller takes whichever matches how the form is built:
 *
 * - A DevExtreme dxForm - the dominant shape - takes the
 *   WRAPPER (.DbForm() in Razor, datablaze.ui.form.shell() in script), which puts .db-form on the
 *   widget. DevExtreme owns that form's structure - its layout grid, item padding and group markup -
 *   so the rules for it repaint dx's own parts and move no box. dx.light paints those parts with
 *   literals (#333 labels, a #c9302c required mark, a #d9534f invalid family, a 20px group caption
 *   over a #ddd rule); each rule below names the one it replaces. Three rules move a box (PP-D72): the
 *   help line takes the 4px top margin the standalone door's help has (dx.light sets none, so the
 *   line sat flush under its editor), an item with no label room of its own takes the form's
 *   field gap in place of dx.light's 10px item padding (the "no label room" rule below), and a
 *   captioned group gives up the 20px of padding under its last item (the group-content rule below).
 * - Editors laid out by hand take the STANDALONE helpers in SharedMvc/SharedForm.cs, which render the
 *   manifest's parts inside a .db-form region. Those rules own the box as well, because nothing else
 *   lays that markup out. A NATIVE text field, select or textarea written straight into a field's
 *   control (the invite-link dialogs, the email template's test address) takes the outlined editor's
 *   edge, radius, height and focus from the rule under .db-form-field__control below: the native twin
 *   of the dx edge above (PP-D72, audit rows ivl-01, ivl-02, ivl-03, ivl-10), so the page's scripts
 *   keep reading it with .val() and nothing has to become a DevExtreme editor to look like one.
 *
 * SCOPED TO .db-form, never a bare DevExtreme selector. This stylesheet also reaches the five m2mNet
 * login pages, and an editor outside a form that adopted the construct keeps exactly what it had. The
 * one bare editor rule the package does carry - focus, on every text editor, in 08-dropdown-editor.css
 * - is deliberately NOT restated here, except for native fields below: a form editor takes that
 * focus border and ring like every other editor in the product. Only an INVALID editor overrides it,
 * below, because the focus ring is the brand colour and an error that turns brand-coloured the moment
 * someone clicks into it to fix it has stopped saying what is wrong.
 *
 * The colours are the --dz-form-* hooks in 00-tokens.css - invalid and warning over the status scale's
 * two accents, never a literal and never the brand. */

.db-form {
    font-family: var(--dz-font-family);
    color: var(--dz-ink);
}

/* ---- The dxForm door ------------------------------------------------------------------------- */

/* The label. dx.light: `.dx-field-item-label-text { color: #333 }` at the widget's own 14px. Ink, not
   body: a form label names what you are about to type, and in grey it read as a disabled field. */
.db-form .dx-field-item-label-text {
    color: var(--dz-ink);
    font-size: var(--dz-field-label-font-size);
    font-weight: var(--dz-field-label-font-weight);
}

/* dx.light: `.dx-field-item-label-location-top { margin-bottom: 4px }` - the same 4px the detail label
   takes, but written as a literal. Restated through the token so the two cannot drift. */
.db-form .dx-field-item-label-location-top {
    margin-bottom: var(--dz-field-label-gap);
}

/* dx.light: `.dx-field-item-required-mark { color: #c9302c }`. Red through the token, not the literal. */
.db-form .dx-field-item-required-mark {
    color: var(--dz-form-required-color);
}

/* dx.light: `.dx-field-item-optional-mark { color: #afafaf }`, which is 2.2:1 on white. */
.db-form .dx-field-item-optional-mark {
    color: var(--dz-meta);
}

/* dx.light: `.dx-field-item-help-text { font-style: italic; color: #333 }`. The guide's small text is
   upright; italic is DevExtreme's own house style and nothing of ours. The 4px top margin matches
   .db-form-field__help below (PP-D72, audit row B-D); dx.light sets none. */
.db-form .dx-field-item-help-text {
    margin-top: var(--dz-space-half);
    color: var(--dz-form-help-color);
    font-size: var(--dz-form-help-font-size);
    font-style: normal;
    line-height: 1.55;
}

/* AN ITEM WITH NO LABEL ROOM OF ITS OWN (PP-D72, audit row C-G5). dx.light spaces every item below the
   first row with `.dx-layout-manager .dx-field-item:not(.dx-first-row) { padding-top: 10px }`. The
   Products' form puts its labels inside the editor (Static or Floating label mode), not in a
   `.dx-field-item-label` beside it, and a labelled editor brings its own top room: two stacked text
   fields read about 17px apart. A checkbox, a tab panel or a grid brings none, so it read 10px under
   the field above it (measured on that form: Description to Is serialized, and the tab panel). Such
   an item takes the form's field gap instead. An item that has a label of its own, or a labelled
   editor as its content (with or without a help line, which wraps the content), is left as DevExtreme
   spaces it. Needs :has (Chromium 105, Safari 15.4, Firefox 121). The exclusion sits in :where so
   that it adds no specificity: a :has() counts its most specific argument, which would put this
   rule at (0,7,0) and out of reach of any later spacing rule; written this way it is (0,4,0), one
   class above dx.light's (0,3,0) and no more. */
.db-form .dx-layout-manager .dx-field-item:not(.dx-first-row):not(:where(:has(> .dx-field-item-label,
    > .dx-field-item-content > :is(.dx-texteditor-with-label, .dx-texteditor-with-floating-label),
    > .dx-field-item-content-wrapper > .dx-field-item-content > :is(.dx-texteditor-with-label, .dx-texteditor-with-floating-label)))) {
    padding-top: var(--dz-form-field-gap);
}

/* A group caption is a section title. dx.light: `.dx-form-group-caption { font-size: 20px }`, the
   guide's Heading 4 - a page-level size inside a form that already sits under a page title. */
.db-form .dx-form-group-caption {
    color: var(--dz-ink);
    font-size: var(--dz-form-section-title-font-size);
    font-weight: var(--dz-form-section-title-font-weight);
}

/* dx.light: `.dx-form-group-with-caption > .dx-form-group-content { border-top: 1px solid #ddd }`. The
   colour is the hook's. The 6px above the rule and the 19px under it belong to the caption and stay.

   THE GAP AFTER A CAPTIONED GROUP IS EVEN (PP-D72, audit row A-E, acc-13). dx.light also sets
   `padding-bottom: 20px` on this content, and the group's own item takes the form's field gap above
   it (the "no label room" rule above), so keeping the 20 would stack it on the 16. Every group is
   followed by that one 16, or by the 17 a labelled field brings. The last group in a form ends on
   its last item and the host's own padding closes it. */
.db-form .dx-form-group-with-caption > .dx-form-group-content {
    padding-bottom: 0;
    border-top-color: var(--dz-hairline);
}

/* ---- Editors, in either door ----------------------------------------------------------------- */

/* Resting edge. dx.light: `.dx-texteditor.dx-editor-outlined { border: 1px solid #ddd; border-radius:
   4px }`. The guide: Gray 300 "Input borders", and the 6px sm radius for "buttons, inputs". */
.db-form .dx-texteditor.dx-editor-outlined {
    border-color: var(--dz-border);
    border-radius: var(--dz-control-radius);
}

/* dx.light: `.dx-texteditor.dx-state-hover { border-color: rgba(51,122,183,.4) }` - DevExtreme's stock
   blue, which 08-dropdown-editor.css also removes from focus. Meta is one step darker
   than the resting edge, which is all a hover has to say. */
.db-form .dx-texteditor.dx-editor-outlined.dx-state-hover {
    border-color: var(--dz-meta);
}

/* READ-ONLY IS A VALUE, NOT A DISABLED EDITOR. dx.light gives it a dashed edge, which is an editor
   that looks broken rather than one that is showing you a fact. With no edge and no fill the field
   reads the way the same fact does on a detail page. */
.db-form .dx-texteditor.dx-editor-outlined.dx-state-readonly,
.db-form .db-form-field--readonly .dx-texteditor.dx-editor-outlined {
    border-color: transparent;
    background-color: transparent;
}

/* DISABLED. dx.light: `.dx-state-disabled.dx-widget, .dx-state-disabled .dx-widget { opacity: .5 }`.
 *
 * That fade COMPOUNDS. The descendant half matches every widget inside a disabled container, and a
 * dxForm nests four of them around each field (layout manager, responsive box, box, editor), so a
 * disabled form's editors rendered at 0.5 x 0.45^4 = 2% - invisible rather than disabled (measured on
 * Billing Defaults > Transaction Numbers). So nothing in a form fades as a container, and the state
 * is drawn on the control that has it:
 *
 * - a TEXT EDITOR keeps full opacity and reads as disabled through its surface - sunken fill,
 *   hairline edge, body-grey value (body on sunken is 5.1:1, over the 4.5:1 floor);
 * - a checkbox takes the same surface (below);
 * - a switch, radio or button has no surface to change, so it takes the package's one disabled
 *   opacity, once. */
.db-form.dx-state-disabled,
.db-form .dx-state-disabled.dx-widget,
.db-form .dx-state-disabled .dx-widget {
    opacity: 1;
}

.db-form .dx-texteditor.dx-editor-outlined.dx-state-disabled,
.db-form .dx-state-disabled .dx-texteditor.dx-editor-outlined,
.db-form.dx-state-disabled .dx-texteditor.dx-editor-outlined {
    border-color: var(--dz-hairline);
    background-color: var(--dz-sunken);
}

/* -webkit-text-fill-color as well: Chrome paints a disabled <input>'s text with it, over `color`. */
.db-form .dx-texteditor.dx-state-disabled .dx-texteditor-input,
.db-form .dx-state-disabled .dx-texteditor .dx-texteditor-input,
.db-form.dx-state-disabled .dx-texteditor .dx-texteditor-input {
    color: var(--dz-body);
    -webkit-text-fill-color: var(--dz-body);
}

.db-form .dx-switch.dx-state-disabled,
.db-form .dx-radiogroup.dx-state-disabled,
.db-form .dx-button.dx-state-disabled,
.db-form .dx-state-disabled .dx-switch,
.db-form .dx-state-disabled .dx-radiogroup,
.db-form .dx-state-disabled .dx-button,
.db-form.dx-state-disabled .dx-switch,
.db-form.dx-state-disabled .dx-radiogroup,
.db-form.dx-state-disabled .dx-button {
    opacity: var(--dz-disabled-opacity);
}

/* A CHECKBOX takes the text editor's surface, not the fade. dx.light fades its ICON directly
   (`.dx-state-disabled .dx-checkbox .dx-checkbox-icon { opacity: .4 }`), and a pale tick on a pale box
   is the same "invisible" defect. The box goes sunken with a hairline edge and the tick goes body
   grey, which also takes the checked box off the accent - a disabled control is not a live choice. */
.db-form .dx-checkbox.dx-state-disabled .dx-checkbox-icon,
.db-form .dx-state-disabled .dx-checkbox .dx-checkbox-icon,
.db-form.dx-state-disabled .dx-checkbox .dx-checkbox-icon {
    opacity: 1;
    border-color: var(--dz-hairline);
    background-color: var(--dz-sunken);
    color: var(--dz-body);
}

/* Its inline text, which dx.light fades by the same .4. */
.db-form .dx-checkbox.dx-state-disabled .dx-checkbox-text,
.db-form .dx-state-disabled .dx-checkbox-text {
    opacity: 1;
    color: var(--dz-body);
}

/* INVALID. dx.light spreads this over three rules - a 40% edge at rest, the full colour on hover and
   focus, and a filled badge - and every one of them is its own #d9534f. All three repoint here. The
   standalone door's --invalid modifier takes the same edge, so a server-side error on a hand-laid
   field and a client-side one on a dxForm field look like one thing. */
.db-form .dx-invalid.dx-texteditor.dx-editor-outlined,
.db-form .dx-invalid.dx-texteditor.dx-editor-outlined.dx-state-hover,
.db-form .db-form-field--invalid .dx-texteditor.dx-editor-outlined {
    border-color: var(--dz-form-invalid-color);
}

/* !important: beats 08-dropdown-editor.css `.dx-texteditor.dx-state-focused` border-color and box-shadow, which are themselves !important. */
.db-form .dx-invalid.dx-texteditor.dx-state-focused,
.db-form .db-form-field--invalid .dx-texteditor.dx-state-focused {
    border-color: var(--dz-form-invalid-color) !important;
    box-shadow: 0 0 0 var(--dz-control-focus-width) var(--dz-form-invalid-ring) !important;
}

.db-form .dx-invalid.dx-texteditor.dx-show-invalid-badge .dx-texteditor-input-container::after {
    background-color: var(--dz-form-invalid-color);
}

/* THE MESSAGE. dx.light: a solid #d9534f box with white type. DevExtreme floats it over whatever sits
   below the field, so it keeps a surface - it cannot go transparent without printing over the next
   label - but the surface is the card and the colour moves to the edge and the words, which is the
   outline shape the package already uses for every other state. */
.db-form .dx-invalid-message > .dx-overlay-content {
    border: 1px solid var(--dz-form-invalid-color);
    border-radius: var(--dz-control-radius);
    background-color: var(--dz-card);
    box-shadow: var(--dz-surface-float-shadow);
    color: var(--dz-form-invalid-color);
    font-size: var(--dz-form-message-font-size);
}

/* WARNING - the standalone door only. DevExtreme's validation has no warning state; this is for a
   server that accepts a value and still wants to say something about it. */
.db-form .db-form-field--warning .dx-texteditor.dx-editor-outlined {
    border-color: var(--dz-form-warning-color);
}

/* ---- The standalone door ------------------------------------------------------------------------ */

.db-form-section + .db-form-section {
    margin-top: var(--dz-form-section-gap);
}

/* The same title and rule a dxForm group caption renders as, above, so the two doors agree. */
.db-form-section__title {
    margin: 0 0 var(--dz-form-field-gap);
    padding-bottom: var(--dz-space-1);
    border-bottom: 1px solid var(--dz-hairline);
    color: var(--dz-ink);
    font-family: var(--dz-font-family);
    font-size: var(--dz-form-section-title-font-size);
    font-weight: var(--dz-form-section-title-font-weight);
    line-height: 1.3;
}

/* ONE TRACK THAT MAY SHRINK. A grid's implicit track is `auto`, whose minimum is the widest item's
   min-content, so a wide child (a grid of 572px in a 342px dialog body at 390) grew the track past
   its container and the whole body scrolled sideways, cutting the child's right edge, instead of the
   child scrolling inside itself. minmax(0, 1fr) lets the track go to the container's width; the child
   then takes its own overflow. At a normal width the track is the container's width either way (an
   auto track stretches to fill it), so nothing moves. Every item sits on this one track, so there is
   no per-child min-width to forget; .db-form-field below already takes min-width: 0 in its own
   column. */
.db-form-section__body {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: var(--dz-form-field-gap);
}

.db-form-field {
    display: flex;
    flex-direction: column;
    min-width: 0;
}

/* The detail field's label, line for line (.db-field__label), because it is the same thing. */
.db-form-field__label {
    display: block;
    margin-bottom: var(--dz-field-label-gap);
    color: var(--dz-ink);
    font-family: var(--dz-font-family);
    font-size: var(--dz-field-label-font-size);
    font-weight: var(--dz-field-label-font-weight);
    line-height: 1.3;
}

.db-form-field__required {
    margin-inline-start: var(--dz-space-half);
    color: var(--dz-form-required-color);
}

.db-form-field__control {
    min-width: 0;
}

/* A NATIVE FIELD IN THE STANDALONE DOOR (PP-D72, audit rows ivl-01, ivl-02, ivl-03, ivl-10; also the
 * email template's test address, acc-24 and acc-25). A hand-laid <input>, <select> or <textarea> that
 * is the direct child of a field's control keeps Bootstrap's look in Voyager: a #ced4da edge, a 4px
 * radius, a 38px box and a blue focus ring, next to dx editors drawn with the rules above. It takes
 * the dx editor's own values instead:
 *
 * - the resting edge and radius of `.dx-texteditor.dx-editor-outlined` (the "Resting edge" rule);
 * - the 40px visual height, as a floor: min-height, not height, so a textarea that grows with its
 *   rows keeps its own height and so a field a host sized taller is not cut down;
 * - the same hover and invalid edge as the dx door, the invalid one through the standalone door's own
 *   .db-form-field--invalid modifier (never :invalid, which would paint an empty required field red
 *   before anyone touched it);
 * - the package focus: the --dz-control-focus-* border and ring of 08-dropdown-editor.css's editor
 *   rule, which a native control does not match because it is no .dx-texteditor.
 *
 * Only a DIRECT child of the control, and never a checkbox, radio, hidden or file input, whose box is
 * not an edge to paint. A DevExtreme editor's own <input> sits deeper, so no dx editor is reached.
 * The selector is (0,4,1) on focus, over Bootstrap's `.form-control:focus` at (0,2,0), so the host
 * stylesheet's order does not matter. The hover rule's editable guard sits in :where(), so hover is
 * (0,4,1) as well and the later focus rule wins on a field that is both hovered and focused. No new class: the field's own markup is what is matched. */
.db-form .db-form-field__control > :is(input:not([type="checkbox"], [type="radio"], [type="hidden"], [type="file"]), select, textarea) {
    border: 1px solid var(--dz-border);
    border-radius: var(--dz-control-radius);
}

.db-form .db-form-field__control > :is(input:not([type="checkbox"], [type="radio"], [type="hidden"], [type="file"]), select) {
    min-height: var(--dz-control-visual-height);
}

.db-form .db-form-field__control > :is(input:not([type="checkbox"], [type="radio"], [type="hidden"], [type="file"]), select, textarea):hover:where(:not(:disabled, [readonly])) {
    border-color: var(--dz-meta);
}

.db-form .db-form-field__control > :is(input:not([type="checkbox"], [type="radio"], [type="hidden"], [type="file"]), select, textarea):focus {
    border-color: var(--dz-control-focus-color);
    box-shadow: 0 0 0 var(--dz-control-focus-width) var(--dz-control-focus-ring-color);
}

/* The invalid edge and ring of the dx door's "INVALID" rules above, on the hover and focus states too,
   for the reason given there: an error must not turn brand-coloured when someone clicks into it. */
.db-form .db-form-field--invalid .db-form-field__control > :is(input:not([type="checkbox"], [type="radio"], [type="hidden"], [type="file"]), select, textarea),
.db-form .db-form-field--invalid .db-form-field__control > :is(input:not([type="checkbox"], [type="radio"], [type="hidden"], [type="file"]), select, textarea):hover {
    border-color: var(--dz-form-invalid-color);
}

.db-form .db-form-field--invalid .db-form-field__control > :is(input:not([type="checkbox"], [type="radio"], [type="hidden"], [type="file"]), select, textarea):focus {
    border-color: var(--dz-form-invalid-color);
    box-shadow: 0 0 0 var(--dz-control-focus-width) var(--dz-form-invalid-ring);
}

.db-form-field__help {
    margin-top: var(--dz-space-half);
    color: var(--dz-form-help-color);
    font-family: var(--dz-font-family);
    font-size: var(--dz-form-help-font-size);
    line-height: 1.55;
}

/* In the flow, not floated: a hand-laid field has room for its own message line. */
.db-form-field__message {
    margin-top: var(--dz-space-half);
    color: var(--dz-form-invalid-color);
    font-family: var(--dz-font-family);
    font-size: var(--dz-form-message-font-size);
    line-height: 1.4;
}

.db-form-field__message:empty {
    display: none;
}

.db-form-field--warning .db-form-field__message {
    color: var(--dz-form-warning-color);
}

/* THE ACTION ROW. End-aligned, because a form is read top to bottom and the commit comes at the end of
   the line the eye is already on. A destructive action that should sit apart - Delete on an edit page -
   goes BEFORE the spacer, which pushes everything after it to the far side. That placement is the
   package's default, not a settled rule: the modernization inventory lists "exact left and right
   placement rules for dialog and page actions" as an open decision, and this is the one place to
   change it when it is made. */
.db-form-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: flex-end;
    gap: var(--dz-form-actions-gap);
    margin-top: var(--dz-form-section-gap);
}

.db-form-actions__spacer {
    flex: 1 1 auto;
}

/* THE NOTIFICATION.
 *
 * The toast datablaze.ui.notify raises (src/notify.js). DevExtreme owns the toast - its overlay, its
 * position, its stacking and its timing - and this owns the surface only.
 *
 * SCOPED TO .db-notify, which notify.js puts on the toast's wrapper through DevExtreme's wrapperAttr.
 * A toast raised straight through DevExpress.ui.notify keeps dx.light's paint, so call sites move
 * one at a time rather than every toast repainting under code that never asked.
 *
 * THE OUTLINE SHAPE, not dx.light's solid fill: the card surface and ink words, with the kind on a 4px
 * leading edge. See the --dz-notify-* tokens for the contrast measurement that decides it.
 *
 * DevExtreme's kind icon is hidden. It is a white glyph drawn as a background image for a coloured
 * fill, so on the card surface it is white on white; the edge carries the kind instead, and the words
 * carry the message - which is what a screen reader gets, since the icon is decorative. */

.db-notify .dx-toast-content {
    border: 1px solid var(--dz-hairline);
    border-inline-start: var(--dz-notify-edge-width) solid var(--dz-notify-accent);
    border-radius: var(--dz-radius-md);
    background-color: var(--dz-card);
    box-shadow: var(--dz-surface-float-shadow);
    color: var(--dz-ink);
    font-family: var(--dz-font-family);
    font-size: var(--dz-notify-font-size);
    font-weight: 500;
}

.db-notify .dx-toast-icon {
    display: none;
}

/* The kind, one scoped token each, so the rule above reads a single name whatever the kind. The
   scoped token is the pattern --dz-badge-accent already uses: declared only on the modifier, never on
   :root, and never given a fallback - a toast with no kind is a bug the edge shows. */
.db-notify--success {
    --dz-notify-accent: var(--dz-notify-success-color);
}

.db-notify--info {
    --dz-notify-accent: var(--dz-notify-info-color);
}

.db-notify--warning {
    --dz-notify-accent: var(--dz-notify-warning-color);
}

.db-notify--error {
    --dz-notify-accent: var(--dz-notify-error-color);
}

/* A CHOICE TILE: one saved thing picked from a row of them (manifest `choiceTile`). The markup is
 * built by datablaze.ui.choiceTile (src/choicetile.js); every value is the --dz-choice-tile-* band in
 * 00-tokens.css, which carries the reasoning for the selected edge, the ink marker and the muted
 * step.
 *
 * Every selector starts at column 0: validate.js reads a class as painted only from a rule written
 * there, so a nested indent would fail the build for a class that is painted.
 *
 * THE ROW WRAPS and every tile is the same width, so a row of saved payment methods reads as a set
 * of like things rather than a sentence of different-sized boxes. Tiles in one line stretch to the
 * tallest, so a tile with a two-line meta does not leave its neighbours short. */
.db-choice-tiles {
    display: flex;
    flex-wrap: wrap;
    align-items: stretch;
    gap: var(--dz-choice-tile-gap);
}

.db-choice-tile {
    position: relative;
    display: flex;
    flex-direction: column;
    box-sizing: border-box;
    flex: 0 0 var(--dz-choice-tile-width);
    min-height: var(--dz-choice-tile-min-height);
    padding: var(--dz-choice-tile-padding);
    border: 1px solid var(--dz-choice-tile-border-color);
    border-radius: var(--dz-choice-tile-radius);
    background-color: var(--dz-choice-tile-background);
}

.db-choice-tile--selectable {
    cursor: pointer;
}

.db-choice-tile--selectable:hover {
    border-color: var(--dz-choice-tile-hover-border-color);
}

/* The inset shadow makes up the edge's second pixel INSIDE the border, so choosing a tile changes its
   paint and never its size or where its text sits. */
.db-choice-tile--selected,
.db-choice-tile--selected.db-choice-tile--selectable:hover {
    border-color: var(--dz-choice-tile-selected-border-color);
    box-shadow: inset 0 0 0 calc(var(--dz-choice-tile-selected-edge-width) - 1px) var(--dz-choice-tile-selected-border-color);
    background-color: var(--dz-choice-tile-selected-background);
}

.db-choice-tile--muted {
    opacity: var(--dz-choice-tile-muted-opacity);
}

/* A keyboard viewer reaches every tile, so each draws the product's focus ring rather than the
   browser's. Offset so it clears the selected edge instead of doubling it. */
.db-choice-tile--selectable:focus-visible,
.db-choice-tile--add:focus-visible,
.db-choice-tile__action:focus-visible {
    outline: var(--dz-control-focus-width) solid var(--dz-focus-color);
    outline-offset: 2px;
}

/* THE HEAD ROW holds the marker on the leading edge and the actions on the trailing one. Every tile
   that has either draws it, so a tile without the marker keeps the same title line as one with it. */
.db-choice-tile__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    min-height: 20px;
}

.db-choice-tile__marker {
    color: var(--dz-choice-tile-marker-color);
    font-size: var(--dz-eyebrow-font-size);
    font-weight: var(--dz-eyebrow-font-weight);
    letter-spacing: var(--dz-eyebrow-letter-spacing);
    line-height: var(--dz-eyebrow-line-height);
    text-transform: uppercase;
}

.db-choice-tile__actions {
    display: flex;
    gap: 4px;
    margin-left: auto;
}

/* 20px box, 24px pointer target through the ::before, as the edit affordance beside a value. */
.db-choice-tile__action {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 20px;
    height: 20px;
    color: var(--dz-choice-tile-action-color);
    text-decoration: none;
    cursor: pointer;
}

.db-choice-tile__action::before {
    position: absolute;
    inset: -2px;
    content: "";
}

.db-choice-tile__action:hover,
.db-choice-tile__action:focus-visible {
    color: var(--dz-choice-tile-action-hover-color);
}

.db-choice-tile__action .db-icon {
    --db-icon-size: var(--dz-choice-tile-action-glyph-size);
}

.db-choice-tile__title {
    margin: 2px 0;
    color: var(--dz-choice-tile-title-color);
    font-size: var(--dz-choice-tile-title-font-size);
    font-weight: var(--dz-choice-tile-title-font-weight);
    overflow-wrap: anywhere;
}

/* The rename field takes the title's place and its type, so starting an edit does not jump the tile.
   The edge and ring are the product's control pair. */
.db-choice-tile__title-input {
    box-sizing: border-box;
    width: 100%;
    padding: 2px 6px;
    border: 1px solid var(--dz-choice-tile-border-color);
    border-radius: var(--dz-radius-xs);
    background-color: var(--dz-choice-tile-background);
    color: var(--dz-choice-tile-title-color);
    font: inherit;
}

.db-choice-tile__title-input:focus {
    border-color: var(--dz-control-focus-color);
    outline: none;
}

.db-choice-tile__meta {
    color: var(--dz-choice-tile-meta-color);
    font-size: var(--dz-choice-tile-meta-font-size);
    line-height: 1.4;
}

/* THE ADD TILE is a tile's outline with nothing chosen in it: dashed, because it is a place a new one
   will go rather than a thing that exists. */
.db-choice-tile--add {
    align-items: center;
    justify-content: center;
    border-style: dashed;
    color: var(--dz-choice-tile-add-color);
    font-weight: 500;
    cursor: pointer;
}

.db-choice-tile--add:hover {
    background-color: var(--dz-choice-tile-add-hover-background);
}

/* THE BOARD (ADO #143, fix plan 3.5). Lanes of cards a user drags between - Plan/EditBundle's Pools
 * tab is the first caller, one lane per pool and one card per plan. Markup comes from
 * datablaze.ui.board (src/board.js); the drag itself is the caller's dxSortable, which this sheet
 * never styles, so a DevExtreme upgrade cannot break the paint.
 *
 * WHY NOT .db-card. A detail card is a titled panel of fields that stays put. A board card is one
 * record a user picks up and moves; it is smaller, has no header strip, and carries a category
 * marker. WHY NOT the card list (DbCardList): that is a grid's rows drawn as cards, paged and in
 * one column, and nothing moves between lists.
 *
 * Geometry (the 250px lane, the 230px card, the 400px scroll well) is the bundle editor's own,
 * moved here unchanged. */
.db-board {
    white-space: nowrap;
}

.db-board__lane {
    display: inline-block;
    margin: var(--dz-space-half);
    vertical-align: top;
    white-space: normal;
    border-radius: var(--dz-board-lane-radius);
    background-color: var(--dz-board-lane-background);
}

/* The title is the lane's drag handle when the lanes themselves are sortable, hence the pointer. */
.db-board__lane-title {
    padding: 10px 10px 0 30px;
    color: var(--dz-ink);
    font-size: var(--dz-board-lane-title-font-size);
    font-weight: var(--dz-board-lane-title-font-weight);
    cursor: pointer;
}

/* An icon action beside the title (delete a pool). The rename beside it is .db-edit-affordance;
 * this is the same bare glyph for the meanings that affordance does not cover. Body at rest, Ink
 * on hover, never a literal: the old page painted it #000, which vanished on the dark ground. */
.db-board__lane-action {
    margin-left: var(--dz-space-half);
    color: var(--dz-body);
    text-decoration: none;
}

.db-board__lane-action:hover,
.db-board__lane-action:focus {
    color: var(--dz-ink);
    text-decoration: none;
}

.db-board__lane-body {
    width: 250px;
    height: 400px;
}

/* min-height keeps an empty lane a drop target. The empty text is the caller's, through
 * data-db-empty, because only the caller knows what dropping here does. */
.db-board__lane-items {
    min-height: 380px;
}

.db-board__lane-items:empty::after {
    content: attr(data-db-empty);
    display: block;
    padding: 10px 30px;
    color: var(--dz-board-empty-color);
    font-size: 13px;
}

.db-board__card {
    position: relative;
    box-sizing: border-box;
    width: 230px;
    margin: 10px;
    padding: 10px 20px;
    border: 1px solid var(--dz-board-card-border-color);
    border-radius: var(--dz-board-card-radius);
    background-color: var(--dz-board-card-background);
    box-shadow: var(--dz-board-card-shadow);
    color: var(--dz-ink);
    cursor: pointer;
}

.db-board__card-title {
    padding-bottom: 10px;
}

.db-board__card-meta {
    color: var(--dz-board-card-meta-color);
}

/* The category marker: a 5px strip down the card's left edge. Its colour is the category scale in
 * 00-tokens.css, chosen by a --<n> modifier, or --none for a record with no category. */
.db-board__card-marker {
    position: absolute;
    top: 10px;
    bottom: 10px;
    left: 5px;
    width: 5px;
    border-radius: 2px;
    background-color: var(--dz-category-none);
}

.db-board__card-marker--1 { background-color: var(--dz-category-1); }
.db-board__card-marker--2 { background-color: var(--dz-category-2); }
.db-board__card-marker--3 { background-color: var(--dz-category-3); }
.db-board__card-marker--4 { background-color: var(--dz-category-4); }
.db-board__card-marker--5 { background-color: var(--dz-category-5); }
.db-board__card-marker--6 { background-color: var(--dz-category-6); }
.db-board__card-marker--7 { background-color: var(--dz-category-7); }
.db-board__card-marker--8 { background-color: var(--dz-category-8); }
.db-board__card-marker--9 { background-color: var(--dz-category-9); }
.db-board__card-marker--10 { background-color: var(--dz-category-10); }
.db-board__card-marker--11 { background-color: var(--dz-category-11); }
.db-board__card-marker--12 { background-color: var(--dz-category-12); }

/* The board and its lane bodies are dxScrollViews in practice, and 22-devextreme-adapter.css gives
 * every themed .dx-scrollview-content the card ground (at 0,2,1). Inside the board the lane is the
 * ground and the card is the card, so the scroll content stays clear. */
.db-board.dx-scrollview .dx-scrollview-content,
.db-board__lane-body.dx-scrollview .dx-scrollview-content {
    background-color: transparent;
}

/* GRID CELL AND ROW STATES (ADO #143, fix plan 3.5). The vocabulary is the manifest's gridStates
 * block; a host adds the class from its onCellPrepared / onRowPrepared.
 *
 * NOT SCOPED TO .db-grid on purpose. The grids that need them are mostly built in script with
 * their own options (Plan/EditBundle's Tiers and Pricing), not through an archetype, and these
 * classes are opt-in by name: nothing outside this vocabulary carries them.
 *
 * The three that paint (read-only, parent, inactive) are (0,5,1), without !important. They have to
 * clear 22-devextreme-adapter.css's `html[data-db-theme] %dx%-rowsview .dx-row >
 * td:not(.dx-pointer-events-none)` grounds and inks at (0,4,2), which a (0,3,1) selector lost to on
 * every themed page: a parent row stayed the card, and read-only and inactive kept the row ink.
 * :root carries the step on an unthemed host too, and the :not() matches the adapter's own, so the
 * fixed-column overlay's transparent cells stay unpainted. */

/* A click edits this cell: the text cursor says so before the click. */
.dx-datagrid-rowsview .dx-data-row > td.db-grid-cell--editable,
.dx-treelist-rowsview .dx-data-row > td.db-grid-cell--editable {
    cursor: text;
}

/* This cell never takes an edit, beside cells that do. A step back in ink, not a disabled fade: the
 * value is still real data and must stay legible. */
:root .dx-datagrid-rowsview .dx-data-row > td.db-grid-cell--readonly:not(.dx-pointer-events-none),
:root .dx-treelist-rowsview .dx-data-row > td.db-grid-cell--readonly:not(.dx-pointer-events-none) {
    color: var(--dz-grid-readonly-cell-color);
}

/* A row heading the rows beneath it. A ground step, not a colour: it groups, it does not mean. */
:root .dx-datagrid-rowsview .dx-data-row.db-grid-row--parent > td:not(.dx-pointer-events-none),
:root .dx-treelist-rowsview .dx-data-row.db-grid-row--parent > td:not(.dx-pointer-events-none) {
    background-color: var(--dz-grid-parent-row-background);
}

/* A row whose record no longer counts - a voided transaction. A step back in ink rather than a fade,
 * so the row stays legible; the status column beside it says why. In the dark theme Meta is the ink
 * (no grey text on dark), so there the status column carries it alone. */
:root .dx-datagrid-rowsview .dx-data-row.db-grid-row--inactive > td:not(.dx-pointer-events-none),
:root .dx-treelist-rowsview .dx-data-row.db-grid-row--inactive > td:not(.dx-pointer-events-none) {
    color: var(--dz-grid-inactive-row-color);
}

/* An editable cell's dropdown, while it is open for editing. A host may let it grow past a narrow
 * column (Plan/EditBundle's SKU lookups float at a 200px minimum over their neighbours, which is
 * that page's layout); this is the surface that makes it read as a raised editor rather than
 * text bleeding over the next cell. */
.dx-datagrid-rowsview .dx-data-row > td.db-grid-cell--editable.dx-editor-cell .dx-selectbox,
.dx-treelist-rowsview .dx-data-row > td.db-grid-cell--editable.dx-editor-cell .dx-selectbox {
    background-color: var(--dz-card);
    box-shadow: var(--dz-dropdown-shadow);
}

/* A PLAIN DATA TABLE (ADO #143, fix plan 3.5): a hand-written <table> of figures that is not a grid -
 * nothing to sort, page, filter or select, so a DataGrid would be the wrong control. Account/Dashboard's
 * invoice comparison is the first caller: per section, a row of column headings, a total row, then
 * the line items with every other row shaded.
 *
 * The vocabulary is the manifest's table block. The parts are ROW classes - .db-table__head on the
 * column-heading row, .db-table__total on a section's total row - and the states are row modifiers:
 * --rule draws a hairline under the row, --alt shades it. Cell padding and column widths are the
 * host's layout.
 *
 * Every colour is a grid's: the total row is the header band a grid header and a card header strip
 * share, and the shading is a grid's alternate row, so a table beside a grid reads as the same family. */
.db-table {
    border-collapse: collapse;
}

.db-table__head > td {
    font-weight: 600;
}

.db-table__total > td {
    background-color: var(--dz-table-total-background);
    color: var(--dz-table-total-ink);
    font-size: var(--dz-table-total-font-size);
    font-weight: 600;
}

.db-table__row--rule > td {
    border-bottom: 1px solid var(--dz-hairline);
}

.db-table__row--alt > td {
    background-color: var(--dz-grid-row-alternate-background);
}

/* THE DIALOG (ADO #143). The modal a page raises over a dxPopup: datablaze.ui.dialog
 * (src/dialog.js) attaches the behaviour, and SharedMvc or datablaze.ui.dialog.confirm writes the
 * frame. DevExtreme owns the overlay, its position and its stacking; this owns the surface.
 *
 * SCOPED TO .db-dialog, which sits on the popup's wrapper (DevExtreme's wrapperAttr). Every rule
 * starts there, so a popup that is not a dialog, and a host that has not adopted the construct, keep
 * their paint. The wrapper selectors name .dx-popup-wrapper as well: the theme's overlay rules in
 * 22-devextreme-adapter.css sit at (0,2,1), and these must win over them for a dialog only.
 *
 * ONE SURFACE. The header shares the body's ground and is set off by a hairline beneath it. The
 * footer of a sheet, and of a popup while its body scrolls, sits on the page tint under its own
 * rule; a popup whose body fits is flat (PP-D79). The overlay content's radius and overflow clip
 * it to the frame's bottom corners.
 *
 * TWO CONTAINERS. db-dialog--popup is a centred card with all four corners rounded; db-dialog--sheet
 * is pinned to the bottom of a narrow viewport with its top corners rounded, a grabber bar above the
 * header, and footer buttons that share the width. dialog.js swaps them at the manifest's breakpoint. */

.db-dialog.dx-popup-wrapper.dx-overlay-shader {
    background-color: var(--dz-dialog-scrim);
}

/* A BUSY DIALOG DIMS. A DevExtreme load panel whose container is a dialog's overlay content, or a
   node inside the dialog (a body section, a grid), mounts its wrapper inside the .db-dialog wrapper;
   with shading on, the wrapper is the shader. LoadPanel's generic-platform default is
   `shadingColor: "transparent"`, which dx writes as an INLINE background-color on that wrapper, so
   no stylesheet rule can win without !important and the shaded panel drew nothing. The page-level
   shadingColor literals are gone (the style guide keeps colour in the package), so the dim comes from
   here. Only a SHADED load panel carries .dx-overlay-shader, so an unshaded one (shading off, the
   grid's default) stays clear; the wrapper's own .db-dialog.dx-popup-wrapper is not a descendant of
   itself, so the dialog's scrim above is untouched; and a load panel outside every dialog is not
   reached, so a page-level panel looks as it did. The panel's card is a child with its own opaque
   surface, which this does not touch (PP-D24). */
.db-dialog .dx-loadpanel-wrapper.dx-overlay-shader {
    background-color: var(--dz-dialog-busy-shade) !important;
}

/* (0,4,0): above dx.light's focused-popup outline at (0,3,0), so an open dialog draws no frame. */
.db-dialog.dx-overlay-wrapper.dx-popup-wrapper > .dx-overlay-content {
    border: 0;
    border-radius: var(--dz-dialog-radius);
    background-color: var(--dz-dialog-background);
    box-shadow: var(--dz-dialog-shadow);
    color: var(--dz-dialog-title-ink);
    font-family: var(--dz-font-family);
    overflow: hidden;
}

/* A sheet is full width, so DevExtreme sizes its auto height with the flex strategy: the overlay
   content is a flex container and the popup content box a flex item of it with a 100% basis. In
   DevExtreme's wrapping row the line takes the item's full height whatever the container's
   max-height, so the box would stay at its content height and the footer would fall below the
   viewport. A nowrap column lets the box shrink along the height axis to the cap, as it does in a
   popup (PP-D25), while the width still fills. Drop this rule and a capped sheet loses its pinned
   footer. */
.db-dialog--sheet.dx-overlay-wrapper.dx-popup-wrapper > .dx-overlay-content {
    flex-direction: column;
    flex-wrap: nowrap;
    border-radius: var(--dz-dialog-radius) var(--dz-dialog-radius) 0 0;
}

/* PINNED CHROME AT THE CAP (PP-D25). An auto-height dialog has no definite height for the frame's
   100% to resolve against: DevExtreme gives the popup content box height:auto and a max-height
   inline, and its dx-popup-content-scrollable class would make the box scroll the whole frame, so
   the title and footer would scroll away with the body. The box is a clipping flex column instead:
   the frame shrinks to the cap (min-height:0 lets it go below its content), so the body takes the
   overflow and the header and footer stay put. Under the cap nothing shrinks, so a short dialog is
   as short as its content. A workflow's definite height resolves the 100% directly. The selector
   is the direct chain, so a popup nested in a dialog is never reached. */
.db-dialog.dx-overlay-wrapper.dx-popup-wrapper > .dx-overlay-content > .dx-popup-content {
    display: flex;
    flex-direction: column;
    height: 100%;
    padding: 0;
    background-color: var(--dz-dialog-background);
    overflow: hidden;
}

.db-dialog--popup .db-dialog__frame,
.db-dialog--sheet .db-dialog__frame {
    display: flex;
    flex-direction: column;
    height: 100%;
    min-height: 0;
}

/* THE HEADER AND THE FOOTER ARE ONE HEIGHT, 56px (PP-D67). The two bars frame the body, and drew at
   57 (padding 16 and 8 around the 32px close control, plus the 1px rule) and 65 (padding 12 and 12
   around the 40px actions, plus the rule), so the dialog read lopsided. The BAR is the 56, rule
   included, so the rule counts as part of the padding on its side: the header has 12 above and 11 +
   the rule below its 32px control, the footer the rule + 7 above and 8 below its 40px actions. That
   is why one side of each is a token less 1px. The close control sits at the top edge of the
   heading and the title's 4px top padding centres its line on it, so a title-only header is
   centred and a header with chips grows below. 36-legacy-popup.css repeats the header and footer
   padding for a plain DevExtreme popup's bars, and its stylesheet harness fails when the two
   differ. A bar whose content wraps (a long summary, a sheet's stacked buttons, chips) grows past
   56 rather than clip. */
.db-dialog .db-dialog__header {
    display: flex;
    align-items: flex-start;
    gap: var(--dz-space-1);
    padding: var(--dz-space-1half) var(--dz-space-2) calc(var(--dz-space-1half) - 1px) var(--dz-space-3);
    background-color: var(--dz-dialog-background);
    border-bottom: 1px solid var(--dz-dialog-rule);
}

/* The sheet's grabber: a short bar above the header that says the sheet came up from the bottom. */
.db-dialog--sheet .db-dialog__header {
    position: relative;
    padding-top: var(--dz-space-3);
}

.db-dialog--sheet .db-dialog__header::before {
    content: "";
    position: absolute;
    top: var(--dz-space-1);
    left: 50%;
    width: 36px;
    height: 4px;
    margin-left: -18px;
    border-radius: var(--dz-radius-pill);
    background-color: var(--dz-dialog-sheet-grabber);
}

.db-dialog .db-dialog__heading {
    flex: 1;
    min-width: 0;
}

.db-dialog .db-dialog__title {
    margin: 0;
    padding-top: var(--dz-space-half);
    color: var(--dz-dialog-title-ink);
    font-size: 18px;
    font-weight: 600;
    line-height: 1.3;
}

.db-dialog .db-dialog__chips {
    display: flex;
    flex-wrap: wrap;
    gap: var(--dz-space-half);
    margin-top: var(--dz-space-1);
}

.db-dialog .db-dialog__chips:empty {
    display: none;
}

/* A chip names what the dialog acts on: a meta label and its value in title ink. Outlined on the
   dialog's own ground, never filled with the page ground, where meta ink falls under 4.5:1. */
.db-dialog .db-dialog__chip {
    display: inline-flex;
    align-items: baseline;
    gap: var(--dz-space-half);
    padding: 2px var(--dz-space-1);
    border: 1px solid var(--dz-dialog-rule);
    border-radius: var(--dz-radius-pill);
    background-color: var(--dz-dialog-background);
    color: var(--dz-dialog-meta-ink);
    font-size: 12px;
    line-height: 18px;
}

.db-dialog .db-dialog__chip b {
    color: var(--dz-dialog-title-ink);
    font-weight: 600;
}

/* The close control: a cross drawn from two bars, never a glyph character. */
.db-dialog .db-dialog__close {
    position: relative;
    flex: none;
    width: 32px;
    height: 32px;
    padding: 0;
    border: 0;
    border-radius: var(--dz-radius-sm);
    background: transparent;
    color: var(--dz-dialog-meta-ink);
    cursor: pointer;
}

.db-dialog .db-dialog__close::before,
.db-dialog .db-dialog__close::after {
    content: "";
    position: absolute;
    top: 50%;
    left: 50%;
    width: 14px;
    height: 2px;
    margin: -1px 0 0 -7px;
    border-radius: 1px;
    background-color: currentColor;
    transform: rotate(45deg);
}

.db-dialog .db-dialog__close::after {
    transform: rotate(-45deg);
}

.db-dialog .db-dialog__close:hover {
    color: var(--dz-dialog-title-ink);
}

.db-dialog .db-dialog__close:focus-visible {
    outline: 2px solid var(--dz-focus-color);
    outline-offset: 0;
}

.db-dialog .db-dialog__body {
    flex: 1;
    min-height: 0;
    overflow: auto;
    padding: var(--dz-space-3);
}

/* THE BODY'S RHYTHM IS EVEN (PP-D72, audit rows A-B, B-A, C-G3, C-G4; the band is 24, PP-D78). The band
   takes 24 above and 24 below (it was 16 each: the form read squished), and the first and last block
   inside it drop their own outer margin, so the gap to the header and to the footer is the band's
   alone. A guided step is the same frame, so it follows the same rule. This sets the body only; the
   empty body (PP-D66) and the bars' heights (PP-D67) are untouched. The one optical exception is the
   lead paragraph that opens the body, trimmed below.

   WHICH BLOCK IS THE EDGE. A plain :first-child / :last-child misses it in two shapes:
   - A view's own container. A div with no class, or a .db-form region, does not stop its first child's
     margin: the two collapse. The reset therefore reaches the edge block of the body and of up to two
     containers inside it. An element with a class (a callout, a card, a list the package draws) may
     paint a box of its own, so its content keeps its margin. A third container is not entered.
   - What is not drawn. "First" and "last" count only what takes part in the layout, so a script, a
     style, a template, an <input type="hidden">, a [hidden] node, a DevExtreme overlay's placeholder
     (.dx-overlay), a node DevExtreme hides (.dx-state-invisible, from .Visible(false)) and an inline
     display:none do not make the next block the second child.
   :nth-child(1 of ...) and :nth-last-child(1 of ...) need Chromium 111, Safari 9 and Firefox 113 (the
   sheet already needs :has, Chromium 105 and Firefox 121). */
.db-dialog :is(.db-dialog__body, .db-dialog__step) > :nth-child(1 of :not(script, style, template, input[type="hidden"], [hidden], .dx-overlay, .dx-state-invisible, [style*="display: none"], [style*="display:none"])),
.db-dialog :is(.db-dialog__body, .db-dialog__step) > :is(:not([class]), .db-form):nth-child(1 of :not(script, style, template, input[type="hidden"], [hidden], .dx-overlay, .dx-state-invisible, [style*="display: none"], [style*="display:none"])) > :nth-child(1 of :not(script, style, template, input[type="hidden"], [hidden], .dx-overlay, .dx-state-invisible, [style*="display: none"], [style*="display:none"])),
.db-dialog :is(.db-dialog__body, .db-dialog__step) > :is(:not([class]), .db-form):nth-child(1 of :not(script, style, template, input[type="hidden"], [hidden], .dx-overlay, .dx-state-invisible, [style*="display: none"], [style*="display:none"])) > :not([class]):nth-child(1 of :not(script, style, template, input[type="hidden"], [hidden], .dx-overlay, .dx-state-invisible, [style*="display: none"], [style*="display:none"])) > :nth-child(1 of :not(script, style, template, input[type="hidden"], [hidden], .dx-overlay, .dx-state-invisible, [style*="display: none"], [style*="display:none"])) {
    margin-top: 0;
}

.db-dialog :is(.db-dialog__body, .db-dialog__step) > :nth-last-child(1 of :not(script, style, template, input[type="hidden"], [hidden], .dx-overlay, .dx-state-invisible, [style*="display: none"], [style*="display:none"])),
.db-dialog :is(.db-dialog__body, .db-dialog__step) > :is(:not([class]), .db-form):nth-last-child(1 of :not(script, style, template, input[type="hidden"], [hidden], .dx-overlay, .dx-state-invisible, [style*="display: none"], [style*="display:none"])) > :nth-last-child(1 of :not(script, style, template, input[type="hidden"], [hidden], .dx-overlay, .dx-state-invisible, [style*="display: none"], [style*="display:none"])),
.db-dialog :is(.db-dialog__body, .db-dialog__step) > :is(:not([class]), .db-form):nth-last-child(1 of :not(script, style, template, input[type="hidden"], [hidden], .dx-overlay, .dx-state-invisible, [style*="display: none"], [style*="display:none"])) > :not([class]):nth-last-child(1 of :not(script, style, template, input[type="hidden"], [hidden], .dx-overlay, .dx-state-invisible, [style*="display: none"], [style*="display:none"])) > :nth-last-child(1 of :not(script, style, template, input[type="hidden"], [hidden], .dx-overlay, .dx-state-invisible, [style*="display: none"], [style*="display:none"])) {
    margin-bottom: 0;
}

/* THE LEAD THAT OPENS THE BODY IS TRIMMED, ABOUT 4px (PP-D78). A paragraph's 20px line box carries 3-4px of
   half-leading above its glyphs, which reads as extra margin on top of the band; a field's crisp border
   has none. Cole's approved look is 20px visible above the lead text and 24px below the select box, so
   the lead, and only the lead, pulls up by --dz-dialog-lead-trim. It is found the way the edge reset
   above finds the first block (the body's or step's first drawn child, and the first drawn child of up
   to two containers inside it), and each selector is that reset's with the lead class added, so this
   rule always outranks its margin-top: 0. A lead anywhere else (after a form, a callout, a row of
   actions) keeps its own margins, and no other block type is trimmed. */
.db-dialog :is(.db-dialog__body, .db-dialog__step) > .db-dialog__lead:nth-child(1 of :not(script, style, template, input[type="hidden"], [hidden], .dx-overlay, .dx-state-invisible, [style*="display: none"], [style*="display:none"])),
.db-dialog :is(.db-dialog__body, .db-dialog__step) > :is(:not([class]), .db-form):nth-child(1 of :not(script, style, template, input[type="hidden"], [hidden], .dx-overlay, .dx-state-invisible, [style*="display: none"], [style*="display:none"])) > .db-dialog__lead:nth-child(1 of :not(script, style, template, input[type="hidden"], [hidden], .dx-overlay, .dx-state-invisible, [style*="display: none"], [style*="display:none"])),
.db-dialog :is(.db-dialog__body, .db-dialog__step) > :is(:not([class]), .db-form):nth-child(1 of :not(script, style, template, input[type="hidden"], [hidden], .dx-overlay, .dx-state-invisible, [style*="display: none"], [style*="display:none"])) > :not([class]):nth-child(1 of :not(script, style, template, input[type="hidden"], [hidden], .dx-overlay, .dx-state-invisible, [style*="display: none"], [style*="display:none"])) > .db-dialog__lead:nth-child(1 of :not(script, style, template, input[type="hidden"], [hidden], .dx-overlay, .dx-state-invisible, [style*="display: none"], [style*="display:none"])) {
    margin-top: calc(-1 * var(--dz-dialog-lead-trim));
}

/* AN EMPTY BODY IS NOT DRAWN (PP-D66). A title-only confirm ("Delete this subscription?") writes a
   body with nothing in it, and the band's padding alone drew a blank strip between the title and the
   actions. :empty is live, so a body that is filled after the dialog opens (a controller writing
   its facts, a view that fills it on show) takes its band back the moment it has a child; a body
   holding only whitespace (markup a Razor view indented) is not empty here and keeps its band, the
   safe direction. With the band gone the header's hairline and the footer's would sit side by side
   as one thicker line, so the footer yields its own: the header's stays, and on a sheet the footer's
   tint still marks where the actions begin (a popup's footer is flat unless its body scrolls, and an
   empty body never does, PP-D79). */
.db-dialog .db-dialog__body:empty {
    display: none;
}

.db-dialog .db-dialog__body:empty + .db-dialog__footer {
    /* PP-D67: the rule's 1px goes back to the padding, so the footer stays 56px (8 + 40 + 8). */
    padding-top: var(--dz-space-1);
    border-top: 0;
}

/* An image in a dialog body is never wider than the body, so a sheet's narrow body does not scroll
   sideways for a logo. An image already narrower keeps its size: only max-width is set, never a width
   or an auto height, so a height a view gives it (a logo's height attribute) still applies. Where
   that fixed height meets the width cap, contain keeps the picture's proportions inside its box
   instead of stretching it. */
.db-dialog .db-dialog__body img {
    max-width: 100%;
    object-fit: contain;
}

/* A FILE PICKER IN A SHEET'S BODY. DevExtreme draws the picker as a table: the select button beside a
   cell that holds the browser's own file input, stretched invisibly over the drop zone. The input is
   already `width: 100%`, but a percentage width does not lower the input's intrinsic width (the
   browser's button and "No file chosen"), and the table cannot be narrower than its cells' intrinsic
   widths, so on a phone the picker is wider than the body and the body scrolls sideways (Send
   Invoice's custom attachments: the body measured 404px of scroll in a 380px sheet at 390). A zero
   width with a full-width minimum takes no intrinsic width and still fills the cell, so the whole drop
   zone stays clickable. A popup sizes to its content, so it keeps the picker as DevExtreme draws it. */
.db-dialog--sheet .db-dialog__body .dx-fileuploader-input {
    width: 0;
    min-width: 100%;
}

/* THE PICKER'S SIDE INSET (PP-D72, audit row B-C). The picker's select button sat 10px right of every
   other control in the body: dx.light pads the wrapper `.dx-fileuploader-wrapper { padding: 7px }` and
   gives the drop zone a transparent border, `.dx-fileuploader-input-wrapper { padding: 7px 0; border:
   3px dashed transparent }`. Both side insets go, so the button lines up with the body's content edge.
   The top and bottom stay as DevExtreme draws them, and the drag-over border keeps its top and bottom
   (it loses the 3px at each side). 36-legacy-popup.css mirrors this for the unported popups. */
.db-dialog .db-dialog__body .dx-fileuploader-wrapper {
    padding-inline: 0;
}

.db-dialog .db-dialog__body .dx-fileuploader-input-wrapper {
    border-inline-width: 0;
}

/* A grid-size dialog on a sheet that fills (a workflow, or a body holding a grid) gives its grid
   whatever height the body has left, below any lead such as a toolbar, instead of the fixed Height
   its view gives it as a popup. A zero flex basis outranks that inline height, and the grid sizes its
   rows from the rendered root. [data-db-grid] is the grid's root: DevExtreme's .dx-datagrid is the
   root's child. A fitted sheet has no definite height to share, so without the fill class the grid
   keeps its view's fixed Height instead of collapsing; a popup keeps its content-fit height, so
   nothing here applies above the breakpoint. */
.db-dialog--sheet.db-dialog--sheet-fill .db-dialog__frame[data-db-dialog-size="grid"] .db-dialog__body {
    display: flex;
    flex-direction: column;
}

.db-dialog--sheet.db-dialog--sheet-fill .db-dialog__frame[data-db-dialog-size="grid"] .db-dialog__body > [data-db-grid] {
    flex: 1 1 0;
    min-height: 0;
}

/* A GRID'S TOOLBAR STRIP ON A SHEET. DevExtreme pins the strip's `before` group to the left edge and
   its `after` group to the right edge, each as wide as its items. On a phone a strip whose items add
   up to more than the body (Adjust Quantity's file picker, Delete Selected, Add and the search box
   in 342px; MEASURED at 390: Delete Selected at x=-129, the picker under the search box) runs the
   groups over each other and the `after` group past the left edge, out of reach. On a sheet the strip
   is a wrapping row of its groups instead: each group takes its own width, the `after` group keeps
   the trailing edge, and the items that do not fit drop to the next line. A strip that fits on one
   line reads as before. DevExtreme's own rules are one or two classes deep, so these win on the
   selector alone. A popup keeps the strip as DevExtreme draws it. */
.db-dialog--sheet .dx-datagrid-header-panel .dx-toolbar .dx-toolbar-items-container,
.db-dialog--sheet .dx-treelist-header-panel .dx-toolbar .dx-toolbar-items-container {
    display: flex;
    flex-wrap: wrap;
    row-gap: var(--dz-space-half);
    height: auto;
    min-height: var(--dz-control-visual-height);
}

.db-dialog--sheet .dx-datagrid-header-panel .dx-toolbar .dx-toolbar-before,
.db-dialog--sheet .dx-treelist-header-panel .dx-toolbar .dx-toolbar-before,
.db-dialog--sheet .dx-datagrid-header-panel .dx-toolbar .dx-toolbar-center,
.db-dialog--sheet .dx-treelist-header-panel .dx-toolbar .dx-toolbar-center,
.db-dialog--sheet .dx-datagrid-header-panel .dx-toolbar .dx-toolbar-after,
.db-dialog--sheet .dx-treelist-header-panel .dx-toolbar .dx-toolbar-after {
    position: static;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    row-gap: var(--dz-space-half);
    max-width: 100%;
    height: auto;
    padding: 0;
}

.db-dialog--sheet .dx-datagrid-header-panel .dx-toolbar .dx-toolbar-after,
.db-dialog--sheet .dx-treelist-header-panel .dx-toolbar .dx-toolbar-after {
    margin-inline-start: auto;
}

/* An item, and the widget in it, never run wider than the sheet: the file picker a view sizes in an
   inline width (350px in Add Quantity) is the case. */
.db-dialog--sheet .dx-datagrid-header-panel .dx-toolbar .dx-toolbar-item,
.db-dialog--sheet .dx-treelist-header-panel .dx-toolbar .dx-toolbar-item,
.db-dialog--sheet .dx-datagrid-header-panel .dx-toolbar .dx-toolbar-item-content > .dx-widget,
.db-dialog--sheet .dx-treelist-header-panel .dx-toolbar .dx-toolbar-item-content > .dx-widget {
    max-width: 100%;
}

/* [hidden] wins anywhere inside a dialog: a component's own display (a flex row, a callout) would
   otherwise outrank the user-agent rule, and a section a view hides with the attribute would show.
   A guided step the script hides is the case named on its own. */
.db-dialog [hidden],
.db-dialog .db-dialog__step[hidden] {
    display: none !important;
}

.db-dialog .db-dialog__footer {
    display: flex;
    align-items: center;
    gap: var(--dz-space-2);
    /* 56px with its rule (PP-D67, explained at the header): 1 + 7 + the 40px actions + 8. */
    padding: calc(var(--dz-space-1) - 1px) var(--dz-space-3) var(--dz-space-1);
    border-top: 1px solid var(--dz-dialog-rule);
    background-color: var(--dz-dialog-footer-background);
}

/* A POPUP'S FOOTER IS FLAT UNLESS THE BODY SCROLLS (PP-D79). On a small dialog (Public IP) the tinted
   footer and its hairline felt heavy, so a popup whose body fits draws the footer on the dialog's own
   ground with no top rule; where the body scrolls, the tint and the hairline stay, because the edge is
   what marks where the scroll stops. dialog.js writes data-db-scrolls="true" on the frame while the
   body scrolls (contract.dialog.scrollsAttribute), so this keys on the attribute and not on a media
   query, and a dialog nobody has measured yet (no script, before the first layout) reads as flat. A
   sheet is outside this rule and always keeps the tint and the rule above: on a phone the footer is a
   bar of its own under the thumb. The footer stays 56px: the rule's 1px goes back to the padding (the
   empty body's footer below does the same), 8 + the 40px actions + 8. */
.db-dialog:not(.db-dialog--sheet) .db-dialog__frame:not([data-db-scrolls="true"]) .db-dialog__footer {
    padding-top: var(--dz-space-1);
    border-top: 0;
    background-color: var(--dz-dialog-background);
}

.db-dialog .db-dialog__summary {
    flex: 1;
    min-width: 0;
    color: var(--dz-dialog-footer-meta-ink);
    font-size: 13px;
}

.db-dialog .db-dialog__actions {
    display: flex;
    gap: var(--dz-space-1);
}

/* EXTRA ACTIONS (PP-D57). The footer's first child when a dialog declares more actions than its one
   secondary: a danger Delete, a commit variant, or a side action (Clear, Reset to defaults, Abort:
   PP-D80), opposite the commit. It is its own container, not part of the actions row, so the actions
   keep their order (Cancel, Commit, or Cancel, Secondary, Commit for a paired secondary, PP-D75: the
   order is the DOM's, so the popup and the sheet need no rule of their own) and sit at the trailing
   edge beside the summary's flexible space; the extras sit at the leading edge. A footer that holds extras wraps, so a long label
   (Move Linked (remove from existing groups)) drops the actions to a row of their own, still at
   the trailing edge, instead of pushing them off the dialog. A footer with none matches nothing
   here and is as it was. A browser without :has() keeps the footer on one line: visibly tight,
   not broken. */
.db-dialog .db-dialog__extra-actions {
    display: flex;
    gap: var(--dz-space-1);
}

.db-dialog .db-dialog__footer:has(> .db-dialog__extra-actions) {
    flex-wrap: wrap;
}

.db-dialog .db-dialog__footer:has(> .db-dialog__extra-actions) > .db-dialog__actions {
    margin-inline-start: auto;
}

/* THE BUSY INDICATOR. controller.busy(promise) fills it in the footer, before the actions, and
   empties it when the last span settles: a spinner and the word Working..., in the footer's meta ink,
   so the dialog says what the disabled buttons mean. The spinner is a ring with one dark arc, turning;
   under prefers-reduced-motion it holds still and the word carries the state. The node is a status
   region, so it is always rendered (a region that appears with its text is often not announced); an
   empty one is taken out of the footer's flow, so it adds no gap beside the actions. */
.db-dialog .db-dialog__busy {
    display: inline-flex;
    align-items: center;
    gap: var(--dz-space-1);
    color: var(--dz-dialog-footer-meta-ink);
    font-size: 13px;
}

.db-dialog .db-dialog__busy:empty {
    position: absolute;
}

.db-dialog .db-dialog__spinner {
    flex: none;
    box-sizing: border-box;
    width: 16px;
    height: 16px;
    border: 2px solid var(--dz-dialog-rule);
    border-top-color: var(--dz-dialog-footer-meta-ink);
    border-radius: 50%;
    animation: db-dialog-spin 0.8s linear infinite;
}

@keyframes db-dialog-spin {
    to {
        transform: rotate(360deg);
    }
}

@media (prefers-reduced-motion: reduce) {
    .db-dialog .db-dialog__spinner {
        animation: none;
    }
}

/* On a sheet the summary sits above the actions and the buttons share the width. Only the sides
   narrow: the vertical padding stays the shared bar's (PP-D67), so a sheet whose actions fit one row
   is the same 56px, and a wrapped one grows by its rows. */
.db-dialog--sheet .db-dialog__footer {
    flex-wrap: wrap;
    padding-inline: var(--dz-space-2);
}

.db-dialog--sheet .db-dialog__summary:empty {
    display: none;
}

/* The row wraps: a button whose label is longer than the sheet's share (Move Linked (remove from
   existing groups)) drops to its own line instead of pushing its neighbours off the sheet. The
   100px basis keeps short buttons sharing the width equally, as they did with a zero basis. */
.db-dialog--sheet .db-dialog__actions {
    flex: 1 0 100%;
    flex-wrap: wrap;
}

.db-dialog--sheet .db-dialog__actions > * {
    flex: 1 1 100px;
}

/* The extras take a row of their own above the buttons and share its width the way the buttons do,
   so a lone Delete spans the sheet and two share it. */
.db-dialog--sheet .db-dialog__extra-actions {
    flex: 1 0 100%;
    flex-wrap: wrap;
}

.db-dialog--sheet .db-dialog__extra-actions > * {
    flex: 1 1 100px;
}

/* GUIDED: the step rail on the leading edge of a popup, header, body and footer beside it. The rail
   reads the page ground so it sits back from the body; its secondary ink is the rail's own meta
   hook, because --dz-dialog-meta-ink falls under 4.5:1 there. A sheet drops the rail for the
   count and bar; a popup shows the count but not the bar, which the rail already says. */
.db-dialog--guided .db-dialog__frame {
    display: grid;
    grid-template-columns: 220px minmax(0, 1fr);
    grid-template-rows: auto minmax(0, 1fr) auto;
}

.db-dialog--guided .db-dialog__rail {
    grid-column: 1;
    grid-row: 1 / span 3;
    padding: var(--dz-space-3) var(--dz-space-2);
    background-color: var(--dz-dialog-rail-background);
    overflow: auto;
}

.db-dialog--guided .db-dialog__header,
.db-dialog--guided .db-dialog__body,
.db-dialog--guided .db-dialog__footer {
    grid-column: 2;
}

.db-dialog--sheet.db-dialog--guided .db-dialog__frame {
    display: flex;
}

.db-dialog--sheet.db-dialog--guided .db-dialog__rail {
    display: none;
}

.db-dialog .db-dialog__steps {
    display: flex;
    flex-direction: column;
    gap: 2px;
    margin: 0;
    padding: 0;
    list-style: none;
}

.db-dialog .db-dialog__step-item {
    margin: 0;
    padding: 0;
}

/* One row per step: a numbered dot, then the title over the value chosen for it. A done step's
   row is a button back to it, so the button's own chrome is reset. */
.db-dialog .db-dialog__step-row {
    display: flex;
    gap: var(--dz-space-1half);
    width: 100%;
    padding: var(--dz-space-1) var(--dz-space-1);
    border: 0;
    border-radius: var(--dz-radius-sm);
    background: transparent;
    color: var(--dz-dialog-rail-meta-ink);
    font: inherit;
    font-size: 14px;
    line-height: 1.35;
    text-align: left;
}

.db-dialog button.db-dialog__step-row {
    cursor: pointer;
}

.db-dialog button.db-dialog__step-row:hover {
    background-color: var(--dz-dialog-background);
}

.db-dialog button.db-dialog__step-row:focus-visible {
    outline: 2px solid var(--dz-focus-color);
    outline-offset: 0;
}

.db-dialog .db-dialog__step-number {
    display: flex;
    flex: none;
    align-items: center;
    justify-content: center;
    width: 24px;
    height: 24px;
    box-sizing: border-box;
    border: 1.5px solid var(--dz-dialog-step-border);
    border-radius: 50%;
    background-color: var(--dz-dialog-background);
    color: var(--dz-dialog-rail-meta-ink);
    font-size: 12px;
}

.db-dialog .db-dialog__step-text {
    display: flex;
    flex-direction: column;
    min-width: 0;
    padding-top: 2px;
}

.db-dialog .db-dialog__step-title {
    overflow-wrap: anywhere;
}

.db-dialog .db-dialog__step-value {
    color: var(--dz-dialog-rail-meta-ink);
    font-family: var(--dz-font-family-data);
    font-size: 12px;
    font-weight: 400;
    overflow-wrap: anywhere;
}

/* A done step's number is filled and carries a check drawn from two borders in place of its digit
   (the number is aria-hidden; the row's title names the step). */
.db-dialog .db-dialog__step-item[data-state="done"] .db-dialog__step-number {
    position: relative;
    border-color: var(--dz-dialog-step-done);
    background-color: var(--dz-dialog-step-done);
    color: transparent;
}

.db-dialog .db-dialog__step-item[data-state="done"] .db-dialog__step-number::after {
    content: "";
    position: absolute;
    inset: 0;
    width: 5px;
    height: 9px;
    margin: auto;
    border: solid var(--dz-dialog-step-done-ink);
    border-width: 0 2px 2px 0;
    transform: rotate(45deg);
}

.db-dialog .db-dialog__step-item[aria-current="step"] .db-dialog__step-row {
    color: var(--dz-dialog-title-ink);
    font-weight: 600;
}

.db-dialog .db-dialog__step-item[aria-current="step"] .db-dialog__step-number {
    border-color: var(--dz-dialog-step-current);
    background-color: var(--dz-dialog-step-current);
    color: var(--dz-dialog-step-current-ink);
}

.db-dialog .db-dialog__progress {
    display: flex;
    flex: none;
    align-items: center;
    gap: var(--dz-space-1);
    padding-top: var(--dz-space-1);
}

.db-dialog .db-dialog__count {
    color: var(--dz-dialog-meta-ink);
    font-size: 12px;
}

.db-dialog .db-dialog__track {
    display: block;
    width: 80px;
    height: 4px;
    border-radius: var(--dz-radius-pill);
    background-color: var(--dz-dialog-rule);
    overflow: hidden;
}

.db-dialog--popup .db-dialog__track {
    display: none;
}

.db-dialog .db-dialog__bar {
    display: block;
    width: 0;
    height: 100%;
    background-color: var(--dz-dialog-step-current);
}

/* A STEP'S HEADING AND LEAD. The heading names what the step asks, a size below the dialog's title
   so a bare h3 does not outweigh it; the lead under it says what the choice does, in body ink, with
   a small gap before the step's content. __lead is the same line for a simple dialog's body. */
.db-dialog .db-dialog__step-heading {
    margin: 0;
    color: var(--dz-dialog-title-ink);
    font-size: 15px;
    font-weight: 600;
    line-height: 1.35;
}

.db-dialog .db-dialog__lead,
.db-dialog .db-dialog__step-lead {
    margin: var(--dz-space-half) 0 var(--dz-space-2);
    color: var(--dz-dialog-body-ink);
    font-size: 14px;
    line-height: 1.45;
}

/* A STEP WITHOUT A LEAD (PP-D72, audit row C-G1). A heading followed straight by its content (Change
   plan's plan list) had no gap at all, because the heading sets no bottom margin. A lead keeps its
   own half-step top margin; anything else takes a step of 12. */
.db-dialog .db-dialog__step-heading + :not(.db-dialog__step-lead, .db-dialog__lead) {
    margin-top: var(--dz-space-1half);
}

/* PROSE AFTER A FORM REGION. A form region (.db-form: Html.DbFormRegion, or a .DbForm() / form.shell()
   wrapper) ends flush on its last field's help line, and a lead's own top margin is only a half
   step, so the paragraph that follows it read as part of the help text. It takes the form's section
   gap instead. Only a lead that directly follows a form region matches, so a dialog with no .db-form
   (Change plan, Default plan: PP-D18) lays out as before. */
.db-dialog .db-form + .db-dialog__lead {
    margin-top: var(--dz-form-section-gap);
}

/* A FORM REGION AFTER A BLOCK (PP-D72, audit rows C-G1 and A-A). The mirror of the rule above: a
   region that follows a list, a summary or a row of actions took no gap, so its first label sat
   against the block (Activate's plan list, Invoice's summary). It takes the form's section gap. The
   rule is the general sibling (~), not the adjacent one, because a partial between the block and the
   region can end in a <script>. What counts as a block excludes what is not drawn (hidden nodes,
   script, style, template, an <input type="hidden">, a DevExtreme overlay's placeholder, a node
   DevExtreme hides (.dx-state-invisible), an inline display:none, and an empty div, p, ul or ol: the
   host or message line a script fills later) and the lead and step heading,
   which keep their own spacing. An empty element is only empty while it has no child, so a
   placeholder a view indents with whitespace still counts as a block, the safe direction (as with
   PP-D66's empty body). A lead directly above the region keeps its own 16 as the gap: block, lead,
   form would otherwise take the section gap, because the lead's 16 bottom margin and the region's
   top margin collapse to the larger of the two (24, not 16). The region that follows a lead with
   something between them (a <script>) is not that case and takes the section gap. Change plan and
   Default plan hold no .db-form (PP-D18), so they are unchanged. */
.db-dialog :is(.db-dialog__body, .db-dialog__step) > :not([hidden], script, style, template, input[type="hidden"], .dx-overlay, .dx-state-invisible, [style*="display: none"], [style*="display:none"], div:empty, p:empty, ul:empty, ol:empty, .db-dialog__lead, .db-dialog__step-lead, .db-dialog__step-heading) ~ .db-form:not(.db-dialog__lead + *, .db-dialog__step-lead + *) {
    margin-top: var(--dz-form-section-gap);
}

/* A CALLOUT IN A DIALOG (PP-D72, audit row C-G1). The callout carries no margin of its own
   (24-callout.css), so the one that follows Change plan's plan list sat flush against it. A callout
   that opens the body loses this to the body's first-child rule above. The rule is any callout inside
   a dialog, not only the one in Change plan: it also reaches the cobrand manager's callouts, the
   create and assign errors (CobrandManager/Index.cshtml) and the assign preview's warnings
   (voyager.cobrands.manager.js). Each is a block after content in a dialog body, so the 16 above
   is wanted there too; no behaviour was added for them. */
.db-dialog .db-callout {
    margin-top: var(--dz-space-2);
}

/* BODY ACTIONS. A row of secondary actions in a simple dialog's body (Remove default plan), set
   apart from the content above it and aligned to the start. */
.db-dialog .db-dialog__body-actions {
    display: flex;
    flex-wrap: wrap;
    justify-content: flex-start;
    gap: var(--dz-space-2);
    margin-top: var(--dz-space-2);
}

/* A lead after the actions row (PP-D72, audit row C-G6): the lead's own top margin is a half step,
   so the paragraph under the row read as part of it. It takes the row's own 16. */
.db-dialog .db-dialog__body-actions + .db-dialog__lead {
    margin-top: var(--dz-space-2);
}

/* A RUN'S PROGRESS BODY (PP-D41). The body of a dialog that shows a batch working through: a status
   row (a mark slot, then the bar), the counts under it, and the failure reasons under those. The
   host owns the numbers and the order things appear (it unhides the done mark, fills the bar, writes
   the counts and the reasons); the package owns how each looks.

   Every colour is a status hook: the bar is a FILL, so it reads the active accent (3.37:1 on the dark
   card, over the 3:1 a graphic needs; 4.89:1 on the light one); the done mark and the failure text
   are TEXT, so they read the text hooks the dark theme lifts (active 5.83:1, danger 5.42:1 on the
   dark card; 4.89:1 and 5.70:1 on the light one). The track is clear, so an empty bar is nothing
   but the row's height. [hidden] on the mark, the failures or a count row already wins (above). */
.db-dialog .db-dialog__run-status {
    display: flex;
    align-items: center;
    gap: var(--dz-space-1);
}

/* The slot a spinner and the done mark take turns in: fixed so the bar does not move when one
   replaces the other. */
.db-dialog .db-dialog__run-mark {
    display: flex;
    flex: none;
    align-items: center;
    justify-content: center;
    width: 28px;
}

/* The done mark: a check drawn from two borders (a glyph character is never an icon here). Sized to
   read at the mark slot's 28px: the box is 12x24, which turned 45 degrees spans about 25px, so it
   fills the slot without touching its edges. */
.db-dialog .db-dialog__run-done {
    display: block;
    width: var(--dz-space-1half);
    height: var(--dz-space-3);
    box-sizing: border-box;
    border: solid var(--dz-active-text-color);
    border-width: 0 var(--dz-space-half) var(--dz-space-half) 0;
    transform: rotate(45deg);
}

/* On the dxProgressBar's own element. The container is the 32px rounded track and the range is its
   fill. */
.db-dialog .db-dialog__run-bar {
    flex: 1 1 auto;
    min-width: 0;
}

.db-dialog .db-dialog__run-bar .dx-progressbar-container {
    height: var(--dz-space-4);
    border: none;
    border-radius: var(--dz-radius-xs);
    background-color: transparent;
}

.db-dialog .db-dialog__run-bar .dx-progressbar-container .dx-progressbar-range {
    border: none;
    border-radius: var(--dz-radius-xs);
    background-color: var(--dz-status-active-accent);
}

/* The counts sit under the bar, at its end. Each is a number in strong weight and its word, in a box
   with a floor wide enough for the longest ("N successful" at 14px) so two of them line up, and that
   never wraps: a bigger number widens the box instead of breaking the line. The 8px margin sets them
   off the bar they were flush under (PP-D72, audit row B-J). */
.db-dialog .db-dialog__run-counts {
    margin-top: var(--dz-space-1);
    text-align: right;
}

.db-dialog .db-dialog__run-count {
    display: inline-block;
    min-width: 112px;
    white-space: nowrap;
}

.db-dialog .db-dialog__run-number {
    font-weight: 600;
}

/* Why the failures failed, one per line, in the danger text. Capped so a long run does not push the
   footer off the dialog: past the cap it scrolls. */
.db-dialog .db-dialog__run-failures {
    width: 100%;
    max-height: 48px;
    padding-top: var(--dz-space-half);
    overflow-y: auto;
    text-align: right;
    color: var(--dz-danger-text-color);
}

/* CONFIRM AND ALERT BODY PARTS. The details list under a confirm's or alert's message, in body
   ink; the facts, a dl of label and value rows spaced like the review rows but with no rule; the
   typed-phrase gate, set apart from the content above, with the phrase in strong weight (the
   package has no code face). */
.db-dialog .db-dialog__details {
    margin: var(--dz-space-1) 0 0;
    padding-left: var(--dz-space-3);
    list-style: disc;
    color: var(--dz-dialog-body-ink);
}

.db-dialog .db-dialog__details > li + li {
    margin-top: var(--dz-space-half);
}

.db-dialog .db-dialog__facts {
    margin: var(--dz-space-1) 0 0;
    padding: 0;
}

.db-dialog .db-dialog__fact {
    display: flex;
    align-items: baseline;
    gap: var(--dz-space-2);
    padding: var(--dz-space-1half) 0;
}

.db-dialog .db-dialog__fact > dt {
    flex: none;
    width: 160px;
    margin: 0;
    color: var(--dz-dialog-meta-ink);
    font-size: 13px;
    font-weight: 400;
}

.db-dialog .db-dialog__fact > dd {
    flex: 1;
    min-width: 0;
    margin: 0;
    color: var(--dz-dialog-title-ink);
    font-size: 14px;
    font-weight: 500;
    overflow-wrap: anywhere;
}

/* A LINKED FACT. A fact given an href shows its value as an anchor in the link tokens, which lift
   to a readable tint in the dark theme (voyager.darkmode.css) so the link clears 4.5:1 on the
   dialog ground in both themes. The weight and size come from the dd. */
.db-dialog .db-dialog__fact > dd > a {
    color: var(--dz-link);
}

.db-dialog .db-dialog__fact > dd > a:hover,
.db-dialog .db-dialog__fact > dd > a:focus {
    color: var(--dz-link-hover);
    text-decoration: underline;
}

.db-dialog .db-dialog__phrase {
    margin-top: var(--dz-space-3);
}

.db-dialog .db-dialog__phrase > label {
    display: block;
    margin-bottom: var(--dz-space-1);
    color: var(--dz-dialog-body-ink);
}

.db-dialog .db-dialog__phrase-value {
    font-weight: 600;
}

/* THE REVIEW LIST. A dl of rows, each a label, its value and an optional Edit back to the step that
   set it, at the row's end. Rows are split by the dialog's rule; a callout or a form row between
   two rows keeps its own paint. A changed value shows the old one struck through in meta ink, then
   the new one. */
.db-dialog .db-dialog__review {
    margin: 0;
    padding: 0;
}

.db-dialog .db-dialog__review-row {
    display: flex;
    align-items: baseline;
    gap: var(--dz-space-2);
    padding: var(--dz-space-1half) 0;
    border-bottom: 1px solid var(--dz-dialog-rule);
}

.db-dialog .db-dialog__review-row > dt {
    flex: none;
    width: 160px;
    margin: 0;
    color: var(--dz-dialog-meta-ink);
    font-size: 13px;
    font-weight: 400;
}

.db-dialog .db-dialog__review-row > dd {
    flex: 1;
    min-width: 0;
    margin: 0;
    color: var(--dz-dialog-title-ink);
    font-size: 14px;
    overflow-wrap: anywhere;
}

.db-dialog .db-dialog__review-was {
    margin-right: var(--dz-space-1);
    color: var(--dz-dialog-meta-ink);
    text-decoration: line-through;
}

/* The Edit link ends its row's value (inside the dd), floated to the row's end. */
.db-dialog .db-dialog__review-edit {
    float: right;
    margin-left: var(--dz-space-2);
    padding: 0;
    border: 0;
    background: transparent;
    color: var(--dz-dialog-title-ink);
    font: inherit;
    font-size: 13px;
    font-weight: 600;
    text-decoration: underline;
    cursor: pointer;
}

.db-dialog .db-dialog__review-edit:focus-visible {
    outline: 2px solid var(--dz-focus-color);
    outline-offset: 2px;
}

/* On a sheet the label sits above its value. */
.db-dialog--sheet .db-dialog__review-row,
.db-dialog--sheet .db-dialog__fact {
    flex-wrap: wrap;
    row-gap: var(--dz-space-half);
}

.db-dialog--sheet .db-dialog__review-row > dt,
.db-dialog--sheet .db-dialog__fact > dt {
    flex: 1 0 100%;
    width: auto;
}

/* THIN, NEUTRAL SCROLLBARS, APP-WIDE (ClickUp 86894yg24, Brad A95).
 *
 * "Thin, neutral: thumb in the border/meta tone, transparent track, darker on hover, light/dark
 * aware, not a tenant setting." The scrollbar is chrome, not a themed surface a reseller repoints --
 * it reads the package's own neutral spine (--dz-border/--dz-meta), the same tokens a control's own
 * resting edge already uses, with no schema key and no compiler emission.
 *
 * TWO MECHANISMS, DELIBERATELY NOT COMBINED ON ONE SELECTOR. The standard `scrollbar-width` /
 * `scrollbar-color` properties have no hover state at all -- the spec defines no part a rule can
 * target -- so "darker on hover" can only be built with the legacy `::-webkit-scrollbar-thumb:hover`
 * pseudo-element, which every Blink/WebKit browser (Chrome, Edge, Safari) still supports alongside
 * the standard properties. MEASURED (Chrome 121+): when BOTH are declared on the same element, Chrome
 * uses the standard properties and the `::-webkit-scrollbar*` rules on that element never run at all
 * -- so declaring both unconditionally would silently drop the hover state in Chrome, the one browser
 * this product is built and tested in (`html.dxChrome`). `@supports not selector(::-webkit-scrollbar)`
 * is true only for a browser with no webkit pseudo-element to lose (Firefox): there, the static
 * standard properties are the only way to theme a scrollbar at all, hover included or not.
 *
 * WL-24 FIX ROUND 1 (ClickUp 86894yg24), PM ruling D3 -- CORRECTS the paragraph this replaces, which
 * claimed "`*` and `html` reach the same elements for an inherited-from-root mechanism". They do not:
 * WebKit's scrollbar pseudo-elements are NOT inherited (there is no property to inherit -- each
 * scrolling box gets its own, independent scrollbar, the way `::before` on a child is never the
 * parent's `::before`). Voyager's layout does not scroll `<html>` at all -- `<main id="app-content">`
 * (Views/Shared/_Layout.cshtml) is the real scroller -- so `html::-webkit-scrollbar*` painted a bar
 * nobody ever saw: MEASURED live, `<html>`'s own scrollbar width was 0 and `#app-content`'s rendered
 * the stock Chrome bar with arrow buttons, `::-webkit-scrollbar-thumb` background `rgba(0,0,0,0)` (no
 * rule matched it at all). `*::-webkit-scrollbar*` below reaches every scrolling box directly, which
 * is the only way these pseudo-elements CAN reach more than one element -- there is no shorthand for
 * "every scroller descended from here" the way a real property has. The standard properties stay on
 * `html` alone: `scrollbar-width`/`scrollbar-color` ARE normal inherited properties (confirmed live,
 * Firefox code path), so one declaration at the root already reaches `#app-content` and everything
 * else without repeating it per element. */
:root {
    --dz-scrollbar-thumb-hover: #797573;
    --dz-scrollbar-track: transparent;
    /* WL-24 FIX ROUND 1, PM ruling D4: --dz-border alone measured under the 3:1 non-text floor a
     * styled scrollbar, as an author-painted control, owes (WCAG 1.4.11) -- 1.81:1 on --dz-card,
     * 1.69:1 on --dz-page in light -- so the RESTING thumb can no longer be --dz-border by itself.
     * Dark already clears 3.41:1 on --dz-card unmixed and is explicitly left alone (PM: "Dark is
     * fine") -- dark gets its own explicit declaration (voyager.darkmode.css) pinning it back to plain
     * --dz-border, and this one is LIGHT'S resting value only.
     *
     * WL-28 FIX ROUND 2 (ClickUp 86894yg24), PM ruling A107, SUPERSEDES A106 below (round 1 repointed
     * rest to --dz-meta and hover to --dz-body so the thumb would follow a tenant Surface through the
     * subhead/body/meta ramp). The adversary found the ramp was the wrong source: WhitelabelCompiler.cs
     * clamps body and meta independently, each only until it first clears 4.5:1, so whenever both fail
     * the same binding ground -- the sunken band, almost always -- they land on the SAME value and the
     * hover step vanishes for a tenant Surface, bodyInk or pageBackground alike
     * (Wl28ScrollbarHoverAdversaryTests measured it at exactly 1.00:1 apart, against A95's "darker on
     * hover"). Worse, meta also carries a tenant's bodyInk HUE, not just its lightness, so a brand-blue
     * bodyInk tinted the RESTING thumb too -- before this round only hover ever took the tenant's hue,
     * and A95 asks for "neutral ... not a tenant setting" (Wl28SwitchAndThumbTenantAdversaryTests).
     *
     * A107: the thumb is NEUTRAL and never reads the text ramp, in either state. Both values here are
     * measured literals chosen to reproduce exactly the PRE-WL-28 untouched rendering: rest is WL-24
     * round 1's own solved value, unchanged; hover is what --dz-meta used to resolve to for an
     * untouched tenant, now frozen rather than read live. Neither moves with a customized Surface,
     * bodyInk or pageBackground any more -- that is deliberate, not a regression of A106's goal: a
     * dedicated, neutral Surface-derived override is a later WhitelabelCompiler.cs item (A107's own
     * ruling), and these literals are exactly what that override is meant to replace, the same way an
     * untouched tenant's CSS default is replaceable everywhere else in this package. */
    --dz-scrollbar-thumb: #8c8886;
}

@supports not selector(::-webkit-scrollbar) {
    html {
        scrollbar-width: thin;
        scrollbar-color: var(--dz-scrollbar-thumb) var(--dz-scrollbar-track);
    }
}

*::-webkit-scrollbar,
*::-webkit-scrollbar-corner {
    width: 10px;
    height: 10px;
    background: transparent;
}

*::-webkit-scrollbar-track {
    background: var(--dz-scrollbar-track);
}

*::-webkit-scrollbar-thumb {
    background-color: var(--dz-scrollbar-thumb);
    border-radius: var(--dz-radius-pill);
    /* A transparent border the same width as the inset below lets the thumb look thinner than the
       10px track without actually narrowing the hit target WCAG 2.5.8 cares about on a touch
       device -- the fill is a padded box, the clickable thumb is the full lane. */
    border: 2px solid transparent;
    background-clip: padding-box;
}

*::-webkit-scrollbar-thumb:hover {
    background-color: var(--dz-scrollbar-thumb-hover);
}

/* DEVEXTREME'S OWN SIMULATED SCROLLBAR (useNative:false), drawn inside the widget rather than as a
 * native browser scrollbar -- a grid wider than its container, a popup's content, the header-filter
 * list -- so neither the standard properties above nor the webkit pseudo-elements above reach it at
 * all; `color-scheme` does not reach it either (22-devextreme-adapter.css's own comment on
 * `.dx-scrollable-scroll-content` says so). 22-devextreme-adapter.css names the THUMB colour off
 * --dz-scrollbar-thumb (the SAME fixed token this file declares, gated behind html[data-db-theme] so
 * light mode gets it too since WL-20a); this adds only the piece A95 asks for that the adapter rule
 * did not: a transparent track and a hover step, so DevExtreme's own bar matches the native one above
 * exactly rather than merely sharing its resting thumb colour. `html[data-db-theme]` (any value), the
 * same gate the adapter rule already uses, so this reaches light and dark alike and nothing here
 * fires on an unthemed host that never sets the attribute.
 *
 * STRUCTURE (MEASURED live, Subscribers/Endpoints lists): `.dx-scrollable-scrollbar` (DevExtreme's
 * OWN hover target -- it carries `dx-scrollbar-hoverable` and is what the widget's JS watches to
 * reveal the bar at all) wraps `.dx-scrollable-scroll` (the track/bar region the adapter's
 * `background-color: var(--dz-scrollbar-thumb)` rule already paints, on its `-content` child) wraps
 * `.dx-scrollable-scroll-content` (the thumb fill). Hovering the OUTER element, not the track, to
 * match the hit region DevExtreme itself treats as "hovering the scrollbar". */
html[data-db-theme] .dx-scrollable-scroll {
    background-color: var(--dz-scrollbar-track);
}

html[data-db-theme] .dx-scrollable-scrollbar:hover .dx-scrollable-scroll-content {
    background-color: var(--dz-scrollbar-thumb-hover);
}

/* THE UNPORTED POPUP (ADO #143). Until each popup is ported to the shared dialog (35-dialog.css),
 * every DevExtreme popup that carries a title paints like the dialog: the same surface, radius,
 * shadow and scrim, a title on the surface set off by a hairline, a quiet close control, and a
 * footer on the page tint. In Voyager the tenant popup-title colour (--dz-popup-title-*) stops
 * painting on these popups: ROOT (0,8,0) out-ranks the four rules in Site.css that paint it (two
 * title rules at up to (0,3,1), two close-icon rules at up to (0,5,1)), which stay for Atlas and Olympus, which
 * link Site.css without this package. Structure stays DevExtreme's; this owns surface only.
 *
 * ROOT, which starts every rule below, is the popup wrapper minus what is not a dialog-shaped popup:
 *   .db-dialog              a ported popup, painted by 35-dialog.css
 *   .dx-popover-wrapper     popovers and tooltips (dx-tooltip-wrapper): anchored callouts
 *   .dx-dropdowneditor-overlay, .dx-datebox-wrapper
 *                           the pop-ups of editors, which have no title bar
 *   .dx-header-filter-menu  the grid's header-filter menu (21-grid-header-filter.css), a titleless
 *                           popup whose own paint must not change. DevExtreme marks its
 *                           dx-dropdowneditor-overlay on the overlay content, not the wrapper, so
 *                           the dropdown-editor exclusion above does not catch it.
 *   .dx-filterbuilder-overlay
 *                           the filter builder's menus (field, operation and group pickers), which
 *                           have no title bar and must not wear dialog chrome.
 * Each class above sits on the wrapper element (dx.web.debug.js: popover, tooltip, dropdown editor and
 * datebox add theirs with $wrapper()/_wrapperClassExternal; the header filter adds its own through
 * wrapperAttr; the filter builder adds dx-filterbuilder-overlay to its menus' popup wrapper).
 *
 * LATENT, NOT EXCLUDED: a DataGrid export menu with more than one format (its
 * _wrapperClassExternal, dx-datagrid-export-menu, replaces dx-dropdowneditor-overlay) and a
 * scheduler view switcher (the same replacement) match ROOT, so they would get the dialog's radius,
 * shadow and font with no chrome. Neither renders in Voyager today; add a :not() for the one that
 * starts to render.
 *
 * TENANT RULES. A tenant custom.css rule on the popup title, such as Covenant's
 * `div.dx-popup-wrapper div.dx-popup-normal div.dx-popup-title` (0,3,3), loses to ROOT on
 * specificity. A tenant `!important` rule does not lose that way: the same files fill the close
 * control on hover (`... .dx-button.dx-closebutton.dx-state-hover { background-color: ... !important }`,
 * (0,6,3)), which would put the title-ink icon on a red or grey square. The close control's
 * background is therefore `!important` here as well, so specificity decides and ROOT's (0,10,0)
 * wins. The stylesheet harness reads the tenant stylesheets and fails when one declares an
 * `!important` close-control property that this file does not declare `!important` too.
 *
 * A popup with no title (showTitle false and no toolbar items) keeps its content padding and has no
 * footer rule: the body and footer rules key on a preceding .dx-popup-title. Its surface, shadow and
 * scrim still match the dialog. A full-screen popup (.dx-popup-fullscreen) fills the viewport, so it
 * keeps square corners and no shadow. */

.dx-popup-wrapper:not(.db-dialog):not(.dx-popover-wrapper):not(.dx-tooltip-wrapper):not(.dx-dropdowneditor-overlay):not(.dx-datebox-wrapper):not(.dx-header-filter-menu):not(.dx-filterbuilder-overlay).dx-overlay-shader {
    background-color: var(--dz-dialog-scrim);
}

.dx-popup-wrapper:not(.db-dialog):not(.dx-popover-wrapper):not(.dx-tooltip-wrapper):not(.dx-dropdowneditor-overlay):not(.dx-datebox-wrapper):not(.dx-header-filter-menu):not(.dx-filterbuilder-overlay) > .dx-overlay-content {
    border: 0;
    border-radius: var(--dz-dialog-radius);
    background-color: var(--dz-dialog-background);
    box-shadow: var(--dz-dialog-shadow);
    color: var(--dz-dialog-title-ink);
    font-family: var(--dz-font-family);
    overflow: hidden;
}

/* A full-screen popup fills the viewport, so rounded corners would show the scrim at its corners and
   a lift shadow has nothing to lift off. (0,10,0), above the surface rule at (0,9,0). */
.dx-popup-wrapper:not(.db-dialog):not(.dx-popover-wrapper):not(.dx-tooltip-wrapper):not(.dx-dropdowneditor-overlay):not(.dx-datebox-wrapper):not(.dx-header-filter-menu):not(.dx-filterbuilder-overlay) > .dx-overlay-content.dx-popup-fullscreen {
    border-radius: 0;
    box-shadow: none;
}

/* Beats dx.light's `.dx-popup-title.dx-toolbar { padding: 6px 20px }` and its `border-bottom: 1px
   solid` hairline and its transparent background. */
.dx-popup-wrapper:not(.db-dialog):not(.dx-popover-wrapper):not(.dx-tooltip-wrapper):not(.dx-dropdowneditor-overlay):not(.dx-datebox-wrapper):not(.dx-header-filter-menu):not(.dx-filterbuilder-overlay) .dx-popup-title {
    padding: var(--dz-space-1half) var(--dz-space-2) calc(var(--dz-space-1half) - 1px) var(--dz-space-3);
    border-bottom: 1px solid var(--dz-dialog-rule);
    background-color: var(--dz-dialog-background);
    color: var(--dz-dialog-title-ink);
}

/* Beats dx.light's `.dx-popup-title.dx-toolbar .dx-toolbar-label { font-size: 20px }` (0,3,0). The
   label's inner div sets no font of its own, so it inherits from here. */
.dx-popup-wrapper:not(.db-dialog):not(.dx-popover-wrapper):not(.dx-tooltip-wrapper):not(.dx-dropdowneditor-overlay):not(.dx-datebox-wrapper):not(.dx-header-filter-menu):not(.dx-filterbuilder-overlay) .dx-popup-title .dx-toolbar-label {
    color: var(--dz-dialog-title-ink);
    font-size: 18px;
    font-weight: 600;
    line-height: 1.3;
}

/* The close control: DevExtreme's own text button, sized and inked like the dialog's cross. */
.dx-popup-wrapper:not(.db-dialog):not(.dx-popover-wrapper):not(.dx-tooltip-wrapper):not(.dx-dropdowneditor-overlay):not(.dx-datebox-wrapper):not(.dx-header-filter-menu):not(.dx-filterbuilder-overlay) .dx-popup-title .dx-closebutton {
    width: 32px;
    height: 32px;
    min-width: 32px;
    margin: 0;
    border-radius: var(--dz-radius-sm);
    /* !important: tenant stylesheets fill this control on hover with an !important rule (see TENANT
       RULES above), and a filled square makes the icon fail 3:1. */
    background-color: transparent !important;
}

.dx-popup-wrapper:not(.db-dialog):not(.dx-popover-wrapper):not(.dx-tooltip-wrapper):not(.dx-dropdowneditor-overlay):not(.dx-datebox-wrapper):not(.dx-header-filter-menu):not(.dx-filterbuilder-overlay) .dx-popup-title .dx-closebutton .dx-icon {
    color: var(--dz-dialog-meta-ink);
}

.dx-popup-wrapper:not(.db-dialog):not(.dx-popover-wrapper):not(.dx-tooltip-wrapper):not(.dx-dropdowneditor-overlay):not(.dx-datebox-wrapper):not(.dx-header-filter-menu):not(.dx-filterbuilder-overlay) .dx-popup-title .dx-closebutton:hover .dx-icon {
    color: var(--dz-dialog-title-ink);
}

.dx-popup-wrapper:not(.db-dialog):not(.dx-popover-wrapper):not(.dx-tooltip-wrapper):not(.dx-dropdowneditor-overlay):not(.dx-datebox-wrapper):not(.dx-header-filter-menu):not(.dx-filterbuilder-overlay) .dx-popup-title .dx-closebutton.dx-state-focused,
.dx-popup-wrapper:not(.db-dialog):not(.dx-popover-wrapper):not(.dx-tooltip-wrapper):not(.dx-dropdowneditor-overlay):not(.dx-datebox-wrapper):not(.dx-header-filter-menu):not(.dx-filterbuilder-overlay) .dx-popup-title .dx-closebutton:focus-visible {
    outline: 2px solid var(--dz-focus-color);
    outline-offset: 0;
}

/* Titled popups only: the content follows its title as a sibling inside .dx-overlay-content, so an
   untitled popup keeps the padding its caller set. .dx-popup-no-padding is the caller's opt-out. */
.dx-popup-wrapper:not(.db-dialog):not(.dx-popover-wrapper):not(.dx-tooltip-wrapper):not(.dx-dropdowneditor-overlay):not(.dx-datebox-wrapper):not(.dx-header-filter-menu):not(.dx-filterbuilder-overlay):not(.dx-popup-no-padding) .dx-popup-title ~ .dx-popup-content {
    padding: var(--dz-space-3);
}

/* A file picker in a titled popup loses the side insets dx.light gives it, as in the dialog's body
   (35-dialog.css, PP-D72 audit row B-C): `.dx-fileuploader-wrapper { padding: 7px }` and the drop zone's
   3px transparent border in `.dx-fileuploader-input-wrapper`. It is scoped as the body rule above is,
   to a titled popup that did not opt out of padding: only there is the picker's edge meant to line up
   with the 24px content band, so a popup that kept its caller's own padding keeps DevExtreme's inset
   too. */
.dx-popup-wrapper:not(.db-dialog):not(.dx-popover-wrapper):not(.dx-tooltip-wrapper):not(.dx-dropdowneditor-overlay):not(.dx-datebox-wrapper):not(.dx-header-filter-menu):not(.dx-filterbuilder-overlay):not(.dx-popup-no-padding) .dx-popup-title ~ .dx-popup-content .dx-fileuploader-wrapper {
    padding-inline: 0;
}

.dx-popup-wrapper:not(.db-dialog):not(.dx-popover-wrapper):not(.dx-tooltip-wrapper):not(.dx-dropdowneditor-overlay):not(.dx-datebox-wrapper):not(.dx-header-filter-menu):not(.dx-filterbuilder-overlay):not(.dx-popup-no-padding) .dx-popup-title ~ .dx-popup-content .dx-fileuploader-input-wrapper {
    border-inline-width: 0;
}

.dx-popup-wrapper:not(.db-dialog):not(.dx-popover-wrapper):not(.dx-tooltip-wrapper):not(.dx-dropdowneditor-overlay):not(.dx-datebox-wrapper):not(.dx-header-filter-menu):not(.dx-filterbuilder-overlay) .dx-popup-title ~ .dx-popup-bottom {
    padding: calc(var(--dz-space-1) - 1px) var(--dz-space-3) var(--dz-space-1);
    border-top: 1px solid var(--dz-dialog-rule);
    background-color: var(--dz-dialog-footer-background);
}

/* THE CODE BLOCK (PP-D34).
 *
 * The block datablaze.ui.codeBlock.json builds (src/codeblock.js): preformatted structured text a viewer
 * reads and never edits. The card ground and the hairline are the dialog's own, so the block reads as
 * a field of the dialog; the token colours were measured on that ground (see the --dz-code-* tokens).
 *
 * `white-space: pre` and overflow auto, not wrapping: JSON is read by its indentation, and a
 * wrapped line hides which level a value belongs to. The block scrolls inside --dz-code-max-height
 * rather than growing the dialog, and the script makes it focusable so the keyboard can scroll it. */

.db-code-block {
    box-sizing: border-box;
    margin: 0;
    padding: var(--dz-space-1) var(--dz-space-1half);
    max-height: var(--dz-code-max-height);
    overflow: auto;
    border: 1px solid var(--dz-hairline);
    border-radius: var(--dz-radius-md);
    background-color: var(--dz-card);
    color: var(--dz-ink);
    font-family: var(--dz-code-font-family);
    font-size: var(--dz-code-font-size);
    line-height: 1.5;
    white-space: pre;
}

/* The block is a tab stop (it scrolls), so it paints focus through the package focus hook: a tenant
   that repoints --dz-focus-color moves this ring with every other focus ring in the product. */
.db-code-block:focus-visible {
    outline: 2px solid var(--dz-focus-color);
}

.db-code-block__key {
    color: var(--dz-code-key-color);
}

.db-code-block__string {
    color: var(--dz-code-string-color);
}

.db-code-block__number {
    color: var(--dz-code-number-color);
}

.db-code-block__literal {
    color: var(--dz-code-literal-color);
}

/* A code block in a dialog body never shrinks to make room. A grid-size dialog on a sheet makes its
   body a flex column (35-dialog.css) so a grid can fill what is left; an overflow:auto item like
   this one has no content-sized minimum there, so under a tall facts list it would shrink to a
   sliver and the body would never scroll. At its own height, up to --dz-code-max-height where it
   scrolls inside itself, the body scrolls past it instead. */
.db-dialog .db-dialog__body > .db-code-block {
    flex: none;
}

/* THE LOGO PLATE (ClickUp 86894yg24, WL-38). A fixed light plate for third-party artwork - a
 * carrier's or vendor's logo - so dark strokes on a transparent PNG stay legible in dark mode and
 * on a dark tenant Surface. The plate and its edge are --dz-logo-plate and --dz-logo-plate-border,
 * which are deliberately NOT restated for dark (see 00-tokens.css). Never put a tenant's own brand
 * logo on this: that one is the tenant's choice.
 *
 * Put the class on the <img> itself. It is border-box, like the Site.css global, so an image given
 * a height keeps its outer size and the artwork shrinks by the padding and border rather than the
 * plate overflowing a fixed-height slot. */
.db-logo-plate {
    box-sizing: border-box;
    background: var(--dz-logo-plate);
    border: 1px solid var(--dz-logo-plate-border);
    border-radius: var(--dz-radius-sm);
    padding: 2px 8px;
    vertical-align: middle;
}

/* SUMMERNOTE (A110, ClickUp 86894yg24, WL-33 -- REVERSES A85 for the dialog button only).
 *
 * The TOS HTML editor (Voyager's TosDocumentEdit.cshtml) is a vendored Summernote instance
 * (Scripts/summernote-bs4.js, Content/summernote-bs4.css). Three other hosts mount the SAME editor:
 * Account > Edit Email Templates (voyager.account.emailtemplate.js), Invoice > Send
 * (voyager.invoice.invoicesend.js) and the Marketplace manager's item description
 * (voyager.marketplace.manager.js) -- every rule below reaches all four, since Voyager is the one
 * host that has opted into this package (COMPONENTS.md) and nothing here is scoped to a TOS-only id.
 *
 * CHROME FOLLOWS SURFACE/BORDER/INK. CONTENT STAYS AS AUTHORED. Nothing here touches `.note-editable`'s
 * own descendant elements or their authored colours: an inline `style="color:..."` the author typed
 * always wins over an ancestor's `color`, the same way any themed page's body text does, so this is
 * additive (a default for UNCOLOURED text) rather than a restyle of what was authored.
 *
 * WL-33 fix round 1 (PM ruling, adversary 2534f8fac, D2/4d) CORRECTS THIS FILE'S OWN FIRST-ROUND CLAIM
 * that "nothing touched Summernote's dark mode before this item". That was wrong: summernote-bs4.js's
 * own renderer BORROWS three Bootstrap card classes --
 *   .note-editor.note-frame.card, .note-toolbar.card-header, .note-editable.card-block
 * (Scripts/summernote-bs4.js's `editor`/`toolbar`/`editable` renderer.create() calls) -- and
 * voyager.darkmode.css already carries `html[data-db-theme="dark"] .card{...}` /
 * `.card-header{...}` rules (NOT `.card-block`, which Bootstrap's own shipped CSS never defines
 * either, so `.note-editable` reaches no card rule at all). Those dark rules are (0,2,1)
 * (html+attr+class), which beat a round-1 bare `.note-editor.note-frame`/`.note-toolbar` rule
 * (0,2,0)/(0,1,0) outright in dark regardless of load order, and TIE a bare `html[data-db-theme]
 * .note-toolbar` rule's (0,2,1) -- a tie that darkmode.css would still win on source order (it loads
 * after this package). The selectors below are deliberately qualified (`.card`/`.card-header` named
 * explicitly) to out-specify both the Bootstrap light rule AND voyager.darkmode.css's dark rule in
 * EVERY case, so this file -- not an accidental coincidence of which class happens to win -- is the
 * one true source for this chrome in both themes. `.note-editable` needed no such qualifier: nothing
 * else ever reaches `.card-block`.
 */

html[data-db-theme] .note-editor.note-frame.card,
html[data-db-theme] .note-editor.note-airframe {
    background-color: var(--dz-surface);
    border-color: var(--dz-border);
}

html[data-db-theme] .note-toolbar.card-header {
    background: var(--dz-sunken);
    border-color: var(--dz-border);
}

html[data-db-theme] .note-editor.note-frame .note-statusbar,
html[data-db-theme] .note-editor.note-airframe .note-statusbar {
    background-color: var(--dz-sunken);
    border-top-color: var(--dz-border);
}

html[data-db-theme] .note-editor .note-editing-area .note-editable {
    background-color: var(--dz-surface);
    color: var(--dz-ink);
}

/* THE TOOLBAR'S OWN GLYPH BUTTONS (bold/italic/link/etc, built `.note-btn.btn.btn-light.btn-sm` --
 * Scripts/summernote-bs4.js's own generic button renderer). The package already has a role for
 * exactly this -- "chrome, not an action with a meaning of its own" -- --dz-button-standard-*, the
 * same tokens an Action/Cancel/Disclosure/Chrome role already resolves (00-tokens.css), so this reuses
 * that scale rather than declaring a parallel one. `.note-btn-group .note-btn` (two classes) already
 * out-specifies bare `.btn-light` (one class) at every matching Bootstrap state below, so no
 * html[data-db-theme] prefix is needed to win -- added anyway for consistency with every other rule in
 * this file. The colour-picker popover/palette and the drag-and-drop overlay are NOT themed here,
 * deliberately out of this item's scope. */
html[data-db-theme] .note-btn-group .note-btn {
    color: var(--dz-button-standard-color);
    background-color: var(--dz-button-standard-background);
    border-color: var(--dz-button-standard-border-color);
}

html[data-db-theme] .note-btn-group .note-btn:hover {
    color: var(--dz-button-standard-hover-color);
    background-color: var(--dz-button-standard-hover-background);
    border-color: var(--dz-button-standard-hover-border-color);
}

/* WL-33 fix round 2 (adversary B4 x16, PM item 3): EVERY Bootstrap btn-light ring/fill state, not just
 * plain :focus. Bootstrap's rules, and what out-ranks each (mine carry the html[data-db-theme] prefix):
 *   .btn-light:focus, .focus                                         (0,2,0)  rgba(216,217,219,.5) ring
 *   .btn-light:not(:disabled):not(.disabled):active[.active]         (0,4,0)  #dae0e5 fill
 *   .btn-light:not(:disabled):not(.disabled):active:focus / .active:focus (0,5,0)  the same grey ring
 *   .show > .btn-light.dropdown-toggle  [:focus]                     (0,3,0)/(0,4,0)  #dae0e5 OPEN fill + ring
 * The pressed-and-focused and toggled-on-and-focused rings were (0,5,0) against round 1's (0,4,1), so the
 * grey ring still showed; they are restated here at (0,6,1) with the package focus ring. The dropdown-OPEN
 * state (a style/colour menu showing) lost its #dae0e5 fill when round 1's rest rule out-ranked it, so open
 * now takes the standard ACTIVE (pressed) treatment -- the cue "this menu is open" survives, on the
 * package's own scale rather than Bootstrap's. */
html[data-db-theme] .note-btn-group .note-btn:focus,
html[data-db-theme] .note-btn-group .note-btn.focus,
html[data-db-theme] .note-btn-group .note-btn:not(:disabled):not(.disabled):active:focus,
html[data-db-theme] .note-btn-group .note-btn:not(:disabled):not(.disabled).active:focus,
html[data-db-theme] .note-btn-group.show > .note-btn.dropdown-toggle:focus {
    box-shadow: 0 0 0 var(--dz-control-focus-width) var(--dz-focus-ring);
}

html[data-db-theme] .note-btn-group .note-btn:not(:disabled):not(.disabled):active,
html[data-db-theme] .note-btn-group .note-btn:not(:disabled):not(.disabled).active,
html[data-db-theme] .note-btn-group.show > .note-btn.dropdown-toggle {
    color: var(--dz-button-standard-hover-color);
    background-color: var(--dz-button-standard-active-background);
    border-color: var(--dz-button-standard-hover-border-color);
}

/* THE INSERT LINK/IMAGE/VIDEO DIALOG SUBMIT BUTTON. A85 (WL-20c) deliberately left bare
 * `.btn-primary` unmapped: Summernote builds this button in JS (Scripts/summernote-bs4.js) as
 * `btn btn-primary note-btn note-btn-primary note-{link,image,video}-btn`, so a GLOBAL `.btn-primary`
 * mapping also repainted Marketplace's own green `.appointments-schedule-btn` and Atlas's map
 * buttons, and Bootstrap's own :hover/:focus/:active rules outranked a flat (0,1,0) mapping's
 * specificity, flipping the button's colour BETWEEN states -- A85's own finding.
 *
 * A110 narrows the target to `.note-btn-primary`, the class ONLY Summernote's own dialog button
 * carries (nothing else in the product), so nothing else `.btn-primary` still reaches moves, and
 * mirrors every one of Bootstrap's own state selectors (Content/bootstrap.css) at matching or
 * greater specificity -- the html[data-db-theme] prefix this file's sibling
 * (22-devextreme-adapter.css) uses throughout to outrank vendor CSS without `!important`:
 *   .note-btn-primary                                              (0,1,0) vs html[data-db-theme] + it (0,2,1)
 *   .note-btn-primary:hover                                        (0,2,0) vs ..................... (0,3,1)
 *   .note-btn-primary:focus, .focus                                 (0,2,0) vs ..................... (0,3,1)
 *   .note-btn-primary.disabled, :disabled                           (0,2,0) vs ..................... (0,3,1)
 *   .note-btn-primary:not(:disabled):not(.disabled):active[.active] (0,4,0) vs ..................... (0,5,1)
 * WL-33 fix round 1 (PM ruling, adversary 2534f8fac, D2) adds the state A85 itself named but round 0
 * missed: PRESSED-AND-FOCUSED. Chromium focuses a button on mousedown, so holding the mouse down is
 * `:active:focus`, and Bootstrap's own rule for it --
 *   .btn-primary:not(:disabled):not(.disabled):active:focus, ...active:focus, .show > ...dropdown-toggle:focus
 *   { box-shadow: 0 0 0 .2rem rgba(38,143,255,.5) }
 * -- is (0,5,0) (5 pseudo-classes-and-classes, no plain element). This narrows to `.note-btn-primary`
 * the same way every other state above does, at (0,6,1), and reuses --dz-button-primary-focus-ring
 * rather than a pressed-specific ring: 07-button-roles.css's own `.dx-state-focused.dx-state-active`
 * rule is the package's existing precedent for "pressed-and-focused keeps the SAME ring as focused
 * alone, only colour/background change" -- it declares no box-shadow of its own, letting the plain
 * `.dx-state-focused` rule's ring persist underneath. No dropdown-toggle variant here either, same
 * reason as above. */
html[data-db-theme] .note-btn-primary {
    color: var(--dz-button-primary-color);
    background-color: var(--dz-button-primary-background);
    border-color: var(--dz-button-primary-border-color);
}

html[data-db-theme] .note-btn-primary:hover {
    color: var(--dz-button-primary-hover-color);
    background-color: var(--dz-button-primary-hover-background);
    border-color: var(--dz-button-primary-hover-border-color);
}

html[data-db-theme] .note-btn-primary:focus,
html[data-db-theme] .note-btn-primary.focus {
    box-shadow: 0 0 0 var(--dz-control-focus-width) var(--dz-button-primary-focus-ring);
}

html[data-db-theme] .note-btn-primary.disabled,
html[data-db-theme] .note-btn-primary:disabled {
    color: var(--dz-button-primary-color);
    background-color: var(--dz-button-primary-background);
    border-color: var(--dz-button-primary-border-color);
}

html[data-db-theme] .note-btn-primary:not(:disabled):not(.disabled):active,
html[data-db-theme] .note-btn-primary:not(:disabled):not(.disabled).active {
    color: var(--dz-button-primary-active-color);
    background-color: var(--dz-button-primary-active-background);
    border-color: var(--dz-button-primary-active-background);
}

html[data-db-theme] .note-btn-primary:not(:disabled):not(.disabled):active:focus,
html[data-db-theme] .note-btn-primary:not(:disabled):not(.disabled).active:focus {
    box-shadow: 0 0 0 var(--dz-control-focus-width) var(--dz-button-primary-focus-ring);
}

/* THE COACHMARK (src/coachmark.js).
 *
 * A non-modal fly-out over a DevExtreme popover. Its wrapper wears .db-coachmark, so every rule
 * below starts there and a popover that is not a coachmark keeps its paint. The surface, ink, edge,
 * shadow and width are the --dz-coachmark-* hooks in 00-tokens.css, which a tenant can repoint.
 *
 * html[data-db-theme] raises a rule over dx.light's popover frame, `.dx-popover-wrapper
 * .dx-overlay-content` (white ground, #ddd edge), the same way the generic popover arrow in
 * 22-devextreme-adapter.css does. The arrow is restated here because that rule fills it from
 * --dz-card, and a coachmark's ground is its own hook. */

html[data-db-theme] .db-coachmark .dx-overlay-content {
    max-width: var(--dz-coachmark-max-width);
    border: var(--dz-coachmark-edge) solid var(--dz-coachmark-border);
    background-color: var(--dz-coachmark-surface);
    color: var(--dz-coachmark-ink);
    border-radius: var(--dz-dialog-radius);
    box-shadow: var(--dz-coachmark-shadow);
}

html[data-db-theme] .db-coachmark .dx-popup-content {
    padding: var(--dz-space-2);
    border-radius: inherit;
}

html[data-db-theme] .db-coachmark.dx-popover-wrapper .dx-popover-arrow::after {
    background: var(--dz-coachmark-surface);
    border-color: var(--dz-coachmark-border);
}

.db-coachmark .db-coachmark__step {
    margin: 0 0 var(--dz-space-half);
    color: var(--dz-coachmark-meta-ink);
    font-size: 12px;
    line-height: 1.3;
}

.db-coachmark .db-coachmark__title {
    margin: 0 0 var(--dz-space-half);
    color: var(--dz-coachmark-ink);
    font-size: 14px;
    font-weight: 600;
    line-height: 1.3;
}

.db-coachmark .db-coachmark__message {
    margin: 0;
    color: var(--dz-coachmark-ink);
    font-size: 13px;
    line-height: 1.45;
}

/* A coachmark with media is exactly --dz-coachmark-max-width wide, so the fly-out is the same size
 * before and after the video loads and never resizes. (A coachmark without media shrinks to its text.) */
html[data-db-theme] .db-coachmark.db-coachmark--media .dx-overlay-content {
    width: var(--dz-coachmark-max-width);
}

/* The frame fills the fly-out's content box: the fly-out's width less the popup content's padding on
 * both sides (--dz-space-2) and the fly-out's own edge on both sides (--dz-coachmark-edge). The frame is
 * border-box, so its own edge is inside that width. */
.db-coachmark .db-coachmark__media {
    display: block;
    box-sizing: border-box;
    width: calc(var(--dz-coachmark-max-width) - 2 * var(--dz-space-2) - 2 * var(--dz-coachmark-edge));
    aspect-ratio: var(--dz-coachmark-media-aspect);
    border: var(--dz-coachmark-edge) solid var(--dz-coachmark-border);
    border-radius: var(--dz-coachmark-media-radius);
    overflow: hidden;
    margin-top: var(--dz-space-1);
}

.db-coachmark .db-coachmark__media video,
.db-coachmark .db-coachmark__media img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.db-coachmark .db-coachmark__actions {
    display: flex;
    justify-content: flex-end;
    gap: var(--dz-space-1);
    margin-top: var(--dz-space-1half);
}
